* move editor data into a singleton Signed-off-by: Jess Frazelle <github@jessfraz.com> * debounce on update Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * make select on extrude work Signed-off-by: Jess Frazelle <github@jessfraz.com> * highlight range Signed-off-by: Jess Frazelle <github@jessfraz.com> * highlight range Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix errors Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * almost forgot the error pane Signed-off-by: Jess Frazelle <github@jessfraz.com> * loint Signed-off-by: Jess Frazelle <github@jessfraz.com> * call out to codemirror Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix tauri; Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * more efficient Signed-off-by: Jess Frazelle <github@jessfraz.com> * create the modals in the hook Signed-off-by: Jess Frazelle <github@jessfraz.com> * Revert "create the modals in the hook" This reverts commit bbeba85030763cf7235a09fa24247dbf120f2a64. * change todo Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
1715 lines
56 KiB
TypeScript
1715 lines
56 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { getUtils } from './test-utils'
|
|
import waitOn from 'wait-on'
|
|
import { roundOff } from 'lib/utils'
|
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
|
import { secrets } from './secrets'
|
|
import {
|
|
TEST_SETTINGS,
|
|
TEST_SETTINGS_KEY,
|
|
TEST_SETTINGS_CORRUPTED,
|
|
TEST_SETTINGS_ONBOARDING,
|
|
} from './storageStates'
|
|
import * as TOML from '@iarna/toml'
|
|
|
|
/*
|
|
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
|
|
just from the nature of the stream, running the test with debugger and pasting the below
|
|
into the console can be useful to get coords
|
|
|
|
document.addEventListener('mousemove', (e) =>
|
|
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
|
|
)
|
|
*/
|
|
|
|
const commonPoints = {
|
|
startAt: '[9.06, -12.22]',
|
|
num1: 9.14,
|
|
num2: 18.2,
|
|
// num1: 9.64,
|
|
// num2: 19.19,
|
|
}
|
|
|
|
test.beforeEach(async ({ context, page }) => {
|
|
// wait for Vite preview server to be up
|
|
await waitOn({
|
|
resources: ['tcp:3000'],
|
|
timeout: 5000,
|
|
})
|
|
|
|
await context.addInitScript(
|
|
async ({ token, settingsKey, settings }) => {
|
|
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
|
localStorage.setItem('persistCode', ``)
|
|
localStorage.setItem(settingsKey, settings)
|
|
},
|
|
{
|
|
token: secrets.token,
|
|
settingsKey: TEST_SETTINGS_KEY,
|
|
settings: TOML.stringify({ settings: TEST_SETTINGS }),
|
|
}
|
|
)
|
|
// kill animations, speeds up tests and reduced flakiness
|
|
await page.emulateMedia({ reducedMotion: 'reduce' })
|
|
})
|
|
|
|
test.setTimeout(60000)
|
|
|
|
test('Basic sketch', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
|
|
|
// click on "Start Sketch" button
|
|
await u.clearCommandLogs()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
|
|
// select a plane
|
|
await page.mouse.click(700, 200)
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const part001 = startSketchOn('-XZ')`
|
|
)
|
|
await u.closeDebugPanel()
|
|
|
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
|
|
|
const startXPx = 600
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|
await page.waitForTimeout(100)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
await page.waitForTimeout(100)
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)`)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)`)
|
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)
|
|
|> line([-${commonPoints.num2}, 0], %)`)
|
|
|
|
// deselect line tool
|
|
await page.getByRole('button', { name: 'Line' }).click()
|
|
await page.waitForTimeout(100)
|
|
|
|
// click between first two clicks to get center of the line
|
|
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
|
await page.waitForTimeout(100)
|
|
|
|
// hold down shift
|
|
await page.keyboard.down('Shift')
|
|
// click between the latest two clicks to get center of the line
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
|
|
|
|
// selected two lines therefore there should be two cursors
|
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
|
|
|
await page.getByRole('button', { name: 'Equal Length' }).click()
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %, 'seg01')
|
|
|> line([0, ${commonPoints.num1}], %)
|
|
|> angledLine([180, segLen('seg01', %)], %)`)
|
|
})
|
|
|
|
test('Can moving camera', async ({ page, context }) => {
|
|
test.skip(process.platform === 'darwin', 'Can moving camera')
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openAndClearDebugPanel()
|
|
await u.closeKclCodePanel()
|
|
|
|
const camPos: [number, number, number] = [0, 85, 85]
|
|
const bakeInRetries = async (
|
|
mouseActions: any,
|
|
xyz: [number, number, number],
|
|
cnt = 0
|
|
) => {
|
|
// hack that we're implemented our own retry instead of using retries built into playwright.
|
|
// however each of these camera drags can be flaky, because of udp
|
|
// and so putting them together means only one needs to fail to make this test extra flaky.
|
|
// this way we can retry within the test
|
|
// We could break them out into separate tests, but the longest past of the test is waiting
|
|
// for the stream to start, so it can be good to bundle related things together.
|
|
|
|
await u.updateCamPosition(camPos)
|
|
await page.waitForTimeout(100)
|
|
|
|
// rotate
|
|
await u.closeDebugPanel()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
// const yo = page.getByTestId('cam-x-position').inputValue()
|
|
|
|
await u.doAndWaitForImageDiff(async () => {
|
|
await mouseActions()
|
|
|
|
await u.openAndClearDebugPanel()
|
|
|
|
await u.closeDebugPanel()
|
|
await page.waitForTimeout(100)
|
|
}, 300)
|
|
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByTestId('cam-x-position').isVisible()
|
|
|
|
const vals = await Promise.all([
|
|
page.getByTestId('cam-x-position').inputValue(),
|
|
page.getByTestId('cam-y-position').inputValue(),
|
|
page.getByTestId('cam-z-position').inputValue(),
|
|
])
|
|
const xError = Math.abs(Number(vals[0]) + xyz[0])
|
|
const yError = Math.abs(Number(vals[1]) + xyz[1])
|
|
const zError = Math.abs(Number(vals[2]) + xyz[2])
|
|
|
|
let shouldRetry = false
|
|
|
|
if (xError > 5 || yError > 5 || zError > 5) {
|
|
if (cnt > 2) {
|
|
console.log('xVal', vals[0], 'xError', xError)
|
|
console.log('yVal', vals[1], 'yError', yError)
|
|
console.log('zVal', vals[2], 'zError', zError)
|
|
|
|
throw new Error('Camera position not as expected')
|
|
}
|
|
shouldRetry = true
|
|
}
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
if (shouldRetry) await bakeInRetries(mouseActions, xyz, cnt + 1)
|
|
}
|
|
await bakeInRetries(async () => {
|
|
await page.mouse.move(700, 200)
|
|
await page.mouse.down({ button: 'right' })
|
|
await page.mouse.move(600, 303)
|
|
await page.mouse.up({ button: 'right' })
|
|
}, [4, -10.5, -120])
|
|
|
|
await bakeInRetries(async () => {
|
|
await page.keyboard.down('Shift')
|
|
await page.mouse.move(600, 200)
|
|
await page.mouse.down({ button: 'right' })
|
|
await page.mouse.move(700, 200, { steps: 2 })
|
|
await page.mouse.up({ button: 'right' })
|
|
await page.keyboard.up('Shift')
|
|
}, [-10, -85, -85])
|
|
|
|
await u.updateCamPosition(camPos)
|
|
|
|
await u.clearCommandLogs()
|
|
await u.closeDebugPanel()
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(200)
|
|
|
|
// zoom
|
|
await u.doAndWaitForImageDiff(async () => {
|
|
await page.keyboard.down('Control')
|
|
await page.mouse.move(700, 400)
|
|
await page.mouse.down({ button: 'right' })
|
|
await page.mouse.move(700, 300)
|
|
await page.mouse.up({ button: 'right' })
|
|
await page.keyboard.up('Control')
|
|
|
|
await u.openDebugPanel()
|
|
await page.waitForTimeout(300)
|
|
await u.clearCommandLogs()
|
|
|
|
await u.closeDebugPanel()
|
|
}, 300)
|
|
|
|
// zoom with scroll
|
|
await u.openAndClearDebugPanel()
|
|
// TODO, it appears we don't get the cam setting back from the engine when the interaction is zoom into `backInRetries` once the information is sent back on zoom
|
|
// await expect(Math.abs(Number(await page.getByTestId('cam-x-position').inputValue()) + 12)).toBeLessThan(1.5)
|
|
// await expect(Math.abs(Number(await page.getByTestId('cam-y-position').inputValue()) - 85)).toBeLessThan(1.5)
|
|
// await expect(Math.abs(Number(await page.getByTestId('cam-z-position').inputValue()) - 85)).toBeLessThan(1.5)
|
|
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
await bakeInRetries(async () => {
|
|
await page.mouse.move(700, 400)
|
|
await page.mouse.wheel(0, -100)
|
|
}, [1, -94, -94])
|
|
})
|
|
|
|
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1000, height: 500 })
|
|
await page.goto('/')
|
|
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
// check no error to begin with
|
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
|
|
/* add the following code to the editor (# error is not a valid line)
|
|
# error
|
|
const topAng = 30
|
|
const bottomAng = 25
|
|
*/
|
|
await page.click('.cm-content')
|
|
await page.keyboard.type('# error')
|
|
|
|
// press arrows to clear autocomplete
|
|
await page.keyboard.press('ArrowLeft')
|
|
await page.keyboard.press('ArrowRight')
|
|
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type('const topAng = 30')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type('const bottomAng = 25')
|
|
await page.keyboard.press('Enter')
|
|
|
|
// error in guter
|
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
|
|
// error text on hover
|
|
await page.hover('.cm-lint-marker-error')
|
|
await expect(page.getByText("found unknown token '#'")).toBeVisible()
|
|
|
|
// select the line that's causing the error and delete it
|
|
await page.getByText('# error').click()
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.down('Shift')
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.up('Shift')
|
|
await page.keyboard.press('Backspace')
|
|
|
|
// wait for .cm-lint-marker-error not to be visible
|
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
|
|
// let's check we get an error when defining the same variable twice
|
|
await page.getByText('const bottomAng = 25').click()
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type("// Let's define the same thing twice")
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type('const topAng = 42')
|
|
|
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
await expect(page.locator('.cm-lintRange.cm-lintRange-error')).toBeVisible()
|
|
|
|
await page.locator('.cm-lintRange.cm-lintRange-error').hover()
|
|
await expect(page.locator('.cm-diagnosticText')).toBeVisible()
|
|
await expect(page.getByText('Cannot redefine topAng')).toBeVisible()
|
|
|
|
const secondTopAng = await page.getByText('topAng').first()
|
|
await secondTopAng?.dblclick()
|
|
await page.keyboard.type('otherAng')
|
|
|
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
})
|
|
|
|
/* Ignore this test for now since its causing engine to crash
|
|
*
|
|
* test('if your kcl gets an error from the engine it is inlined', async ({
|
|
page,
|
|
}) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const box = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([0, 10], %)
|
|
|> line([10, 0], %)
|
|
|> line([0, -10], %, 'revolveAxis')
|
|
|> close(%)
|
|
|> extrude(10, %)
|
|
|
|
const sketch001 = startSketchOn(box, "revolveAxis")
|
|
|> startProfileAt([5, 10], %)
|
|
|> line([0, -10], %)
|
|
|> line([2, 0], %)
|
|
|> line([0, 10], %)
|
|
|> close(%)
|
|
|> revolve({
|
|
axis: getEdge('revolveAxis', box),
|
|
angle: 90
|
|
}, %)
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setViewportSize({ width: 1000, height: 500 })
|
|
await page.goto('/')
|
|
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// error in guter
|
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
|
|
// error text on hover
|
|
await page.hover('.cm-lint-marker-error')
|
|
await expect(
|
|
page.getByText(
|
|
'sketch profile must lie entirely on one side of the revolution axis'
|
|
)
|
|
).toBeVisible()
|
|
})*/
|
|
|
|
test('executes on load', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([-6.95, 4.98], %)
|
|
|> line([25.1, 0.41], %)
|
|
|> line([0.73, -14.93], %)
|
|
|> line([-23.44, 0.52], %)`
|
|
)
|
|
})
|
|
await page.setViewportSize({ width: 1000, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
// expand variables section
|
|
const variablesTabButton = page.getByRole('tab', {
|
|
name: 'Variables',
|
|
exact: false,
|
|
})
|
|
await variablesTabButton.click()
|
|
|
|
// can find part001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor)
|
|
// part001 only shows up in the variables summary if it's been executed
|
|
await page.waitForFunction(() => {
|
|
const variablesElement = document.querySelector(
|
|
'.pretty-json-container'
|
|
) as HTMLDivElement
|
|
return variablesElement.innerHTML.includes('part001')
|
|
})
|
|
await expect(
|
|
page.locator('.pretty-json-container >> text=part001')
|
|
).toBeVisible()
|
|
})
|
|
|
|
test('re-executes', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem('persistCode', `const myVar = 5`)
|
|
})
|
|
await page.setViewportSize({ width: 1000, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
const variablesTabButton = page.getByRole('tab', {
|
|
name: 'Variables',
|
|
exact: false,
|
|
})
|
|
await variablesTabButton.click()
|
|
// expect to see "myVar:5"
|
|
await expect(
|
|
page.locator('.pretty-json-container >> text=myVar:5')
|
|
).toBeVisible()
|
|
|
|
// change 5 to 67
|
|
await page.getByText('const myVar').click()
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.press('Backspace')
|
|
await page.keyboard.type('67')
|
|
|
|
await expect(
|
|
page.locator('.pretty-json-container >> text=myVar:67')
|
|
).toBeVisible()
|
|
})
|
|
|
|
const sketchOnPlaneAndBackSideTest = async (
|
|
page: any,
|
|
plane: string,
|
|
clickCoords: { x: number; y: number }
|
|
) => {
|
|
const u = getUtils(page)
|
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
const camCmdBackSide: [number, number, number] = [-100, -100, -100]
|
|
let camPos: [number, number, number] = [100, 100, 100]
|
|
if (plane === '-XY' || plane === '-YZ' || plane === '-XZ') {
|
|
camPos = camCmdBackSide
|
|
}
|
|
|
|
const code = `const part001 = startSketchOn('${plane}')
|
|
|> startProfileAt([1.14, -1.54], %)`
|
|
|
|
await u.openDebugPanel()
|
|
|
|
await u.clearCommandLogs()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await u.updateCamPosition(camPos)
|
|
|
|
await u.closeDebugPanel()
|
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
|
await page.waitForTimeout(300) // wait for animation
|
|
|
|
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
|
|
|
|
// draw a line
|
|
const startXPx = 600
|
|
|
|
await u.closeDebugPanel()
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
|
|
|
await page.getByRole('button', { name: 'Line' }).click()
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await u.clearCommandLogs()
|
|
await u.removeCurrentCode()
|
|
}
|
|
|
|
test.describe('Can create sketches on all planes and their back sides', () => {
|
|
test('XY', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(
|
|
page,
|
|
'XY',
|
|
{ x: 600, y: 388 } // red plane
|
|
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
|
)
|
|
})
|
|
|
|
test('YZ', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
|
|
})
|
|
|
|
test('XZ', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 80 }) // blue plane
|
|
})
|
|
|
|
test('-XY', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
|
})
|
|
|
|
test('-YZ', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
|
|
})
|
|
|
|
test('-XZ', async ({ page }) => {
|
|
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 427 }) // back of blue plane
|
|
})
|
|
})
|
|
|
|
test('Auto complete works', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
const lspStartPromise = page.waitForEvent('console', async (message) => {
|
|
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
|
// but that doesn't seem to make it to the console for macos/safari :(
|
|
if (message.text().includes('start kcl lsp')) {
|
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await lspStartPromise
|
|
|
|
// this test might be brittle as we add and remove functions
|
|
// but should also be easy to update.
|
|
// tests clicking on an option, selection the first option
|
|
// and arrowing down to an option
|
|
|
|
await page.click('.cm-content')
|
|
await page.keyboard.type('const part001 = start')
|
|
|
|
// expect there to be three auto complete options
|
|
await expect(page.locator('.cm-completionLabel')).toHaveCount(3)
|
|
await page.getByText('startSketchOn').click()
|
|
await page.keyboard.type("'XZ'")
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type(' |> startProfi')
|
|
// expect there be a single auto complete option that we can just hit enter on
|
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
|
await page.waitForTimeout(100)
|
|
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
|
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.type('12')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type(' |> lin')
|
|
|
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
|
await page.waitForTimeout(100)
|
|
// press arrow down twice then enter to accept xLine
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Enter')
|
|
// finish line with comment
|
|
await page.keyboard.type('5')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.type(' // lin')
|
|
await page.waitForTimeout(100)
|
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('XZ')
|
|
|> startProfileAt([3.14, 12], %)
|
|
|> xLine(5, %) // lin`)
|
|
})
|
|
|
|
test('Stored settings are validated and fall back to defaults', async ({
|
|
page,
|
|
context,
|
|
}) => {
|
|
const u = getUtils(page)
|
|
|
|
// Override beforeEach test setup
|
|
// with corrupted settings
|
|
await context.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 page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
// Check the settings were reset
|
|
const storedSettings = TOML.parse(
|
|
await page.evaluate(
|
|
({ settingsKey }) => localStorage.getItem(settingsKey) || '{}',
|
|
{ settingsKey: TEST_SETTINGS_KEY }
|
|
)
|
|
) as { settings: SaveSettingsPayload }
|
|
|
|
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)
|
|
expect(storedSettings.settings.projects?.defaultProjectName).toBe(undefined)
|
|
})
|
|
|
|
test('Project settings can be set and override user settings', async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' })
|
|
await page
|
|
.getByRole('button', { name: 'Start Sketch' })
|
|
.waitFor({ state: 'visible' })
|
|
|
|
// Open the settings modal with the browser keyboard shortcut
|
|
await page.keyboard.press('Meta+Shift+,')
|
|
|
|
await expect(
|
|
page.getByRole('heading', { name: 'Settings', exact: true })
|
|
).toBeVisible()
|
|
await page
|
|
.locator('select[name="app-theme"]')
|
|
.selectOption({ value: 'light' })
|
|
|
|
// Verify the toast appeared
|
|
await expect(
|
|
page.getByText(`Set theme to "light" for this project`)
|
|
).toBeVisible()
|
|
// Check that the theme changed
|
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
|
|
|
// Check that the user setting was not changed
|
|
await page.getByRole('radio', { name: 'User' }).click()
|
|
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
|
|
|
|
// Roll back to default "system" theme
|
|
await page
|
|
.getByText(
|
|
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
|
|
)
|
|
.hover()
|
|
await page
|
|
.getByRole('button', {
|
|
name: 'Roll back theme ; Has tooltip: Roll back to match default',
|
|
})
|
|
.click()
|
|
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
|
|
|
|
// Check that the project setting did not change
|
|
await page.getByRole('radio', { name: 'Project' }).click()
|
|
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
|
|
})
|
|
|
|
test('Onboarding redirects and code updating', async ({ page }) => {
|
|
const u = 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', 'const sigmaAllow = 15000')
|
|
localStorage.setItem(settingsKey, settings)
|
|
},
|
|
{
|
|
settingsKey: TEST_SETTINGS_KEY,
|
|
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
|
|
}
|
|
)
|
|
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
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('Selections work on fresh and edited sketch', async ({ page }) => {
|
|
// tests mapping works on fresh sketch and edited sketch
|
|
// tests using hovers which is the same as selections, because if
|
|
// source ranges are wrong, hovers won't work
|
|
const u = getUtils(page)
|
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
const xAxisClick = () =>
|
|
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
|
|
const emptySpaceClick = () =>
|
|
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
|
const topHorzSegmentClick = () =>
|
|
page.mouse.click(709, 290).then(() => page.waitForTimeout(100))
|
|
const bottomHorzSegmentClick = () =>
|
|
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
|
|
|
|
await u.clearCommandLogs()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
// select a plane
|
|
await page.mouse.click(700, 200)
|
|
await page.waitForTimeout(700) // wait for animation
|
|
|
|
const startXPx = 600
|
|
await u.closeDebugPanel()
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)`)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)`)
|
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)
|
|
|> line([-${commonPoints.num2}, 0], %)`)
|
|
|
|
// deselect line tool
|
|
await page.getByRole('button', { name: 'Line' }).click()
|
|
|
|
await u.closeDebugPanel()
|
|
const selectionSequence = async () => {
|
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
|
|
|
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
|
|
|
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
|
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
|
|
// and will be an easy fix if it breaks because we change the colour
|
|
await expect(page.locator('.bg-yellow-200')).toBeVisible()
|
|
|
|
// check mousing off, than mousing onto another line
|
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
|
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
|
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
|
|
|
// now check clicking works including axis
|
|
|
|
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
|
|
await topHorzSegmentClick()
|
|
await page.keyboard.down('Shift')
|
|
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
|
await expect(absYButton).toBeDisabled()
|
|
await xAxisClick()
|
|
await page.keyboard.up('Shift')
|
|
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
|
await expect(absYButton).not.toBeDisabled()
|
|
|
|
// clear selection by clicking on nothing
|
|
await emptySpaceClick()
|
|
|
|
// same selection but click the axis first
|
|
await xAxisClick()
|
|
await expect(absYButton).toBeDisabled()
|
|
await page.keyboard.down('Shift')
|
|
await topHorzSegmentClick()
|
|
|
|
await page.keyboard.up('Shift')
|
|
await expect(absYButton).not.toBeDisabled()
|
|
|
|
// clear selection by clicking on nothing
|
|
await emptySpaceClick()
|
|
|
|
// check the same selection again by putting cursor in code first then selecting axis
|
|
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
|
await page.keyboard.down('Shift')
|
|
await expect(absYButton).toBeDisabled()
|
|
await xAxisClick()
|
|
await page.keyboard.up('Shift')
|
|
await expect(absYButton).not.toBeDisabled()
|
|
|
|
// clear selection by clicking on nothing
|
|
await emptySpaceClick()
|
|
|
|
// select segment in editor than another segment in scene and check there are two cursors
|
|
// TODO change this back to shift click in the scene, not cmd click in the editor
|
|
await bottomHorzSegmentClick()
|
|
|
|
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
|
|
|
await page.keyboard.down(process.platform === 'linux' ? 'Control' : 'Meta')
|
|
await page.waitForTimeout(100)
|
|
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
|
|
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
|
await page.waitForTimeout(500)
|
|
await page.keyboard.up(process.platform === 'linux' ? 'Control' : 'Meta')
|
|
|
|
// clear selection by clicking on nothing
|
|
await emptySpaceClick()
|
|
}
|
|
|
|
await selectionSequence()
|
|
|
|
// hovering in fresh sketch worked, lets try exiting and re-entering
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await page.waitForTimeout(200)
|
|
// wait for execution done
|
|
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// select a line
|
|
// await topHorzSegmentClick()
|
|
await page.getByText(commonPoints.startAt).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
await page.waitForTimeout(100)
|
|
|
|
// enter sketch again
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(300) // wait for animation
|
|
|
|
// hover again and check it works
|
|
await selectionSequence()
|
|
})
|
|
|
|
test.describe('Command bar tests', () => {
|
|
test('Command bar works and can change a setting', async ({ page }) => {
|
|
// Brief boilerplate
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' })
|
|
|
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
|
|
// First try opening the command bar and closing it
|
|
// It has a different label on mac and windows/linux, "Meta+K" and "Ctrl+/" respectively
|
|
await page
|
|
.getByRole('button', { name: 'Ctrl+/' })
|
|
.or(page.getByRole('button', { name: '⌘K' }))
|
|
.click()
|
|
await expect(cmdSearchBar).toBeVisible()
|
|
await page.keyboard.press('Escape')
|
|
await expect(cmdSearchBar).not.toBeVisible()
|
|
|
|
// Now try the same, but with the keyboard shortcut, check focus
|
|
await page.keyboard.press('Meta+K')
|
|
await expect(cmdSearchBar).toBeVisible()
|
|
await expect(cmdSearchBar).toBeFocused()
|
|
|
|
// Try typing in the command bar
|
|
await page.keyboard.type('theme')
|
|
const themeOption = page.getByRole('option', {
|
|
name: 'Settings · app · theme',
|
|
})
|
|
await expect(themeOption).toBeVisible()
|
|
await themeOption.click()
|
|
const themeInput = page.getByPlaceholder('Select an option')
|
|
await expect(themeInput).toBeVisible()
|
|
await expect(themeInput).toBeFocused()
|
|
// Select dark theme
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
|
|
'data-headlessui-state',
|
|
'active'
|
|
)
|
|
await page.keyboard.press('Enter')
|
|
|
|
// Check the toast appeared
|
|
await expect(
|
|
page.getByText(`Set theme to "system" for this project`)
|
|
).toBeVisible()
|
|
// Check that the theme changed
|
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
|
})
|
|
|
|
test('Can extrude from the command bar', async ({ page }) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const distance = sqrt(20)
|
|
const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([-6.95, 4.98], %)
|
|
|> line([25.1, 0.41], %)
|
|
|> line([0.73, -14.93], %)
|
|
|> line([-23.44, 0.52], %)
|
|
|> close(%)
|
|
`
|
|
)
|
|
})
|
|
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
// Make sure the stream is up
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await u.clearCommandLogs()
|
|
await page.getByText('|> line([0.73, -14.93], %)').click()
|
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
|
|
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
await page.keyboard.press('Meta+K')
|
|
await expect(cmdSearchBar).toBeVisible()
|
|
|
|
// Search for extrude command and choose it
|
|
await page.getByRole('option', { name: 'Extrude' }).click()
|
|
|
|
// Assert that we're on the distance step
|
|
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
|
|
|
// Assert that the an alternative variable name is chosen,
|
|
// since the default variable name is already in use (distance)
|
|
await page.getByRole('button', { name: 'Create new variable' }).click()
|
|
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
|
'distance001'
|
|
)
|
|
|
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
|
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
|
await continueButton.click()
|
|
|
|
// Review step and argument hotkeys
|
|
await expect(submitButton).toBeEnabled()
|
|
await page.keyboard.press('Backspace')
|
|
|
|
// Assert we're back on the distance step
|
|
await expect(
|
|
page.getByRole('button', { name: 'Distance 12', exact: false })
|
|
).toBeDisabled()
|
|
|
|
await continueButton.click()
|
|
await submitButton.click()
|
|
|
|
// Check that the code was updated
|
|
await u.waitForCmdReceive('extrude')
|
|
// Unfortunately this indentation seems to matter for the test
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const distance = sqrt(20)
|
|
const distance001 = 5 + 7
|
|
const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([-6.95, 4.98], %)
|
|
|> line([25.1, 0.41], %)
|
|
|> line([0.73, -14.93], %)
|
|
|> line([-23.44, 0.52], %)
|
|
|> close(%)
|
|
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
|
|
)
|
|
})
|
|
})
|
|
|
|
test('Can add multiple sketches', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
|
|
|
// click on "Start Sketch" button
|
|
await u.clearCommandLogs()
|
|
await u.doAndWaitForImageDiff(
|
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
|
200
|
|
)
|
|
|
|
// select a plane
|
|
await page.mouse.click(700, 200)
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const part001 = startSketchOn('-XZ')`
|
|
)
|
|
|
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
|
|
|
const startXPx = 600
|
|
await u.closeDebugPanel()
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|
await page.waitForTimeout(100)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
await page.waitForTimeout(100)
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)`)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)`)
|
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
|
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt(${commonPoints.startAt}, %)
|
|
|> line([${commonPoints.num1}, 0], %)
|
|
|> line([0, ${commonPoints.num1}], %)
|
|
|> line([-${commonPoints.num2}, 0], %)`
|
|
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
|
|
|
|
// exit the sketch
|
|
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await u.updateCamPosition([0, 100, 100])
|
|
|
|
// start a new sketch
|
|
await u.clearCommandLogs()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
await page.mouse.click(673, 384)
|
|
|
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
|
await u.clearAndCloseDebugPanel()
|
|
|
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
|
const startAt2 = '[0.93,-1.25]'
|
|
await expect(
|
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
|
).toBe(
|
|
`${finalCodeFirstSketch}
|
|
const part002 = startSketchOn('XY')
|
|
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
|
)
|
|
await page.waitForTimeout(100)
|
|
|
|
await u.closeDebugPanel()
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
await page.waitForTimeout(100)
|
|
|
|
const num2 = 0.94
|
|
await expect(
|
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
|
).toBe(
|
|
`${finalCodeFirstSketch}
|
|
const part002 = startSketchOn('XY')
|
|
|> startProfileAt(${startAt2}, %)
|
|
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
|
)
|
|
|
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
|
await expect(
|
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
|
).toBe(
|
|
`${finalCodeFirstSketch}
|
|
const part002 = startSketchOn('XY')
|
|
|> startProfileAt(${startAt2}, %)
|
|
|> line([${num2}, 0], %)
|
|
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|
|
)
|
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
|
await expect(
|
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
|
).toBe(
|
|
`${finalCodeFirstSketch}
|
|
const part002 = startSketchOn('XY')
|
|
|> startProfileAt(${startAt2}, %)
|
|
|> line([${num2}, 0], %)
|
|
|> line([0, ${roundOff(num2 - 0.01)}], %)
|
|
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|
|
)
|
|
})
|
|
|
|
test('ProgramMemory can be serialised', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part = startSketchOn('XY')
|
|
|> startProfileAt([0, 0], %)
|
|
|> line([0, 1], %)
|
|
|> line([1, 0], %)
|
|
|> line([0, -1], %)
|
|
|> close(%)
|
|
|> extrude(1, %)
|
|
|> patternLinear3d({
|
|
axis: [1, 0, 1],
|
|
repetitions: 3,
|
|
distance: 6
|
|
}, %)`
|
|
)
|
|
})
|
|
await page.setViewportSize({ width: 1000, height: 500 })
|
|
await page.goto('/')
|
|
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()
|
|
|
|
// wait for execution done
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
const forbiddenMessages = ['cannot serialize tagged newtype variant']
|
|
forbiddenMessages.forEach((forbiddenMessage) => {
|
|
messages.forEach((message) => {
|
|
expect(message).not.toContain(forbiddenMessage)
|
|
})
|
|
})
|
|
})
|
|
|
|
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
|
|
page,
|
|
}) => {
|
|
const u = getUtils(page)
|
|
const selectionsSnippets = {
|
|
extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)',
|
|
extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)',
|
|
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
|
|
editOnly: '|> startProfileAt([15.79, -14.6], %)',
|
|
}
|
|
await page.addInitScript(
|
|
async ({
|
|
extrudeAndEditBlocked,
|
|
extrudeAndEditBlockedInFunction,
|
|
extrudeAndEditAllowed,
|
|
editOnly,
|
|
}: any) => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('-XZ')
|
|
${extrudeAndEditBlocked}
|
|
|> line([25.96, 2.93], %)
|
|
|> line([5.25, -5.72], %)
|
|
|> line([-2.01, -10.35], %)
|
|
|> line([-27.65, -2.78], %)
|
|
|> close(%)
|
|
|> extrude(5, %)
|
|
const part002 = startSketchOn('-XZ')
|
|
${extrudeAndEditAllowed}
|
|
|> line([10.32, 6.47], %)
|
|
|> line([9.71, -6.16], %)
|
|
|> line([-3.08, -9.86], %)
|
|
|> line([-12.02, -1.54], %)
|
|
|> close(%)
|
|
const part003 = startSketchOn('-XZ')
|
|
${editOnly}
|
|
|> line([27.55, -1.65], %)
|
|
|> line([4.95, -8], %)
|
|
|> line([-20.38, -10.12], %)
|
|
|> line([-15.79, 17.08], %)
|
|
|
|
fn yohey = (pos) => {
|
|
const part004 = startSketchOn('-XZ')
|
|
${extrudeAndEditBlockedInFunction}
|
|
|> line([27.55, -1.65], %)
|
|
|> line([4.95, -10.53], %)
|
|
|> line([-20.38, -8], %)
|
|
|> line([-15.79, 17.08], %)
|
|
return ''
|
|
}
|
|
|
|
yohey([15.79, -34.6])
|
|
`
|
|
)
|
|
},
|
|
selectionsSnippets
|
|
)
|
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
|
|
// wait for execution done
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// wait for start sketch as a proxy for the stream being ready
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
|
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).not.toBeVisible()
|
|
|
|
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.getByText(selectionsSnippets.editOnly).click()
|
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page
|
|
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
|
|
.click()
|
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).not.toBeVisible()
|
|
|
|
// selecting an editable sketch but clicking "start sktech" should start a new sketch and not edit the existing one
|
|
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.mouse.click(700, 200)
|
|
// expect main content to contain `part005` i.e. started a new sketch
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
/part005 = startSketchOn\('-XZ'\)/
|
|
)
|
|
})
|
|
|
|
test('Deselecting line tool should mean nothing happens on click', async ({
|
|
page,
|
|
}) => {
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
|
|
|
// click on "Start Sketch" button
|
|
await u.clearCommandLogs()
|
|
await u.doAndWaitForImageDiff(
|
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
|
200
|
|
)
|
|
|
|
await page.mouse.click(700, 200)
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const part001 = startSketchOn('-XZ')`
|
|
)
|
|
|
|
await page.waitForTimeout(300)
|
|
|
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
// deselect the line tool by clicking it
|
|
await page.getByRole('button', { name: 'Line' }).click()
|
|
|
|
await page.mouse.click(700, 200)
|
|
await page.waitForTimeout(100)
|
|
await page.mouse.click(700, 250)
|
|
await page.waitForTimeout(100)
|
|
await page.mouse.click(750, 200)
|
|
await page.waitForTimeout(100)
|
|
|
|
// expect no change
|
|
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
|
|
|
|
// select line tool again
|
|
await page.getByRole('button', { name: 'Line' }).click()
|
|
|
|
await u.closeDebugPanel()
|
|
|
|
// line tool should work as expected again
|
|
await page.mouse.click(700, 200)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(700, 300)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(750, 300)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
})
|
|
|
|
test('Can edit segments by dragging their handles', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([4.61, -14.01], %)
|
|
|> line([12.73, -0.09], %)
|
|
|> tangentialArcTo([24.95, -5.38], %)`
|
|
)
|
|
})
|
|
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const startPX = [652, 418]
|
|
const lineEndPX = [794, 416]
|
|
const arcEndPX = [893, 318]
|
|
|
|
const dragPX = 30
|
|
|
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
const step5 = { steps: 5 }
|
|
|
|
// drag startProfieAt handle
|
|
await page.mouse.move(startPX[0], startPX[1])
|
|
await page.mouse.down()
|
|
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag line handle
|
|
await page.mouse.move(lineEndPX[0] + dragPX, lineEndPX[1] - dragPX)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
lineEndPX[0] + dragPX * 2,
|
|
lineEndPX[1] - dragPX * 2,
|
|
step5
|
|
)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag tangentialArcTo handle
|
|
await page.mouse.move(arcEndPX[0], arcEndPX[1])
|
|
await page.mouse.down()
|
|
await page.mouse.move(arcEndPX[0] + dragPX, arcEndPX[1] - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
|
|
// expect the code to have changed
|
|
await expect(page.locator('.cm-content'))
|
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([7.01, -11.79], %)
|
|
|> line([14.69, 2.73], %)
|
|
|> tangentialArcTo([27.6, -3.25], %)`)
|
|
})
|
|
|
|
const doSnapAtDifferentScales = async (
|
|
page: any,
|
|
camPos: [number, number, number],
|
|
scale = 1,
|
|
fudge = 0
|
|
) => {
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
|
|
const code = `const part001 = startSketchOn('XZ')
|
|
|> startProfileAt([${roundOff(scale * 87.68)}, ${roundOff(scale * 43.84)}], %)
|
|
|> line([${roundOff(scale * 175.36)}, 0], %)
|
|
|> line([0, -${roundOff(scale * 175.36) + fudge}], %)
|
|
|> close(%)`
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
|
|
|
await u.clearCommandLogs()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
|
|
await u.openAndClearDebugPanel()
|
|
await u.updateCamPosition(camPos)
|
|
await u.closeDebugPanel()
|
|
|
|
// select a plane
|
|
await page.mouse.click(700, 200)
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const part001 = startSketchOn('XZ')`
|
|
)
|
|
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
const pointA = [700, 200]
|
|
const pointB = [900, 200]
|
|
const pointC = [900, 400]
|
|
|
|
// draw three lines
|
|
await page.mouse.click(pointA[0], pointA[1])
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(pointB[0], pointB[1])
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(pointC[0], pointC[1])
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.move(pointA[0] - 12, pointA[1] + 12)
|
|
const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7]
|
|
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], { steps: 10 })
|
|
|
|
await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1])
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
|
|
|
// exit sketch
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.removeCurrentCode()
|
|
}
|
|
|
|
test.describe('Snap to close works (at any scale)', () => {
|
|
test('[0, 100, 100]', async ({ page }) => {
|
|
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01, 0.01)
|
|
})
|
|
|
|
test('[0, 10000, 10000]', async ({ page }) => {
|
|
await doSnapAtDifferentScales(page, [0, 10000, 10000])
|
|
})
|
|
})
|
|
|
|
test('Sketch on face', async ({ page }) => {
|
|
const u = getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('-XZ')
|
|
|> startProfileAt([3.29, 7.86], %)
|
|
|> line([2.48, 2.44], %)
|
|
|> line([2.66, 1.17], %)
|
|
|> line([3.75, 0.46], %)
|
|
|> line([4.99, -0.46], %)
|
|
|> line([3.3, -2.12], %)
|
|
|> line([2.16, -3.33], %)
|
|
|> line([0.85, -3.08], %)
|
|
|> line([-0.18, -3.36], %)
|
|
|> line([-3.86, -2.73], %)
|
|
|> line([-17.67, 0.85], %)
|
|
|> close(%)
|
|
|> extrude(5 + 7, %)`
|
|
)
|
|
})
|
|
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(793, 133)
|
|
|
|
const firstClickPosition = [612, 238]
|
|
const secondClickPosition = [661, 242]
|
|
const thirdClickPosition = [609, 267]
|
|
|
|
await page.waitForTimeout(300)
|
|
|
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
|
|> startProfileAt([1.03, 1.03], %)
|
|
|> line([4.18, -0.35], %)
|
|
|> line([-4.44, -2.13], %)
|
|
|> close(%)`)
|
|
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await u.updateCamPosition([1049, 239, 686])
|
|
await u.closeDebugPanel()
|
|
|
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.setViewportSize({ width: 1200, height: 1200 })
|
|
await u.openAndClearDebugPanel()
|
|
await u.updateCamPosition([452, -152, 1166])
|
|
await u.closeDebugPanel()
|
|
await page.waitForTimeout(200)
|
|
|
|
const pointToDragFirst = [787, 565]
|
|
await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1])
|
|
await page.mouse.down()
|
|
await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], {
|
|
steps: 5,
|
|
})
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
|
|> startProfileAt([1.03, 1.03], %)
|
|
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
|
process?.env?.CI ? 0.24 : 0.2
|
|
}], %)
|
|
|> line([-4.44, -2.13], %)
|
|
|> close(%)`)
|
|
|
|
// exit sketch
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
|
|
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
|
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
|
await page.waitForTimeout(100)
|
|
|
|
await page.keyboard.press('Enter')
|
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
|
await page.keyboard.press('Enter')
|
|
|
|
await expect(page.locator('.cm-content'))
|
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
|
|> startProfileAt([1.03, 1.03], %)
|
|
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
|
process?.env?.CI ? 0.24 : 0.2
|
|
}], %)
|
|
|> line([-4.44, -2.13], %)
|
|
|> close(%)
|
|
|> extrude(5 + 7, %)`)
|
|
})
|
|
|
|
test('Can code mod a line length', async ({ page }) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('XY')
|
|
|> startProfileAt([-10, -10], %)
|
|
|> line([20, 0], %)
|
|
|> line([0, 20], %)
|
|
|> xLine(-20, %)
|
|
`
|
|
)
|
|
})
|
|
|
|
const u = getUtils(page)
|
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// Click the line of code for xLine.
|
|
await page.getByText(`xLine(-20, %)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
await page.waitForTimeout(100)
|
|
|
|
// enter sketch again
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(300) // wait for animation
|
|
|
|
const startXPx = 500
|
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
|
await page.mouse.click(615, 133)
|
|
await page.getByRole('button', { name: 'length', exact: true }).click()
|
|
await page.getByText('Add constraining value').click()
|
|
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
|
|
)
|
|
})
|
|
|
|
test('Extrude from command bar selects extrude line after', async ({
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`const part001 = startSketchOn('XY')
|
|
|> startProfileAt([-10, -10], %)
|
|
|> line([20, 0], %)
|
|
|> line([0, 20], %)
|
|
|> xLine(-20, %)
|
|
|> close(%)
|
|
`
|
|
)
|
|
})
|
|
|
|
const u = getUtils(page)
|
|
await page.setViewportSize({ width: 1200, height: 500 })
|
|
await page.goto('/')
|
|
await u.waitForAuthSkipAppStart()
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// Click the line of code for xLine.
|
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
await page.waitForTimeout(100)
|
|
|
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
await page.waitForTimeout(100)
|
|
await page.keyboard.press('Enter')
|
|
await page.waitForTimeout(100)
|
|
await page.keyboard.press('Enter')
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
|
` |> extrude(5 + 7, %)`
|
|
)
|
|
})
|