Compare commits
73 Commits
franknoiro
...
v0.24.11
Author | SHA1 | Date | |
---|---|---|---|
e099c95c5f | |||
f23bc673aa | |||
b60c1e874d | |||
5857684ebc | |||
e8fc6bc037 | |||
5bdd090119 | |||
669cab8737 | |||
f1ea60d6ab | |||
3faec650b1 | |||
b2b62ec163 | |||
5b798c2aa3 | |||
a23bd1f034 | |||
4d00dddfd8 | |||
f055acb6a6 | |||
bf9d88e9a5 | |||
712a3790e8 | |||
f1ab8602a2 | |||
a1f72b1d5a | |||
f9048b8882 | |||
4b0f83d3ac | |||
62c27e0809 | |||
6a09bbc0ef | |||
997f60e1e5 | |||
6f1d7138c0 | |||
3dabab2c74 | |||
13986fcfd7 | |||
d1e21d673e | |||
ca3a88b4df | |||
fb57df2cad | |||
57a91cdb26 | |||
33079b4151 | |||
a75157573b | |||
e29345fbf6 | |||
35c7183809 | |||
b9fe3ed9e0 | |||
5a25b60485 | |||
4b9f71c994 | |||
e86a5622c8 | |||
a503d1ce50 | |||
11a94cc99e | |||
e295f82495 | |||
0b9cf2dc21 | |||
eb58507e93 | |||
ca28a5f549 | |||
6f4bbdb79e | |||
6773dbe7ff | |||
acfc2b47fa | |||
9dc7ff9797 | |||
308a0fb06e | |||
214ae6f512 | |||
8d54fec589 | |||
4bfbecd3e7 | |||
dff3848a00 | |||
f875efab1b | |||
3f082c8222 | |||
e1c45bdb33 | |||
5cb5dbd689 | |||
0a8881bc69 | |||
be9438160e | |||
77b565f781 | |||
c843dfad95 | |||
a3ff0a45eb | |||
4617ad0fed | |||
5fa51a2f92 | |||
4218777afb | |||
8b1b462e29 | |||
2bc99ba39b | |||
ffe0da6dcd | |||
d27afb8c74 | |||
1c778bf373 | |||
5df8a943a9 | |||
ab729dbcdb | |||
84865eaed0 |
12
.github/workflows/ci.yml
vendored
@ -89,16 +89,20 @@ jobs:
|
|||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
|
|
||||||
- run: yarn simpleserver:ci
|
- run: yarn simpleserver:ci
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
|
||||||
- name: Install Chromium Browser
|
- name: Install Chromium Browser
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: yarn playwright install chromium --with-deps
|
run: yarn playwright install chromium --with-deps
|
||||||
|
|
||||||
- name: run unit tests
|
- name: run unit tests
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: yarn test:nowatch
|
run: yarn test:nowatch
|
||||||
env:
|
env:
|
||||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
|
||||||
- name: check for changes
|
- name: check for changes
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
id: git-check
|
id: git-check
|
||||||
run: |
|
run: |
|
||||||
git add src/lang/std/artifactMapGraphs
|
git add src/lang/std/artifactMapGraphs
|
||||||
@ -107,7 +111,7 @@ jobs:
|
|||||||
else echo "modified=false" >> $GITHUB_OUTPUT
|
else echo "modified=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
- name: Commit changes, if any
|
- name: Commit changes, if any
|
||||||
if: steps.git-check.outputs.modified == 'true'
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git config --local user.name "github-actions[bot]"
|
git config --local user.name "github-actions[bot]"
|
||||||
@ -531,7 +535,7 @@ jobs:
|
|||||||
project_id: kittycadapi
|
project_id: kittycadapi
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: artifact
|
path: artifact
|
||||||
glob: '*/Zoo*'
|
glob: '*/Zoo*'
|
||||||
@ -539,13 +543,13 @@ jobs:
|
|||||||
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: last_update.json
|
path: last_update.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
- name: Upload download endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: last_download.json
|
path: last_download.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
12
.github/workflows/playwright.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
|
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -112,7 +112,7 @@ jobs:
|
|||||||
run: yarn build:local
|
run: yarn build:local
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
run: |
|
run: |
|
||||||
yarn playwright test --project="Google Chrome" --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
@ -171,7 +171,7 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="Google Chrome" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
@ -186,7 +186,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="Google Chrome" --last-failed --grep-invert=@snapshot || true
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
@ -325,7 +325,7 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="webkit" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
yarn playwright test --project="webkit" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
@ -340,7 +340,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="webkit" --last-failed --grep-invert=@snapshot || true
|
yarn playwright test --project="webkit" --config=playwright.ci.config.ts --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
|
11
README.md
@ -110,6 +110,17 @@ Note that these became separate apps on Macos, so make sure you open the right o
|
|||||||
|
|
||||||
<img width="1232" alt="image (1)" src="https://user-images.githubusercontent.com/29681384/211947073-e76b4933-bef5-4636-bc4d-e930ac8e290f.png">
|
<img width="1232" alt="image (1)" src="https://user-images.githubusercontent.com/29681384/211947073-e76b4933-bef5-4636-bc4d-e930ac8e290f.png">
|
||||||
|
|
||||||
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
|
Which commands from setup are one off vs need to be run every time?
|
||||||
|
|
||||||
|
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
|
||||||
|
yarn start # or yarn build:local && yarn serve for slower but more production-like build
|
||||||
|
```
|
||||||
|
|
||||||
## Before submitting a PR
|
## Before submitting a PR
|
||||||
|
|
||||||
Before you submit a contribution PR to this repo, please ensure that:
|
Before you submit a contribution PR to this repo, please ensure that:
|
||||||
|
@ -25,5 +25,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
|
|
||||||
Sketching on the chamfered face does not currently work.
|
Sketching on the chamfered face does not currently work.
|
||||||
|
|
||||||
- **Shell**: Shell is only working for `end` faces, not for `side` or `start`
|
- **Shell**: Shell sometimes does not work when arcs or fillets are involved.
|
||||||
faces. We are tracking the engine side bug on this.
|
We are tracking the engine side bug on this.
|
||||||
|
37
docs/kcl/assertEqual.md
Normal file
@ -21,6 +21,7 @@ layout: manual
|
|||||||
* [`arc`](kcl/arc)
|
* [`arc`](kcl/arc)
|
||||||
* [`asin`](kcl/asin)
|
* [`asin`](kcl/asin)
|
||||||
* [`assert`](kcl/assert)
|
* [`assert`](kcl/assert)
|
||||||
|
* [`assertEqual`](kcl/assertEqual)
|
||||||
* [`assertGreaterThan`](kcl/assertGreaterThan)
|
* [`assertGreaterThan`](kcl/assertGreaterThan)
|
||||||
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
|
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
|
||||||
* [`assertLessThan`](kcl/assertLessThan)
|
* [`assertLessThan`](kcl/assertLessThan)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legAngX"
|
title: "legAngX"
|
||||||
excerpt: "Returns the angle of the given leg for x."
|
excerpt: "Compute the angle of the given leg for x."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the angle of the given leg for x.
|
Compute the angle of the given leg for x.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legAngY"
|
title: "legAngY"
|
||||||
excerpt: "Returns the angle of the given leg for y."
|
excerpt: "Compute the angle of the given leg for y."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the angle of the given leg for y.
|
Compute the angle of the given leg for y.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legLen"
|
title: "legLen"
|
||||||
excerpt: "Returns the length of the given leg."
|
excerpt: "Compute the length of the given leg."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the length of the given leg.
|
Compute the length of the given leg.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
3734
docs/kcl/std.json
153
e2e/playwright/basic-sketch.spec.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { test, expect, Page } from '@playwright/test'
|
||||||
|
import {
|
||||||
|
getUtils,
|
||||||
|
TEST_COLORS,
|
||||||
|
setup,
|
||||||
|
tearDown,
|
||||||
|
commonPoints,
|
||||||
|
PERSIST_MODELING_CONTEXT,
|
||||||
|
} from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.setTimeout(120000)
|
||||||
|
|
||||||
|
async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
// If we have the code pane open, we should see the code.
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator).toHaveText(``)
|
||||||
|
} else {
|
||||||
|
// Ensure we don't see the code.
|
||||||
|
await expect(u.codeLocator).not.toBeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)`)
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)`)
|
||||||
|
} else {
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
}
|
||||||
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|
|> line([-${commonPoints.num2}, 0], %)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deselect line tool
|
||||||
|
await page.getByRole('button', { name: 'Line', exact: true }).click()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.WHITE)).toBeLessThan(3)
|
||||||
|
await expect(
|
||||||
|
await u.getGreatestPixDiff(line1, [249, 249, 249])
|
||||||
|
).toBeLessThan(3)
|
||||||
|
}
|
||||||
|
// click between first two clicks to get center of the line
|
||||||
|
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||||
|
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hold down shift
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
// click between the latest two clicks to get center of the line
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
|
||||||
|
|
||||||
|
// selected two lines therefore there should be two cursors
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Length: open menu' }).click()
|
||||||
|
await page.getByRole('button', { name: 'Equal Length' }).click()
|
||||||
|
|
||||||
|
// Open the code pane.
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await expect(u.codeLocator).toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %, $seg01)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|
|> angledLine([180, segLen(seg01)], %)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Basic sketch', () => {
|
||||||
|
test('code pane open at start', async ({ page }) => {
|
||||||
|
await doBasicSketch(page, ['code'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('code pane closed at start', async ({ page }) => {
|
||||||
|
// Load the app with the code panes
|
||||||
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
persistModelingContext,
|
||||||
|
JSON.stringify({ openPanes: [] })
|
||||||
|
)
|
||||||
|
}, PERSIST_MODELING_CONTEXT)
|
||||||
|
await doBasicSketch(page, [])
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,111 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Can create sketches on all planes and their back sides', () => {
|
||||||
|
const sketchOnPlaneAndBackSideTest = async (
|
||||||
|
page: any,
|
||||||
|
plane: string,
|
||||||
|
clickCoords: { x: number; y: number }
|
||||||
|
) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
const coord =
|
||||||
|
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
|
||||||
|
const camCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
vantage: { x: coord, y: coord, z: coord },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const updateCamCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = `const sketch001 = startSketchOn('${plane}')
|
||||||
|
|> startProfileAt([0.9, -1.22], %)`
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
await u.sendCustomCmd(camCommand)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd(updateCamCommand)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||||
|
await page.waitForTimeout(300) // wait for animation
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Line', exact: true })
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// draw a line
|
||||||
|
const startXPx = 600
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Line', exact: true }).click()
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.removeCurrentCode()
|
||||||
|
}
|
||||||
|
test('XY', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(
|
||||||
|
page,
|
||||||
|
'XY',
|
||||||
|
{ x: 600, y: 388 } // red plane
|
||||||
|
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('YZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('XZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-XY', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-YZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-XZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||||
|
})
|
||||||
|
})
|
219
e2e/playwright/code-pane-and-errors.spec.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { bracket } from 'lib/exampleKcl'
|
||||||
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Code pane and errors', () => {
|
||||||
|
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, bracket)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Ensure no badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||||
|
|
||||||
|
// Delete a character to break the KCL
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await page.getByText('extrude(').click()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, bracket)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 900 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Ensure no badge is present
|
||||||
|
const codePaneButton = page.getByRole('button', { name: 'KCL Code pane' })
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||||
|
|
||||||
|
// Delete a character to break the KCL
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await page.getByText('extrude(').click()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Close the code pane
|
||||||
|
await codePaneButton.click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
// Ensure we have no errors in the gutter.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Open the code pane
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Ensure badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter, since error out of view.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Click the badge.
|
||||||
|
const badge = page.locator('#code-badge')
|
||||||
|
await expect(badge).toBeVisible()
|
||||||
|
await badge.click()
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the error to see the error message
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText(
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Ensure badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter, since error out of view.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// click in the editor to focus it
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// go to the start of the editor and enter more text which will trigger
|
||||||
|
// a lint error.
|
||||||
|
// GO to the start of the editor.
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.type('const foo_bar = 1')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// ensure we have a lint error
|
||||||
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Click the badge.
|
||||||
|
const badge = page.locator('#code-badge')
|
||||||
|
await expect(badge).toBeVisible()
|
||||||
|
await badge.click()
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the error to see the error message
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText(
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
364
e2e/playwright/command-bar-tests.spec.ts
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Command bar tests', () => {
|
||||||
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> xLine(-20, %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click the line of code for xLine.
|
||||||
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Fillet from command bar', async ({ page }) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-5, -5], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(-10, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const selectSegment = () => page.getByText(`line([0, -10], %)`).click()
|
||||||
|
|
||||||
|
await selectSegment()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByRole('button', { name: 'Fillet' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||||
|
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
const commandName = 'debug panel'
|
||||||
|
const commandOption = page.getByRole('option', {
|
||||||
|
name: commandName,
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
const commandLevelArgButton = page.getByRole('button', { name: 'level' })
|
||||||
|
const commandThemeArgButton = page.getByRole('button', { name: 'value' })
|
||||||
|
const paneSelector = page.getByRole('button', { name: 'debug panel' })
|
||||||
|
// This selector changes after we set the setting
|
||||||
|
let commandOptionInput = page.getByPlaceholder('On')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
// First try opening the command bar and closing it
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Commands', exact: false })
|
||||||
|
.or(page.getByRole('button', { name: '⌘K' }))
|
||||||
|
.click()
|
||||||
|
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
|
// Try typing in the command bar
|
||||||
|
await cmdSearchBar.fill(commandName)
|
||||||
|
await expect(commandOption).toBeVisible()
|
||||||
|
await commandOption.click()
|
||||||
|
const toggleInput = page.getByPlaceholder('On')
|
||||||
|
await expect(toggleInput).toBeVisible()
|
||||||
|
await expect(toggleInput).toBeFocused()
|
||||||
|
// Select On
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Set show debug panel to "false" for this project`)
|
||||||
|
).toBeVisible()
|
||||||
|
// Check that the visibility changed
|
||||||
|
await expect(paneSelector).not.toBeVisible()
|
||||||
|
|
||||||
|
commandOptionInput = page.getByPlaceholder('off')
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||||
|
await commandBarButton.click()
|
||||||
|
await cmdSearchBar.focus()
|
||||||
|
await cmdSearchBar.fill(commandName)
|
||||||
|
await commandOption.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await commandOptionInput.focus()
|
||||||
|
await commandOptionInput.fill('on')
|
||||||
|
await commandLevelArgButton.click()
|
||||||
|
await expect(commandLevelArgButton).toBeDisabled()
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2881
|
||||||
|
await commandThemeArgButton.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await expect(commandLevelArgButton).toHaveText('level: project')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
// Put the cursor in the code editor
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
|
||||||
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
|
// Try typing in the command bar
|
||||||
|
await cmdSearchBar.fill('theme')
|
||||||
|
const themeOption = page.getByRole('option', {
|
||||||
|
name: 'Settings · app · theme',
|
||||||
|
})
|
||||||
|
await expect(themeOption).toBeVisible()
|
||||||
|
await themeOption.click()
|
||||||
|
const themeInput = page.getByPlaceholder('dark')
|
||||||
|
await expect(themeInput).toBeVisible()
|
||||||
|
await expect(themeInput).toBeFocused()
|
||||||
|
// Select dark theme
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Set theme to "system" as a user default`)
|
||||||
|
).toBeVisible()
|
||||||
|
// Check that the theme changed
|
||||||
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can extrude from the command bar', async ({ page }) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const distance = sqrt(20)
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-6.95, 10.98], %)
|
||||||
|
|> line([25.1, 0.41], %)
|
||||||
|
|> line([0.73, -20.93], %)
|
||||||
|
|> line([-23.44, 0.52], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// Make sure the stream is up
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
||||||
|
|
||||||
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
|
// Search for extrude command and choose it
|
||||||
|
await page.getByRole('option', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
// Assert that we're on the selection step
|
||||||
|
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||||
|
// Select a face
|
||||||
|
await page.mouse.move(700, 200)
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
// Assert that we're on the distance step
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
|
).toBeDisabled()
|
||||||
|
|
||||||
|
// Assert that the an alternative variable name is chosen,
|
||||||
|
// since the default variable name is already in use (distance)
|
||||||
|
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||||
|
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||||
|
'distance001'
|
||||||
|
)
|
||||||
|
|
||||||
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||||
|
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
||||||
|
await continueButton.click()
|
||||||
|
|
||||||
|
// Review step and argument hotkeys
|
||||||
|
await expect(submitButton).toBeEnabled()
|
||||||
|
await expect(submitButton).toBeFocused()
|
||||||
|
await submitButton.press('Backspace')
|
||||||
|
|
||||||
|
// Assert we're back on the distance step
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
|
).toBeDisabled()
|
||||||
|
|
||||||
|
await continueButton.click()
|
||||||
|
await submitButton.click()
|
||||||
|
|
||||||
|
// Check that the code was updated
|
||||||
|
await u.waitForCmdReceive('extrude')
|
||||||
|
// Unfortunately this indentation seems to matter for the test
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const distance = sqrt(20)
|
||||||
|
const distance001 = ${KCL_DEFAULT_LENGTH}
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-6.95, 10.98], %)
|
||||||
|
|> line([25.1, 0.41], %)
|
||||||
|
|> line([0.73, -20.93], %)
|
||||||
|
|> line([-23.44, 0.52], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(distance001, sketch001)`.replace(
|
||||||
|
/(\r\n|\n|\r)/gm,
|
||||||
|
''
|
||||||
|
) // remove newlines
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can switch between sketch tools via command bar', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const rectangleToolCommand = page.getByRole('option', {
|
||||||
|
name: 'rectangle',
|
||||||
|
})
|
||||||
|
const rectangleToolButton = page.getByRole('button', {
|
||||||
|
name: 'Corner rectangle',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
const lineToolCommand = page.getByRole('option', {
|
||||||
|
name: 'Line',
|
||||||
|
})
|
||||||
|
const lineToolButton = page.getByRole('button', {
|
||||||
|
name: 'Line',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' })
|
||||||
|
const arcToolButton = page.getByRole('button', {
|
||||||
|
name: 'Tangential Arc',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start a sketch
|
||||||
|
await sketchButton.click()
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
// Switch between sketch tools via the command bar
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await rectangleToolCommand.click()
|
||||||
|
await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await lineToolCommand.click()
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// Click in the scene a couple times to draw a line
|
||||||
|
// so tangential arc is valid
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.mouse.move(700, 300, { steps: 5 })
|
||||||
|
await page.mouse.click(700, 300)
|
||||||
|
|
||||||
|
// switch to tangential arc via command bar
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await arcToolCommand.click()
|
||||||
|
await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
})
|
||||||
|
})
|
519
e2e/playwright/copilot-ghost-test.spec.ts
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
test.describe('Copilot ghost text', () => {
|
||||||
|
test.skip(true, 'Needs to get covered again')
|
||||||
|
|
||||||
|
test('completes code in empty file', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hit enter a few times.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test.skip('copilot disabled in sketch mode no select plane', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Click sketch mode.
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Exit sketch mode.
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('copilot disabled in sketch mode after selecting plane', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Click sketch mode.
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Escape to exit the tool.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Escape again to exit sketch mode.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hit enter a few times.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Enter in code scoots it down', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await page.keyboard.type('{thing: "blah"}', { delay: 0 })
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
|
|
||||||
|
// We wanna make sure the code saves.
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
// Ctrl+z
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Ctrl+shift+z
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
|
|
||||||
|
// We wanna make sure the code saves.
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`{thing: "blah"}fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Once for the enter.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
// Once for the text.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
// TODO when we make codemirror a widget, we can test this.
|
||||||
|
//await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('backspace in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('focus outside code pane rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going outside the editor should hide the ghost text.
|
||||||
|
await page.mouse.move(0, 0)
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
.waitFor({ state: 'visible' })
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
})
|
867
e2e/playwright/editor-tests.spec.ts
Normal file
@ -0,0 +1,867 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Editor tests', () => {
|
||||||
|
test('can comment out code with ctrl+/', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('/')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
// |> close(%)`)
|
||||||
|
|
||||||
|
// uncomment the code
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('/')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you click the format button it formats your code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
await page.locator('#code-pane button:first-child').click()
|
||||||
|
await page.locator('button:has-text("Format code")').click()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('fold gutters work', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
const fullCode = `const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
||||||
|
// up, its an annoying codemirror thing.
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
const foldGutterFoldLine = page.locator('[title="Fold line"]')
|
||||||
|
const foldGutterUnfoldLine = page.locator('[title="Unfold line"]')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(fullCode)
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Make sure we have a fold gutter
|
||||||
|
await expect(foldGutterFoldLine).toBeVisible()
|
||||||
|
await expect(foldGutterUnfoldLine).not.toBeVisible()
|
||||||
|
|
||||||
|
// Collapse the code
|
||||||
|
await foldGutterFoldLine.click()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XY')… `
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
|
||||||
|
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||||
|
await expect(foldGutterUnfoldLine.nth(1)).toBeVisible()
|
||||||
|
|
||||||
|
// Expand the code
|
||||||
|
await foldGutterUnfoldLine.nth(1).click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(fullCode)
|
||||||
|
|
||||||
|
// Delete all the code.
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
// Select all
|
||||||
|
await page.keyboard.press('Control+A')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.keyboard.press('Meta+A')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
|
||||||
|
|
||||||
|
await expect(foldGutterUnfoldLine).not.toBeVisible()
|
||||||
|
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hover over functions shows function description', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// focus the editor
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
// Hover over the startSketchOn function
|
||||||
|
await page.getByText('startSketchOn').hover()
|
||||||
|
await expect(page.locator('.hover-tooltip')).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText(
|
||||||
|
'Start a new 2-dimensional sketch on a specific plane or face'
|
||||||
|
)
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the line function
|
||||||
|
await page.getByText('line').first().hover()
|
||||||
|
await expect(page.locator('.hover-tooltip')).toBeVisible()
|
||||||
|
await expect(page.getByText('Draw a line')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you use the format keyboard binding it formats your code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
localStorage.setItem('disableAxis', 'true')
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// focus the editor
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
// Hit alt+shift+f to format the code
|
||||||
|
await page.keyboard.press('Alt+Shift+KeyF')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const my_snake_case_var = 5')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const myCamelCaseVar = 5')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// press arrows to clear autocomplete
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-info')
|
||||||
|
await expect(
|
||||||
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// select the line that's causing the error and delete it
|
||||||
|
await page.getByText('const my_snake_case_var = 5').click()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// wait for .cm-lint-marker-info not to be visible
|
||||||
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you fixup kcl errors you clear lints', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
|
|> line([2.48, 2.44], %)
|
||||||
|
|> line([2.66, 1.17], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
await page.getByText(' |> line([2.48, 2.44], %)').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker-error').first()
|
||||||
|
).not.toBeVisible()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
await page.keyboard.type(')')
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker-error').first()
|
||||||
|
).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
/* add the following code to the editor ($ error is not a valid line)
|
||||||
|
$ error
|
||||||
|
const topAng = 30
|
||||||
|
const bottomAng = 25
|
||||||
|
*/
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('$ error')
|
||||||
|
|
||||||
|
// press arrows to clear autocomplete
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const topAng = 30')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const bottomAng = 25')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
|
// select the line that's causing the error and delete it
|
||||||
|
await page.getByText('$ error').click()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// wait for .cm-lint-marker-error not to be visible
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// let's check we get an error when defining the same variable twice
|
||||||
|
await page.getByText('const bottomAng = 25').click()
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type("// Let's define the same thing twice")
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const topAng = 42')
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker.cm-lint-marker-error')
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
await page.locator('.cm-lint-marker.cm-lint-marker-error').hover()
|
||||||
|
await expect(page.locator('.cm-diagnosticText').first()).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText('Cannot redefine `topAng`').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
const secondTopAng = page.getByText('topAng').first()
|
||||||
|
await secondTopAng?.dblclick()
|
||||||
|
await page.keyboard.type('otherAng')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const length = .750
|
||||||
|
const width = 0.500
|
||||||
|
const height = 0.500
|
||||||
|
const dia = 4
|
||||||
|
|
||||||
|
fn squareHole = (l, w) => {
|
||||||
|
const squareHoleSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-width / 2, -length / 2], %)
|
||||||
|
|> lineTo([width / 2, -length / 2], %)
|
||||||
|
|> lineTo([width / 2, length / 2], %)
|
||||||
|
|> lineTo([-width / 2, length / 2], %)
|
||||||
|
|> close(%)
|
||||||
|
return squareHoleSketch
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Click on the bottom of the code editor to add a new line
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|
||||||
|
|> circle([0, 0], dia/2, %)
|
||||||
|
|> hole(squareHole(length, width, height), %)
|
||||||
|
|> extrude(height, %)`)
|
||||||
|
|
||||||
|
// error in gutter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
await page.hover('.cm-lint-marker-error:first-child')
|
||||||
|
await expect(
|
||||||
|
page.getByText('Expected 2 arguments, got 3').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// Make sure there are two diagnostics
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||||
|
})
|
||||||
|
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const box = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %, $revolveAxis)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(10, %)
|
||||||
|
|
||||||
|
const sketch001 = startSketchOn(box, revolveAxis)
|
||||||
|
|> startProfileAt([5, 10], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> line([2, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> close(%)
|
||||||
|
|> revolve({
|
||||||
|
axis: revolveAxis,
|
||||||
|
angle: 90
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const searchText =
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
await expect(page.getByText(searchText)).toBeVisible()
|
||||||
|
})
|
||||||
|
test.describe('Autocomplete works', () => {
|
||||||
|
test('with enter/click to accept the completion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// this test might be brittle as we add and remove functions
|
||||||
|
// but should also be easy to update.
|
||||||
|
// tests clicking on an option, selection the first option
|
||||||
|
// and arrowing down to an option
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const sketch001 = start')
|
||||||
|
|
||||||
|
// expect there to be six auto complete options
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toHaveCount(8)
|
||||||
|
// this makes sure we can accept a completion with click
|
||||||
|
await page.getByText('startSketchOn').click()
|
||||||
|
await page.keyboard.type("'XZ'")
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(' |> startProfi')
|
||||||
|
// expect there be a single auto complete option that we can just hit enter on
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
||||||
|
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type('12')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type(' |> lin')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
// press arrow down twice then enter to accept xLine
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
// finish line with comment
|
||||||
|
await page.keyboard.type('5')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
|
||||||
|
await page.keyboard.type(' // ')
|
||||||
|
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
|
||||||
|
await page.waitForTimeout(700)
|
||||||
|
await page.keyboard.type('lin ')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||||
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.14, 12], %)
|
||||||
|
|> xLine(5, %) // lin`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with tab to accept the completion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// this test might be brittle as we add and remove functions
|
||||||
|
// but should also be easy to update.
|
||||||
|
// tests clicking on an option, selection the first option
|
||||||
|
// and arrowing down to an option
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const sketch001 = startSketchO')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// Make sure just hitting tab will take the only one left
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toHaveCount(1)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.type("'XZ'")
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(' |> startProfi')
|
||||||
|
// expect there be a single auto complete option that we can just hit enter on
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab') // accepting the auto complete, not a new line
|
||||||
|
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.type('12')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type(' |> lin')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
// press arrow down twice then tab to accept xLine
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
// finish line with comment
|
||||||
|
await page.keyboard.type('5')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
|
||||||
|
await page.keyboard.type(' // ')
|
||||||
|
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
|
||||||
|
await page.waitForTimeout(700)
|
||||||
|
await page.keyboard.type('lin ')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||||
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.14, 12], %)
|
||||||
|
|> xLine(5, %) // lin`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const startPX = [665, 458]
|
||||||
|
|
||||||
|
const dragPX = 40
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// expect the code to have changed
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)const extrude001 = extrude(5, sketch001)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now hit undo
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
|
||||||
|
// please fix together
|
||||||
|
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const startPX = [665, 458]
|
||||||
|
|
||||||
|
const dragPX = 40
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(400)
|
||||||
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
|
|
||||||
|
// drag startProfieAt handle
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||||
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// drag line handle
|
||||||
|
// we wait so it saves the code
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||||
|
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||||
|
})
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// we wait so it saves the code
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
// drag tangentialArcTo handle
|
||||||
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||||
|
targetPosition: {
|
||||||
|
x: tangentEnd.x + dragPX,
|
||||||
|
y: tangentEnd.y + dragPX,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
|
||||||
|
// expect the code to have changed
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([15.4, -2.74], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> line([2.65, -2.69], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([15.4, -2.74], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo again.
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo again.
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
})
|
||||||
|
})
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 49 KiB |