Compare commits
56 Commits
franknoiro
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
cf4655ff6a | |||
e338c39eab | |||
d1d98897dc | |||
98b4aaea84 | |||
1faad75462 | |||
496a6c2768 | |||
5bb1bf520e | |||
a5377f50ff | |||
2fffa7e6d8 | |||
98c50eacc5 | |||
8f5c8447ea | |||
ded9f9b89f | |||
086bf9b736 | |||
129e07f059 | |||
137e528944 | |||
b9a07c9393 | |||
f18f975e1c | |||
6cd1af23e6 | |||
cc247ace0e | |||
68bcee9298 | |||
c2500c39e6 | |||
38016e0137 | |||
b263ef049a | |||
1bde0e7333 | |||
2301cc153a | |||
545332ee27 | |||
725e207bd1 | |||
75819f880e | |||
3c1c1303e1 | |||
60e62d435e | |||
78616f4074 | |||
566183ffa7 | |||
3e435bebf4 | |||
faae28b025 | |||
2d91549a02 | |||
46874f4d0a | |||
a132b35f99 | |||
6e04760e1c | |||
082b1cad74 | |||
84ddae384e | |||
7566954b13 | |||
4b8c37c2c8 | |||
6d69b560be | |||
aa81d616cc | |||
7ee5a1c26b | |||
0763d1d11d | |||
0f52d7b21f | |||
25d252787c | |||
1bd5dc20ef | |||
cd6b9c2166 | |||
f79a59d415 | |||
eda97a3058 | |||
5cbb9d0697 | |||
be3f9b7b64 | |||
6179c66ae9 | |||
117957d7c2 |
@ -1,59 +0,0 @@
|
||||
# bash strict mode
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --shard=$1/$2 || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
fi
|
||||
# # send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=4
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --last-failed || true
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
fi
|
||||
# send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
retry=$((retry + 1))
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
# if it still fails after 3 retrys, then fail the job
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
20
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -1,15 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# bash strict mode
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu || true
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows || true
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos || true
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
@ -28,11 +30,11 @@ while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows -- --last-failed || true
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
elif [[ "$3" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
|
156
.github/workflows/e2e-tests.yml
vendored
@ -1,7 +1,7 @@
|
||||
name: E2E Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [ main, pierremtb/move-tests-to-electron ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
@ -33,13 +33,13 @@ jobs:
|
||||
rust:
|
||||
- 'src/wasm-lib/**'
|
||||
|
||||
browser:
|
||||
electron:
|
||||
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
||||
name: playwright:browser:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores]
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -123,13 +123,13 @@ jobs:
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: build web
|
||||
run: yarn build:local
|
||||
- name: build electron
|
||||
shell: bash
|
||||
run: yarn tron:package
|
||||
- name: Run ubuntu/chrome snapshots
|
||||
shell: bash
|
||||
run: |
|
||||
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
@ -186,12 +186,12 @@ jobs:
|
||||
with:
|
||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run playwright/chrome flow (with retries)
|
||||
- name: Run playwright/electron flow (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
.github/ci-cd-scripts/playwright-browser-chrome.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
.github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
@ -199,11 +199,6 @@ jobs:
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: send to axiom
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
@ -221,136 +216,3 @@ jobs:
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
|
||||
electron:
|
||||
name: playwright:electron:${{matrix.os}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: check-rust-changes
|
||||
steps:
|
||||
- name: Tune GitHub-hosted runner network
|
||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- uses: KittyCAD/action-install-cli@main
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: yarn
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install Playwright Browsers
|
||||
shell: bash
|
||||
run: yarn playwright install chromium --with-deps
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
name: wasm-bundle
|
||||
workflow: build-and-store-wasm.yml
|
||||
branch: main
|
||||
path: src/wasm-lib/pkg
|
||||
- name: copy wasm blob
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
shell: bash
|
||||
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
||||
continue-on-error: true
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Cache Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: OR Cache Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: install good sed
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
shell: bash
|
||||
run: |
|
||||
brew install gnu-sed
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install vector
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
/tmp/vector.sh -y -no-modify-path
|
||||
mkdir -p /tmp/vector
|
||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||
cat /tmp/vector.toml
|
||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||
- name: Build Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: OR Build Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: build electron
|
||||
shell: bash
|
||||
run: yarn tron:package
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run electron tests (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
.github/ci-cd-scripts/playwright-electron.sh ${{ matrix.os }}
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
IS_UBUNTU: ${{ startsWith(matrix.os, 'ubuntu') && 'true' || 'false' }}
|
||||
#DEBUG: 'pw:browser*'
|
||||
- name: send to axiom
|
||||
if: ${{ !cancelled() && (success() || failure()) && !startsWith(matrix.os, 'windows') }}
|
||||
shell: bash
|
||||
run: |
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
17
README.md
@ -388,23 +388,6 @@ yarn test:unit:local
|
||||
|
||||
#### E2E Tests
|
||||
|
||||
**Playwright Browser**
|
||||
|
||||
These E2E tests run in a browser (without electron).
|
||||
There are tests that are skipped if they are ran in a windows OS or Linux OS. We can use playwright tags to implement test skipping.
|
||||
|
||||
Breaking down the command `yarn test:playwright:browser:chrome:windows`
|
||||
- The application is `playwright`
|
||||
- The runtime is a `browser`
|
||||
- The specific `browser` is `chrome`
|
||||
- The test should run in a `windows` environment. It will skip tests that are broken or flaky in the windows OS.
|
||||
|
||||
```
|
||||
yarn test:playwright:browser:chrome
|
||||
yarn test:playwright:browser:chrome:windows
|
||||
yarn test:playwright:browser:chrome:ubuntu
|
||||
```
|
||||
|
||||
**Playwright Electron**
|
||||
|
||||
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
|
||||
|
@ -1,22 +1,11 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
import { setupElectron, tearDown } from './test-utils'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Electron app header tests', () => {
|
||||
test(
|
||||
'Open Command Palette button has correct shortcut',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// No space before the shortcut since it checks textContent.
|
||||
let text
|
||||
@ -34,21 +23,14 @@ test.describe('Electron app header tests', () => {
|
||||
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandsButton).toBeVisible()
|
||||
await expect(commandsButton).toHaveText(text)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'User settings has correct shortcut',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Open the user sidebar menu.
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
@ -59,8 +41,6 @@ test.describe('Electron app header tests', () => {
|
||||
const userSettingsButton = page.getByTestId('user-settings')
|
||||
await expect(userSettingsButton).toBeVisible()
|
||||
await expect(userSettingsButton).toHaveText(text)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,29 +1,26 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
setup,
|
||||
tearDown,
|
||||
commonPoints,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
} from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
|
||||
test.setTimeout(120000)
|
||||
|
||||
async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||
async function doBasicSketch(
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
openPanes: string[]
|
||||
) {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout()
|
||||
await u.openDebugPanel()
|
||||
|
||||
// If we have the code pane open, we should see the code.
|
||||
@ -148,13 +145,11 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||
}
|
||||
|
||||
test.describe('Basic sketch', () => {
|
||||
test('code pane open at start', { tag: ['@skipWin'] }, async ({ page }) => {
|
||||
// Skip on windows it is being weird.
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
await doBasicSketch(page, ['code'])
|
||||
test.fixme('code pane open at start', async ({ page, homePage }) => {
|
||||
await doBasicSketch(page, homePage, ['code'])
|
||||
})
|
||||
|
||||
test('code pane closed at start', async ({ page }) => {
|
||||
test.fixme('code pane closed at start', async ({ page, homePage }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async (persistModelingContext) => {
|
||||
localStorage.setItem(
|
||||
@ -162,6 +157,6 @@ test.describe('Basic sketch', () => {
|
||||
JSON.stringify({ openPanes: [] })
|
||||
)
|
||||
}, PERSIST_MODELING_CONTEXT)
|
||||
await doBasicSketch(page, [])
|
||||
await doBasicSketch(page, homePage, [])
|
||||
})
|
||||
})
|
||||
|
@ -1,27 +1,21 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
import { getUtils } from './test-utils'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
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,
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
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 page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const coord =
|
||||
@ -83,32 +77,39 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
await u.clearCommandLogs()
|
||||
await u.removeCurrentCode()
|
||||
}
|
||||
test('XY', async ({ page }) => {
|
||||
test('XY', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
'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('YZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', { x: 700, y: 250 }) // green plane
|
||||
})
|
||||
|
||||
test('XZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||
test('XZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||
})
|
||||
|
||||
test('-XY', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
||||
test('-XY', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-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('-YZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', {
|
||||
x: 700,
|
||||
y: 219,
|
||||
}) // back of green plan
|
||||
})
|
||||
|
||||
test('-XZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||
test('-XZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||
})
|
||||
})
|
||||
|
@ -1,28 +1,15 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
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,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -41,8 +28,8 @@ extrude001 = extrude(5, sketch001)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -64,9 +51,9 @@ extrude001 = extrude(5, sketch001)`
|
||||
|
||||
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
}) => {
|
||||
await page.goto('http://localhost:3000')
|
||||
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
@ -74,8 +61,8 @@ extrude001 = extrude(5, sketch001)`
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, bracket)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 900 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 900 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -91,8 +78,9 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||
|
||||
// Delete a character to break the KCL
|
||||
await u.openKclCodePanel()
|
||||
await page.getByText('thickness, bracketLeg1Sketch)').click()
|
||||
await editor.openPane()
|
||||
await editor.scrollToText('thickness, bracketLeg1Sketch)')
|
||||
await page.getByText('extrude(thickness, bracketLeg1Sketch)').click()
|
||||
await page.keyboard.press('Backspace')
|
||||
|
||||
// Ensure that a badge appears on the button
|
||||
@ -116,7 +104,10 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// Open the code pane
|
||||
await u.openKclCodePanel()
|
||||
await editor.openPane()
|
||||
|
||||
// Go to our problematic code again (missing closing paren!)
|
||||
await editor.scrollToText('extrude(thickness, bracketLeg1Sketch')
|
||||
|
||||
// Ensure that a badge appears on the button
|
||||
await expect(codePaneButtonHolder).toContainText('notification')
|
||||
@ -129,18 +120,18 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||
test.fixme('When error is not in view you can click the badge to scroll to it', async ({
|
||||
page,
|
||||
homePage,
|
||||
context,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
await page.addInitScript((code) => {
|
||||
await context.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.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
@ -164,24 +155,24 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(
|
||||
page
|
||||
.getByText(
|
||||
'sketch profile must lie entirely on one side of the revolution axis'
|
||||
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: 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 ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
await page.addInitScript((code) => {
|
||||
await context.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.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
@ -241,11 +232,9 @@ extrude001 = extrude(5, sketch001)`
|
||||
test(
|
||||
'Opening multiple panes persists when switching projects',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
// Setup multiple projects.
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await Promise.all([
|
||||
@ -262,11 +251,10 @@ test(
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await test.step('Opening the bracket project should load', async () => {
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
@ -309,30 +297,21 @@ test(
|
||||
await expect(page.locator('#variables-pane')).toBeVisible()
|
||||
await expect(page.locator('#logs-pane')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'external change of file contents are reflected in editor',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const PROJECT_DIR_NAME = 'lee-was-here'
|
||||
const {
|
||||
electronApp,
|
||||
page,
|
||||
dir: projectsDir,
|
||||
} = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const { dir: projectsDir } = await context.folderSetupFn(async (dir) => {
|
||||
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
||||
await fsp.mkdir(aProjectDir, { recursive: true })
|
||||
},
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await test.step('Open the project', async () => {
|
||||
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
||||
@ -351,7 +330,5 @@ test(
|
||||
)
|
||||
await u.editorTextMatches(content)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,19 +1,12 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { getUtils } from './test-utils'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
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,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -29,9 +22,9 @@ test.describe('Command bar tests', () => {
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -52,7 +45,7 @@ test.describe('Command bar tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Fillet from command bar', async ({ page }) => {
|
||||
test('Fillet from command bar', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -68,8 +61,8 @@ extrude001 = extrude(-10, sketch001)`
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -93,10 +86,10 @@ extrude001 = extrude(-10, sketch001)`
|
||||
|
||||
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
@ -153,7 +146,7 @@ extrude001 = extrude(-10, sketch001)`
|
||||
// Check that the visibility changed
|
||||
await expect(paneSelector).not.toBeVisible()
|
||||
|
||||
commandOptionInput = page.getByPlaceholder('off')
|
||||
commandOptionInput = page.locator('[id="option-input"]')
|
||||
|
||||
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||
await commandBarButton.click()
|
||||
@ -174,10 +167,10 @@ extrude001 = extrude(-10, sketch001)`
|
||||
|
||||
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
@ -221,7 +214,7 @@ extrude001 = extrude(-10, sketch001)`
|
||||
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||
})
|
||||
|
||||
test('Can extrude from the command bar', async ({ page }) => {
|
||||
test('Can extrude from the command bar', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -237,9 +230,9 @@ extrude001 = extrude(-10, sketch001)`
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Make sure the stream is up
|
||||
await u.openDebugPanel()
|
||||
@ -293,26 +286,19 @@ extrude001 = extrude(-10, sketch001)`
|
||||
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(
|
||||
`distance = sqrt(20)
|
||||
distance001 = ${KCL_DEFAULT_LENGTH}
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-6.95, 10.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -20.93], %)
|
||||
|> line([-23.44, 0.52], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(distance001, sketch001)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'extrude001 = extrude(distance001, sketch001)'
|
||||
)
|
||||
})
|
||||
|
||||
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()
|
||||
test('Can switch between sketch tools via command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
|
@ -1,23 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
test.describe('Copilot ghost text', () => {
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
test.skip(true, 'Needs to get covered again')
|
||||
|
||||
test('completes code in empty file', async ({ page }) => {
|
||||
test('completes code in empty file', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -52,12 +45,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test.skip('copilot disabled in sketch mode no select plane', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -101,12 +95,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test('copilot disabled in sketch mode after selecting plane', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -184,12 +179,12 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowUp in code rejects the suggestion', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -212,12 +207,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowDown in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -240,12 +238,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowLeft in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -268,12 +269,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowRight in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -296,12 +300,12 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('Enter in code scoots it down', async ({ page }) => {
|
||||
test('Enter in code scoots it down', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -326,12 +330,15 @@ test.describe('Copilot ghost text', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
|
||||
test('Ctrl+shift+z in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -360,12 +367,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForTimeout(800)
|
||||
await u.codeLocator.click()
|
||||
@ -420,15 +428,17 @@ test.describe('Copilot ghost text', () => {
|
||||
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(``)
|
||||
})
|
||||
//await expect(page.locator('.cm-content')).toHaveText(``) })
|
||||
|
||||
test('delete in code rejects the suggestion', async ({ page }) => {
|
||||
test('delete in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -453,12 +463,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('backspace in code rejects the suggestion', async ({ page }) => {
|
||||
test('backspace in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -483,12 +496,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('focus outside code pane rejects the suggestion', async ({ page }) => {
|
||||
test('focus outside code pane rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -515,3 +531,4 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,14 +1,6 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
function countNewlines(input: string): number {
|
||||
let count = 0
|
||||
@ -24,13 +16,14 @@ test.describe('Debug pane', () => {
|
||||
test('Artifact IDs in the artifact graph are stable across code edits', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
}) => {
|
||||
const code = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 1], %)
|
||||
`
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const tree = page.getByTestId('debug-feature-tree')
|
||||
const segment = tree.locator('li', {
|
||||
@ -39,7 +32,7 @@ test.describe('Debug pane', () => {
|
||||
})
|
||||
|
||||
await test.step('Test setup', async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openKclCodePanel()
|
||||
await u.openDebugPanel()
|
||||
// Set the code in the code editor.
|
||||
|
@ -1,39 +1,31 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { join } from 'path'
|
||||
import { test, expect } from './zoo-test'
|
||||
import path from 'path'
|
||||
import {
|
||||
getUtils,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
getPlaywrightDownloadDir,
|
||||
} from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'export works on the first try',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
async ({ page, context }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(bracketDir, 'other.kcl')
|
||||
path.join(bracketDir, 'other.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -93,12 +85,16 @@ test(
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
const firstFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile(exportFileName)
|
||||
const outputGltf = await fsp.readFile(firstFileFullPath)
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
@ -107,9 +103,6 @@ test(
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(300_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(exportFileName)
|
||||
})
|
||||
})
|
||||
|
||||
@ -170,12 +163,16 @@ test(
|
||||
expect(exportingToastMessage).not.toBeVisible(),
|
||||
]))
|
||||
|
||||
const secondFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile(exportFileName)
|
||||
const outputGltf = await fsp.readFile(secondFileFullPath)
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
@ -184,13 +181,7 @@ test(
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(100_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(exportFileName)
|
||||
})
|
||||
await electronApp.close()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import fsp from 'fs/promises'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import {
|
||||
@ -6,26 +6,16 @@ import {
|
||||
darkModePlaneColorXZ,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
} from './test-utils'
|
||||
|
||||
import { join } from 'path'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Editor tests', () => {
|
||||
test('can comment out code with ctrl+/', async ({ page }) => {
|
||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -66,11 +56,12 @@ test.describe('Editor tests', () => {
|
||||
|
||||
test('if you click the format button it formats your code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -96,11 +87,12 @@ test.describe('Editor tests', () => {
|
||||
|
||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -151,9 +143,7 @@ test.describe('Editor tests', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('fold gutters work', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
test('fold gutters work', async ({ page, homePage }) => {
|
||||
const fullCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -171,9 +161,9 @@ test.describe('Editor tests', () => {
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
||||
// up, its an annoying codemirror thing.
|
||||
@ -224,7 +214,10 @@ test.describe('Editor tests', () => {
|
||||
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('hover over functions shows function description', async ({ page }) => {
|
||||
test('hover over functions shows function description', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -237,9 +230,9 @@ test.describe('Editor tests', () => {
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -268,6 +261,7 @@ test.describe('Editor tests', () => {
|
||||
|
||||
test('if you use the format keyboard binding it formats your code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -282,9 +276,9 @@ test.describe('Editor tests', () => {
|
||||
)
|
||||
localStorage.setItem('disableAxis', 'true')
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -310,6 +304,7 @@ test.describe('Editor tests', () => {
|
||||
|
||||
test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -324,9 +319,9 @@ test.describe('Editor tests', () => {
|
||||
)
|
||||
localStorage.setItem('disableAxis', 'true')
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -369,11 +364,14 @@ test.describe('Editor tests', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||
test('if you write kcl with lint errors you get lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||
@ -409,7 +407,10 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('if you fixup kcl errors you clear lints', async ({ page }) => {
|
||||
test('if you fixup kcl errors you clear lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -423,9 +424,9 @@ test.describe('Editor tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -447,11 +448,14 @@ test.describe('Editor tests', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||
test('if you write invalid kcl you get inlined errors', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -518,9 +522,9 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
})
|
||||
|
||||
// TODO currently multiple source ranges are not supported
|
||||
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
||||
test.fixme('error with 2 source ranges gets 2 diagnostics', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -543,9 +547,11 @@ test.describe('Editor tests', () => {
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -571,7 +577,7 @@ test.describe('Editor tests', () => {
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type(`extrusion = startSketchOn('XY')
|
||||
|> circle({ center = [0, 0], radius = dia/2 }, %)
|
||||
|> circle({ center: [0, 0], radius: dia/2 }, %)
|
||||
|> hole(squareHole(length, width, height), %)
|
||||
|> extrude(height, %)`)
|
||||
|
||||
@ -586,10 +592,11 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||
})
|
||||
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`box = startSketchOn('XY')
|
||||
@ -607,17 +614,16 @@ test.describe('Editor tests', () => {
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
|> revolve({
|
||||
axis = revolveAxis,
|
||||
angle = 90
|
||||
axis: revolveAxis,
|
||||
angle: 90
|
||||
}, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await page.goto('/')
|
||||
await u.waitForPageLoad()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
|
||||
@ -628,12 +634,15 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.getByText(searchText)).toBeVisible()
|
||||
})
|
||||
test.describe('Autocomplete works', () => {
|
||||
test('with enter/click to accept the completion', async ({ page }) => {
|
||||
test('with enter/click to accept the completion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// tests clicking on an option, selection the first option
|
||||
// and arrowing down to an option
|
||||
@ -702,12 +711,12 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('with tab to accept the completion', async ({ page }) => {
|
||||
test('with tab to accept the completion', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// this test might be brittle as we add and remove functions
|
||||
// but should also be easy to update.
|
||||
@ -773,9 +782,13 @@ test.describe('Editor tests', () => {
|
||||
|> xLine(5, %) // lin`)
|
||||
})
|
||||
})
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
@ -786,9 +799,9 @@ test.describe('Editor tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -847,7 +860,10 @@ test.describe('Editor tests', () => {
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||
test('Can undo a sketch modification with ctrl+z', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -861,9 +877,9 @@ test.describe('Editor tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -890,7 +906,7 @@ test.describe('Editor tests', () => {
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const startPX = [665, 397]
|
||||
const startPX = [1200 / 2, 500 / 2]
|
||||
|
||||
const dragPX = 40
|
||||
|
||||
@ -904,9 +920,9 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
|
||||
// drag startProfieAt handle
|
||||
// drag startProfileAt handle
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||
sourcePosition: { x: startPX[0] + 68, y: startPX[1] + 147 },
|
||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
@ -944,8 +960,8 @@ test.describe('Editor tests', () => {
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([15.4, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
@ -958,8 +974,8 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([15.4, -2.78], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
@ -971,7 +987,7 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
@ -996,10 +1012,8 @@ test.describe('Editor tests', () => {
|
||||
test.fixme(
|
||||
`Can use the import stdlib function on a local OBJ file`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ page, context }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'cube')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
@ -1007,10 +1021,10 @@ test.describe('Editor tests', () => {
|
||||
join(bracketDir, 'cube.obj')
|
||||
)
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), '')
|
||||
},
|
||||
})
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
// Locators and constants
|
||||
const u = await getUtils(page)
|
||||
@ -1068,8 +1082,6 @@ test.describe('Editor tests', () => {
|
||||
})
|
||||
.toBeGreaterThan(15)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -54,13 +54,13 @@ export class EditorFixture {
|
||||
}
|
||||
}
|
||||
if (!shouldNormalise) {
|
||||
const expectStart = expect(this.codeContent)
|
||||
const expectStart = expect.poll(() => this.codeContent.textContent())
|
||||
if (not) {
|
||||
const result = await expectStart.not.toContainText(code, { timeout })
|
||||
const result = await expectStart.not.toContain(code)
|
||||
await resetPane()
|
||||
return result
|
||||
}
|
||||
const result = await expectStart.toContainText(code, { timeout })
|
||||
const result = await expectStart.toContain(code)
|
||||
await resetPane()
|
||||
return result
|
||||
}
|
||||
@ -147,4 +147,20 @@ export class EditorFixture {
|
||||
openPane() {
|
||||
return openPane(this.page, this.paneButtonTestId)
|
||||
}
|
||||
scrollToText(text: string) {
|
||||
return this.page.evaluate((scrollToText: string) => {
|
||||
// editorManager is available on the window object.
|
||||
// @ts-ignore
|
||||
let index = editorManager._editorView.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(scrollToText)
|
||||
// @ts-ignore
|
||||
editorManager._editorView.dispatch({
|
||||
selection: {
|
||||
anchor: index,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
}, text)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import type {
|
||||
BrowserContext,
|
||||
ElectronApplication,
|
||||
Page,
|
||||
TestInfo,
|
||||
Page,
|
||||
} from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
import { getUtils, setup, setupElectron, tearDown } from '../test-utils'
|
||||
|
||||
import { getUtils, setup, setupElectron } from '../test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { CmdBarFixture } from './cmdBarFixture'
|
||||
@ -20,11 +20,11 @@ export class AuthenticatedApp {
|
||||
public readonly page: Page
|
||||
public readonly context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public readonly viewPortSize = { width: 1000, height: 500 }
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this.page = page
|
||||
this.context = context
|
||||
this.page = page
|
||||
this.testInfo = testInfo
|
||||
}
|
||||
|
||||
@ -49,9 +49,7 @@ export class AuthenticatedApp {
|
||||
}
|
||||
}
|
||||
|
||||
interface Fixtures {
|
||||
app: AuthenticatedApp
|
||||
tronApp: AuthenticatedTronApp
|
||||
export interface Fixtures {
|
||||
cmdBar: CmdBarFixture
|
||||
editor: EditorFixture
|
||||
toolbar: ToolbarFixture
|
||||
@ -61,9 +59,11 @@ interface Fixtures {
|
||||
export class AuthenticatedTronApp {
|
||||
public readonly _page: Page
|
||||
public page: Page
|
||||
public readonly context: BrowserContext
|
||||
public context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public electronApp?: ElectronApplication
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public dir: string = ''
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this._page = page
|
||||
@ -79,15 +79,24 @@ export class AuthenticatedTronApp {
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
} = { fixtures: {} }
|
||||
) {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
const { electronApp, page, context, dir } = await setupElectron({
|
||||
testInfo: this.testInfo,
|
||||
folderSetupFn: arg.folderSetupFn,
|
||||
cleanProjectDir: arg.cleanProjectDir,
|
||||
appSettings: arg.appSettings,
|
||||
})
|
||||
this.page = page
|
||||
this.context = context
|
||||
this.electronApp = electronApp
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
this.dir = dir
|
||||
|
||||
// Easier to access throughout utils
|
||||
this.page.dir = dir
|
||||
|
||||
// Setup localStorage, addCookies, reload
|
||||
await setup(this.context, this.page, this.testInfo)
|
||||
|
||||
await page.setViewportSize(this.viewPortSize)
|
||||
|
||||
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
||||
const fixture = arg.fixtures[key]
|
||||
@ -110,32 +119,20 @@ export class AuthenticatedTronApp {
|
||||
})
|
||||
}
|
||||
|
||||
export const test = base.extend<Fixtures>({
|
||||
app: async ({ page, context }, use, testInfo) => {
|
||||
await use(new AuthenticatedApp(context, page, testInfo))
|
||||
},
|
||||
tronApp: async ({ page, context }, use, testInfo) => {
|
||||
await use(new AuthenticatedTronApp(context, page, testInfo))
|
||||
},
|
||||
cmdBar: async ({ page }, use) => {
|
||||
export const fixtures = {
|
||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new CmdBarFixture(page))
|
||||
},
|
||||
editor: async ({ page }, use) => {
|
||||
editor: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new EditorFixture(page))
|
||||
},
|
||||
toolbar: async ({ page }, use) => {
|
||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new ToolbarFixture(page))
|
||||
},
|
||||
scene: async ({ page }, use) => {
|
||||
scene: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new SceneFixture(page))
|
||||
},
|
||||
homePage: async ({ page }, use) => {
|
||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new HomePageFixture(page))
|
||||
},
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
export { expect } from '@playwright/test'
|
||||
}
|
||||
|
@ -14,10 +14,14 @@ interface HomePageState {
|
||||
export class HomePageFixture {
|
||||
public page: Page
|
||||
|
||||
projectSection!: Locator
|
||||
projectCard!: Locator
|
||||
projectCardTitle!: Locator
|
||||
projectCardFile!: Locator
|
||||
projectCardFolder!: Locator
|
||||
projectButtonNew!: Locator
|
||||
projectButtonContinue!: Locator
|
||||
projectTextName!: Locator
|
||||
sortByDateBtn!: Locator
|
||||
sortByNameBtn!: Locator
|
||||
|
||||
@ -28,11 +32,19 @@ export class HomePageFixture {
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.projectSection = this.page.getByTestId('home-section')
|
||||
|
||||
this.projectCard = this.page.getByTestId('project-link')
|
||||
this.projectCardTitle = this.page.getByTestId('project-title')
|
||||
this.projectCardFile = this.page.getByTestId('project-file-count')
|
||||
this.projectCardFolder = this.page.getByTestId('project-folder-count')
|
||||
|
||||
this.projectButtonNew = this.page.getByTestId('home-new-file')
|
||||
this.projectTextName = this.page.getByTestId('cmd-bar-arg-value')
|
||||
this.projectButtonContinue = this.page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
|
||||
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
|
||||
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
|
||||
}
|
||||
@ -91,10 +103,25 @@ export class HomePageFixture {
|
||||
.toEqual(expectedState)
|
||||
}
|
||||
|
||||
createAndGoToProject = async (projectTitle: string) => {
|
||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||
await this.projectButtonNew.click()
|
||||
await this.projectTextName.click()
|
||||
await this.projectTextName.fill(projectTitle)
|
||||
await this.projectButtonContinue.click()
|
||||
}
|
||||
|
||||
openProject = async (projectTitle: string) => {
|
||||
const projectCard = this.projectCard.locator(
|
||||
this.page.getByText(projectTitle)
|
||||
)
|
||||
await projectCard.click()
|
||||
}
|
||||
|
||||
goToModelingScene = async (name: string = 'testDefault') => {
|
||||
// On web this is a no-op. There is no project view.
|
||||
if (process.env.PLATFORM === 'web') return
|
||||
|
||||
await this.createAndGoToProject(name)
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,9 @@ export class SceneFixture {
|
||||
|
||||
expectState = async (expected: SceneSerialised) => {
|
||||
return expect
|
||||
.poll(() => this._serialiseScene(), {
|
||||
message: `Expected scene state to match`,
|
||||
.poll(async () => await this._serialiseScene(), {
|
||||
intervals: [1_000, 2_000, 10_000],
|
||||
timeout: 60000,
|
||||
})
|
||||
.toEqual(expected)
|
||||
}
|
||||
@ -187,7 +188,10 @@ export class SceneFixture {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
await this.waitForExecutionDone()
|
||||
await this.page
|
||||
.locator(`[data-receive-command-type="default_camera_get_settings"]`)
|
||||
.first()
|
||||
.waitFor()
|
||||
const position = await Promise.all([
|
||||
this.page.getByTestId('cam-x-position').inputValue().then(Number),
|
||||
this.page.getByTestId('cam-y-position').inputValue().then(Number),
|
||||
@ -238,6 +242,7 @@ export class SceneFixture {
|
||||
}
|
||||
|
||||
async clickGizmoMenuItem(name: string) {
|
||||
await this.gizmo.hover()
|
||||
await this.gizmo.click({ button: 'right' })
|
||||
const buttonToTest = this.page.getByRole('button', {
|
||||
name: name,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from './fixtureSetup'
|
||||
import { expect } from '../zoo-test'
|
||||
import { doAndWaitForImageDiff } from '../test-utils'
|
||||
|
||||
export class ToolbarFixture {
|
||||
|
@ -1,29 +1,22 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { setupElectron, tearDown, executorInputPath } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { executorInputPath } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'When machine-api server not found butt is disabled and shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
|
||||
@ -47,28 +40,23 @@ test(
|
||||
// that the machine-api server is not found
|
||||
await makeButton.hover()
|
||||
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'When machine-api server not found home screen & project status shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const notFoundText = 'Machine API server was not discovered'
|
||||
|
||||
@ -91,7 +79,5 @@ test(
|
||||
|
||||
await networkMachineToggle.hover()
|
||||
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
12
e2e/playwright/null.spec.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// These tests are meant to simply test starting and stopping the electron
|
||||
// application, check it can make it to the project pane, and nothing more.
|
||||
// It also tests our test wrappers are working.
|
||||
// Additionally this serves as a nice minimal example.
|
||||
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Open the application', () => {
|
||||
test('see the project view', async ({ page, context }) => {
|
||||
await expect(page.getByTestId('home-section')).toBeVisible()
|
||||
})
|
||||
})
|
@ -1,79 +1,63 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { join } from 'path'
|
||||
import fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
createProject,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath, createProject } from './test-utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
TEST_SETTINGS_ONBOARDING_START,
|
||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
} from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
if (testInfo.tags.includes('@electron')) {
|
||||
return
|
||||
}
|
||||
await setup(context, page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||
// for all these tests.
|
||||
|
||||
test.describe('Onboarding tests', () => {
|
||||
test('Onboarding code is shown in the editor', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.removeItem('persistCode')
|
||||
localStorage.removeItem(settingsKey)
|
||||
},
|
||||
{ settingsKey: TEST_SETTINGS_KEY }
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
})
|
||||
|
||||
test(
|
||||
'Desktop: fresh onboarding executes and loads',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
'Onboarding code is shown in the editor',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
})
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Desktop: fresh onboarding executes and loads',
|
||||
{
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ page, homePage }, testInfo) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||
await createProject({ name: 'project-link', page })
|
||||
@ -93,60 +77,71 @@ test.describe('Onboarding tests', () => {
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test('Code resets after confirmation', async ({ page }) => {
|
||||
test(
|
||||
'Code resets after confirmation',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||
|
||||
// Load the page up with some code so we see the confirmation warning
|
||||
// when we go to replay onboarding
|
||||
await page.addInitScript((code) => {
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Replay the onboarding
|
||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||
const replayButton = page.getByRole('button', { name: 'Replay onboarding' })
|
||||
const replayButton = page.getByRole('button', {
|
||||
name: 'Replay onboarding',
|
||||
})
|
||||
await expect(replayButton).toBeVisible()
|
||||
await replayButton.click()
|
||||
|
||||
// Ensure we see the warning, and that the code has not yet updated
|
||||
await expect(
|
||||
page.getByText('Replaying onboarding resets your code')
|
||||
).toBeVisible()
|
||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
await expect(nextButton).toBeVisible()
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
|
||||
// Ensure we persisted the code to local storage.
|
||||
// Playwright's addInitScript method unfortunately will reset
|
||||
// this code if we try reloading the page as a test,
|
||||
// so this is our best way to test persistence afaik.
|
||||
expect(
|
||||
await page.evaluate(() => {
|
||||
return localStorage.getItem('persistCode')
|
||||
})
|
||||
).toContain('// Shelf Bracket')
|
||||
})
|
||||
|
||||
test('Click through each onboarding step', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
// There used to be old code here that checked if we stored the reset
|
||||
// code into localStorage but that isnt the case on desktop. It gets
|
||||
// saved to the file system, which we have other tests for.
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Click through each onboarding step',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.setItem('persistCode', '')
|
||||
@ -154,107 +149,113 @@ test.describe('Onboarding tests', () => {
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await expect(nextButton).toBeVisible()
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
}
|
||||
|
||||
// Finish the onboarding
|
||||
await expect(nextButton).toBeVisible()
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
await expect(page.url()).not.toContain('onboarding')
|
||||
})
|
||||
|
||||
test('Onboarding redirects and code updating', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', 'sigmaAllow = 15000')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
|
||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
test(
|
||||
'Onboarding redirects and code updating',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: '/export',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const originalCode = 'sigmaAllow = 15000'
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// Test that the redirect happened
|
||||
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||
)
|
||||
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||
)
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText('')
|
||||
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
})
|
||||
|
||||
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
|
||||
page,
|
||||
}) => {
|
||||
test.skip(
|
||||
process.platform === 'darwin',
|
||||
"Skip on macOS, because Playwright isn't behaving the same as the actual browser"
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings, badCode }) => {
|
||||
localStorage.setItem('persistCode', badCode)
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
badCode,
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForURL('**' + onboardingPaths.PARAMETRIC_MODELING, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
// Test that the redirect happened
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.getByTestId('onboarding-next').hover()
|
||||
await page.getByTestId('onboarding-next').click()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
||||
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Onboarding code gets reset to demo on Interactive Numbers step',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: '/parametric-modeling',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
|
||||
async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect
|
||||
.poll(() => page.url())
|
||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
|
||||
@ -270,6 +271,7 @@ test.describe('Onboarding tests', () => {
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
@ -277,13 +279,25 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
// Check that the code has been reset
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Avatar text updates depending on image load success', async ({
|
||||
page,
|
||||
}) => {
|
||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
||||
// anything to do with the avatar inside the onboarding test. Due to the
|
||||
// low impact of an avatar not showing I'm changing this to fixme.
|
||||
test.fixme(
|
||||
'Avatar text updates depending on image load success',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
@ -295,11 +309,8 @@ test.describe('Onboarding tests', () => {
|
||||
}
|
||||
)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const avatarLocator = await page
|
||||
@ -327,13 +338,16 @@ test.describe('Onboarding tests', () => {
|
||||
})
|
||||
|
||||
// 404 the CI avatar image
|
||||
await page.route('https://lh3.googleusercontent.com/**', async (route) => {
|
||||
await page.route(
|
||||
'https://lh3.googleusercontent.com/**',
|
||||
async (route) => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!',
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||
|
||||
@ -341,13 +355,22 @@ test.describe('Onboarding tests', () => {
|
||||
await expect(avatarLocator).not.toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test("Avatar text doesn't mention avatar when no avatar", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.fixme(
|
||||
"Avatar text doesn't mention avatar when no avatar",
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
|
||||
@ -360,11 +383,8 @@ test.describe('Onboarding tests', () => {
|
||||
}
|
||||
)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||
@ -390,23 +410,28 @@ test.describe('Onboarding tests', () => {
|
||||
for (const feature of userMenuFeatures) {
|
||||
await expect(onboardingOverlayLocator).toContainText(feature)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test(
|
||||
'Restarting onboarding on desktop takes one attempt',
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'dismissed',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
// Our constants
|
||||
@ -418,9 +443,8 @@ test(
|
||||
const restartOnboardingButton = page.getByRole('button', {
|
||||
name: 'Reset onboarding',
|
||||
})
|
||||
const restartConfirmationButton = page.getByRole('button', {
|
||||
name: 'Make a new project',
|
||||
})
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
|
||||
const tutorialProjectIndicator = page
|
||||
.getByTestId('project-sidebar-toggle')
|
||||
.filter({ hasText: 'Tutorial Project 00' })
|
||||
@ -439,7 +463,7 @@ test(
|
||||
})
|
||||
|
||||
await test.step('Navigate into project', async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -455,8 +479,8 @@ test(
|
||||
await helpMenuButton.click()
|
||||
await restartOnboardingButton.click()
|
||||
|
||||
await expect(restartConfirmationButton).toBeVisible()
|
||||
await restartConfirmationButton.click()
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
})
|
||||
|
||||
await test.step('Confirm that the onboarding has restarted', async () => {
|
||||
@ -480,11 +504,9 @@ test(
|
||||
|
||||
await restartOnboardingSettingsButton.click()
|
||||
// Since the code is empty, we should not see the confirmation dialog
|
||||
await expect(restartConfirmationButton).not.toBeVisible()
|
||||
await expect(nextButton).not.toBeVisible()
|
||||
await expect(tutorialProjectIndicator).toBeVisible()
|
||||
await expect(tutorialModalText).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,20 +1,34 @@
|
||||
import { test, expect, AuthenticatedApp } from './fixtures/fixtureSetup'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { EditorFixture } from './fixtures/editorFixture'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||
|
||||
test(
|
||||
'verify extruding circle works',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, cmdBar, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
test('verify extruding circle works', async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/test-circle-extrude.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||
await app.initialise(file)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
||||
|
||||
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
||||
@ -27,7 +41,17 @@ test(
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
await editor.expectState({
|
||||
activeLines: [],
|
||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
||||
highlightedCode: circleSnippet,
|
||||
diagnostics: [],
|
||||
})
|
||||
|
||||
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
|
||||
await moveToCircle()
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
await editor.expectState({
|
||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
||||
highlightedCode: circleSnippet,
|
||||
diagnostics: [],
|
||||
})
|
||||
@ -40,6 +64,8 @@ test(
|
||||
})
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
})
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
})
|
||||
|
||||
await test.step('do extrude flow and check extrude code is added to editor', async () => {
|
||||
await toolbar.extrudeButton.click()
|
||||
@ -66,13 +92,12 @@ test(
|
||||
|
||||
await editor.expectEditor.toContain(expectString)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test.describe('verify sketch on chamfer works', () => {
|
||||
const _sketchOnAChamfer =
|
||||
(
|
||||
app: AuthenticatedApp,
|
||||
page: Page,
|
||||
editor: EditorFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
scene: SceneFixture
|
||||
@ -124,7 +149,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
await clickChamfer()
|
||||
// timeout wait for engine animation is unavoidable
|
||||
await app.page.waitForTimeout(600)
|
||||
await page.waitForTimeout(1000)
|
||||
await editor.expectEditor.toContain(afterChamferSelectSnippet)
|
||||
})
|
||||
await test.step('make sure a basic sketch can be added', async () => {
|
||||
@ -135,7 +160,9 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
pixelDiff: 50,
|
||||
})
|
||||
await rectangle2ndClick()
|
||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet)
|
||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
||||
@ -150,18 +177,29 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
})
|
||||
})
|
||||
}
|
||||
test(
|
||||
'works on all edge selections and can break up multi edges in a chamfer array',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
test('works on all edge selections and can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl')
|
||||
await app.initialise(file)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
@ -175,8 +213,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
getOppositeEdge(seg01)
|
||||
]}, %)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
@ -207,8 +244,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
]
|
||||
}, %)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
@ -233,9 +269,8 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
getNextAdjacentEdge(seg02)
|
||||
]
|
||||
}, %)`,
|
||||
afterChamferSelectSnippet:
|
||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([75.8, 317.2], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003) - 90,
|
||||
@ -257,8 +292,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
length = 30,
|
||||
tags = [getNextAdjacentEdge(yo)]
|
||||
}, %)`,
|
||||
afterChamferSelectSnippet:
|
||||
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||
afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||
|
||||
@ -360,23 +394,31 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test(
|
||||
'Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
test('Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
const file = await app.getInputFile(
|
||||
'e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
||||
)
|
||||
await app.initialise(file)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
@ -390,8 +432,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
getOppositeEdge(seg01)
|
||||
]}, extrude001)`,
|
||||
beforeChamferSnippetEnd: '}, extrude001)',
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
@ -448,48 +489,54 @@ sketch002 = startSketchOn(extrude001, seg03)
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
app,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const viewPortSize = { width: 1200, height: 500 }
|
||||
|
||||
await page.setBodyDimensions(viewPortSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Constants and locators
|
||||
// These are mappings from screenspace to KCL coordinates,
|
||||
// until we merge in our coordinate system helpers
|
||||
const xzPlane = [
|
||||
app.viewPortSize.width * 0.65,
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width * 0.65,
|
||||
viewPortSize.height * 0.3,
|
||||
] as const
|
||||
const originSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||
app.viewPortSize.height / 2,
|
||||
viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||
viewPortSize.height / 2,
|
||||
],
|
||||
kcl: [0, 0],
|
||||
} as const
|
||||
const xAxisSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width * 0.75,
|
||||
app.viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||
viewPortSize.width * 0.75,
|
||||
viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||
],
|
||||
kcl: [16.95, 0],
|
||||
kcl: [20.34, 0],
|
||||
} as const
|
||||
const offYAxis = {
|
||||
screen: [
|
||||
app.viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||
viewPortSize.height * 0.3,
|
||||
],
|
||||
kcl: [6.78, 6.78],
|
||||
kcl: [8.14, 6.78],
|
||||
} as const
|
||||
const yAxisSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||
viewPortSize.height * 0.3,
|
||||
],
|
||||
kcl: [0, 6.78],
|
||||
} as const
|
||||
@ -510,15 +557,13 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||
}
|
||||
|
||||
await app.initialise()
|
||||
|
||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||
await editor.closePane()
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
await moveToXzPlane()
|
||||
await clickOnXzPlane()
|
||||
// timeout wait for engine animation is unavoidable
|
||||
await app.page.waitForTimeout(600)
|
||||
await page.waitForTimeout(600)
|
||||
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
||||
})
|
||||
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
||||
@ -553,11 +598,15 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
})
|
||||
|
||||
test(`Verify user can double-click to edit a sketch`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const initialCode = `closedSketch = startSketchOn('XZ')
|
||||
|> circle({ center = [8, 5], radius = 2 }, %)
|
||||
openSketch = startSketchOn('XY')
|
||||
@ -566,15 +615,24 @@ openSketch = startSketchOn('XY')
|
||||
|> xLine(5, %)
|
||||
|> tangentialArcTo([10, 0], %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
const viewPortSize = { width: 1000, height: 500 }
|
||||
await page.setBodyDimensions(viewPortSize)
|
||||
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const pointInsideCircle = {
|
||||
x: app.viewPortSize.width * 0.63,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
x: viewPortSize.width * 0.63,
|
||||
y: viewPortSize.height * 0.5,
|
||||
}
|
||||
const pointOnPathAfterSketching = {
|
||||
x: app.viewPortSize.width * 0.58,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
x: viewPortSize.width * 0.65,
|
||||
y: viewPortSize.height * 0.5,
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
||||
@ -607,41 +665,59 @@ openSketch = startSketchOn('XY')
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await exitSketch()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Drag the sketch line out of the axis view which blocks the click
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: {
|
||||
x: viewPortSize.width * 0.7,
|
||||
y: viewPortSize.height * 0.5,
|
||||
},
|
||||
targetPosition: {
|
||||
x: viewPortSize.width * 0.7,
|
||||
y: viewPortSize.height * 0.4,
|
||||
},
|
||||
})
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await test.step(`Double-click on the open sketch`, async () => {
|
||||
await moveToOpenPath()
|
||||
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
||||
// There is a full execution after exiting sketch that clears the scene.
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await dblClickOpenPath()
|
||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
// Wait for enter sketch mode to complete
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await editor.expectState({
|
||||
activeLines: [`|>xLine(5,%)`],
|
||||
highlightedCode: 'xLine(5,%)',
|
||||
activeLines: [`|>tangentialArcTo([10,0],%)`],
|
||||
highlightedCode: 'tangentialArcTo([10,0],%)',
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test(`Offset plane point-and-click`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await app.initialise()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 700, y: 150 }
|
||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||
})
|
||||
@ -684,20 +760,30 @@ const loftPointAndClickCases = [
|
||||
]
|
||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||
app,
|
||||
context,
|
||||
homePage,
|
||||
page,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
plane001 = offsetPlane('XZ', 50)
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
@ -716,7 +802,7 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
await clickOnSketch1()
|
||||
await page.keyboard.down('Shift')
|
||||
await clickOnSketch2()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.up('Shift')
|
||||
}
|
||||
|
||||
|
@ -1,49 +1,41 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import {
|
||||
doExport,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
isOutOfViewInScrollContainer,
|
||||
Paths,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
createProject,
|
||||
getPlaywrightDownloadDir,
|
||||
} from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import fs from 'fs'
|
||||
import { join } from 'path'
|
||||
import path from 'path'
|
||||
import { DEFAULT_PROJECT_KCL_FILE } from 'lib/constants'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'projects reload if a new one is created, deleted, or renamed externally',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
let externalCreatedProjectName = 'external-created-project'
|
||||
|
||||
let targetDir = ''
|
||||
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
targetDir = dir
|
||||
setTimeout(() => {
|
||||
const myDir = join(dir, externalCreatedProjectName)
|
||||
const myDir = path.join(dir, externalCreatedProjectName)
|
||||
;(async () => {
|
||||
await fsp.mkdir(myDir)
|
||||
await fsp.writeFile(
|
||||
join(myDir, DEFAULT_PROJECT_KCL_FILE),
|
||||
path.join(myDir, DEFAULT_PROJECT_KCL_FILE),
|
||||
'sca ba be bop de day wawa skee'
|
||||
)
|
||||
})().catch(console.error)
|
||||
}, 5000)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const projectLinks = page.getByTestId('project-link')
|
||||
|
||||
@ -51,34 +43,27 @@ test(
|
||||
await expect(projectLinks).toContainText(externalCreatedProjectName)
|
||||
|
||||
await fsp.rename(
|
||||
join(targetDir, externalCreatedProjectName),
|
||||
join(targetDir, externalCreatedProjectName + '1')
|
||||
path.join(targetDir, externalCreatedProjectName),
|
||||
path.join(targetDir, externalCreatedProjectName + '1')
|
||||
)
|
||||
|
||||
externalCreatedProjectName += '1'
|
||||
await expect(projectLinks).toContainText(externalCreatedProjectName)
|
||||
|
||||
await fsp.rm(join(targetDir, externalCreatedProjectName), {
|
||||
await fsp.rm(path.join(targetDir, externalCreatedProjectName), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
})
|
||||
|
||||
await expect(projectLinks).toHaveCount(0)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'click help/keybindings from home page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -89,28 +74,23 @@ test(
|
||||
await page.getByTestId('keybindings-button').click()
|
||||
// Make sure the keyboard shortcuts modal is visible.
|
||||
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'click help/keybindings from project page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -131,27 +111,22 @@ test(
|
||||
await page.getByTestId('keybindings-button').click()
|
||||
// Make sure the keyboard shortcuts modal is visible.
|
||||
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'when code with error first loads you get errors in console',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(join(dir, 'broken-code'), { recursive: true })
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('broken-code-test.kcl'),
|
||||
join(dir, 'broken-code', 'main.kcl')
|
||||
path.join(dir, 'broken-code', 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('broken-code')).toBeVisible()
|
||||
|
||||
@ -169,8 +144,6 @@ test(
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
const crypticErrorText = `Expected a tag declarator`
|
||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
@ -181,20 +154,17 @@ test.describe('Can export from electron app', () => {
|
||||
test(
|
||||
`Can export using ${method}`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const u = await getUtils(page)
|
||||
|
||||
page.on('console', console.log)
|
||||
@ -242,12 +212,17 @@ test.describe('Can export from electron app', () => {
|
||||
)
|
||||
})
|
||||
|
||||
const filepath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
'main.gltf'
|
||||
)
|
||||
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile('main.gltf')
|
||||
const outputGltf = await fsp.readFile(filepath)
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
@ -258,10 +233,8 @@ test.describe('Can export from electron app', () => {
|
||||
.toBeGreaterThan(300_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm('main.gltf')
|
||||
await fsp.rm(filepath)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -269,10 +242,8 @@ test.describe('Can export from electron app', () => {
|
||||
test(
|
||||
'Rename and delete projects, also spam arrow keys when renaming',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
@ -297,10 +268,9 @@ test(
|
||||
)
|
||||
const _1995 = new Date('1995-01-01T00:03:33')
|
||||
fs.utimesSync(`${dir}/lego/main.kcl`, _1995, _1995)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -465,26 +435,21 @@ test(
|
||||
// expect the name not to have changed
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'pressing "delete" on home screen should do nothing',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/router-template-slate/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -498,8 +463,6 @@ test(
|
||||
// expect to still be on the home page
|
||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||
await expect(page.getByText('Your Projects')).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
@ -507,17 +470,14 @@ test.describe(`Project management commands`, () => {
|
||||
test(
|
||||
`Rename from project page`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = `my_project_to_rename`
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/${projectName}/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -537,7 +497,7 @@ test.describe(`Project management commands`, () => {
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
|
||||
await projectHomeLink.click()
|
||||
@ -565,25 +525,20 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(projectHomeLink.first()).toBeVisible()
|
||||
await expect(projectHomeLink.first()).toContainText(projectRenamedName)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Delete from project page`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = `my_project_to_delete`
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/${projectName}/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -600,7 +555,7 @@ test.describe(`Project management commands`, () => {
|
||||
const noProjectsMessage = page.getByText('No Projects found')
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
|
||||
await projectHomeLink.click()
|
||||
@ -622,24 +577,19 @@ test.describe(`Project management commands`, () => {
|
||||
await test.step(`Check the project was deleted and we navigated home`, async () => {
|
||||
await expect(noProjectsMessage).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
test(
|
||||
`Rename from home page`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = `my_project_to_rename`
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/${projectName}/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
// Constants and locators
|
||||
@ -657,7 +607,7 @@ test.describe(`Project management commands`, () => {
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
await expect(projectHomeLink).toBeVisible()
|
||||
})
|
||||
@ -682,24 +632,19 @@ test.describe(`Project management commands`, () => {
|
||||
).toBeVisible()
|
||||
await expect(projectHomeLink).not.toHaveText(projectName)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
test(
|
||||
`Delete from home page`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = `my_project_to_delete`
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/${projectName}/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
// Constants and locators
|
||||
@ -715,7 +660,7 @@ test.describe(`Project management commands`, () => {
|
||||
const noProjectsMessage = page.getByText('No Projects found')
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
await expect(projectHomeLink).toBeVisible()
|
||||
})
|
||||
@ -736,8 +681,6 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(projectHomeLink).not.toBeVisible()
|
||||
await expect(noProjectsMessage).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
@ -745,11 +688,9 @@ test.describe(`Project management commands`, () => {
|
||||
test(
|
||||
'File in the file pane should open with a single click',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = 'router-template-slate'
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
@ -759,10 +700,9 @@ test(
|
||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||
`${dir}/${projectName}/otherThingToClickOn.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -786,35 +726,30 @@ test(
|
||||
await expect(u.codeLocator).toContainText(
|
||||
'A mounting bracket for the Focusrite Scarlett Solo audio interface'
|
||||
)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Nested directories in project without main.kcl do not create main.kcl',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
let testDir: string | undefined
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(join(dir, 'router-template-slate', 'nested'), {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(path.join(dir, 'router-template-slate', 'nested'), {
|
||||
recursive: true,
|
||||
})
|
||||
await fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(dir, 'router-template-slate', 'nested', 'slate.kcl')
|
||||
path.join(dir, 'router-template-slate', 'nested', 'slate.kcl')
|
||||
)
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(dir, 'router-template-slate', 'nested', 'bracket.kcl')
|
||||
path.join(dir, 'router-template-slate', 'nested', 'bracket.kcl')
|
||||
)
|
||||
testDir = dir
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -842,44 +777,41 @@ test(
|
||||
if (testDir !== undefined) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
await expect(
|
||||
fsp.access(join(testDir, 'router-template-slate', 'main.kcl'))
|
||||
fsp.access(path.join(testDir, 'router-template-slate', 'main.kcl'))
|
||||
).rejects.toThrow()
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
await expect(
|
||||
fsp.access(join(testDir, 'router-template-slate', 'nested', 'main.kcl'))
|
||||
fsp.access(
|
||||
path.join(testDir, 'router-template-slate', 'nested', 'main.kcl')
|
||||
)
|
||||
).rejects.toThrow()
|
||||
}
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test.fixme(
|
||||
'Deleting projects, can delete individual project, can still create projects after deleting all',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectData = [
|
||||
['router-template-slate', 'cylinder.kcl'],
|
||||
['bracket', 'focusrite_scarlett_mounting_braket.kcl'],
|
||||
['lego', 'lego.kcl'],
|
||||
]
|
||||
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// Do these serially to ensure the order is correct
|
||||
for (const [name, file] of projectData) {
|
||||
await fsp.mkdir(join(dir, name), { recursive: true })
|
||||
await fsp.mkdir(path.join(dir, name), { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath(file),
|
||||
join(dir, name, `main.kcl`)
|
||||
path.join(dir, name, `main.kcl`)
|
||||
)
|
||||
// Wait 1s between each project to ensure the order is correct
|
||||
await new Promise((r) => setTimeout(r, 1_000))
|
||||
}
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
|
||||
await test.step('delete the middle project, i.e. the bracket project', async () => {
|
||||
@ -932,19 +864,15 @@ test.fixme(
|
||||
page.getByTestId('project-link').filter({ hasText: 'project-000' })
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Can load a file with CRLF line endings',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = path.join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
|
||||
const file = await fsp.readFile(
|
||||
@ -954,14 +882,13 @@ test(
|
||||
// Replace both \r optionally so we don't end up with \r\r\n
|
||||
const fileWithCRLF = file.replace(/\r?\n/g, '\r\n')
|
||||
await fsp.writeFile(
|
||||
join(routerTemplateDir, 'main.kcl'),
|
||||
path.join(routerTemplateDir, 'main.kcl'),
|
||||
fileWithCRLF,
|
||||
'utf-8'
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -974,37 +901,32 @@ test(
|
||||
await expect(u.codeLocator).toContainText('routerDiameter')
|
||||
await expect(u.codeLocator).toContainText('templateGap')
|
||||
await expect(u.codeLocator).toContainText('minClampingDistance')
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Can sort projects on home page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectData = [
|
||||
['router-template-slate', 'cylinder.kcl'],
|
||||
['bracket', 'focusrite_scarlett_mounting_braket.kcl'],
|
||||
['lego', 'lego.kcl'],
|
||||
]
|
||||
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// Do these serially to ensure the order is correct
|
||||
for (const [name, file] of projectData) {
|
||||
await fsp.mkdir(join(dir, name), { recursive: true })
|
||||
await fsp.mkdir(path.join(dir, name), { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath(file),
|
||||
join(dir, name, `main.kcl`)
|
||||
path.join(dir, name, `main.kcl`)
|
||||
)
|
||||
// Wait 1s between each project to ensure the order is correct
|
||||
await new Promise((r) => setTimeout(r, 1_000))
|
||||
}
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const getAllProjects = () => page.getByTestId('project-link').all()
|
||||
|
||||
@ -1086,18 +1008,15 @@ test(
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test.fixme(
|
||||
'When the project folder is empty, user can create new project and open it.',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({ testInfo })
|
||||
async ({ page }, testInfo) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1180,24 +1099,21 @@ extrude001 = extrude(200, sketch001)`)
|
||||
await createProject({ name, page, returnHome: true })
|
||||
await expect(projectLinks.getByText(name)).toBeVisible()
|
||||
}
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await Promise.all([
|
||||
fsp.mkdir(join(dir, 'router-template-slate'), { recursive: true }),
|
||||
fsp.mkdir(join(dir, 'bracket'), { recursive: true }),
|
||||
fsp.mkdir(path.join(dir, 'router-template-slate'), { recursive: true }),
|
||||
fsp.mkdir(path.join(dir, 'bracket'), { recursive: true }),
|
||||
])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
join(
|
||||
path.join(
|
||||
'src',
|
||||
'wasm-lib',
|
||||
'tests',
|
||||
@ -1205,10 +1121,10 @@ test(
|
||||
'inputs',
|
||||
'router-template-slate.kcl'
|
||||
),
|
||||
join(dir, 'router-template-slate', 'main.kcl')
|
||||
path.join(dir, 'router-template-slate', 'main.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
join(
|
||||
path.join(
|
||||
'src',
|
||||
'wasm-lib',
|
||||
'tests',
|
||||
@ -1216,13 +1132,12 @@ test(
|
||||
'inputs',
|
||||
'focusrite_scarlett_mounting_braket.kcl'
|
||||
),
|
||||
join(dir, 'bracket', 'main.kcl')
|
||||
path.join(dir, 'bracket', 'main.kcl')
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1281,18 +1196,14 @@ test(
|
||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||
await expect(page.getByText('New Project')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'You can change the root projects directory and nothing is lost',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page, electronApp }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await Promise.all([
|
||||
fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }),
|
||||
fsp.mkdir(`${dir}/bracket`, { recursive: true }),
|
||||
@ -1307,9 +1218,8 @@ test(
|
||||
`${dir}/bracket/main.kcl`
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1335,8 +1245,7 @@ test(
|
||||
.locator('section#projectDirectory input')
|
||||
.inputValue()
|
||||
|
||||
// Can't use Playwright filechooser since this is happening in electron.
|
||||
const handleFile = electronApp.evaluate(
|
||||
const handleFile = electronApp?.evaluate(
|
||||
async ({ dialog }, filePaths) => {
|
||||
dialog.showOpenDialog = () =>
|
||||
Promise.resolve({ canceled: false, filePaths })
|
||||
@ -1346,9 +1255,9 @@ test(
|
||||
await page.getByTestId('project-directory-button').click()
|
||||
await handleFile
|
||||
|
||||
await expect(page.locator('section#projectDirectory input')).toHaveValue(
|
||||
newProjectDirName
|
||||
)
|
||||
await expect
|
||||
.poll(() => page.locator('section#projectDirectory input').inputValue())
|
||||
.toContain(newProjectDirName)
|
||||
|
||||
await page.getByTestId('settings-close-button').click()
|
||||
|
||||
@ -1366,7 +1275,7 @@ test(
|
||||
|
||||
await page.getByTestId('project-directory-settings-link').click()
|
||||
|
||||
const handleFile = electronApp.evaluate(
|
||||
const handleFile = electronApp?.evaluate(
|
||||
async ({ dialog }, filePaths) => {
|
||||
dialog.showOpenDialog = () =>
|
||||
Promise.resolve({ canceled: false, filePaths })
|
||||
@ -1387,15 +1296,13 @@ test(
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Search projects on desktop home',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectData = [
|
||||
['basic bracket', 'focusrite_scarlett_mounting_braket.kcl'],
|
||||
['basic-cube', 'basic_fillet_cube_end.kcl'],
|
||||
@ -1403,20 +1310,17 @@ test(
|
||||
['router-template-slate', 'router-template-slate.kcl'],
|
||||
['Ancient Temple Block', 'lego.kcl'],
|
||||
]
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// Do these serially to ensure the order is correct
|
||||
for (const [name, file] of projectData) {
|
||||
await fsp.mkdir(join(dir, name), { recursive: true })
|
||||
await fsp.mkdir(path.join(dir, name), { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath(file),
|
||||
join(dir, name, `main.kcl`)
|
||||
path.join(dir, name, `main.kcl`)
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1448,19 +1352,15 @@ test(
|
||||
await expect(page.getByText(name)).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'file pane is scrollable when there are many files',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const testDir = join(dir, 'testProject')
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testDir = path.join(dir, 'testProject')
|
||||
await fsp.mkdir(testDir, { recursive: true })
|
||||
const fileNames = [
|
||||
'angled_line.kcl',
|
||||
@ -1526,13 +1426,12 @@ test(
|
||||
for (const fileName of fileNames) {
|
||||
await fsp.copyFile(
|
||||
executorInputPath(fileName),
|
||||
join(testDir, fileName)
|
||||
path.join(testDir, fileName)
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1558,30 +1457,25 @@ test(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'select all in code editor does not actually select all, just what is visible (regression)',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// src/wasm-lib/tests/executor/inputs/mike_stress_test.kcl
|
||||
const name = 'mike_stress_test'
|
||||
const testDir = join(dir, name)
|
||||
const testDir = path.join(dir, name)
|
||||
await fsp.mkdir(testDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath(`${name}.kcl`),
|
||||
join(testDir, 'main.kcl')
|
||||
path.join(testDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1621,20 +1515,15 @@ test(
|
||||
expect(selectedText.length).toBe(0)
|
||||
await expect(u.codeLocator).toHaveText('')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Settings persist across restarts',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
{ tag: '@electron', cleanProjectDir: true },
|
||||
async ({ page }, testInfo) => {
|
||||
await test.step('We can change a user setting like theme', async () => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -1645,26 +1534,14 @@ test(
|
||||
await expect(page.getByTestId('app-theme')).toHaveValue('dark')
|
||||
|
||||
await page.getByTestId('app-theme').selectOption('light')
|
||||
|
||||
await electronApp.close()
|
||||
})
|
||||
|
||||
await test.step('Starting the app again and we can see the same theme', async () => {
|
||||
let { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
cleanProjectDir: false,
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.reload()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
|
||||
await page.getByTestId('user-settings').click()
|
||||
|
||||
await expect(page.getByTestId('app-theme')).toHaveValue('light')
|
||||
|
||||
await electronApp.close()
|
||||
})
|
||||
}
|
||||
)
|
||||
@ -1673,11 +1550,8 @@ test(
|
||||
test.fixme(
|
||||
'Original project name persist after onboarding',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const getAllProjects = () => page.getByTestId('project-link').all()
|
||||
page.on('console', console.log)
|
||||
@ -1709,7 +1583,5 @@ test.fixme(
|
||||
await expect(projectLink).toContainText(projectNames[index])
|
||||
}
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,32 +1,22 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { join } from 'path'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import path from 'path'
|
||||
import * as fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath } from './test-utils'
|
||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Regression tests', () => {
|
||||
// bugs we found that don't fit neatly into other categories
|
||||
test('bad model has inline error #3251', async ({ page }) => {
|
||||
test('bad model has inline error #3251', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch2 = startSketchOn("XY")
|
||||
@ -38,9 +28,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
@ -56,6 +47,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
})
|
||||
test('user should not have to press down twice in cmdbar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||
@ -64,26 +56,38 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch2 = startSketchOn("XY")
|
||||
sketch001 = startSketchAt([-0, -0])
|
||||
|> line([0, 0], %)
|
||||
|> line([-4.84, -5.29], %)
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([82.33, 238.21], %)
|
||||
|> angledLine([0, 288.63], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
197.97
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await page.goto('/')
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step('Check arrow down works', async () => {
|
||||
await page.getByTestId('command-bar-open-button').hover()
|
||||
await page.getByTestId('command-bar-open-button').click()
|
||||
|
||||
await page
|
||||
.getByRole('option', { name: 'floppy disk arrow Export' })
|
||||
.click()
|
||||
const floppy = page.getByRole('option', {
|
||||
name: 'floppy disk arrow Export',
|
||||
})
|
||||
|
||||
await floppy.click()
|
||||
|
||||
// press arrow down key twice
|
||||
await page.keyboard.press('ArrowDown')
|
||||
@ -115,7 +119,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
)
|
||||
})
|
||||
})
|
||||
test('executes on load', async ({ page }) => {
|
||||
test('executes on load', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -127,9 +131,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|> line([-23.44, 0.52], %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// expand variables section
|
||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||
@ -148,14 +153,15 @@ sketch001 = startSketchAt([-0, -0])
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('re-executes', async ({ page }) => {
|
||||
test('re-executes', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', `myVar = 5`)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||
await variablesTabButton.click()
|
||||
@ -174,7 +180,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
page.locator('.pretty-json-container >> text=myVar:67')
|
||||
).toBeVisible()
|
||||
})
|
||||
test('ProgramMemory can be serialised', async ({ page }) => {
|
||||
test('ProgramMemory can be serialised', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -193,13 +199,14 @@ sketch001 = startSketchAt([-0, -0])
|
||||
}, %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
const messages: string[] = []
|
||||
|
||||
// Listen for all console events and push the message text to an array
|
||||
page.on('console', (message) => messages.push(message.text()))
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -212,19 +219,26 @@ sketch001 = startSketchAt([-0, -0])
|
||||
})
|
||||
})
|
||||
})
|
||||
test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
|
||||
|
||||
// Not relevant to us anymore, or at least for the time being.
|
||||
test.skip('ensure the Zoo logo is not a link in browser app', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const zooLogo = page.locator('[data-testid="app-logo"]')
|
||||
// Make sure it's not a link
|
||||
await expect(zooLogo).not.toHaveAttribute('href')
|
||||
})
|
||||
|
||||
test(
|
||||
'Position _ Is Out Of Range... regression test',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ context, page, homePage }) => {
|
||||
// SKip on windows, its being weird.
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
@ -233,8 +247,8 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.addInitScript(async () => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`exampleSketch = startSketchOn("XZ")
|
||||
@ -250,8 +264,9 @@ sketch001 = startSketchAt([-0, -0])
|
||||
})
|
||||
|
||||
await expect(async () => {
|
||||
await page.goto('/')
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
|
||||
timeout: 1_000,
|
||||
@ -306,6 +321,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
test('when engine fails export we handle the failure and alert the user', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(
|
||||
@ -316,9 +332,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -374,7 +391,6 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.clearCommandLogs()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
@ -408,7 +424,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
test(
|
||||
'ensure you can not export while an export is already going',
|
||||
{ tag: ['@skipLinux', '@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// This is being weird on ubuntu and windows.
|
||||
test.skip(
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
@ -428,9 +444,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -500,20 +517,17 @@ sketch001 = startSketchAt([-0, -0])
|
||||
test(
|
||||
`Network health indicator only appears in modeling view`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Locators
|
||||
@ -539,13 +553,12 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await u.waitForPageLoad()
|
||||
await expect(networkHealthIndicator).toContainText('Connected')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -561,8 +574,9 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', '')
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await u.closeKclCodePanel()
|
||||
})
|
||||
|
||||
|
@ -1,27 +1,20 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { test as test2, expect as expect2 } from './fixtures/fixtureSetup'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
|
||||
import {
|
||||
getMovementUtils,
|
||||
getUtils,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
setup,
|
||||
tearDown,
|
||||
} from './test-utils'
|
||||
import { uuidv4, roundOff } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Sketch tests', () => {
|
||||
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
const selectionsSnippets = {
|
||||
@ -79,9 +72,9 @@ test.describe('Sketch tests', () => {
|
||||
},
|
||||
selectionsSnippets
|
||||
)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -108,6 +101,7 @@ test.describe('Sketch tests', () => {
|
||||
})
|
||||
test('Can delete most of a sketch and the line tool will still work', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -120,12 +114,9 @@ test.describe('Sketch tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect(async () => {
|
||||
await page.mouse.click(700, 200)
|
||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
@ -142,8 +133,7 @@ test.describe('Sketch tests', () => {
|
||||
await page.keyboard.press('Home')
|
||||
await page.keyboard.up('Shift')
|
||||
await page.keyboard.press('Backspace')
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
@ -151,26 +141,31 @@ test.describe('Sketch tests', () => {
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await expect(async () => {
|
||||
await page.mouse.move(700, 200, { steps: 25 })
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||
.toBe(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([12.34, -12.34], %)
|
||||
|> yLine(12.34, %)
|
||||
|
||||
`)
|
||||
await expect
|
||||
.poll(u.crushKclCodeIntoOneLineAndThenMaybeSome, { timeout: 1000 })
|
||||
.toBe(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61,-14.01], %)
|
||||
|> yLine(15.95, %)
|
||||
`
|
||||
.replaceAll(' ', '')
|
||||
.replaceAll('\n', '')
|
||||
)
|
||||
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
||||
})
|
||||
test('Can exit selection of face', async ({ page }) => {
|
||||
|
||||
test('Can exit selection of face', async ({ page, homePage }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', ``)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await expect(
|
||||
@ -187,6 +182,7 @@ test.describe('Sketch tests', () => {
|
||||
test.describe('Can edit segments by dragging their handles', () => {
|
||||
const doEditSegmentsByDraggingHandle = async (
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
openPanes: string[]
|
||||
) => {
|
||||
// Load the app with the code panes
|
||||
@ -202,9 +198,8 @@ test.describe('Sketch tests', () => {
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -318,7 +313,7 @@ test.describe('Sketch tests', () => {
|
||||
|> line([1.97, 2.06], %)
|
||||
|> close(%)`)
|
||||
}
|
||||
test('code pane open at start-handles', async ({ page }) => {
|
||||
test('code pane open at start-handles', async ({ page, homePage }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -331,10 +326,10 @@ test.describe('Sketch tests', () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
await doEditSegmentsByDraggingHandle(page, ['code'])
|
||||
await doEditSegmentsByDraggingHandle(page, homePage, ['code'])
|
||||
})
|
||||
|
||||
test('code pane closed at start-handles', async ({ page }) => {
|
||||
test('code pane closed at start-handles', async ({ page, homePage }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async (persistModelingContext) => {
|
||||
localStorage.setItem(
|
||||
@ -342,12 +337,14 @@ test.describe('Sketch tests', () => {
|
||||
JSON.stringify({ openPanes: [] })
|
||||
)
|
||||
}, PERSIST_MODELING_CONTEXT)
|
||||
await doEditSegmentsByDraggingHandle(page, [])
|
||||
await doEditSegmentsByDraggingHandle(page, homePage, [])
|
||||
})
|
||||
})
|
||||
|
||||
test('Can edit a circle center and radius by dragging its handles', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -358,9 +355,8 @@ test.describe('Sketch tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -399,6 +395,7 @@ test.describe('Sketch tests', () => {
|
||||
).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(1)
|
||||
@ -409,7 +406,9 @@ test.describe('Sketch tests', () => {
|
||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
|
||||
await editor.expectEditor.not.toContain(prevContent)
|
||||
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
})
|
||||
|
||||
@ -422,18 +421,20 @@ test.describe('Sketch tests', () => {
|
||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||
targetPosition: { x: lineEnd.x + dragPX * 2, y: lineEnd.y + dragPX },
|
||||
})
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
await editor.expectEditor.not.toContain(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
})
|
||||
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [7.26, -2.37], radius = 11.44 }, %)
|
||||
`)
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [7.26, -2.37], radius = 11.44 }, %)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -448,9 +449,8 @@ test.describe('Sketch tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -504,11 +504,11 @@ test.describe('Sketch tests', () => {
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
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 },
|
||||
sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
|
||||
targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
@ -517,8 +517,8 @@ test.describe('Sketch tests', () => {
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||
targetPosition: {
|
||||
x: tangentEnd.x + dragPX,
|
||||
y: tangentEnd.y + dragPX,
|
||||
x: tangentEnd.x,
|
||||
y: tangentEnd.y - 15,
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
@ -528,8 +528,8 @@ test.describe('Sketch tests', () => {
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> line([12.68, -1.09], %)
|
||||
|> tangentialArcTo([24.89, 0.68], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
@ -537,6 +537,7 @@ test.describe('Sketch tests', () => {
|
||||
|
||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -551,9 +552,8 @@ test.describe('Sketch tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -636,12 +636,13 @@ test.describe('Sketch tests', () => {
|
||||
|> close(%)
|
||||
|> revolve({ axis = "X" }, %)`)
|
||||
})
|
||||
test('Can add multiple sketches', async ({ page }) => {
|
||||
test('Can add multiple sketches', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
|
||||
@ -736,9 +737,8 @@ test.describe('Sketch tests', () => {
|
||||
scale = 1
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const code = `sketch001 = startSketchOn('-XZ')
|
||||
@ -820,16 +820,19 @@ test.describe('Sketch tests', () => {
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.removeCurrentCode()
|
||||
}
|
||||
test('[0, 100, 100]', async ({ page }) => {
|
||||
test('[0, 100, 100]', async ({ page, homePage }) => {
|
||||
await homePage.goToModelingScene()
|
||||
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
|
||||
})
|
||||
|
||||
test('[0, 10000, 10000]', async ({ page }) => {
|
||||
test('[0, 10000, 10000]', async ({ page, homePage }) => {
|
||||
await homePage.goToModelingScene()
|
||||
await doSnapAtDifferentScales(page, [0, 10000, 10000])
|
||||
})
|
||||
})
|
||||
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||
await page.addInitScript(async () => {
|
||||
@ -847,9 +850,9 @@ test.describe('Sketch tests', () => {
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -885,7 +888,10 @@ test.describe('Sketch tests', () => {
|
||||
timeout: 10_000,
|
||||
})
|
||||
})
|
||||
test("Existing sketch with bad code delete user's code", async ({ page }) => {
|
||||
test("Existing sketch with bad code delete user's code", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -903,9 +909,9 @@ extrude001 = extrude(5, sketch001)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -1049,12 +1055,8 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
|
||||
test('empty-scene default-planes act as expected', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
/**
|
||||
* Tests the following things
|
||||
* 1) The the planes are there on load because the scene is empty
|
||||
@ -1067,9 +1069,7 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
*/
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -1117,7 +1117,7 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
|
||||
// click start Sketch
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 5 })
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
|
||||
const hoveredColor: [number, number, number] = [93, 93, 127]
|
||||
// now that we're expecting the user to select a plan, it does respond to hover
|
||||
await expect
|
||||
@ -1144,8 +1144,6 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.reload()
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -1157,16 +1155,9 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
).toBeLessThan(3)
|
||||
})
|
||||
|
||||
test('Can attempt to sketch on revolved face', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -1191,7 +1182,7 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
)
|
||||
})
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -1223,9 +1214,10 @@ sketch002 = startSketchOn(extrude001, 'END')
|
||||
|
||||
test('Can sketch on face when user defined function was used in the sketch', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Checking for a regression that performs a sketch when a user defined function
|
||||
// is declared at the top of the file and used in the sketch that is being drawn on.
|
||||
@ -1279,7 +1271,7 @@ const rail = startSketchOn('XZ')
|
||||
|
||||
const center = { x: 600, y: 250 }
|
||||
const rectangleSize = 20
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Start a sketch
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
@ -1318,27 +1310,33 @@ const rail = startSketchOn('XZ')
|
||||
})
|
||||
})
|
||||
|
||||
test2.describe('Sketch mode should be toleratant to syntax errors', () => {
|
||||
test2(
|
||||
test.describe('Sketch mode should be toleratant to syntax errors', () => {
|
||||
test(
|
||||
'adding a syntax error, recovers after fixing',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, scene, editor, toolbar }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'a codemirror error appears in this test only on windows, that causes the test to fail only because of our "no new error" logic, but it can not be replicated locally'
|
||||
async ({ page, homePage, context, scene, editor, toolbar }) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl')
|
||||
await app.initialise(file)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const [objClick] = scene.makeMouseHelpers(600, 250)
|
||||
const arrowHeadLocation = { x: 604, y: 129 } as const
|
||||
const arrowHeadLocation = { x: 706, y: 129 } as const
|
||||
const arrowHeadWhite: [number, number, number] = [255, 255, 255]
|
||||
const backgroundGray: [number, number, number] = [28, 28, 28]
|
||||
const verifyArrowHeadColor = async (c: [number, number, number]) =>
|
||||
scene.expectPixelColor(c, arrowHeadLocation, 15)
|
||||
|
||||
await test.step('check chamfer selection changes cursor positon', async () => {
|
||||
await expect2(async () => {
|
||||
await expect(async () => {
|
||||
// sometimes initial click doesn't register
|
||||
await objClick()
|
||||
await editor.expectActiveLinesToBe([
|
||||
@ -1374,24 +1372,36 @@ test2.describe('Sketch mode should be toleratant to syntax errors', () => {
|
||||
// this checks sketch segments have been drawn
|
||||
await verifyArrowHeadColor(arrowHeadWhite)
|
||||
})
|
||||
await app.page.waitForTimeout(100)
|
||||
await page.waitForTimeout(100)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test2.describe(`Sketching with offset planes`, () => {
|
||||
test2(
|
||||
`Can select an offset plane to sketch on`,
|
||||
async ({ app, scene, toolbar, editor }) => {
|
||||
test.describe(`Sketching with offset planes`, () => {
|
||||
test(`Can select an offset plane to sketch on`, async ({
|
||||
context,
|
||||
page,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
// We seed the scene with a single offset plane
|
||||
await app.initialise(`offsetPlane001 = offsetPlane("XY", 10)`)
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`offsetPlane001 = offsetPlane("XY", 10)`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
|
||||
|
||||
await test2.step(`Start sketching on the offset plane`, async () => {
|
||||
await test.step(`Start sketching on the offset plane`, async () => {
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
|
||||
await test2.step(`Hovering should highlight code`, async () => {
|
||||
await test.step(`Hovering should highlight code`, async () => {
|
||||
await planeHover()
|
||||
await editor.expectState({
|
||||
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||
@ -1400,22 +1410,18 @@ test2.describe(`Sketching with offset planes`, () => {
|
||||
})
|
||||
})
|
||||
|
||||
await test2.step(
|
||||
`Clicking should select the plane and enter sketch mode`,
|
||||
async () => {
|
||||
await test.step(`Clicking should select the plane and enter sketch mode`, async () => {
|
||||
await planeClick()
|
||||
// Have to wait for engine-side animation to finish
|
||||
await app.page.waitForTimeout(600)
|
||||
await expect2(toolbar.lineBtn).toBeEnabled()
|
||||
await page.waitForTimeout(600)
|
||||
await expect(toolbar.lineBtn).toBeEnabled()
|
||||
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
||||
await editor.expectState({
|
||||
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||
diagnostics: [],
|
||||
highlightedCode: '',
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -47,7 +47,12 @@ test.beforeEach(async ({ page }) => {
|
||||
|
||||
test.setTimeout(60_000)
|
||||
|
||||
test(
|
||||
|
||||
// We test this end to end already - getting this to work on web just to take
|
||||
// a snapshot of it feels weird. I'd rather our regular tests fail.
|
||||
// The primary failure is doExport now relies on the filesystem. We can follow
|
||||
// up with another PR if we want this back.
|
||||
test.skip(
|
||||
'exports of each format should work',
|
||||
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
||||
async ({ page, context }) => {
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
@ -109,242 +109,21 @@ keychain = startSketchOn("XY")
|
||||
|> close(%)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
// generated from /home/paultag/Downloads/zma-logomark.svg
|
||||
fn svg = (surface, origin, depth) => {
|
||||
let a0 = surface |> startProfileAt([origin[0] + 45.430427, origin[1] + -14.627736], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 0.764157 ],
|
||||
control2: [ 0, 1.528314 ],
|
||||
to: [ 0, 2.292469 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -3.03202, 0 ],
|
||||
control2: [ -6.064039, 0 ],
|
||||
to: [ -9.09606, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -1.077657 ],
|
||||
control2: [ 0, -2.155312 ],
|
||||
to: [ 0, -3.232969 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.741805, 0 ],
|
||||
control2: [ 5.483613, 0 ],
|
||||
to: [ 8.225417, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.740682, -2.961815 ],
|
||||
control2: [ -5.490342, -5.925794 ],
|
||||
to: [ -8.225417, -8.886255 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -0.723995 ],
|
||||
control2: [ 0, -1.447988 ],
|
||||
to: [ 0, -2.171981 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.712124, 0.05061 ],
|
||||
control2: [ 1.511636, -0.09877 ],
|
||||
to: [ 2.172096, 0.07005 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.68573, 0.740811 ],
|
||||
control2: [ 1.371459, 1.481622 ],
|
||||
to: [ 2.057187, 2.222436 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -0.76416 ],
|
||||
control2: [ 0, -1.52832 ],
|
||||
to: [ 0, -2.29248 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 3.032013, 0 ],
|
||||
control2: [ 6.064026, 0 ],
|
||||
to: [ 9.096038, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 1.077657 ],
|
||||
control2: [ 0, 2.155314 ],
|
||||
to: [ 0, 3.232973 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.741312, 0 ],
|
||||
control2: [ -5.482623, 0 ],
|
||||
to: [ -8.223936, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.741313, 2.961108 ],
|
||||
control2: [ 5.482624, 5.922216 ],
|
||||
to: [ 8.223936, 8.883325 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 0.724968 ],
|
||||
control2: [ 0, 1.449938 ],
|
||||
to: [ 0, 2.174907 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.712656, -0.05145 ],
|
||||
control2: [ -1.512554, 0.09643 ],
|
||||
to: [ -2.173592, -0.07298 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.685222, -0.739834 ],
|
||||
control2: [ -1.370445, -1.479669 ],
|
||||
to: [ -2.055669, -2.219505 ]
|
||||
}, %)
|
||||
keychain1 = startSketchOn("XY")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([width, 0], %)
|
||||
|> lineTo([width, height], %)
|
||||
|> lineTo([0, height], %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
let a1 = surface |> startProfileAt([origin[0] + 57.920488, origin[1] + -15.244943], %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.78904, 0.106635 ],
|
||||
control2: [ -5.052548, -2.969529 ],
|
||||
to: [ -4.055141, -5.598369 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.841523, -0.918736 ],
|
||||
control2: [ 0.439412, -1.541892 ],
|
||||
to: [ -0.368488, -2.214378 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.418245, -0.448461 ],
|
||||
control2: [ -0.836489, -0.896922 ],
|
||||
to: [ -1.254732, -1.345384 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.76806, 2.995359 ],
|
||||
control2: [ -2.32667, 8.18409 ],
|
||||
to: [ 0.897655, 10.678932 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.562822, 2.186098 ],
|
||||
control2: [ 6.605111, 2.28043 ],
|
||||
to: [ 9.271202, 0.226476 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.743744, -0.797465 ],
|
||||
control2: [ -1.487487, -1.594932 ],
|
||||
to: [ -2.231232, -2.392397 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.672938, 0.421422 ],
|
||||
control2: [ -1.465362, 0.646946 ],
|
||||
to: [ -2.259264, 0.64512 ]
|
||||
}, %)
|
||||
keychain2 = startSketchOn("XY")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([width, 0], %)
|
||||
|> lineTo([width, height], %)
|
||||
|> lineTo([0, height], %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a2 = surface |> startProfileAt([origin[0] + 62.19406300000001, origin[1] + -19.500698999999997], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.302938, 1.281141 ],
|
||||
control2: [ -1.53575, 2.434288 ],
|
||||
to: [ -0.10908, 3.279477 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.504637, 0.54145 ],
|
||||
control2: [ 1.009273, 1.082899 ],
|
||||
to: [ 1.513909, 1.624348 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.767778, -2.995425 ],
|
||||
control2: [ 2.327135, -8.184384 ],
|
||||
to: [ -0.897661, -10.679047 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.562947, -2.186022 ],
|
||||
control2: [ -6.604089, -2.279606 ],
|
||||
to: [ -9.271196, -0.227813 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.744231, 0.797952 ],
|
||||
control2: [ 1.488461, 1.595904 ],
|
||||
to: [ 2.232692, 2.393856 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.302377, -1.564629 ],
|
||||
control2: [ 5.793126, -0.15358 ],
|
||||
to: [ 6.396577, 2.547372 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.08981, 0.346302 ],
|
||||
control2: [ 0.134865, 0.704078 ],
|
||||
to: [ 0.13476, 1.061807 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a3 = surface |> startProfileAt([origin[0] + 74.124866, origin[1] + -15.244943], %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.78904, 0.106635 ],
|
||||
control2: [ -5.052549, -2.969529 ],
|
||||
to: [ -4.055142, -5.598369 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.841527, -0.918738 ],
|
||||
control2: [ 0.43941, -1.541892 ],
|
||||
to: [ -0.368497, -2.214367 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.418254, -0.448466 ],
|
||||
control2: [ -0.836507, -0.896931 ],
|
||||
to: [ -1.254761, -1.345395 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.768019, 2.995371 ],
|
||||
control2: [ -2.326624, 8.184088 ],
|
||||
to: [ 0.897678, 10.678932 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.56289, 2.186191 ],
|
||||
control2: [ 6.60516, 2.280307 ],
|
||||
to: [ 9.271371, 0.226476 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.743808, -0.797465 ],
|
||||
control2: [ -1.487616, -1.594932 ],
|
||||
to: [ -2.231424, -2.392397 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.672916, 0.421433 ],
|
||||
control2: [ -1.465344, 0.646926 ],
|
||||
to: [ -2.259225, 0.64512 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a4 = surface |> startProfileAt([origin[0] + 77.57333899999998, origin[1] + -16.989262999999998], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.743298, 0.797463 ],
|
||||
control2: [ 1.486592, 1.594926 ],
|
||||
to: [ 2.229888, 2.392389 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.767827, -2.995393 ],
|
||||
control2: [ 2.327103, -8.184396 ],
|
||||
to: [ -0.897672, -10.679047 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.562939, -2.186037 ],
|
||||
control2: [ -6.604077, -2.279589 ],
|
||||
to: [ -9.271185, -0.227813 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.744243, 0.797952 ],
|
||||
control2: [ 1.488486, 1.595904 ],
|
||||
to: [ 2.232729, 2.393856 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.302394, -1.564623 ],
|
||||
control2: [ 5.793201, -0.153598 ],
|
||||
to: [ 6.396692, 2.547372 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.32074, 1.215468 ],
|
||||
control2: [ 0.06159, 2.564765 ],
|
||||
to: [ -0.690452, 3.573243 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
box = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
@ -364,18 +143,12 @@ box = startSketchOn('XY')
|
||||
axis: revolveAxis,
|
||||
angle: 90
|
||||
}, %)
|
||||
return 0
|
||||
}
|
||||
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0.0, 0.0], %)
|
||||
|> xLine(0.0, %)
|
||||
|> close(%)
|
||||
|
||||
|
||||
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
|
||||
|
||||
startSketchOn(keychain, 'end')
|
||||
|> circle({ center: [
|
||||
width / 2,
|
||||
height - (keychainHoleSize + 1.5)
|
||||
], radius: keychainHoleSize }, %)
|
||||
|> extrude(-thickness, %)`
|
||||
`
|
||||
|
||||
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
||||
|
@ -1,29 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { commonPoints, getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { commonPoints, getUtils } from './test-utils'
|
||||
|
||||
test.describe('Test network and connection issues', () => {
|
||||
test('simulate network down and network little widget', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
|
||||
@ -62,7 +49,7 @@ test.describe('Test network and connection issues', () => {
|
||||
})
|
||||
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Offline')
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
@ -93,26 +80,19 @@ test.describe('Test network and connection issues', () => {
|
||||
|
||||
test('Engine disconnect & reconnect in sketch mode', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
@ -156,7 +136,7 @@ test.describe('Test network and connection issues', () => {
|
||||
})
|
||||
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Offline')
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
|
||||
// Ensure we are not in sketch mode
|
||||
await expect(
|
||||
|
@ -1,22 +1,20 @@
|
||||
import {
|
||||
expect,
|
||||
Page,
|
||||
Download,
|
||||
BrowserContext,
|
||||
TestInfo,
|
||||
_electron as electron,
|
||||
Locator,
|
||||
test,
|
||||
} from '@playwright/test'
|
||||
import { test, Page } from './zoo-test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import fsp from 'fs/promises'
|
||||
import fsSync from 'fs'
|
||||
import { join } from 'path'
|
||||
import path from 'path'
|
||||
import pixelMatch from 'pixelmatch'
|
||||
import { PNG } from 'pngjs'
|
||||
import { Protocol } from 'playwright-core/types/protocol'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { APP_NAME, COOKIE_NAME } from 'lib/constants'
|
||||
import { COOKIE_NAME } from 'lib/constants'
|
||||
import { secrets } from './secrets'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
@ -30,6 +28,10 @@ import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const toNormalizedCode = (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
}
|
||||
|
||||
type TestColor = [number, number, number]
|
||||
export const TEST_COLORS = {
|
||||
WHITE: [249, 249, 249] as TestColor,
|
||||
@ -98,11 +100,16 @@ async function removeCurrentCode(page: Page) {
|
||||
}
|
||||
|
||||
export async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
||||
await page.getByTestId('custom-cmd-input').fill(JSON.stringify(cmd))
|
||||
const json = JSON.stringify(cmd)
|
||||
await page.getByTestId('custom-cmd-input').fill(json)
|
||||
await expect(page.getByTestId('custom-cmd-input')).toHaveValue(json)
|
||||
await page.getByTestId('custom-cmd-send-button').scrollIntoViewIfNeeded()
|
||||
await page.getByTestId('custom-cmd-send-button').click()
|
||||
}
|
||||
|
||||
async function clearCommandLogs(page: Page) {
|
||||
await page.getByTestId('custom-cmd-input').fill('')
|
||||
await page.getByTestId('clear-commands').scrollIntoViewIfNeeded()
|
||||
await page.getByTestId('clear-commands').click()
|
||||
}
|
||||
|
||||
@ -150,6 +157,19 @@ export async function closePane(page: Page, testId: string) {
|
||||
|
||||
async function openKclCodePanel(page: Page) {
|
||||
await openPane(page, 'code-pane-button')
|
||||
|
||||
// Code Mirror lazy loads text! Wowza! Let's force-load the text for tests.
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
selection: {
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
anchor: editorManager._editorView.docView.length,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function closeKclCodePanel(page: Page) {
|
||||
@ -165,6 +185,9 @@ async function closeKclCodePanel(page: Page) {
|
||||
|
||||
async function openDebugPanel(page: Page) {
|
||||
await openPane(page, 'debug-pane-button')
|
||||
|
||||
// The debug pane needs time to load everything.
|
||||
await page.waitForTimeout(3000)
|
||||
}
|
||||
|
||||
export async function closeDebugPanel(page: Page) {
|
||||
@ -412,6 +435,10 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
.boundingBox({ timeout: 5_000 })
|
||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
||||
codeLocator: page.locator('.cm-content'),
|
||||
crushKclCodeIntoOneLineAndThenMaybeSome: async () => {
|
||||
const code = await page.locator('.cm-content').innerText()
|
||||
return code.replaceAll(' ', '').replaceAll('\n', '')
|
||||
},
|
||||
normalisedEditorCode: async () => {
|
||||
const code = await page.locator('.cm-content').innerText()
|
||||
return normaliseKclNumbers(code)
|
||||
@ -482,13 +509,18 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
)
|
||||
},
|
||||
|
||||
toNormalizedCode: (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
toNormalizedCode(text: string) {
|
||||
return toNormalizedCode(text)
|
||||
},
|
||||
|
||||
editorTextMatches: async (code: string) => {
|
||||
async editorTextMatches(code: string) {
|
||||
const editor = page.locator(editorSelector)
|
||||
return expect(editor).toHaveText(code, { useInnerText: true })
|
||||
return expect
|
||||
.poll(async () => {
|
||||
const text = await editor.textContent()
|
||||
return toNormalizedCode(text ?? '')
|
||||
})
|
||||
.toContain(toNormalizedCode(code))
|
||||
},
|
||||
|
||||
pasteCodeInEditor: async (code: string) => {
|
||||
@ -514,7 +546,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
await page.getByTestId('create-file-button').click()
|
||||
await page.getByTestId('file-rename-field').fill(name)
|
||||
await page.getByTestId('tree-input-field').fill(name)
|
||||
await page.keyboard.press('Enter')
|
||||
})
|
||||
},
|
||||
@ -674,6 +706,34 @@ export const makeTemplate: (
|
||||
}
|
||||
}
|
||||
|
||||
const PLAYWRIGHT_DOWNLOAD_DIR = 'downloads-during-playwright'
|
||||
|
||||
export const getPlaywrightDownloadDir = (page: Page) => {
|
||||
return path.resolve(page.dir, PLAYWRIGHT_DOWNLOAD_DIR)
|
||||
}
|
||||
|
||||
const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
|
||||
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
||||
|
||||
const downloadDir = getPlaywrightDownloadDir(page)
|
||||
|
||||
// Expect there to be at least one file
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const files = await fsp.readdir(downloadDir)
|
||||
return files.length
|
||||
})
|
||||
.toBe(1)
|
||||
|
||||
// Go through the downloads dir and move files to new location
|
||||
const files = await fsp.readdir(downloadDir)
|
||||
|
||||
// Assumption: only ever one file here.
|
||||
for (let file of files) {
|
||||
await fsp.rename(path.resolve(downloadDir, file), toLocation)
|
||||
}
|
||||
}
|
||||
|
||||
export interface Paths {
|
||||
modelPath: string
|
||||
imagePath: string
|
||||
@ -686,7 +746,8 @@ export const doExport = async (
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
): Promise<Paths> => {
|
||||
if (exportFrom === 'dropdown') {
|
||||
await page.getByRole('button', { name: APP_NAME }).click()
|
||||
await page.getByTestId('project-sidebar-toggle').click()
|
||||
|
||||
const exportMenuButton = page.getByRole('button', {
|
||||
name: 'Export current part',
|
||||
})
|
||||
@ -727,25 +788,12 @@ export const doExport = async (
|
||||
}
|
||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||
|
||||
const getPromiseAndResolve = () => {
|
||||
let resolve: any = () => {}
|
||||
const promise = new Promise<Download>((r) => {
|
||||
resolve = r
|
||||
})
|
||||
return [promise, resolve]
|
||||
}
|
||||
|
||||
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
||||
let downloadCnt = 0
|
||||
|
||||
if (exportFrom === 'dropdown')
|
||||
page.on('download', async (download) => {
|
||||
if (downloadCnt === 0) {
|
||||
downloadResolve1(download)
|
||||
}
|
||||
downloadCnt++
|
||||
})
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
|
||||
// This usually happens immediately after. If we're too slow we don't
|
||||
// catch it.
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
|
||||
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
||||
return {
|
||||
modelPath: '',
|
||||
@ -755,15 +803,12 @@ export const doExport = async (
|
||||
}
|
||||
|
||||
// Handle download
|
||||
const download = await downloadPromise1
|
||||
const downloadLocationer = (extra = '', isImage = false) =>
|
||||
`./e2e/playwright/export-snapshots/${output.type}-${
|
||||
'storage' in output ? output.storage : ''
|
||||
}${extra}.${isImage ? 'png' : output.type}`
|
||||
const downloadLocation = downloadLocationer()
|
||||
|
||||
await download.saveAs(downloadLocation)
|
||||
|
||||
if (output.type === 'step') {
|
||||
// stable timestamps for step files
|
||||
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
||||
@ -772,6 +817,12 @@ export const doExport = async (
|
||||
'1970-01-01T00:00:00.0+00:00'
|
||||
)
|
||||
await fsp.writeFile(downloadLocation, newFileContents)
|
||||
} else {
|
||||
// By default all files are downloaded to the same place in playwright
|
||||
// (declared in src/lib/exportSave)
|
||||
// To remain consistent with our old web tests, we want to move some downloads
|
||||
// (images) to another directory.
|
||||
await moveDownloadedFileTo(page, downloadLocation)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -798,6 +849,8 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
||||
// It seems it's best to give the browser about 3s to close things
|
||||
// It's not super reliable but we have no real other choice for now
|
||||
await page.waitForTimeout(3000)
|
||||
|
||||
await testInfo.tronApp?.close()
|
||||
}
|
||||
|
||||
// settingsOverrides may need to be augmented to take more generic items,
|
||||
@ -808,12 +861,19 @@ export async function setup(
|
||||
testInfo?: TestInfo
|
||||
) {
|
||||
await context.addInitScript(
|
||||
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
||||
async ({
|
||||
token,
|
||||
settingsKey,
|
||||
settings,
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
PLAYWRIGHT_TEST_DIR,
|
||||
}) => {
|
||||
localStorage.clear()
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||
localStorage.setItem('persistCode', ``)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
||||
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
|
||||
},
|
||||
{
|
||||
token: secrets.token,
|
||||
@ -830,6 +890,7 @@ export async function setup(
|
||||
} as Partial<SaveSettingsPayload>,
|
||||
}),
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
@ -887,13 +948,17 @@ export async function setupElectron({
|
||||
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
||||
: {}),
|
||||
})
|
||||
|
||||
const context = electronApp.context()
|
||||
const page = await electronApp.firstWindow()
|
||||
|
||||
page.TEST_SETTINGS_FILE_KEY = projectDirName
|
||||
|
||||
context.on('console', console.log)
|
||||
page.on('console', console.log)
|
||||
|
||||
if (cleanProjectDir) {
|
||||
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const settingsOverrides = TOML.stringify(
|
||||
appSettings
|
||||
? {
|
||||
@ -924,7 +989,7 @@ export async function setupElectron({
|
||||
|
||||
await setup(context, page)
|
||||
|
||||
return { electronApp, page, dir: projectDirName }
|
||||
return { electronApp, page, context, dir: projectDirName }
|
||||
}
|
||||
|
||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||
@ -1010,7 +1075,7 @@ export async function createProject({
|
||||
}
|
||||
|
||||
export function executorInputPath(fileName: string): string {
|
||||
return join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||
return path.join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||
}
|
||||
|
||||
export async function doAndWaitForImageDiff(
|
||||
@ -1101,3 +1166,12 @@ export function getPixelRGBs(page: Page) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
|
||||
return expect
|
||||
.poll(async () => {
|
||||
const lines = await page.locator('.cm-activeLine').all()
|
||||
return lines.length
|
||||
})
|
||||
.toBe(lines)
|
||||
}
|
||||
|
@ -1,23 +1,14 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.describe('Testing Camera Movement', () => {
|
||||
test('Can move camera reliably', async ({ page, context }) => {
|
||||
test.skip(process.platform === 'darwin', 'Can move camera reliably')
|
||||
test('Can move camera reliably', async ({ page, context, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
@ -183,6 +174,7 @@ test.describe('Testing Camera Movement', () => {
|
||||
|
||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
||||
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
||||
@ -190,9 +182,9 @@ test.describe('Testing Camera Movement', () => {
|
||||
|
||||
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
@ -344,7 +336,10 @@ test.describe('Testing Camera Movement', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test(`Zoom by scroll should not fire while orbiting`, async ({ page }) => {
|
||||
test(`Zoom by scroll should not fire while orbiting`, async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
/**
|
||||
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
||||
* set within cameraMouseDragGuards in cameraControls.ts,
|
||||
@ -383,7 +378,7 @@ test.describe('Testing Camera Movement', () => {
|
||||
const expectedOrbitCamZPosition = 64.0
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.closeKclCodePanel()
|
||||
// This test requires the mouse controls to be set to Solidworks
|
||||
await u.openDebugPanel()
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils, setup, tearDown, TEST_COLORS } from './test-utils'
|
||||
import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
pollEditorLinesSelectedLength,
|
||||
} from './test-utils'
|
||||
import { XOR } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing constraints', () => {
|
||||
test('Can constrain line length', async ({ page }) => {
|
||||
test('Can constrain line length', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -27,9 +23,10 @@ test.describe('Testing constraints', () => {
|
||||
|
||||
const u = await getUtils(page)
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -41,7 +38,9 @@ test.describe('Testing constraints', () => {
|
||||
|
||||
// enter sketch again
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(500) // wait for animation
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const startXPx = 500
|
||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||
@ -67,12 +66,14 @@ test.describe('Testing constraints', () => {
|
||||
|
||||
// Exit sketch
|
||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||
await page.keyboard.press('Escape')
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).not.toBeVisible()
|
||||
await expect
|
||||
.poll(async () => {
|
||||
await page.keyboard.press('Escape', { delay: 500 })
|
||||
return page.getByRole('button', { name: 'Exit Sketch' }).isVisible()
|
||||
})
|
||||
test(`Remove constraints`, async ({ page }) => {
|
||||
.toBe(true)
|
||||
})
|
||||
test(`Remove constraints`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -92,13 +93,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`)
|
||||
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
@ -111,8 +116,8 @@ part002 = startSketchOn('XZ')
|
||||
await page.getByRole('button', { name: 'remove constraints' }).click()
|
||||
|
||||
await page.getByText('line([39.13, 68.63], %)').click()
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(1)
|
||||
await expect(activeLinesContent[0]).toHaveText('|> line([39.13, 68.63], %)')
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
@ -130,7 +135,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, offset } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -148,25 +153,57 @@ part002 = startSketchOn('XZ')
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
|
||||
const isChecked = await createNewVariableCheckbox.isChecked()
|
||||
const addVariable = testName === 'Add variable'
|
||||
XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct
|
||||
(await createNewVariableCheckbox.click())
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Add constraining value' })
|
||||
.click()
|
||||
|
||||
// Wait for the codemod to take effect
|
||||
await expect(page.locator('.cm-content')).toContainText(`angle: -57,`)
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
`offset: ${offset},`
|
||||
)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
`|> line([74.36, 130.4], %, $seg01)`
|
||||
)
|
||||
await expect(activeLinesContent[1]).toHaveText(`}, %)`)
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Give time for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
])
|
||||
|
||||
await page.mouse.click(line1.x, line1.y)
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||
await page.keyboard.up('Shift')
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -194,6 +231,7 @@ part002 = startSketchOn('XZ')
|
||||
`offset = ${offset},`
|
||||
)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
`|> line([74.36, 130.4], %, $seg01)`
|
||||
@ -229,7 +267,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, value, constraint } of cases) {
|
||||
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -249,13 +287,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
@ -335,7 +377,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, constraint } of cases) {
|
||||
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -355,13 +397,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
])
|
||||
@ -372,9 +418,11 @@ part002 = startSketchOn('XZ')
|
||||
await page.mouse.click(900, 250)
|
||||
}
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -442,7 +490,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, axisSelect } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -462,13 +510,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
@ -552,7 +604,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, constraint } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -572,13 +624,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="${2}"]`
|
||||
)
|
||||
@ -628,7 +684,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async (customCode) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -648,13 +704,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line1 = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="${0}"]`
|
||||
)
|
||||
@ -673,8 +733,8 @@ part002 = startSketchOn('XZ')
|
||||
await page.keyboard.up('Shift')
|
||||
|
||||
// check actives lines
|
||||
await pollEditorLinesSelectedLength(page, codeAfter.length)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(codeAfter.length)
|
||||
|
||||
const constraintMenuButton = page.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -725,7 +785,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -744,13 +804,17 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||
|
||||
@ -777,8 +841,8 @@ part002 = startSketchOn('XZ')
|
||||
// check there are still 2 cursors (they should stay on the same lines as before constraint was applied)
|
||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||
// check actives lines
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(2)
|
||||
|
||||
// check both cursors are where they should be after constraint is applied
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
@ -802,7 +866,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName, axisClick } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -821,21 +885,28 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||
|
||||
// select segment and axis by holding down shift
|
||||
await page.mouse.click(line3.x - 3, line3.y + 20)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(axisClick.x, axisClick.y)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
const constraintMenuButton = page.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
@ -857,6 +928,7 @@ part002 = startSketchOn('XZ')
|
||||
|
||||
test('Horizontally constrained line remains selected after applying constraint', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
test.setTimeout(70_000)
|
||||
await page.addInitScript(async () => {
|
||||
@ -869,9 +941,10 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
||||
@ -879,6 +952,9 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
const lineBefore = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="1"]`,
|
||||
@ -899,11 +975,17 @@ part002 = startSketchOn('XZ')
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.waitForTimeout(500)
|
||||
await page.getByRole('button', { name: 'Horizontal', exact: true }).click()
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
||||
|
||||
// Wait for code editor to settle.
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
// If the overlay-angle is updated the THREE.js scene is in a good state
|
||||
await expect(
|
||||
await page.locator('[data-overlay-index="1"]')
|
||||
@ -913,11 +995,17 @@ part002 = startSketchOn('XZ')
|
||||
`[data-overlay-index="1"]`,
|
||||
0
|
||||
)
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
const linebb = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||
await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
|
||||
await page.mouse.click(linebb.x, linebb.y)
|
||||
|
||||
await expect
|
||||
.poll(async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE))
|
||||
.toBeLessThan(3)
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -931,6 +1019,7 @@ part002 = startSketchOn('XZ')
|
||||
await page.getByLabel('length Value').fill('10')
|
||||
await page.getByRole('button', { name: 'Add constraining value' }).click()
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||
|
||||
|
@ -1,18 +1,9 @@
|
||||
import { _test, _expect } from './playwright-deprecated'
|
||||
import { test } from './fixtures/fixtureSetup'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { TEST_CODE_GIZMO } from './storageStates'
|
||||
|
||||
_test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
_test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
_test.describe('Testing Gizmo', () => {
|
||||
test.describe('Testing Gizmo', () => {
|
||||
const cases = [
|
||||
{
|
||||
testDescription: 'top view',
|
||||
@ -57,14 +48,17 @@ _test.describe('Testing Gizmo', () => {
|
||||
expectedCameraTarget,
|
||||
testDescription,
|
||||
} of cases) {
|
||||
_test(`check ${testDescription}`, async ({ page, browserName }) => {
|
||||
test(`check ${testDescription}`, async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||
}, TEST_CODE_GIZMO)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -117,30 +111,30 @@ _test.describe('Testing Gizmo', () => {
|
||||
|
||||
await Promise.all([
|
||||
// position
|
||||
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expectedCameraPosition.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expectedCameraPosition.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expectedCameraPosition.z.toString()
|
||||
),
|
||||
// target
|
||||
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expectedCameraTarget.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expectedCameraTarget.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expectedCameraTarget.z.toString()
|
||||
),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
_test('Context menu and popover menu', async ({ page }) => {
|
||||
test('Context menu and popover menu', async ({ page, homePage }) => {
|
||||
const testCase = {
|
||||
testDescription: 'Right view',
|
||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||
@ -152,9 +146,9 @@ _test.describe('Testing Gizmo', () => {
|
||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||
}, TEST_CODE_GIZMO)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await page.waitForTimeout(100)
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -196,7 +190,7 @@ _test.describe('Testing Gizmo', () => {
|
||||
const buttonToTest = page.getByRole('button', {
|
||||
name: testCase.testDescription,
|
||||
})
|
||||
await _expect(buttonToTest).toBeVisible()
|
||||
await expect(buttonToTest).toBeVisible()
|
||||
await buttonToTest.click()
|
||||
|
||||
// Now assert we've moved to the correct view
|
||||
@ -215,23 +209,23 @@ _test.describe('Testing Gizmo', () => {
|
||||
|
||||
await Promise.all([
|
||||
// position
|
||||
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.z.toString()
|
||||
),
|
||||
// target
|
||||
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.z.toString()
|
||||
),
|
||||
])
|
||||
@ -242,32 +236,59 @@ _test.describe('Testing Gizmo', () => {
|
||||
const gizmoPopoverButton = page.getByRole('button', {
|
||||
name: 'view settings',
|
||||
})
|
||||
await _expect(gizmoPopoverButton).toBeVisible()
|
||||
await expect(gizmoPopoverButton).toBeVisible()
|
||||
await gizmoPopoverButton.click()
|
||||
await _expect(buttonToTest).toBeVisible()
|
||||
await expect(buttonToTest).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
test('Center on selection from menu', async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`
|
||||
const sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([-108.83, -57.48], %)
|
||||
|> angledLine([0, 105.13], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
77.9
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %)
|
||||
|> close(%)
|
||||
const sketch001 = startSketchOn('XZ')
|
||||
|> circle({
|
||||
center: [818.33, 168.1],
|
||||
radius: 182.8
|
||||
}, %)
|
||||
|> extrude(50, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
const u = await getUtils(page)
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||
await app.initialise(file)
|
||||
await scene.expectState({
|
||||
camera: {
|
||||
position: [4982.21, -23865.37, 13810.64],
|
||||
target: [4982.21, 0, 2737.1],
|
||||
position: [11912.6, -39586.98, 21391.21],
|
||||
target: [11912.6, -635, 3317.49],
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -275,7 +296,7 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
|
||||
await test.step(`Select an edge of this circle`, async () => {
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
'circle({ center: [818.33, 168.1], radius: 182.8 }, %)'
|
||||
await moveToCircle()
|
||||
await clickCircle()
|
||||
await editor.expectState({
|
||||
@ -292,8 +313,8 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
await test.step(`Verify the camera moved`, async () => {
|
||||
await scene.expectState({
|
||||
camera: {
|
||||
position: [0, -23865.37, 11073.53],
|
||||
target: [0, 0, 0],
|
||||
position: [20785.58, -40221.98, 22343.46],
|
||||
target: [20785.58, -1270, 4269.74],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
@ -1,18 +1,8 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.describe('Test toggling perspective', () => {
|
||||
test('via command palette and toggle', async ({ page }) => {
|
||||
test.fixme('via command palette and toggle', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Locators and constants
|
||||
@ -20,7 +10,7 @@ test.describe('Test toggling perspective', () => {
|
||||
const screenHeight = 500
|
||||
const checkedScreenLocation = {
|
||||
x: screenWidth * 0.71,
|
||||
y: screenHeight * 0.4,
|
||||
y: screenHeight * 0.2,
|
||||
}
|
||||
const backgroundColor: [number, number, number] = [29, 29, 29]
|
||||
const xzPlaneColor: [number, number, number] = [82, 55, 96]
|
||||
@ -40,8 +30,8 @@ test.describe('Test toggling perspective', () => {
|
||||
})
|
||||
|
||||
await test.step('Setup', async () => {
|
||||
await page.setViewportSize({ width: screenWidth, height: screenHeight })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: screenWidth, height: screenHeight })
|
||||
await homePage.goToModelingScene()
|
||||
await u.closeKclCodePanel()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(backgroundColor), {
|
||||
@ -52,11 +42,17 @@ test.describe('Test toggling perspective', () => {
|
||||
await expect(projectionToggle).toHaveAttribute('aria-checked', 'true')
|
||||
})
|
||||
|
||||
// Extremely wild note: flicking between ortho and persp actually changes
|
||||
// the orientation of the axis/camera. How can you see this? Well toggle it,
|
||||
// then refresh. You'll see it doesn't match what we left.
|
||||
await test.step('Switch to ortho via command palette', async () => {
|
||||
await commandPaletteButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandOption.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await orthoOption.click()
|
||||
await expect(commandToast).toBeVisible()
|
||||
await expect(commandToast).not.toBeVisible()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(xzPlaneColor), {
|
||||
timeout: 5000,
|
||||
@ -67,27 +63,9 @@ test.describe('Test toggling perspective', () => {
|
||||
})
|
||||
|
||||
await test.step(`Refresh the page and ensure the stream is loaded in ortho`, async () => {
|
||||
// In playwright web, the settings set while testing are not persisted because
|
||||
// the `addInitScript` within `setup` is re-run on page reload
|
||||
await page.addInitScript(
|
||||
({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
modeling: {
|
||||
...TEST_SETTINGS.modeling,
|
||||
cameraProjection: 'orthographic',
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
await page.reload()
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.waitForTimeout(1000)
|
||||
await u.closeKclCodePanel()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(xzPlaneColor), {
|
||||
timeout: 5000,
|
||||
|
@ -1,35 +1,30 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { FILE_EXT } from 'lib/constants'
|
||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing in-app sample loading', () => {
|
||||
/**
|
||||
* Note this test implicitly depends on the KCL sample "car-wheel.kcl",
|
||||
* its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl
|
||||
*/
|
||||
test('Web: should overwrite current code, cannot create new file', async ({
|
||||
editor,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.addInitScript((code) => {
|
||||
await context.addInitScript((code) => {
|
||||
window.localStorage.setItem('persistCode', code)
|
||||
}, bracket)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
})
|
||||
|
||||
// Locators and constants
|
||||
@ -54,13 +49,13 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
const warningText = page.getByText('Overwrite current file and units?')
|
||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const codeLocator = page.locator('.cm-content')
|
||||
const unitsToast = (unit: UnitLength_type) =>
|
||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
await expect(codeLocator).toContainText(bracket.split('\n')[0])
|
||||
await editor.scrollToText(bracket.split('\n')[0])
|
||||
await editor.expectEditor.toContain(bracket.split('\n')[0])
|
||||
})
|
||||
|
||||
await test.step(`Load a KCL sample with the command palette`, async () => {
|
||||
@ -73,7 +68,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
await expect(warningText).toBeVisible()
|
||||
await confirmButton.click()
|
||||
|
||||
await expect(codeLocator).toContainText('// ' + newSample.title)
|
||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||
await expect(unitsToast('in')).toBeVisible()
|
||||
})
|
||||
})
|
||||
@ -86,16 +81,13 @@ test.describe('Testing in-app sample loading', () => {
|
||||
test(
|
||||
'Desktop: should create new file by default, optionally overwrite',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page, dir } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ editor, context, page }, testInfo) => {
|
||||
const { dir } = await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -134,19 +126,19 @@ test.describe('Testing in-app sample loading', () => {
|
||||
page.getByRole('listitem').filter({
|
||||
has: page.getByRole('button', { name }),
|
||||
})
|
||||
const codeLocator = page.locator('.cm-content')
|
||||
const unitsToast = (unit: UnitLength_type) =>
|
||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await projectCard.click()
|
||||
await u.waitForPageLoad()
|
||||
})
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
await expect(codeLocator).toContainText(bracket.split('\n')[0])
|
||||
await editor.scrollToText(bracket.split('\n')[0])
|
||||
await editor.expectEditor.toContain(bracket.split('\n')[0])
|
||||
await u.openFilePanel()
|
||||
|
||||
await expect(projectMenuButton).toContainText('main.kcl')
|
||||
@ -163,7 +155,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Ensure we made and opened a new file`, async () => {
|
||||
await expect(codeLocator).toContainText('// ' + sampleOne.title)
|
||||
await editor.expectEditor.toContain('// ' + sampleOne.title)
|
||||
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
await expect(unitsToast('in')).toBeVisible()
|
||||
@ -182,7 +174,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
|
||||
await expect(codeLocator).toContainText('// ' + sampleTwo.title)
|
||||
await editor.expectEditor.toContain('// ' + sampleTwo.title)
|
||||
await test.step(`Check actual file contents`, async () => {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
@ -198,8 +190,6 @@ test.describe('Testing in-app sample loading', () => {
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
await expect(unitsToast('mm')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,16 +1,9 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
|
||||
import { deg, getUtils, setup, tearDown, wiggleMove } from './test-utils'
|
||||
import { deg, getUtils, wiggleMove } from './test-utils'
|
||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { EditorFixture } from './fixtures/editorFixture'
|
||||
|
||||
test.describe('Testing segment overlays', () => {
|
||||
test.describe('Hover over a segment should show its overlay, hovering over the input overlays should show its popover, clicking the input overlay should constrain/unconstrain it:\nfor the following segments', () => {
|
||||
@ -24,7 +17,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickConstrained =
|
||||
(page: Page) =>
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -58,10 +51,11 @@ test.describe('Testing segment overlays', () => {
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(x, y)
|
||||
await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
expectBeforeUnconstrained
|
||||
)
|
||||
await editor.expectEditor.toContain(expectBeforeUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
const constrainedLocator = page.locator(
|
||||
`[data-constraint-type="${constraintType}"][data-is-constrained="true"]`
|
||||
)
|
||||
@ -71,9 +65,9 @@ test.describe('Testing segment overlays', () => {
|
||||
await page.getByTestId('constraint-symbol-popover').count()
|
||||
).toBeGreaterThan(0)
|
||||
await constrainedLocator.click()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
expectAfterUnconstrained
|
||||
)
|
||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await page.mouse.move(0, 0)
|
||||
await page.waitForTimeout(1000)
|
||||
@ -81,6 +75,7 @@ test.describe('Testing segment overlays', () => {
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(x, y)
|
||||
await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
const unconstrainedLocator = page.locator(
|
||||
`[data-constraint-type="${constraintType}"][data-is-constrained="false"]`
|
||||
@ -92,7 +87,9 @@ test.describe('Testing segment overlays', () => {
|
||||
).toBeGreaterThan(0)
|
||||
await unconstrainedLocator.click()
|
||||
await page.getByText('Add variable').click()
|
||||
await expect(page.locator('.cm-content')).toContainText(expectFinal)
|
||||
await editor.expectEditor.toContain(expectFinal, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +102,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickUnconstrained =
|
||||
(page: Page) =>
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -137,11 +134,12 @@ test.describe('Testing segment overlays', () => {
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(x, y)
|
||||
await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
await expect(page.getByText('Added variable')).not.toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
expectBeforeUnconstrained
|
||||
)
|
||||
await editor.expectEditor.toContain(expectBeforeUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
const unconstrainedLocator = page.locator(
|
||||
`[data-constraint-type="${constraintType}"][data-is-constrained="false"]`
|
||||
)
|
||||
@ -152,9 +150,9 @@ test.describe('Testing segment overlays', () => {
|
||||
).toBeGreaterThan(0)
|
||||
await unconstrainedLocator.click()
|
||||
await page.getByText('Add variable').click()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
expectAfterUnconstrained
|
||||
)
|
||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
await expect(page.getByText('Added variable')).not.toBeVisible()
|
||||
|
||||
await page.mouse.move(0, 0)
|
||||
@ -163,6 +161,7 @@ test.describe('Testing segment overlays', () => {
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(x, y)
|
||||
await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
const constrainedLocator = page.locator(
|
||||
`[data-constraint-type="${constraintType}"][data-is-constrained="true"]`
|
||||
@ -173,11 +172,15 @@ test.describe('Testing segment overlays', () => {
|
||||
await page.getByTestId('constraint-symbol-popover').count()
|
||||
).toBeGreaterThan(0)
|
||||
await constrainedLocator.click()
|
||||
await expect(page.locator('.cm-content')).toContainText(expectFinal)
|
||||
await editor.expectEditor.toContain(expectFinal, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
}
|
||||
test.setTimeout(120000)
|
||||
test('for segments [line, angledLine, lineTo, xLineTo]', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -207,7 +210,7 @@ test.describe('Testing segment overlays', () => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -221,8 +224,8 @@ test.describe('Testing segment overlays', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickConstrained = _clickConstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
@ -340,7 +343,11 @@ test.describe('Testing segment overlays', () => {
|
||||
locator: '[data-overlay-toolbar-index="3"]',
|
||||
})
|
||||
})
|
||||
test('for segments [yLineTo, xLine]', async ({ page }) => {
|
||||
test('for segments [yLineTo, xLine]', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -367,7 +374,7 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -381,7 +388,7 @@ part001 = startSketchOn('XZ')
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(8)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
|
||||
await page.mouse.move(700, 250)
|
||||
await page.waitForTimeout(100)
|
||||
@ -417,6 +424,8 @@ part001 = startSketchOn('XZ')
|
||||
})
|
||||
test('for segments [yLine, angledLineOfXLength, angledLineOfYLength]', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -447,7 +456,7 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -462,8 +471,8 @@ part001 = startSketchOn('XZ')
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickConstrained = _clickConstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
|
||||
let ang = 0
|
||||
|
||||
@ -546,6 +555,8 @@ part001 = startSketchOn('XZ')
|
||||
})
|
||||
test('for segments [angledLineToX, angledLineToY, angledLineThatIntersects]', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -576,7 +587,7 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -590,8 +601,8 @@ part001 = startSketchOn('XZ')
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickConstrained = _clickConstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
|
||||
let ang = 0
|
||||
|
||||
@ -703,7 +714,11 @@ part001 = startSketchOn('XZ')
|
||||
locator: '[data-overlay-toolbar-index="11"]',
|
||||
})
|
||||
})
|
||||
test('for segment [tangentialArcTo]', async ({ page }) => {
|
||||
test('for segment [tangentialArcTo]', async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -733,7 +748,7 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -747,8 +762,8 @@ part001 = startSketchOn('XZ')
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickConstrained = _clickConstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
|
||||
const tangentialArcTo = await u.getBoundingBox(
|
||||
'[data-overlay-index="12"]'
|
||||
@ -777,7 +792,7 @@ part001 = startSketchOn('XZ')
|
||||
locator: '[data-overlay-toolbar-index="12"]',
|
||||
})
|
||||
})
|
||||
test('for segment [circle]', async ({ page }) => {
|
||||
test('for segment [circle]', async ({ page, editor, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -790,7 +805,7 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -806,8 +821,8 @@ part001 = startSketchOn('XZ')
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page)
|
||||
const clickConstrained = _clickConstrained(page)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
|
||||
const hoverPos = { x: 789, y: 114 } as const
|
||||
let ang = await u.getAngle('[data-overlay-index="0"]')
|
||||
@ -833,8 +848,8 @@ part001 = startSketchOn('XZ')
|
||||
expectAfterUnconstrained:
|
||||
'circle({ center = [xAbs001, yAbs001], radius = 8 }, %)',
|
||||
expectFinal: 'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||
ang: ang + 105,
|
||||
steps: 10,
|
||||
ang: ang + 180,
|
||||
steps: 30,
|
||||
locator: '[data-overlay-toolbar-index="0"]',
|
||||
})
|
||||
console.log('circle radius')
|
||||
@ -854,7 +869,7 @@ part001 = startSketchOn('XZ')
|
||||
})
|
||||
test.describe('Testing deleting a segment', () => {
|
||||
const _deleteSegmentSequence =
|
||||
(page: Page) =>
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
codeToBeDeleted,
|
||||
@ -880,17 +895,20 @@ part001 = startSketchOn('XZ')
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(x, y)
|
||||
await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(codeToBeDeleted)
|
||||
await editor.expectEditor.toContain(codeToBeDeleted, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await page.locator(`[data-stdlib-fn-name="${stdLibFnName}"]`).click()
|
||||
await page.getByText('Delete Segment').click()
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toContainText(
|
||||
codeToBeDeleted
|
||||
)
|
||||
await editor.expectEditor.not.toContain(codeToBeDeleted, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
}
|
||||
test('all segment types', async ({ page }) => {
|
||||
test('all segment types', async ({ page, editor, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -920,7 +938,8 @@ part001 = startSketchOn('XZ')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -933,7 +952,7 @@ part001 = startSketchOn('XZ')
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||
const deleteSegmentSequence = _deleteSegmentSequence(page)
|
||||
const deleteSegmentSequence = _deleteSegmentSequence(page, editor)
|
||||
|
||||
let segmentToDelete
|
||||
|
||||
@ -1066,16 +1085,19 @@ part001 = startSketchOn('XZ')
|
||||
5,
|
||||
'[data-overlay-toolbar-index="2"]'
|
||||
)
|
||||
await page.mouse.move(hoverPos.x, hoverPos.y)
|
||||
|
||||
const codeToBeDeleted = 'lineTo([33, 11.5 + 0], %)'
|
||||
await expect(page.locator('.cm-content')).toContainText(codeToBeDeleted)
|
||||
await editor.expectEditor.toContain(codeToBeDeleted, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await page.getByTestId('overlay-menu').click()
|
||||
await page.getByText('Delete Segment').click()
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toContainText(
|
||||
codeToBeDeleted
|
||||
)
|
||||
await editor.expectEditor.not.toContain(codeToBeDeleted, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
segmentToDelete = await getOverlayByIndex(1)
|
||||
ang = await u.getAngle(`[data-overlay-index="${1}"]`)
|
||||
@ -1121,7 +1143,7 @@ part001 = startSketchOn('XZ')
|
||||
const isObj = lineOfInterest.includes('{ angle = 3,')
|
||||
test(`${lineOfInterest.split('(')[0]}${isObj ? '-[obj-input]' : ''}${
|
||||
doesHaveTagOutsideSketch ? '-[tagOutsideSketch]' : ''
|
||||
}`, async ({ page }) => {
|
||||
}`, async ({ page, editor, homePage }) => {
|
||||
await page.addInitScript(
|
||||
async ({ lineToBeDeleted, extraLine }) => {
|
||||
localStorage.setItem(
|
||||
@ -1142,7 +1164,7 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
await page.getByText(lineOfInterest).click()
|
||||
@ -1170,9 +1192,9 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
await page.mouse.move(hoverPos.x + x, hoverPos.y + y)
|
||||
await page.mouse.move(hoverPos.x, hoverPos.y, { steps: 5 })
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
lineOfInterest
|
||||
)
|
||||
await editor.expectEditor.toContain(lineOfInterest, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await page.getByTestId('overlay-menu').click()
|
||||
await page.waitForTimeout(100)
|
||||
@ -1183,9 +1205,9 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
await page.mouse.move(hoverPos.x + x, hoverPos.y + y)
|
||||
await page.mouse.move(hoverPos.x, hoverPos.y, { steps: 5 })
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
lineOfInterest
|
||||
)
|
||||
await editor.expectEditor.toContain(lineOfInterest, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await page.getByTestId('overlay-menu').click()
|
||||
await page.waitForTimeout(100)
|
||||
@ -1201,16 +1223,18 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
)
|
||||
).toBeTruthy()
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
lineOfInterest
|
||||
)
|
||||
await editor.expectEditor.toContain(lineOfInterest, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
} else {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
await expect(page.locator('.cm-content')).not.toContainText(
|
||||
lineOfInterest
|
||||
)
|
||||
await editor.expectEditor.not.toContain(lineOfInterest, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
await expect(page.locator('.cm-content')).not.toContainText('seg01')
|
||||
await editor.expectEditor.not.toContain('seg01', {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1281,6 +1305,8 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
const isObj = before.includes('{ angle = 3')
|
||||
test(`${before.split('(')[0]}${isObj ? '-[obj-input]' : ''}`, async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(
|
||||
async ({ lineToBeDeleted }) => {
|
||||
@ -1300,7 +1326,8 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
await page.getByText(before).click()
|
||||
@ -1333,14 +1360,16 @@ ${extraLine ? 'myVar = segLen(seg01)' : ''}`
|
||||
5,
|
||||
'[data-overlay-toolbar-index="0"]'
|
||||
)
|
||||
await page.mouse.move(x, y)
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(before)
|
||||
await editor.expectEditor.toContain(before, { shouldNormalise: true })
|
||||
|
||||
await page.getByTestId('overlay-menu').click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.getByText('Remove constraints').click()
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(after)
|
||||
await editor.expectEditor.toContain(after, { shouldNormalise: true })
|
||||
|
||||
// check the cursor was left in the correct place after transform
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText('|> ' + after)
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
||||
|
@ -1,24 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { commonPoints, getUtils, setup, tearDown } from './test-utils'
|
||||
import { commonPoints, getUtils } from './test-utils'
|
||||
import { Coords2d } from 'lang/std/sketch'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing selections', () => {
|
||||
test.setTimeout(90_000)
|
||||
test(
|
||||
'Selections work on fresh and edited sketch',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// Skip on windows its being weird.
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
|
||||
@ -27,9 +19,9 @@ test.describe('Testing selections', () => {
|
||||
// source ranges are wrong, hovers won't work
|
||||
const u = await getUtils(page)
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const yAxisClick = () =>
|
||||
@ -264,7 +256,7 @@ test.describe('Testing selections', () => {
|
||||
}
|
||||
)
|
||||
|
||||
test('Solids should be select and deletable', async ({ page }) => {
|
||||
test('Solids should be select and deletable', async ({ page, homePage }) => {
|
||||
test.setTimeout(90_000)
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -329,13 +321,13 @@ part009 = startSketchOn('XY')
|
||||
|> line([0, pipeLength], %)
|
||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||
|> close(%)
|
||||
rev = revolve({ axis = 'y' }, part009)
|
||||
rev = revolve({ axis: 'y' }, part009)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -433,6 +425,7 @@ rev = revolve({ axis = 'y' }, part009)
|
||||
})
|
||||
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -455,9 +448,9 @@ sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
@ -497,6 +490,7 @@ sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
})
|
||||
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||
@ -525,9 +519,9 @@ sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -837,8 +831,11 @@ extrude001 = extrude(100, sketch001)
|
||||
})
|
||||
test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -859,17 +856,20 @@ extrude001 = extrude(10, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
const selectUnExtrudable = () =>
|
||||
page.getByText(`line([4.99, -0.46], %, $seg01)`).click()
|
||||
const selectUnExtrudable = async () => {
|
||||
await editor.scrollToText(`line([4.99, -0.46], %, $seg01)`)
|
||||
await page.getByText(`line([4.99, -0.46], %, $seg01)`).click()
|
||||
}
|
||||
const clickEmpty = () => page.mouse.click(700, 460)
|
||||
await selectUnExtrudable()
|
||||
// expect extrude button to be disabled
|
||||
@ -879,6 +879,7 @@ extrude001 = extrude(10, sketch001)
|
||||
|
||||
// expect active line to contain nothing
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
||||
|
||||
// and extrude to still be disabled
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||
|
||||
@ -904,7 +905,7 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
||||
).not.toBeDisabled()
|
||||
})
|
||||
|
||||
test('Fillet button states test', async ({ page }) => {
|
||||
test('Fillet button states test', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -919,8 +920,8 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -958,6 +959,7 @@ extrude001 = extrude(10, sketch001)`
|
||||
|
||||
test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const cases = [
|
||||
{
|
||||
@ -990,9 +992,9 @@ part001 = startSketchOn('XZ')
|
||||
{ cases }
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.sendCustomCmd({
|
||||
@ -1025,6 +1027,7 @@ part001 = startSketchOn('XZ')
|
||||
})
|
||||
test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -1040,9 +1043,9 @@ extrude001 = extrude(50, sketch001)
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.sendCustomCmd({
|
||||
@ -1125,6 +1128,7 @@ extrude001 = extrude(50, sketch001)
|
||||
})
|
||||
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
const selectionsSnippets = {
|
||||
@ -1180,9 +1184,9 @@ extrude001 = extrude(50, sketch001)
|
||||
},
|
||||
selectionsSnippets
|
||||
)
|
||||
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 1000 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -1222,6 +1226,7 @@ extrude001 = extrude(50, sketch001)
|
||||
|
||||
test('Deselecting line tool should mean nothing happens on click', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
/**
|
||||
* If the line tool is clicked when the state is 'No Points' it will exit Sketch mode.
|
||||
@ -1230,9 +1235,9 @@ extrude001 = extrude(50, sketch001)
|
||||
* To continue to test this workflow, we now enter sketch mode and place a single point before exiting the line tool.
|
||||
*/
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
|
@ -1,14 +1,7 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
createProject,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath, createProject } from './test-utils'
|
||||
import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes'
|
||||
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
import {
|
||||
@ -19,35 +12,16 @@ import {
|
||||
} from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing settings', () => {
|
||||
test('Stored settings are validated and fall back to defaults', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
test(
|
||||
'Stored settings are validated and fall back to defaults',
|
||||
// Override beforeEach test setup
|
||||
// with corrupted settings
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: TEST_SETTINGS_CORRUPTED }),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
appSettings: TEST_SETTINGS_CORRUPTED,
|
||||
},
|
||||
async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Check the settings were reset
|
||||
const storedSettings = TOML.parse(
|
||||
@ -57,24 +31,26 @@ test.describe('Testing settings', () => {
|
||||
)
|
||||
) as { settings: SaveSettingsPayload }
|
||||
|
||||
expect(storedSettings.settings?.app?.theme).toBe(undefined)
|
||||
expect(storedSettings.settings?.app?.theme).toBe('dark')
|
||||
|
||||
// Check that the invalid settings were removed
|
||||
expect(storedSettings.settings?.modeling?.defaultUnit).toBe(undefined)
|
||||
expect(storedSettings.settings?.modeling?.mouseControls).toBe(undefined)
|
||||
expect(storedSettings.settings?.app?.projectDirectory).toBe(undefined)
|
||||
// Check that the invalid settings were changed to good defaults
|
||||
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
|
||||
expect(storedSettings.settings?.modeling?.mouseControls).toBe('KittyCAD')
|
||||
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
|
||||
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
|
||||
undefined
|
||||
'project-$nnn'
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test('Project settings can be set and override user settings', async ({
|
||||
page,
|
||||
}) => {
|
||||
// The behavior is actually broken. Parent always takes precedence
|
||||
test.fixme(
|
||||
'Project settings can be set and override user settings',
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -89,17 +65,20 @@ test.describe('Testing settings', () => {
|
||||
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
|
||||
|
||||
await test.step('Open settings dialog and set "Show debug panel" to on', async () => {
|
||||
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||
await page.keyboard.press('ControlOrMeta+,')
|
||||
await expect(headingLocator).toBeVisible()
|
||||
|
||||
/** Test to close https://github.com/KittyCAD/modeling-app/issues/2713 */
|
||||
await test.step(`Confirm that this dialog has a solid background`, async () => {
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff({ x: 600, y: 250 }, [28, 28, 28]), {
|
||||
.poll(
|
||||
() => u.getGreatestPixDiff({ x: 600, y: 250 }, [28, 28, 28]),
|
||||
{
|
||||
timeout: 1000,
|
||||
message:
|
||||
'Checking for solid background, should not see default plane colors',
|
||||
})
|
||||
}
|
||||
)
|
||||
.toBeLessThan(15)
|
||||
})
|
||||
|
||||
@ -111,7 +90,7 @@ test.describe('Testing settings', () => {
|
||||
await test.step('Open settings with keyboard shortcut', async () => {
|
||||
await page.getByTestId('settings-close-button').click()
|
||||
await page.locator('.cm-content').click()
|
||||
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||
await page.keyboard.press('ControlOrMeta+,')
|
||||
await expect(headingLocator).toBeVisible()
|
||||
})
|
||||
|
||||
@ -119,7 +98,11 @@ test.describe('Testing settings', () => {
|
||||
await expect(
|
||||
page.getByText(`Set show debug panel to "false" for this project`)
|
||||
).toBeVisible()
|
||||
// Check that the theme changed
|
||||
await expect(
|
||||
page.getByText(`Set show debug panel to "false" for this project`)
|
||||
).not.toBeVisible()
|
||||
|
||||
// Check that the debug panel button is gone
|
||||
await expect(paneButtonLocator).not.toBeVisible()
|
||||
|
||||
// Check that the user setting was not changed
|
||||
@ -128,7 +111,9 @@ test.describe('Testing settings', () => {
|
||||
|
||||
// Roll back to default of "off"
|
||||
await await page
|
||||
.getByText('show debug panelRoll back show debug panelRoll back to match')
|
||||
.getByText(
|
||||
'show debug panelRoll back show debug panelRoll back to match'
|
||||
)
|
||||
.hover()
|
||||
await page
|
||||
.getByRole('button', {
|
||||
@ -142,18 +127,21 @@ test.describe('Testing settings', () => {
|
||||
await expect(
|
||||
page.locator('input[name="modeling-showDebugPanel"]')
|
||||
).not.toBeChecked()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Keybindings display the correct hotkey for Command Palette', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step('Open keybindings settings', async () => {
|
||||
// Open the settings modal with the browser keyboard shortcut
|
||||
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||
// Open the settings modal with the keyboard shortcut
|
||||
await page.keyboard.press('ControlOrMeta+,')
|
||||
|
||||
// Go to Keybindings tab.
|
||||
const keybindingsTab = page.getByRole('radio', { name: 'Keybindings' })
|
||||
@ -174,11 +162,13 @@ test.describe('Testing settings', () => {
|
||||
await expect(hotkey).toHaveText(text)
|
||||
})
|
||||
|
||||
test('Project and user settings can be reset', async ({ page }) => {
|
||||
test('Project and user settings can be reset', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
// Selectors and constants
|
||||
@ -261,29 +251,24 @@ test.describe('Testing settings', () => {
|
||||
test.fixme(
|
||||
`Project settings override user settings on desktop`,
|
||||
{ tag: ['@electron', '@skipWin'] },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const projectName = 'bracket'
|
||||
const {
|
||||
electronApp,
|
||||
page,
|
||||
dir: projectDirName,
|
||||
} = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const { dir: projectDirName } = await context.folderSetupFn(
|
||||
async (dir) => {
|
||||
const bracketDir = join(dir, projectName)
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Selectors and constants
|
||||
const tempProjectSettingsFilePath = join(
|
||||
@ -353,22 +338,18 @@ test.describe('Testing settings', () => {
|
||||
await logoLink.click()
|
||||
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Load desktop app with no settings file`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
{
|
||||
tag: '@electron',
|
||||
// This is what makes no settings file get created
|
||||
cleanProjectDir: false,
|
||||
testInfo,
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
},
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Selectors and constants
|
||||
const errorHeading = page.getByRole('heading', {
|
||||
@ -379,25 +360,21 @@ test.describe('Testing settings', () => {
|
||||
// If the app loads without exploding we're in the clear
|
||||
await expect(errorHeading).not.toBeVisible()
|
||||
await expect(projectDirLink).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Load desktop app with a settings file, but no project directory setting`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
{
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
app: {
|
||||
themeColor: '259',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
},
|
||||
async ({ context, page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Selectors and constants
|
||||
const errorHeading = page.getByRole('heading', {
|
||||
@ -408,22 +385,14 @@ test.describe('Testing settings', () => {
|
||||
// If the app loads without exploding we're in the clear
|
||||
await expect(errorHeading).not.toBeVisible()
|
||||
await expect(projectDirLink).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
// It was much easier to test the logo color than the background stream color.
|
||||
test.fixme(
|
||||
'user settings reload on external change, on project and modeling view',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const {
|
||||
electronApp,
|
||||
page,
|
||||
dir: projectDirName,
|
||||
} = await setupElectron({
|
||||
testInfo,
|
||||
{
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
app: {
|
||||
// Doesn't matter what you set it to. It will
|
||||
@ -431,9 +400,13 @@ test.describe('Testing settings', () => {
|
||||
themeColor: '0',
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
async ({ context, page }, testInfo) => {
|
||||
const { dir: projectDirName } = await context.folderSetupFn(
|
||||
async () => {}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const logoLink = page.getByTestId('app-logo')
|
||||
const projectDirLink = page.getByText('Loaded from')
|
||||
@ -467,23 +440,18 @@ test.describe('Testing settings', () => {
|
||||
await changeColor('21')
|
||||
await expect(logoLink).toHaveCSS('--primary-hue', '21')
|
||||
})
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
'project settings reload on external change',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const {
|
||||
electronApp,
|
||||
page,
|
||||
dir: projectDirName,
|
||||
} = await setupElectron({
|
||||
testInfo,
|
||||
})
|
||||
async ({ context, page }, testInfo) => {
|
||||
const { dir: projectDirName } = await context.folderSetupFn(
|
||||
async () => {}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const logoLink = page.getByTestId('app-logo')
|
||||
const projectDirLink = page.getByText('Loaded from')
|
||||
@ -514,18 +482,14 @@ test.describe('Testing settings', () => {
|
||||
await changeColorFs('99')
|
||||
await expect(logoLink).toHaveCSS('--primary-hue', '99')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Closing settings modal should go back to the original file being viewed`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'project-000')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
@ -536,7 +500,6 @@ test.describe('Testing settings', () => {
|
||||
executorInputPath('cylinder.kcl'),
|
||||
join(bracketDir, '2.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
|
||||
const kclCylinder = await fsp.readFile(
|
||||
@ -552,7 +515,7 @@ test.describe('Testing settings', () => {
|
||||
editorTextMatches,
|
||||
} = await getUtils(page, test)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
page.on('console', console.log)
|
||||
|
||||
await test.step('Precondition: Open to second project file', async () => {
|
||||
@ -583,16 +546,15 @@ test.describe('Testing settings', () => {
|
||||
await test.step('Postcondition: Same file content is in editor as before settings opened', async () => {
|
||||
await editorTextMatches(kclCylinder)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test('Changing modeling default unit', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
test('Changing modeling default unit', async ({ page, homePage }) => {
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
const toastMessage = page.getByText(`Successfully created "testDefault"`)
|
||||
await expect(toastMessage).not.toBeVisible()
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -619,7 +581,9 @@ test.describe('Testing settings', () => {
|
||||
await userSettingsTab.click()
|
||||
await defaultUnitSection.hover()
|
||||
await defaultUnitRollbackButton.click()
|
||||
await projectSettingsTab.hover()
|
||||
await projectSettingsTab.click()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
await test.step('Change modeling default unit within project tab', async () => {
|
||||
@ -631,7 +595,10 @@ test.describe('Testing settings', () => {
|
||||
const toastMessage = page.getByText(
|
||||
`Set default unit to "${unitOfMeasure}" for this project`
|
||||
)
|
||||
|
||||
// Assert visibility and disapperance
|
||||
await expect(toastMessage).toBeVisible()
|
||||
await expect(toastMessage).not.toBeVisible()
|
||||
})
|
||||
}
|
||||
await changeUnitOfMeasureInProjectTab('in')
|
||||
@ -643,7 +610,10 @@ test.describe('Testing settings', () => {
|
||||
})
|
||||
|
||||
// Go to the user tab
|
||||
await userSettingsTab.hover()
|
||||
await userSettingsTab.click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await test.step('Change modeling default unit within user tab', async () => {
|
||||
const changeUnitOfMeasureInUserTab = async (unitOfMeasure: string) => {
|
||||
await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => {
|
||||
@ -726,9 +696,9 @@ test.describe('Testing settings', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Changing theme in sketch mode', async ({ page }) => {
|
||||
test('Changing theme in sketch mode', async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(() => {
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
@ -742,7 +712,10 @@ extrude001 = extrude(5, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Selectors and constants
|
||||
const editSketchButton = page.getByRole('button', { name: 'Edit Sketch' })
|
||||
@ -753,7 +726,6 @@ extrude001 = extrude(5, sketch001)
|
||||
const lightThemeSegmentColor: [number, number, number] = [90, 90, 90]
|
||||
|
||||
await test.step(`Get into sketch mode`, async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.mouse.click(700, 200)
|
||||
await expect(editSketchButton).toBeVisible()
|
||||
await editSketchButton.click()
|
||||
@ -792,21 +764,13 @@ extrude001 = extrude(5, sketch001)
|
||||
})
|
||||
})
|
||||
|
||||
test(`Changing system theme preferences (via media query) should update UI and stream`, async ({
|
||||
page,
|
||||
}) => {
|
||||
// Override the settings so that the theme is set to `system`
|
||||
await page.addInitScript(
|
||||
({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
test(
|
||||
`Changing system theme preferences (via media query) should update UI and stream`,
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_DEFAULT_THEME,
|
||||
}),
|
||||
}
|
||||
)
|
||||
// Override the settings so that the theme is set to `system`
|
||||
appSettings: TEST_SETTINGS_DEFAULT_THEME,
|
||||
},
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Selectors and constants
|
||||
@ -822,8 +786,10 @@ extrude001 = extrude(5, sketch001)
|
||||
const toolbar = page.locator('menu').filter({ hasText: 'Start Sketch' })
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(toolbar).toBeVisible()
|
||||
})
|
||||
|
||||
@ -844,35 +810,31 @@ extrude001 = extrude(5, sketch001)
|
||||
.poll(() => streamBackgroundPixelIsColor(darkBackgroundColor))
|
||||
.toBeLessThan(15)
|
||||
})
|
||||
})
|
||||
|
||||
test(`Turning off "Show debug panel" with debug panel open leaves no phantom panel`, async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Turning off "Show debug panel" with debug panel open leaves no phantom panel`,
|
||||
{
|
||||
// Override beforeEach test setup
|
||||
// with debug panel open
|
||||
// but "show debug panel" set to false
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
appSettings: {
|
||||
...TEST_SETTINGS,
|
||||
modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false },
|
||||
},
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistModelingContext',
|
||||
'{"openPanes":["debug"]}'
|
||||
)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false },
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Constants and locators
|
||||
const resizeHandle = page.locator('.sidebar-resize-handles > div.block')
|
||||
@ -894,7 +856,6 @@ extrude001 = extrude(5, sketch001)
|
||||
}
|
||||
|
||||
await test.step(`Initial load with corrupted settings`, async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
// Check that the debug panel is not visible
|
||||
await expect(debugPaneButton).not.toBeVisible()
|
||||
// Check the pane resize handle wrapper is not visible
|
||||
@ -922,5 +883,6 @@ extrude001 = extrude(5, sketch001)
|
||||
await expect(debugPaneButton).not.toBeVisible()
|
||||
await expect(resizeHandle).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,29 +1,16 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
tearDown,
|
||||
setupElectron,
|
||||
createProject,
|
||||
} from './test-utils'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { getUtils, createProject } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await setup(context, page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Text-to-CAD tests', () => {
|
||||
test('basic lego happy case', async ({ page }) => {
|
||||
test('basic lego happy case', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step('Set up', async () => {
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
})
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
@ -43,25 +30,17 @@ test.describe('Text-to-CAD tests', () => {
|
||||
const successToastMessage = page.getByText(`Text-to-CAD successful`)
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
// Hit copy to clipboard.
|
||||
// Hit accept.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton).toBeVisible()
|
||||
|
||||
await copyToClipboardButton.click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click()
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`const`)
|
||||
|
||||
@ -70,29 +49,18 @@ test.describe('Text-to-CAD tests', () => {
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page
|
||||
.getByRole('status')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Text-to-CAD successfulPrompt' })
|
||||
.first()
|
||||
.getByRole('button', { name: 'Close' })
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// The toast should disappear.
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('success model, then ignore success toast, user can create new prompt from command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x6 lego')
|
||||
|
||||
@ -111,10 +79,6 @@ test.describe('Text-to-CAD tests', () => {
|
||||
const successToastMessage = page.getByText(`Text-to-CAD successful`)
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
|
||||
// Can send a new prompt from the command bar.
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -133,12 +97,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('you can reject text-to-cad output and it does nothing', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -170,12 +136,16 @@ test.describe('Text-to-CAD tests', () => {
|
||||
await expect(page.locator('.cm-content')).toContainText(``)
|
||||
})
|
||||
|
||||
test('sending a bad prompt fails, can dismiss', async ({ page }) => {
|
||||
test('sending a bad prompt fails, can dismiss', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -236,12 +206,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('sending a bad prompt fails, can start over from toast', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -324,12 +296,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('sending a bad prompt fails, can ignore toast, can start over from command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -391,19 +365,21 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
// old failure toast should stick around.
|
||||
await expect(failureToastMessage).toBeVisible()
|
||||
await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible()
|
||||
})
|
||||
|
||||
test('ensure you can shift+enter in the prompt box', async ({ page }) => {
|
||||
test('ensure you can shift+enter in the prompt box', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const promptWithNewline = `a 2x4\nlego`
|
||||
|
||||
@ -456,7 +432,7 @@ test.describe('Text-to-CAD tests', () => {
|
||||
test(
|
||||
'can do many at once and get many prompts back, and interact with many',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// Let this test run longer since we've seen it timeout.
|
||||
test.setTimeout(180_000)
|
||||
// skip on windows
|
||||
@ -467,9 +443,10 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -495,8 +472,6 @@ test.describe('Text-to-CAD tests', () => {
|
||||
// We should have three success toasts.
|
||||
await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||
await expect(page.getByText(`a 2x8 lego`)).toBeVisible()
|
||||
await expect(page.getByText(`a 2x10 lego`)).toBeVisible()
|
||||
@ -514,31 +489,15 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
// Ensure you can copy the code for one of the models remaining.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton.first()).toBeVisible()
|
||||
// Click the button.
|
||||
await copyToClipboardButton.first().click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page.locator('[data-negative-button="close"]').first()
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// Ensure the final toast remains.
|
||||
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
||||
await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible()
|
||||
@ -549,40 +508,21 @@ test.describe('Text-to-CAD tests', () => {
|
||||
// Click the button.
|
||||
await copyToClipboardButton.click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyA')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||
|
||||
// Expect the toast to disappear.
|
||||
// Find the toast close button.
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
}
|
||||
)
|
||||
|
||||
test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -631,48 +571,26 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
// Ensure you can copy the code for one of the models remaining.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton.first()).toBeVisible()
|
||||
// Click the button.
|
||||
await copyToClipboardButton.first().click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page
|
||||
.getByRole('status')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Text-to-CAD successfulPrompt' })
|
||||
.first()
|
||||
.getByRole('button', { name: 'Close' })
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// Expect the toast to disappear.
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
||||
await page.waitForTimeout(1000)
|
||||
await test.step(`Send prompt from command bar: ${promptStr}`, async () => {
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
// Click the command bar button
|
||||
await commandBarButton.hover()
|
||||
await commandBarButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Wait for the command bar to appear
|
||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
@ -681,7 +599,9 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
||||
const textToCadCommand = page.getByText('Use the Zoo Text-to-CAD API')
|
||||
await expect(textToCadCommand.first()).toBeVisible()
|
||||
// Click the Text-to-CAD command
|
||||
await textToCadCommand.first().scrollIntoViewIfNeeded()
|
||||
await textToCadCommand.first().click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Enter the prompt.
|
||||
const prompt = page.getByText('Prompt')
|
||||
@ -697,12 +617,13 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
||||
test(
|
||||
'Text-to-CAD functionality',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = 'project-000'
|
||||
const prompt = 'lego 2x4'
|
||||
const textToCadFileName = 'lego-2x4.kcl'
|
||||
|
||||
const { electronApp, page, dir } = await setupElectron({ testInfo })
|
||||
const { dir } = await context.folderSetupFn(async () => {})
|
||||
|
||||
const fileExists = () =>
|
||||
fs.existsSync(join(dir, projectName, textToCadFileName))
|
||||
|
||||
@ -711,7 +632,7 @@ test(
|
||||
test
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Locators
|
||||
const projectMenuButton = page
|
||||
@ -761,7 +682,5 @@ test(
|
||||
// Confirm we've navigated back to the main.kcl file after deletion
|
||||
await expect(projectMenuButton).toContainText('main.kcl')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,19 +1,10 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { doExport, getUtils, makeTemplate, setup, tearDown } from './test-utils'
|
||||
import { doExport, getUtils, makeTemplate } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test('Units menu', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
test.fixme('Units menu', async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const unitsMenuButton = page.getByRole('button', {
|
||||
name: 'Current Units',
|
||||
@ -41,7 +32,7 @@ test('Units menu', async ({ page }) => {
|
||||
await expect(unitsMenuButton).toContainText('mm')
|
||||
})
|
||||
|
||||
test('Successful export shows a success toast', async ({ page }) => {
|
||||
test('Successful export shows a success toast', async ({ page, homePage }) => {
|
||||
// FYI this test doesn't work with only engine running locally
|
||||
// And you will need to have the KittyCAD CLI installed
|
||||
const u = await getUtils(page)
|
||||
@ -61,27 +52,27 @@ part001 = startSketchOn('-XZ')
|
||||
|> yLine(baseHeight, %)
|
||||
|> xLine(baseLen, %)
|
||||
|> angledLineToY({
|
||||
angle: topAng,
|
||||
to: totalHeightHalf,
|
||||
angle = topAng,
|
||||
to = totalHeightHalf,
|
||||
}, %, $seg04)
|
||||
|> xLineTo(totalLen, %, $seg03)
|
||||
|> yLine(-armThick, %, $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: seg04
|
||||
angle = HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle: -bottomAng,
|
||||
to: -totalHeightHalf - armThick,
|
||||
angle = -bottomAng,
|
||||
to = -totalHeightHalf - armThick,
|
||||
}, %, $seg02)
|
||||
|> xLineTo(segEndX(seg03) + 0, %)
|
||||
|> yLine(-segLen(seg01), %)
|
||||
|> angledLineThatIntersects({
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: seg02
|
||||
angle = HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
||||
|> xLineTo(ZERO, %)
|
||||
@ -89,9 +80,9 @@ part001 = startSketchOn('-XZ')
|
||||
|> extrude(4, %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.waitForCmdReceive('extrude')
|
||||
@ -106,25 +97,14 @@ part001 = startSketchOn('-XZ')
|
||||
},
|
||||
page
|
||||
)
|
||||
|
||||
// This is the main thing we're testing,
|
||||
// We test the export functionality across all
|
||||
// file types in snapshot-tests.spec.ts
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Paste should not work unless an input is focused', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// To run this test locally, uncomment Firefox in playwright.config.ts
|
||||
test.skip(
|
||||
browserName !== 'firefox',
|
||||
"This bug is really Firefox-only, which we don't run in CI."
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -164,12 +144,12 @@ test('Paste should not work unless an input is focused', async ({
|
||||
|
||||
test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -181,7 +161,7 @@ test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click()
|
||||
|
||||
// Verify the URL and that you can see a list of shortcuts
|
||||
await expect(page.url()).toContain('?tab=keybindings')
|
||||
await expect.poll(() => page.url()).toContain('?tab=keybindings')
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Enter Sketch Mode' })
|
||||
).toBeAttached()
|
||||
@ -189,12 +169,13 @@ test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
|
||||
test('First escape in tool pops you out of tool, second exits sketch mode', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// Wait for the app to be ready for use
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -258,7 +239,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
||||
|
||||
test.fixme(
|
||||
'Basic default modeling and sketch hotkeys work',
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// This test can run long if it takes a little too long to load
|
||||
@ -285,8 +266,8 @@ test.fixme(
|
||||
})
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -437,10 +418,11 @@ test.fixme(
|
||||
}
|
||||
)
|
||||
|
||||
test('Delete key does not navigate back', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
test('Delete key does not navigate back', async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
const settingsButton = page.getByRole('link', {
|
||||
name: 'Settings',
|
||||
@ -449,20 +431,20 @@ test('Delete key does not navigate back', async ({ page }) => {
|
||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
||||
|
||||
await settingsButton.click()
|
||||
await expect(page.url()).toContain('/settings')
|
||||
await expect.poll(() => page.url()).toContain('/settings')
|
||||
|
||||
// Make sure that delete doesn't go back from settings
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).toContain('/settings')
|
||||
await expect.poll(() => page.url()).toContain('/settings')
|
||||
|
||||
// Now close the settings and try delete again,
|
||||
// make sure it doesn't go back to settings
|
||||
await settingsCloseButton.click()
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).not.toContain('/settings')
|
||||
await expect.poll(() => page.url()).not.toContain('/settings')
|
||||
})
|
||||
|
||||
test('Sketch on face', async ({ page }) => {
|
||||
test('Sketch on face', async ({ page, homePage }) => {
|
||||
test.setTimeout(90_000)
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
@ -485,9 +467,9 @@ test('Sketch on face', async ({ page }) => {
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -541,7 +523,8 @@ test('Sketch on face', async ({ page }) => {
|
||||
|> line([2.45, -0.2], %)
|
||||
|> line([-2.6, -1.25], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`)
|
||||
|> close(%)
|
||||
`)
|
||||
)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
@ -556,7 +539,7 @@ test('Sketch on face', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
await page.waitForTimeout(150)
|
||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 1200 })
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.updateCamPosition([452, -152, 1166])
|
||||
await u.closeDebugPanel()
|
||||
|
305
e2e/playwright/zoo-test.ts
Normal file
@ -0,0 +1,305 @@
|
||||
import {
|
||||
test as playwrightTestFn,
|
||||
TestInfo as TestInfoPlaywright,
|
||||
BrowserContext as BrowserContextPlaywright,
|
||||
Page as PagePlaywright,
|
||||
TestDetails as TestDetailsPlaywright,
|
||||
PlaywrightTestArgs,
|
||||
PlaywrightTestOptions,
|
||||
PlaywrightWorkerArgs,
|
||||
PlaywrightWorkerOptions,
|
||||
ElectronApplication,
|
||||
} from '@playwright/test'
|
||||
|
||||
import {
|
||||
fixtures,
|
||||
Fixtures,
|
||||
AuthenticatedTronApp,
|
||||
AuthenticatedApp,
|
||||
} from './fixtures/fixtureSetup'
|
||||
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
export { expect } from '@playwright/test'
|
||||
|
||||
declare module '@playwright/test' {
|
||||
interface TestInfo {
|
||||
tronApp?: AuthenticatedTronApp
|
||||
}
|
||||
interface BrowserContext {
|
||||
folderSetupFn: (
|
||||
cb: (dir: string) => Promise<void>
|
||||
) => Promise<{ dir: string }>
|
||||
}
|
||||
interface Page {
|
||||
dir: string
|
||||
TEST_SETTINGS_FILE_KEY?: string
|
||||
setBodyDimensions: (dims: {
|
||||
width: number
|
||||
height: number
|
||||
}) => Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
export type TestInfo = TestInfoPlaywright
|
||||
export type BrowserContext = BrowserContextPlaywright
|
||||
export type Page = PagePlaywright
|
||||
export type TestDetails = TestDetailsPlaywright & {
|
||||
cleanProjectDir?: boolean
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
}
|
||||
|
||||
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
||||
// switch between web and electron if needed.
|
||||
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
|
||||
|
||||
// In JavaScript you cannot replace a function's body only (despite functions
|
||||
// are themselves objects, which you'd expect a body property or something...)
|
||||
// So we must redefine the function and then re-attach properties.
|
||||
type PWFunction = (
|
||||
args: PlaywrightTestArgs &
|
||||
Fixtures &
|
||||
PlaywrightWorkerArgs &
|
||||
PlaywrightTestOptions &
|
||||
PlaywrightWorkerOptions & {
|
||||
electronApp?: ElectronApplication
|
||||
},
|
||||
testInfo: TestInfo
|
||||
) => void | Promise<void>
|
||||
|
||||
// The below error is due to the extreme type spaghetti going on. playwright/
|
||||
// types/test.d.ts does not export 2 functions (below is one of them) but tsc
|
||||
// is trying to use a interface name it can't see.
|
||||
// e2e/playwright/zoo-test.ts:64:14 - error TS4023: Exported variable 'test' has
|
||||
// or is using name 'TestFunction' from external module
|
||||
// "/home/lee/Code/Zoo/modeling-app/dirty2/node_modules/playwright/types/test"
|
||||
// but cannot be named.
|
||||
export const test = (
|
||||
desc: string,
|
||||
objOrFn: PWFunction | TestDetails,
|
||||
fnMaybe?: PWFunction
|
||||
) => {
|
||||
const hasTestConf = typeof objOrFn === 'object'
|
||||
const fn = hasTestConf ? fnMaybe : objOrFn
|
||||
|
||||
return pwTestFnWithFixtures(
|
||||
desc,
|
||||
hasTestConf ? objOrFn : {},
|
||||
async (
|
||||
{
|
||||
page,
|
||||
context,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
homePage,
|
||||
request,
|
||||
playwright,
|
||||
browser,
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
colorScheme,
|
||||
clientCertificates,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
geolocation,
|
||||
hasTouch,
|
||||
httpCredentials,
|
||||
ignoreHTTPSErrors,
|
||||
isMobile,
|
||||
javaScriptEnabled,
|
||||
locale,
|
||||
offline,
|
||||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
viewport,
|
||||
baseURL,
|
||||
contextOptions,
|
||||
actionTimeout,
|
||||
navigationTimeout,
|
||||
serviceWorkers,
|
||||
testIdAttribute,
|
||||
browserName,
|
||||
defaultBrowserType,
|
||||
headless,
|
||||
channel,
|
||||
launchOptions,
|
||||
connectOptions,
|
||||
screenshot,
|
||||
trace,
|
||||
video,
|
||||
},
|
||||
testInfo
|
||||
) => {
|
||||
// To switch to web, use PLATFORM=web environment variable.
|
||||
// Only use this for debugging, since the playwright tracer is busted
|
||||
// for electron.
|
||||
|
||||
let tronApp
|
||||
|
||||
if (process.env.PLATFORM === 'web') {
|
||||
tronApp = new AuthenticatedApp(context, page, testInfo)
|
||||
} else {
|
||||
tronApp = new AuthenticatedTronApp(context, page, testInfo)
|
||||
}
|
||||
|
||||
const fixtures: Fixtures = { cmdBar, editor, toolbar, scene, homePage }
|
||||
if (tronApp instanceof AuthenticatedTronApp) {
|
||||
const options = {
|
||||
fixtures,
|
||||
}
|
||||
if (hasTestConf) {
|
||||
Object.assign(options, {
|
||||
appSettings: objOrFn?.appSettings,
|
||||
cleanProjectDir: objOrFn?.cleanProjectDir,
|
||||
})
|
||||
}
|
||||
await tronApp.initialise(options)
|
||||
} else {
|
||||
await tronApp.initialise('')
|
||||
}
|
||||
|
||||
// We need to patch this because addInitScript will bind too late in our
|
||||
// electron tests, never running. We need to call reload() after each call
|
||||
// to guarantee it runs.
|
||||
const oldContextAddInitScript = tronApp.context.addInitScript
|
||||
tronApp.context.addInitScript = async function (a, b) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldContextAddInitScript.apply(this, [a, b])
|
||||
await tronApp.page.reload()
|
||||
}
|
||||
|
||||
// No idea why we mix and match page and context's addInitScript but we do
|
||||
const oldPageAddInitScript = tronApp.page.addInitScript
|
||||
tronApp.page.addInitScript = async function (a: any, b: any) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldPageAddInitScript.apply(this, [a, b])
|
||||
await tronApp.page.reload()
|
||||
}
|
||||
|
||||
// Create a consistent way to resize the page across electron and web.
|
||||
// (lee) I had to do everyhting in the book to make electron change its
|
||||
// damn window size. I succeded in making it consistently and reliably
|
||||
// do it after a whole afternoon.
|
||||
tronApp.page.setBodyDimensions = async function (dims: {
|
||||
width: number
|
||||
height: number
|
||||
}) {
|
||||
await tronApp.page.setViewportSize(dims)
|
||||
|
||||
if (!(tronApp instanceof AuthenticatedTronApp)) {
|
||||
return
|
||||
}
|
||||
|
||||
await tronApp.electronApp?.evaluateHandle(async ({ app }, dims) => {
|
||||
// @ts-ignore sorry jon but see comment in main.ts why this is ignored
|
||||
await app.resizeWindow(dims.width, dims.height)
|
||||
}, dims)
|
||||
|
||||
return tronApp.page.evaluate(
|
||||
async (dims: { width: number; height: number }) => {
|
||||
await window.electron.resizeWindow(dims.width, dims.height)
|
||||
window.document.body.style.width = dims.width + 'px'
|
||||
window.document.body.style.height = dims.height + 'px'
|
||||
window.document.documentElement.style.width = dims.width + 'px'
|
||||
window.document.documentElement.style.height = dims.height + 'px'
|
||||
},
|
||||
dims
|
||||
)
|
||||
}
|
||||
|
||||
// We need to expose this in order for some tests that require folder
|
||||
// creation. Before they used to do this by their own electronSetup({...})
|
||||
// calls.
|
||||
if (tronApp instanceof AuthenticatedTronApp) {
|
||||
tronApp.context.folderSetupFn = function (fn) {
|
||||
return fn(tronApp.dir).then(() => ({
|
||||
dir: tronApp.dir,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// tsc aint smart enough to know this'll never be undefined
|
||||
// but I dont blame it, the logic to know is complex
|
||||
if (fn) {
|
||||
await fn(
|
||||
{
|
||||
context: tronApp.context,
|
||||
page: tronApp.page,
|
||||
electronApp:
|
||||
tronApp instanceof AuthenticatedTronApp
|
||||
? tronApp.electronApp
|
||||
: undefined,
|
||||
...fixtures,
|
||||
request,
|
||||
playwright,
|
||||
browser,
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
colorScheme,
|
||||
clientCertificates,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
geolocation,
|
||||
hasTouch,
|
||||
httpCredentials,
|
||||
ignoreHTTPSErrors,
|
||||
isMobile,
|
||||
javaScriptEnabled,
|
||||
locale,
|
||||
offline,
|
||||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
viewport,
|
||||
baseURL,
|
||||
contextOptions,
|
||||
actionTimeout,
|
||||
navigationTimeout,
|
||||
serviceWorkers,
|
||||
testIdAttribute,
|
||||
browserName,
|
||||
defaultBrowserType,
|
||||
headless,
|
||||
channel,
|
||||
launchOptions,
|
||||
connectOptions,
|
||||
screenshot,
|
||||
trace,
|
||||
video,
|
||||
},
|
||||
testInfo
|
||||
)
|
||||
}
|
||||
|
||||
testInfo.tronApp =
|
||||
tronApp instanceof AuthenticatedTronApp ? tronApp : undefined
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type ZooTest = typeof test
|
||||
|
||||
test.describe = pwTestFnWithFixtures.describe
|
||||
test.beforeEach = pwTestFnWithFixtures.beforeEach
|
||||
test.afterEach = pwTestFnWithFixtures.afterEach
|
||||
test.step = pwTestFnWithFixtures.step
|
||||
test.skip = pwTestFnWithFixtures.skip
|
||||
test.setTimeout = pwTestFnWithFixtures.setTimeout
|
||||
test.fixme = pwTestFnWithFixtures.fixme as unknown as ZooTest
|
||||
test.only = pwTestFnWithFixtures.only
|
||||
test.fail = pwTestFnWithFixtures.fail
|
||||
test.slow = pwTestFnWithFixtures.slow
|
||||
test.beforeAll = pwTestFnWithFixtures.beforeAll
|
||||
test.afterAll = pwTestFnWithFixtures.afterAll
|
||||
test.use = pwTestFnWithFixtures.use
|
||||
test.expect = pwTestFnWithFixtures.expect
|
||||
test.extend = pwTestFnWithFixtures.extend
|
||||
test.info = pwTestFnWithFixtures.info
|
1
interface.d.ts
vendored
@ -7,6 +7,7 @@ import { MachinesListing } from 'components/MachineManagerProvider'
|
||||
type EnvFn = (value?: string) => string
|
||||
|
||||
export interface IElectronAPI {
|
||||
resizeWindow: (width: number, height: number) => Promise<void>
|
||||
open: typeof dialog.showOpenDialog
|
||||
save: typeof dialog.showSaveDialog
|
||||
openExternal: typeof shell.openExternal
|
||||
|
24
package.json
@ -103,24 +103,22 @@
|
||||
"tron:package": "electron-forge package",
|
||||
"tron:make": "electron-forge make",
|
||||
"tron:publish": "electron-forge publish",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"chrome:test": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert='@snapshot'",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
|
||||
"tronb:package": "electron-builder --config electron-builder.yml",
|
||||
"test-setup": "yarn install && yarn build:wasm",
|
||||
"test": "vitest --mode development",
|
||||
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
|
||||
"test:unit:kcl-samples": "vitest run --mode development ./src/lang/kclSamples.test.ts",
|
||||
"test:playwright:browser:chrome": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron'",
|
||||
"test:playwright:browser:chrome:windows": "playwright test --project=\"Google Chrome\" --config=playwright.ci.config.ts --grep-invert=\"@snapshot|@electron|@skipWin\"",
|
||||
"test:playwright:browser:chrome:ubuntu": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron|@skipLinux'",
|
||||
"test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipWin",
|
||||
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipMacos",
|
||||
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipLinux",
|
||||
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipWin",
|
||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipMacos",
|
||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipLinux",
|
||||
"test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
||||
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipWin|@snapshot'",
|
||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
||||
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
||||
},
|
||||
@ -158,8 +156,8 @@
|
||||
"@electron/rebuild": "^3.6.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@types/d3-force": "^3.0.10",
|
||||
|
@ -1,58 +0,0 @@
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
timeout: 120_000, // override the default 30s timeout
|
||||
testDir: './e2e/playwright',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: true,
|
||||
/* Do not retry */
|
||||
retries: 0,
|
||||
/* Different amount of parallelism on CI and local. */
|
||||
workers: 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [
|
||||
['dot'],
|
||||
['list'],
|
||||
['json', { outputFile: './test-results/report.json' }],
|
||||
['html'],
|
||||
],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'retain-on-failure',
|
||||
actionTimeout: 15_000,
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'Google Chrome',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
channel: 'chrome',
|
||||
contextOptions: {
|
||||
/* Chromium is the only one with these permission types */
|
||||
permissions: ['clipboard-write', 'clipboard-read'],
|
||||
},
|
||||
}, // or 'chrome-beta'
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
],
|
||||
|
||||
webServer: {
|
||||
command: 'yarn start',
|
||||
reuseExistingServer: false,
|
||||
},
|
||||
})
|
@ -45,7 +45,7 @@ export default class CodeManager {
|
||||
} else if (storedCode === null) {
|
||||
this.code = bracket
|
||||
} else {
|
||||
this.code = storedCode
|
||||
this.code = storedCode || ''
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +57,10 @@ export default class CodeManager {
|
||||
return this._code
|
||||
}
|
||||
|
||||
localStoragePersistCode(): string {
|
||||
return safeLSGetItem(PERSIST_CODE_KEY) || ''
|
||||
}
|
||||
|
||||
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
|
||||
this.#updateState = setCode
|
||||
}
|
||||
@ -165,7 +169,7 @@ export default class CodeManager {
|
||||
}
|
||||
|
||||
function safeLSGetItem(key: string) {
|
||||
if (typeof window === 'undefined') return null
|
||||
if (typeof window === 'undefined') return
|
||||
return localStorage?.getItem(key)
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,14 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
|
||||
}
|
||||
|
||||
if (window.electron.process.env.IS_PLAYWRIGHT) {
|
||||
// skip file picker, save to default location
|
||||
// Skip file picker, save to the test dir downloads directory
|
||||
const downloadDir = window.electron.join(
|
||||
window.electron.process.env.TEST_SETTINGS_FILE_KEY,
|
||||
'downloads-during-playwright'
|
||||
)
|
||||
await window.electron.mkdir(downloadDir, { recursive: true })
|
||||
await window.electron.writeFile(
|
||||
file.name,
|
||||
window.electron.join(downloadDir, file.name),
|
||||
new Uint8Array(file.contents)
|
||||
)
|
||||
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS, { id: toastId })
|
||||
|
@ -85,12 +85,13 @@ export const fileLoader: LoaderFunction = async (
|
||||
)
|
||||
const isBrowserProject = params.id === decodeURIComponent(BROWSER_PATH)
|
||||
|
||||
let code = ''
|
||||
|
||||
if (!isBrowserProject && projectPathData) {
|
||||
const { projectName, projectPath, currentFileName, currentFilePath } =
|
||||
projectPathData
|
||||
|
||||
const urlObj = new URL(routerData.request.url)
|
||||
let code = ''
|
||||
|
||||
if (!urlObj.pathname.endsWith('/settings')) {
|
||||
const fallbackFile = isDesktop()
|
||||
@ -120,6 +121,10 @@ export const fileLoader: LoaderFunction = async (
|
||||
})
|
||||
code = normalizeLineEndings(code)
|
||||
|
||||
// If persistCode in localStorage is present, it'll persist that code
|
||||
// through *anything*. INTENDED FOR TESTS.
|
||||
code = codeManager.localStoragePersistCode() || code
|
||||
|
||||
// Update both the state and the editor's code.
|
||||
// We explicitly do not write to the file here since we are loading from
|
||||
// the file system and not the editor.
|
||||
@ -147,12 +152,6 @@ export const fileLoader: LoaderFunction = async (
|
||||
? await getProjectInfo(projectPath)
|
||||
: null
|
||||
|
||||
console.log('maybeProjectInfo', {
|
||||
maybeProjectInfo,
|
||||
defaultProjectData,
|
||||
projectPathData,
|
||||
})
|
||||
|
||||
const projectData: IndexLoaderData = {
|
||||
code,
|
||||
project: maybeProjectInfo ?? defaultProjectData,
|
||||
@ -169,7 +168,7 @@ export const fileLoader: LoaderFunction = async (
|
||||
}
|
||||
|
||||
return {
|
||||
code: '',
|
||||
code,
|
||||
project: {
|
||||
name: BROWSER_PROJECT_NAME,
|
||||
path: '/' + BROWSER_PROJECT_NAME,
|
||||
|
11
src/main.ts
@ -139,6 +139,17 @@ app.on('ready', (event, data) => {
|
||||
// There is just not enough code to warrant it and further abstracts everything
|
||||
// which is already quite abstracted
|
||||
|
||||
// @ts-ignore
|
||||
// electron/electron.d.ts has done type = App, making declaration merging not
|
||||
// possible :(
|
||||
app.resizeWindow = async (width: number, height: number) => {
|
||||
return mainWindow?.setSize(width, height)
|
||||
}
|
||||
|
||||
ipcMain.handle('app.resizeWindow', (event, data) => {
|
||||
return mainWindow?.setSize(data[0], data[1])
|
||||
})
|
||||
|
||||
ipcMain.handle('app.getPath', (event, data) => {
|
||||
return app.getPath(data)
|
||||
})
|
||||
|
@ -7,6 +7,8 @@ import packageJson from '../package.json'
|
||||
import { MachinesListing } from 'components/MachineManagerProvider'
|
||||
import chokidar from 'chokidar'
|
||||
|
||||
const resizeWindow = (width: number, height: number) =>
|
||||
ipcRenderer.invoke('app.resizeWindow', [width, height])
|
||||
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
|
||||
const save = (args: any) => ipcRenderer.invoke('dialog.showSaveDialog', args)
|
||||
const openExternal = (url: any) => ipcRenderer.invoke('shell.openExternal', url)
|
||||
@ -18,12 +20,16 @@ const loginWithDeviceFlow = (): Promise<string> =>
|
||||
ipcRenderer.invoke('loginWithDeviceFlow')
|
||||
const onUpdateDownloaded = (
|
||||
callback: (value: { version: string; releaseNotes: string }) => void
|
||||
) => ipcRenderer.on('update-downloaded', (_event, value) => callback(value))
|
||||
) =>
|
||||
ipcRenderer.on('update-downloaded', (_event: any, value) => callback(value))
|
||||
const onUpdateDownloadStart = (
|
||||
callback: (value: { version: string }) => void
|
||||
) => ipcRenderer.on('update-download-start', (_event, value) => callback(value))
|
||||
) =>
|
||||
ipcRenderer.on('update-download-start', (_event: any, value) =>
|
||||
callback(value)
|
||||
)
|
||||
const onUpdateError = (callback: (value: Error) => void) =>
|
||||
ipcRenderer.on('update-error', (_event, value) => callback(value))
|
||||
ipcRenderer.on('update-error', (_event: any, value) => callback(value))
|
||||
const appRestart = () => ipcRenderer.invoke('app.restart')
|
||||
|
||||
const isMac = os.platform() === 'darwin'
|
||||
@ -189,4 +195,5 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
onUpdateError,
|
||||
appRestart,
|
||||
getArgvParsed,
|
||||
resizeWindow,
|
||||
})
|
||||
|
28
yarn.lock
@ -2258,12 +2258,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@playwright/test@^1.46.1":
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.1.tgz#a8dfdcd623c4c23bb1b7ea588058aad41055c188"
|
||||
integrity sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==
|
||||
"@playwright/test@^1.49.0":
|
||||
version "1.49.0"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e"
|
||||
integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==
|
||||
dependencies:
|
||||
playwright "1.46.1"
|
||||
playwright "1.49.0"
|
||||
|
||||
"@react-hook/latest@^1.0.2":
|
||||
version "1.0.3"
|
||||
@ -7800,17 +7800,17 @@ pkg-types@^1.0.3, pkg-types@^1.1.1:
|
||||
mlly "^1.7.1"
|
||||
pathe "^1.1.2"
|
||||
|
||||
playwright-core@1.46.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.1.tgz#28f3ab35312135dda75b0c92a3e5c0e7edb9cc8b"
|
||||
integrity sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==
|
||||
playwright-core@1.49.0:
|
||||
version "1.49.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.49.0.tgz#8e69ffed3f41855b854982f3632f2922c890afcb"
|
||||
integrity sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==
|
||||
|
||||
playwright@1.46.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.1.tgz#ea562bc48373648e10420a10c16842f0b227c218"
|
||||
integrity sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==
|
||||
playwright@1.49.0:
|
||||
version "1.49.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.49.0.tgz#df6b9e05423377a99658202844a294a8afb95d0a"
|
||||
integrity sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==
|
||||
dependencies:
|
||||
playwright-core "1.46.1"
|
||||
playwright-core "1.49.0"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
|