Merge main (tests changed x_x) and pass all constraints.spec tests (pain)

This commit is contained in:
49lf
2024-11-29 14:12:19 -05:00
parent 9a3075533b
commit 7a448760e7
12 changed files with 2076 additions and 1959 deletions

View File

@ -52,10 +52,12 @@ export class SceneFixture {
} }
expectState = async (expected: SceneSerialised) => { expectState = async (expected: SceneSerialised) => {
return expect.poll(async () => await this._serialiseScene(), { return expect
intervals: [1_000, 2_000, 10_000], .poll(async () => await this._serialiseScene(), {
timeout: 60000, intervals: [1_000, 2_000, 10_000],
}).toEqual(expected) timeout: 60000,
})
.toEqual(expected)
} }
reConstruct = (page: Page) => { reConstruct = (page: Page) => {

View File

@ -130,9 +130,7 @@ test.describe('Onboarding tests', () => {
await replayButton.click() await replayButton.click()
// Ensure we see the warning, and that the code has not yet updated // Ensure we see the warning, and that the code has not yet updated
await expect( await expect(page.getByText('Would you like to create')).toBeVisible()
page.getByText('Would you like to create')
).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(initialCode) await expect(page.locator('.cm-content')).toHaveText(initialCode)
const nextButton = page.getByTestId('onboarding-next') const nextButton = page.getByTestId('onboarding-next')
@ -278,7 +276,9 @@ test.describe('Onboarding tests', () => {
await page.setBodyDimensions({ width: 1200, height: 1080 }) await page.setBodyDimensions({ width: 1200, height: 1080 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await expect.poll(() => page.url()).toContain(onboardingPaths.PARAMETRIC_MODELING) await expect
.poll(() => page.url())
.toContain(onboardingPaths.PARAMETRIC_MODELING)
const bracketNoNewLines = bracket.replace(/\n/g, '') const bracketNoNewLines = bracket.replace(/\n/g, '')

View File

@ -7,27 +7,42 @@ import path from 'node:path'
// test file is for testing point an click code gen functionality that's not sketch mode related // test file is for testing point an click code gen functionality that's not sketch mode related
test( test('verify extruding circle works', async ({
'verify extruding circle works', context,
async ({ context, homePage, cmdBar, editor, toolbar, scene }) => { homePage,
const file = await fs.readFile( cmdBar,
path.resolve( editor,
__dirname, toolbar,
'../../', scene,
'./src/wasm-lib/tests/executor/inputs/test-circle-extrude.kcl' }) => {
), const file = await fs.readFile(
'utf-8' path.resolve(
) __dirname,
await context.addInitScript((file) => { '../../',
localStorage.setItem('persistCode', file) './src/wasm-lib/tests/executor/inputs/test-circle-extrude.kcl'
}, file) ),
await homePage.goToModelingScene() 'utf-8'
)
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217) const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => { await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
await scene.clickNoWhere() await scene.clickNoWhere()
await expect(toolbar.extrudeButton).toBeEnabled() await expect(toolbar.extrudeButton).toBeEnabled()
})
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: [],
}) })
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => { await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
@ -35,9 +50,7 @@ test(
const circleSnippet = const circleSnippet =
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)' 'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
await editor.expectState({ await editor.expectState({
activeLines: [ activeLines: ["constsketch002=startSketchOn('XZ')"],
"constsketch002=startSketchOn('XZ')"
],
highlightedCode: circleSnippet, highlightedCode: circleSnippet,
diagnostics: [], diagnostics: [],
}) })
@ -50,34 +63,35 @@ test(
}) })
await expect(toolbar.extrudeButton).toBeEnabled() 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 test.step('do extrude flow and check extrude code is added to editor', async () => {
await toolbar.extrudeButton.click() await toolbar.extrudeButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'distance', currentArgKey: 'distance',
currentArgValue: '5', currentArgValue: '5',
headerArguments: { Selection: '1 face', Distance: '' }, headerArguments: { Selection: '1 face', Distance: '' },
highlightedHeaderArg: 'distance', highlightedHeaderArg: 'distance',
commandName: 'Extrude', commandName: 'Extrude',
})
await cmdBar.progressCmdBar()
const expectString = 'extrude001 = extrude(5, sketch001)'
await editor.expectEditor.not.toContain(expectString)
await cmdBar.expectState({
stage: 'review',
headerArguments: { Selection: '1 face', Distance: '5' },
commandName: 'Extrude',
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(expectString)
}) })
} await cmdBar.progressCmdBar()
)
const expectString = 'extrude001 = extrude(5, sketch001)'
await editor.expectEditor.not.toContain(expectString)
await cmdBar.expectState({
stage: 'review',
headerArguments: { Selection: '1 face', Distance: '5' },
commandName: 'Extrude',
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(expectString)
})
})
test.describe('verify sketch on chamfer works', () => { test.describe('verify sketch on chamfer works', () => {
const _sketchOnAChamfer = const _sketchOnAChamfer =
@ -145,7 +159,9 @@ test.describe('verify sketch on chamfer works', () => {
pixelDiff: 50, pixelDiff: 50,
}) })
await rectangle2ndClick() await rectangle2ndClick()
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, { shouldNormalise: true }) await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, {
shouldNormalise: true,
})
}) })
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => { await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
@ -160,30 +176,35 @@ test.describe('verify sketch on chamfer works', () => {
}) })
}) })
} }
test( test('works on all edge selections and can break up multi edges in a chamfer array', async ({
'works on all edge selections and can break up multi edges in a chamfer array', context,
async ({ context, page, homePage, editor, toolbar, scene }) => { page,
const file = await fs.readFile( homePage,
path.resolve( editor,
__dirname, toolbar,
'../../', scene,
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl' }) => {
), const file = await fs.readFile(
'utf-8' path.resolve(
) __dirname,
await context.addInitScript((file) => { '../../',
localStorage.setItem('persistCode', file) './src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
}, file) ),
await page.setBodyDimensions({ width: 1000, height: 500 }) 'utf-8'
await homePage.goToModelingScene() )
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
await sketchOnAChamfer({ await sketchOnAChamfer({
clickCoords: { x: 570, y: 220 }, clickCoords: { x: 570, y: 220 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer({length = 30,tags = [ chamfer({length = 30,tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
@ -191,10 +212,9 @@ test.describe('verify sketch on chamfer works', () => {
getOppositeEdge(seg01) getOppositeEdge(seg01)
]}, %)`, ]}, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
'sketch002 = startSketchOn(extrude001, seg03)', afterRectangle1stClickSnippet: 'startProfileAt([160.39, 254.59], %)',
afterRectangle1stClickSnippet: 'startProfileAt([160.39, 254.59], %)', afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|> angledLine([ |> angledLine([
segAng(rectangleSegmentA002) - 90, segAng(rectangleSegmentA002) - 90,
105.26 105.26
@ -205,13 +225,13 @@ test.describe('verify sketch on chamfer works', () => {
], %, $rectangleSegmentC001) ], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`, |> close(%)`,
}) })
await sketchOnAChamfer({ await sketchOnAChamfer({
clickCoords: { x: 690, y: 250 }, clickCoords: { x: 690, y: 250 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([ beforeChamferSnippet: `angledLine([
segAng(rectangleSegmentA001) - 90, segAng(rectangleSegmentA001) - 90,
217.26 217.26
], %, $seg01)chamfer({ ], %, $seg01)chamfer({
@ -223,10 +243,9 @@ test.describe('verify sketch on chamfer works', () => {
] ]
}, %)`, }, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
'sketch003 = startSketchOn(extrude001, seg04)', afterRectangle1stClickSnippet: 'startProfileAt([-255.89, 255.28], %)',
afterRectangle1stClickSnippet: 'startProfileAt([-255.89, 255.28], %)', afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|> angledLine([ |> angledLine([
segAng(rectangleSegmentA003) - 90, segAng(rectangleSegmentA003) - 90,
106.84 106.84
@ -237,22 +256,21 @@ test.describe('verify sketch on chamfer works', () => {
], %, $rectangleSegmentC002) ], %, $rectangleSegmentC002)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`, |> close(%)`,
}) })
await sketchOnAChamfer({ await sketchOnAChamfer({
clickCoords: { x: 677, y: 87 }, clickCoords: { x: 677, y: 87 },
cameraPos: { x: -6200, y: 1500, z: 6200 }, cameraPos: { x: -6200, y: 1500, z: 6200 },
cameraTarget: { x: 8300, y: 1100, z: 4800 }, cameraTarget: { x: 8300, y: 1100, z: 4800 },
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({ beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
length = 30, length = 30,
tags = [ tags = [
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02) getNextAdjacentEdge(seg02)
] ]
}, %)`, }, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
'sketch003 = startSketchOn(extrude001, seg04)', afterRectangle1stClickSnippet: 'startProfileAt([37.95, 322.96], %)',
afterRectangle1stClickSnippet: 'startProfileAt([37.95, 322.96], %)', afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|> angledLine([ |> angledLine([
segAng(rectangleSegmentA003) - 90, segAng(rectangleSegmentA003) - 90,
106.84 106.84
@ -263,20 +281,19 @@ test.describe('verify sketch on chamfer works', () => {
], %, $rectangleSegmentC002) ], %, $rectangleSegmentC002)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`, |> close(%)`,
}) })
/// last one /// last one
await sketchOnAChamfer({ await sketchOnAChamfer({
clickCoords: { x: 620, y: 300 }, clickCoords: { x: 620, y: 300 },
cameraPos: { x: -1100, y: -7700, z: 1600 }, cameraPos: { x: -1100, y: -7700, z: 1600 },
cameraTarget: { x: 1450, y: 670, z: 4000 }, cameraTarget: { x: 1450, y: 670, z: 4000 },
beforeChamferSnippet: `chamfer({ beforeChamferSnippet: `chamfer({
length = 30, length = 30,
tags = [getNextAdjacentEdge(yo)] tags = [getNextAdjacentEdge(yo)]
}, %)`, }, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
'sketch005 = startSketchOn(extrude001, seg06)', afterRectangle1stClickSnippet: 'startProfileAt([-59.83, 19.69], %)',
afterRectangle1stClickSnippet: 'startProfileAt([-59.83, 19.69], %)', afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|> angledLine([ |> angledLine([
segAng(rectangleSegmentA005) - 90, segAng(rectangleSegmentA005) - 90,
@ -288,11 +305,11 @@ test.describe('verify sketch on chamfer works', () => {
], %, $rectangleSegmentC004) ], %, $rectangleSegmentC004)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`, |> close(%)`,
}) })
await test.step('verify at the end of the test that final code is what is expected', async () => { await test.step('verify at the end of the test that final code is what is expected', async () => {
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn('XZ') `sketch001 = startSketchOn('XZ')
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag] |> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|> angledLine([0, 268.43], %, $rectangleSegmentA001) |> angledLine([0, 268.43], %, $rectangleSegmentA001)
@ -373,47 +390,48 @@ test.describe('verify sketch on chamfer works', () => {
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%) |> close(%)
`, `,
{ shouldNormalise: true } { shouldNormalise: true }
)
})
}
)
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'
) )
await context.addInitScript((file) => { })
localStorage.setItem('persistCode', file) })
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) 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'
)
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await sketchOnAChamfer({ await sketchOnAChamfer({
clickCoords: { x: 570, y: 220 }, clickCoords: { x: 570, y: 220 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer({length=30,tags=[ chamfer({length=30,tags=[
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02), getNextAdjacentEdge(seg02),
getOppositeEdge(seg01) getOppositeEdge(seg01)
]}, extrude001)`, ]}, extrude001)`,
beforeChamferSnippetEnd: '}, extrude001)', beforeChamferSnippetEnd: '}, extrude001)',
afterChamferSelectSnippet: afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
'sketch002 = startSketchOn(extrude001, seg03)', afterRectangle1stClickSnippet: 'startProfileAt([160.39, 254.59], %)',
afterRectangle1stClickSnippet: 'startProfileAt([160.39, 254.59], %)', afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|> angledLine([ |> angledLine([
segAng(rectangleSegmentA002) - 90, segAng(rectangleSegmentA002) - 90,
105.26 105.26
@ -424,9 +442,9 @@ test.describe('verify sketch on chamfer works', () => {
], %, $rectangleSegmentC001) ], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`, |> close(%)`,
}) })
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn('XZ') `sketch001 = startSketchOn('XZ')
|> startProfileAt([75.8, 317.2], %) |> startProfileAt([75.8, 317.2], %)
|> angledLine([0, 268.43], %, $rectangleSegmentA001) |> angledLine([0, 268.43], %, $rectangleSegmentA001)
|> angledLine([ |> angledLine([
@ -466,10 +484,9 @@ sketch002 = startSketchOn(extrude001, seg03)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%) |> close(%)
`, `,
{ shouldNormalise: true } { shouldNormalise: true }
) )
} })
)
}) })
test(`Verify axis, origin, and horizontal snapping`, async ({ test(`Verify axis, origin, and horizontal snapping`, async ({

View File

@ -56,7 +56,6 @@ test(
}) })
await expect(projectLinks).toHaveCount(0) await expect(projectLinks).toHaveCount(0)
} }
) )
@ -64,7 +63,6 @@ test(
'click help/keybindings from home page', 'click help/keybindings from home page',
{ tag: '@electron' }, { tag: '@electron' },
async ({ page }, testInfo) => { async ({ page }, testInfo) => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log) page.on('console', console.log)
@ -76,7 +74,6 @@ test(
await page.getByTestId('keybindings-button').click() await page.getByTestId('keybindings-button').click()
// Make sure the keyboard shortcuts modal is visible. // Make sure the keyboard shortcuts modal is visible.
await expect(page.getByText('Enter Sketch Mode')).toBeVisible() await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
} }
) )
@ -114,7 +111,6 @@ test(
await page.getByTestId('keybindings-button').click() await page.getByTestId('keybindings-button').click()
// Make sure the keyboard shortcuts modal is visible. // Make sure the keyboard shortcuts modal is visible.
await expect(page.getByText('Enter Sketch Mode')).toBeVisible() await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
} }
) )
@ -477,7 +473,6 @@ test(
await page.hover('.cm-lint-marker-error') await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator` const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
} }
) )
@ -569,7 +564,6 @@ test.describe('Can export from electron app', () => {
// clean up exported file // clean up exported file
await fsp.rm(filepath) await fsp.rm(filepath)
}) })
} }
) )
} }
@ -577,7 +571,7 @@ test.describe('Can export from electron app', () => {
test( test(
'Rename and delete projects, also spam arrow keys when renaming', 'Rename and delete projects, also spam arrow keys when renaming',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page}, testInfo) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }) await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
@ -770,7 +764,6 @@ test(
// expect the name not to have changed // expect the name not to have changed
await expect(page.getByText('bracket')).toBeVisible() await expect(page.getByText('bracket')).toBeVisible()
}) })
} }
) )
@ -799,7 +792,6 @@ test(
// expect to still be on the home page // expect to still be on the home page
await expect(page.getByText('router-template-slate')).toBeVisible() await expect(page.getByText('router-template-slate')).toBeVisible()
await expect(page.getByText('Your Projects')).toBeVisible() await expect(page.getByText('Your Projects')).toBeVisible()
} }
) )
@ -862,7 +854,6 @@ test.describe(`Project management commands`, () => {
await expect(projectHomeLink.first()).toBeVisible() await expect(projectHomeLink.first()).toBeVisible()
await expect(projectHomeLink.first()).toContainText(projectRenamedName) await expect(projectHomeLink.first()).toContainText(projectRenamedName)
}) })
} }
) )
@ -915,7 +906,6 @@ test.describe(`Project management commands`, () => {
await test.step(`Check the project was deleted and we navigated home`, async () => { await test.step(`Check the project was deleted and we navigated home`, async () => {
await expect(noProjectsMessage).toBeVisible() await expect(noProjectsMessage).toBeVisible()
}) })
} }
) )
test( test(
@ -971,7 +961,6 @@ test.describe(`Project management commands`, () => {
).toBeVisible() ).toBeVisible()
await expect(projectHomeLink).not.toHaveText(projectName) await expect(projectHomeLink).not.toHaveText(projectName)
}) })
} }
) )
test( test(
@ -1021,7 +1010,6 @@ test.describe(`Project management commands`, () => {
await expect(projectHomeLink).not.toBeVisible() await expect(projectHomeLink).not.toBeVisible()
await expect(noProjectsMessage).toBeVisible() await expect(noProjectsMessage).toBeVisible()
}) })
} }
) )
}) })
@ -1067,7 +1055,6 @@ test(
await expect(u.codeLocator).toContainText( await expect(u.codeLocator).toContainText(
'A mounting bracket for the Focusrite Scarlett Solo audio interface' 'A mounting bracket for the Focusrite Scarlett Solo audio interface'
) )
} }
) )
@ -1123,10 +1110,11 @@ test(
).rejects.toThrow() ).rejects.toThrow()
// eslint-disable-next-line jest/no-conditional-expect // eslint-disable-next-line jest/no-conditional-expect
await expect( await expect(
fsp.access(path.join(testDir, 'router-template-slate', 'nested', 'main.kcl')) fsp.access(
path.join(testDir, 'router-template-slate', 'nested', 'main.kcl')
)
).rejects.toThrow() ).rejects.toThrow()
} }
} }
) )
@ -1205,7 +1193,6 @@ test.fixme(
page.getByTestId('project-link').filter({ hasText: 'project-000' }) page.getByTestId('project-link').filter({ hasText: 'project-000' })
).toBeVisible() ).toBeVisible()
}) })
} }
) )
@ -1243,7 +1230,6 @@ test(
await expect(u.codeLocator).toContainText('routerDiameter') await expect(u.codeLocator).toContainText('routerDiameter')
await expect(u.codeLocator).toContainText('templateGap') await expect(u.codeLocator).toContainText('templateGap')
await expect(u.codeLocator).toContainText('minClampingDistance') await expect(u.codeLocator).toContainText('minClampingDistance')
} }
) )
@ -1351,7 +1337,6 @@ test(
) )
} }
}) })
} }
) )
@ -1540,7 +1525,6 @@ test(
await expect(page.getByText('router-template-slate')).toBeVisible() await expect(page.getByText('router-template-slate')).toBeVisible()
await expect(page.getByText('New Project')).toBeVisible() await expect(page.getByText('New Project')).toBeVisible()
}) })
} }
) )
@ -1600,9 +1584,9 @@ test(
await page.getByTestId('project-directory-button').click() await page.getByTestId('project-directory-button').click()
await handleFile await handleFile
await expect.poll(() => page.locator('section#projectDirectory input').inputValue()).toContain( await expect
newProjectDirName .poll(() => page.locator('section#projectDirectory input').inputValue())
) .toContain(newProjectDirName)
await page.getByTestId('settings-close-button').click() await page.getByTestId('settings-close-button').click()
@ -1620,7 +1604,6 @@ test(
await page.getByTestId('project-directory-settings-link').click() await page.getByTestId('project-directory-settings-link').click()
const handleFile = electronApp.evaluate( const handleFile = electronApp.evaluate(
async ({ dialog }, filePaths) => { async ({ dialog }, filePaths) => {
dialog.showOpenDialog = () => dialog.showOpenDialog = () =>
@ -1698,7 +1681,6 @@ test(
await expect(page.getByText(name)).toBeVisible() await expect(page.getByText(name)).toBeVisible()
} }
}) })
} }
) )
@ -1804,7 +1786,6 @@ test(
false false
) )
}) })
} }
) )
@ -1863,7 +1844,6 @@ test(
expect(selectedText.length).toBe(0) expect(selectedText.length).toBe(0)
await expect(u.codeLocator).toHaveText('') await expect(u.codeLocator).toHaveText('')
}) })
} }
) )
@ -1883,7 +1863,6 @@ test(
await expect(page.getByTestId('app-theme')).toHaveValue('dark') await expect(page.getByTestId('app-theme')).toHaveValue('dark')
await page.getByTestId('app-theme').selectOption('light') await page.getByTestId('app-theme').selectOption('light')
}) })
await test.step('Starting the app again and we can see the same theme', async () => { await test.step('Starting the app again and we can see the same theme', async () => {
@ -1892,7 +1871,6 @@ test(
page.on('console', console.log) page.on('console', console.log)
await expect(page.getByTestId('app-theme')).toHaveValue('light') await expect(page.getByTestId('app-theme')).toHaveValue('light')
}) })
} }
) )
@ -1934,6 +1912,5 @@ test.fixme(
await expect(projectLink).toContainText(projectNames[index]) await expect(projectLink).toContainText(projectNames[index])
} }
}) })
} }
) )

View File

@ -1,55 +1,62 @@
import { test, expect, Page } from './zoo-test' import { test, expect, Page } from './zoo-test'
import path from 'path' import path from 'path'
import * as fsp from 'fs/promises' import * as fsp from 'fs/promises'
import { import { getUtils, executorInputPath } from './test-utils'
getUtils,
executorInputPath,
} from './test-utils'
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates' import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
import { bracket } from 'lib/exampleKcl' import { bracket } from 'lib/exampleKcl'
test.describe('Regression tests', () => { test.describe('Regression tests', () => {
// bugs we found that don't fit neatly into other categories // bugs we found that don't fit neatly into other categories
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 test('bad model has inline error #3251', async ({
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251 context,
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics page,
const u = await getUtils(page) homePage,
await context.addInitScript(async () => { }) => {
localStorage.setItem( // because the model has `line([0,0]..` it is valid code, but the model is invalid
'persistCode', // regression test for https://github.com/KittyCAD/modeling-app/issues/3251
`sketch2 = startSketchOn("XY") // 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 context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch2 = startSketchOn("XY")
sketch001 = startSketchAt([-0, -0]) sketch001 = startSketchAt([-0, -0])
|> line([0, 0], %) |> line([0, 0], %)
|> line([-4.84, -5.29], %) |> line([-4.84, -5.29], %)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)` |> close(%)`
) )
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
// this is a cryptic error message, fact that all the lines are co-linear from the `line([0,0])` is the issue why
// the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating
const crypticErrorText = `ApiError`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
}) })
test('user should not have to press down twice in cmdbar', async ({
await page.setBodyDimensions({ width: 1000, height: 500 }) page,
homePage,
await homePage.goToModelingScene() }) => {
await u.waitForPageLoad() // 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
// error in guter // Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() const u = await getUtils(page)
await page.addInitScript(async () => {
// error text on hover localStorage.setItem(
await page.hover('.cm-lint-marker-error') 'persistCode',
// this is a cryptic error message, fact that all the lines are co-linear from the `line([0,0])` is the issue why `sketch001 = startSketchOn('XY')
// the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating
const crypticErrorText = `ApiError`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() })
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
// 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 () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XY')
|> startProfileAt([82.33, 238.21], %) |> startProfileAt([82.33, 238.21], %)
|> angledLine([0, 288.63], %, $rectangleSegmentA001) |> angledLine([0, 288.63], %, $rectangleSegmentA001)
|> angledLine([ |> angledLine([
@ -64,114 +71,121 @@ test.describe('Regression tests', () => {
|> close(%) |> close(%)
extrude001 = extrude(50, sketch001) extrude001 = extrude(50, sketch001)
` `
) )
})
await page.setBodyDimensions({ width: 1000, height: 500 })
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()
const floppy = page.getByRole('option', {
name: 'floppy disk arrow Export',
})
await floppy.click()
// press arrow down key twice
await page.keyboard.press('ArrowDown')
await page.waitForTimeout(100)
await page.keyboard.press('ArrowDown')
// STL is the third option, which makes sense for two arrow downs
await expect(page.locator('[data-headlessui-state="active"]')).toHaveText(
'STL'
)
await page.keyboard.press('Escape')
await page.waitForTimeout(200)
await page.keyboard.press('Escape')
await page.waitForTimeout(200)
})
await test.step('Check arrow up works', async () => {
// theme in test is dark, which is the second option, which means we can test arrow up
await page.getByTestId('command-bar-open-button').click()
await page.getByText('The overall appearance of the').click()
await page.keyboard.press('ArrowUp')
await page.waitForTimeout(100)
await expect(page.locator('[data-headlessui-state="active"]')).toHaveText(
'light'
)
})
}) })
test('executes on load', async ({ page, homePage }) => {
await page.setBodyDimensions({ width: 1000, height: 500 }) const u = await getUtils(page)
await page.addInitScript(async () => {
await homePage.goToModelingScene() localStorage.setItem(
await u.waitForPageLoad() 'persistCode',
`sketch001 = startSketchOn('-XZ')
await test.step('Check arrow down works', async () => {
await page.getByTestId('command-bar-open-button').hover()
await page.getByTestId('command-bar-open-button').click()
const floppy = page
.getByRole('option', { name: 'floppy disk arrow Export' })
await floppy.click()
// press arrow down key twice
await page.keyboard.press('ArrowDown')
await page.waitForTimeout(100)
await page.keyboard.press('ArrowDown')
// STL is the third option, which makes sense for two arrow downs
await expect(page.locator('[data-headlessui-state="active"]')).toHaveText(
'STL'
)
await page.keyboard.press('Escape')
await page.waitForTimeout(200)
await page.keyboard.press('Escape')
await page.waitForTimeout(200)
})
await test.step('Check arrow up works', async () => {
// theme in test is dark, which is the second option, which means we can test arrow up
await page.getByTestId('command-bar-open-button').click()
await page.getByText('The overall appearance of the').click()
await page.keyboard.press('ArrowUp')
await page.waitForTimeout(100)
await expect(page.locator('[data-headlessui-state="active"]')).toHaveText(
'light'
)
}) })
test('executes on load', async ({ page, homePage }) => { const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %) |> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %) |> line([25.1, 0.41], %)
|> line([0.73, -14.93], %) |> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)` |> line([-23.44, 0.52], %)`
) )
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
// expand variables section
const variablesTabButton = page.getByTestId('variables-pane-button')
await variablesTabButton.click()
// can find sketch001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor)
// sketch001 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('sketch001')
})
await expect(
page.locator('.pretty-json-container >> text=sketch001')
).toBeVisible()
}) })
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() test('re-executes', async ({ page, homePage }) => {
await u.waitForPageLoad() const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem('persistCode', `myVar = 5`)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
// expand variables section await homePage.goToModelingScene()
const variablesTabButton = page.getByTestId('variables-pane-button') await u.waitForPageLoad()
await variablesTabButton.click()
// can find sketch001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor) const variablesTabButton = page.getByTestId('variables-pane-button')
// sketch001 only shows up in the variables summary if it's been executed await variablesTabButton.click()
await page.waitForFunction(() => { // expect to see "myVar:5"
const variablesElement = document.querySelector( await expect(
'.pretty-json-container' page.locator('.pretty-json-container >> text=myVar:5')
) as HTMLDivElement ).toBeVisible()
return variablesElement.innerHTML.includes('sketch001')
// change 5 to 67
await page.locator('#code-mirror-override').getByText('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()
}) })
await expect( test('ProgramMemory can be serialised', async ({ page, homePage }) => {
page.locator('.pretty-json-container >> text=sketch001') const u = await getUtils(page)
).toBeVisible() }) await page.addInitScript(async () => {
localStorage.setItem(
test('re-executes', async ({ page, homePage }) => { const u = await getUtils(page) 'persistCode',
await page.addInitScript(async () => { `part = startSketchOn('XY')
localStorage.setItem('persistCode', `myVar = 5`)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
const variablesTabButton = page.getByTestId('variables-pane-button')
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.locator('#code-mirror-override').getByText('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() })
test('ProgramMemory can be serialised', async ({ page, homePage }) => { const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`part = startSketchOn('XY')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, 1], %) |> line([0, 1], %)
|> line([1, 0], %) |> line([1, 0], %)
@ -183,51 +197,61 @@ extrude001 = extrude(50, sketch001)
repetitions: 3, repetitions: 3,
distance: 6 distance: 6
}, %)` }, %)`
) )
})
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 homePage.goToModelingScene()
await u.waitForPageLoad()
// 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)
}) })
}) }) 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 homePage.goToModelingScene()
await u.waitForPageLoad()
// 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)
})
})
})
// Not relevant to us anymore, or at least for the time being. // 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) test.skip('ensure the Zoo logo is not a link in browser app', async ({
await page.setBodyDimensions({ width: 1000, height: 500 }) page,
await homePage.goToModelingScene() homePage,
await u.waitForPageLoad() }) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
const zooLogo = page.locator('[data-testid="app-logo"]') const zooLogo = page.locator('[data-testid="app-logo"]')
// Make sure it's not a link // Make sure it's not a link
await expect(zooLogo).not.toHaveAttribute('href') }) await expect(zooLogo).not.toHaveAttribute('href')
})
test('Position _ Is Out Of Range... regression test', { tag: ['@skipWin'] }, async ({ context, page, homePage }) => { // SKip on windows, its being weird. test(
test.skip( 'Position _ Is Out Of Range... regression test',
process.platform === 'win32', { tag: ['@skipWin'] },
'This test is being weird on windows' async ({ context, page, homePage }) => {
) // SKip on windows, its being weird.
test.skip(
process.platform === 'win32',
'This test is being weird on windows'
)
const u = await getUtils(page) const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio // const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`exampleSketch = startSketchOn("XZ") `exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %) |> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %) |> yLineTo(0, %)
@ -236,55 +260,54 @@ extrude001 = extrude(50, sketch001)
example = extrude(5, exampleSketch) example = extrude(5, exampleSketch)
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)` shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
) )
}) })
await expect(async () => { await expect(async () => {
await homePage.goToModelingScene()
await u.waitForPageLoad()
await homePage.goToModelingScene() // error in guter
await u.waitForPageLoad() await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
})
await page.waitForTimeout(200)
// expect it still to be there (sometimes it just clears for a bit?)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
})
}).toPass({ timeout: 40_000, intervals: [1_000] })
// error in guter // error text on hover
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({ await page.hover('.cm-lint-marker-error')
timeout: 1_000, await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
})
await page.waitForTimeout(200)
// expect it still to be there (sometimes it just clears for a bit?)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
})
}).toPass({ timeout: 40_000, intervals: [1_000] })
// error text on hover // Okay execution finished, let's start editing text below the error.
await page.hover('.cm-lint-marker-error') await u.codeLocator.click()
await expect(page.getByText('Unexpected token: |').first()).toBeVisible() // Go to the end of the editor
// This bug happens when there is a diagnostic in the editor and you try to
// edit text below it.
// Or delete a huge chunk of text and then try to edit below it.
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('End')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Okay execution finished, let's start editing text below the error. await page.keyboard.press('Enter')
await u.codeLocator.click() await page.keyboard.press('Enter')
// Go to the end of the editor await page.keyboard.type('thing: "blah"', { delay: 100 })
// This bug happens when there is a diagnostic in the editor and you try to await page.keyboard.press('Enter')
// edit text below it. await page.keyboard.press('ArrowLeft')
// Or delete a huge chunk of text and then try to edit below it.
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('End')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await page.keyboard.press('Enter') await expect(page.locator('.cm-content'))
await page.keyboard.press('Enter') .toContainText(`exampleSketch = startSketchOn("XZ")
await page.keyboard.type('thing: "blah"', { delay: 100 })
await page.keyboard.press('Enter')
await page.keyboard.press('ArrowLeft')
await expect(page.locator('.cm-content'))
.toContainText(`exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %) |> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %) |> yLineTo(0, %)
@ -292,128 +315,27 @@ extrude001 = extrude(50, sketch001)
thing: "blah"`) thing: "blah"`)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() }) await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
}
test('when engine fails export we handle the failure and alert the user', async ({ page, homePage }) => { const u = await getUtils(page)
await page.addInitScript(
async ({ code }) => {
localStorage.setItem('persistCode', code)
;(window as any).playwrightSkipFilePicker = true
},
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
) )
await page.setBodyDimensions({ width: 1000, height: 500 }) test('when engine fails export we handle the failure and alert the user', async ({
page,
await homePage.goToModelingScene() homePage,
await u.waitForPageLoad() }) => {
const u = await getUtils(page)
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// export the model
const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible()
// Click the export button
await exportButton.click()
// Click the stl.
const stlOption = page.getByText('glTF')
await expect(stlOption).toBeVisible()
await page.keyboard.press('Enter')
// Click the checkbox
const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
await expect(engineErrorToastMessage).toBeVisible()
// Make sure the exporting toast is gone
await expect(exportingToastMessage).not.toBeVisible()
// Click the code editor
await page.locator('.cm-content').click()
await page.waitForTimeout(2000)
// Expect the toast to be gone
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
// Now add in code that works.
await page.locator('.cm-content').fill(bracket)
await page.keyboard.press('End')
await page.keyboard.press('Enter')
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Now try exporting
// Click the export button
await exportButton.click()
// Click the stl.
await expect(stlOption).toBeVisible()
await page.keyboard.press('Enter')
// Click the checkbox
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message
await expect(exportingToastMessage).toBeVisible()
// Expect it to succeed.
await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible() })
test('ensure you can not export while an export is already going', { tag: ['@skipLinux', '@skipWin'] }, async ({ page, homePage }) => { // This is being weird on ubuntu and windows.
test.skip(
// eslint-disable-next-line jest/valid-title
process.platform === 'linux' || process.platform === 'win32',
'This test is being weird on ubuntu'
)
const u = await getUtils(page)
await test.step('Set up the code and durations', async () => {
await page.addInitScript( await page.addInitScript(
async ({ code }) => { async ({ code }) => {
localStorage.setItem('persistCode', code) localStorage.setItem('persistCode', code)
;(window as any).playwrightSkipFilePicker = true ;(window as any).playwrightSkipFilePicker = true
}, },
{ { code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
code: bracket,
}
) )
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await u.waitForPageLoad()
// wait for execution done // wait for execution done
await u.openDebugPanel() await u.openDebugPanel()
@ -422,61 +344,175 @@ extrude001 = extrude(50, sketch001)
// expect zero errors in guter // expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
const errorToastMessage = page.getByText(`Error while exporting`) // export the model
const exportingToastMessage = page.getByText(`Exporting...`) const exportButton = page.getByTestId('export-pane-button')
const engineErrorToastMessage = page.getByText(`Nothing to export`) await expect(exportButton).toBeVisible()
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
const successToastMessage = page.getByText(`Exported successfully`)
await test.step('Blocked second export', async () => { // Click the export button
await clickExportButton(page) await exportButton.click()
await expect(exportingToastMessage).toBeVisible() // Click the stl.
const stlOption = page.getByText('glTF')
await expect(stlOption).toBeVisible()
await clickExportButton(page) await page.keyboard.press('Enter')
await test.step('The second export is blocked', async () => { // Click the checkbox
// Find the toast. const submitButton = page.getByText('Confirm Export')
// Look out for the toast message await expect(submitButton).toBeVisible()
await Promise.all([
expect(exportingToastMessage.first()).toBeVisible(),
expect(alreadyExportingToastMessage).toBeVisible(),
])
})
await test.step('The first export still succeeds', async () => { await page.keyboard.press('Enter')
await Promise.all([
expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 }),
expect(errorToastMessage).not.toBeVisible(),
expect(engineErrorToastMessage).not.toBeVisible(),
expect(successToastMessage).toBeVisible({ timeout: 15_000 }),
expect(alreadyExportingToastMessage).not.toBeVisible({
timeout: 15_000,
}),
])
})
})
await test.step('Successful, unblocked export', async () => { // Find the toast.
// Try exporting again. // Look out for the toast message
await clickExportButton(page) const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
await expect(engineErrorToastMessage).toBeVisible()
// Make sure the exporting toast is gone
await expect(exportingToastMessage).not.toBeVisible()
// Click the code editor
await page.locator('.cm-content').click()
await page.waitForTimeout(2000)
// Expect the toast to be gone
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
// Now add in code that works.
await page.locator('.cm-content').fill(bracket)
await page.keyboard.press('End')
await page.keyboard.press('Enter')
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Now try exporting
// Click the export button
await exportButton.click()
// Click the stl.
await expect(stlOption).toBeVisible()
await page.keyboard.press('Enter')
// Click the checkbox
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
// Expect it to succeed. // Expect it to succeed.
await Promise.all([ await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
expect(exportingToastMessage).not.toBeVisible(), await expect(errorToastMessage).not.toBeVisible()
expect(errorToastMessage).not.toBeVisible(), await expect(engineErrorToastMessage).not.toBeVisible()
expect(engineErrorToastMessage).not.toBeVisible(),
expect(alreadyExportingToastMessage).not.toBeVisible(),
])
const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible() await expect(successToastMessage).toBeVisible()
}) }) })
test(
'ensure you can not export while an export is already going',
{ tag: ['@skipLinux', '@skipWin'] },
async ({ page, homePage }) => {
// This is being weird on ubuntu and windows.
test.skip(
// eslint-disable-next-line jest/valid-title
process.platform === 'linux' || process.platform === 'win32',
'This test is being weird on ubuntu'
)
const u = await getUtils(page)
await test.step('Set up the code and durations', async () => {
await page.addInitScript(
async ({ code }) => {
localStorage.setItem('persistCode', code)
;(window as any).playwrightSkipFilePicker = true
},
{
code: bracket,
}
)
await page.setBodyDimensions({ width: 1000, 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()
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
const errorToastMessage = page.getByText(`Error while exporting`)
const exportingToastMessage = page.getByText(`Exporting...`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
const successToastMessage = page.getByText(`Exported successfully`)
await test.step('Blocked second export', async () => {
await clickExportButton(page)
await expect(exportingToastMessage).toBeVisible()
await clickExportButton(page)
await test.step('The second export is blocked', async () => {
// Find the toast.
// Look out for the toast message
await Promise.all([
expect(exportingToastMessage.first()).toBeVisible(),
expect(alreadyExportingToastMessage).toBeVisible(),
])
})
await test.step('The first export still succeeds', async () => {
await Promise.all([
expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 }),
expect(errorToastMessage).not.toBeVisible(),
expect(engineErrorToastMessage).not.toBeVisible(),
expect(successToastMessage).toBeVisible({ timeout: 15_000 }),
expect(alreadyExportingToastMessage).not.toBeVisible({
timeout: 15_000,
}),
])
})
})
await test.step('Successful, unblocked export', async () => {
// Try exporting again.
await clickExportButton(page)
// Find the toast.
// Look out for the toast message
await expect(exportingToastMessage).toBeVisible()
// Expect it to succeed.
await Promise.all([
expect(exportingToastMessage).not.toBeVisible(),
expect(errorToastMessage).not.toBeVisible(),
expect(engineErrorToastMessage).not.toBeVisible(),
expect(alreadyExportingToastMessage).not.toBeVisible(),
])
await expect(successToastMessage).toBeVisible()
})
}
)
test( test(
`Network health indicator only appears in modeling view`, `Network health indicator only appears in modeling view`,
@ -520,57 +556,62 @@ extrude001 = extrude(50, sketch001)
} }
) )
test(`View gizmo stays visible even when zoomed out all the way`, async ({ page, homePage }) => { const u = await getUtils(page) test(`View gizmo stays visible even when zoomed out all the way`, async ({
page,
homePage,
}) => {
const u = await getUtils(page)
// Constants and locators // Constants and locators
const planeColor: [number, number, number] = [170, 220, 170] const planeColor: [number, number, number] = [170, 220, 170]
const bgColor: [number, number, number] = [27, 27, 27] const bgColor: [number, number, number] = [27, 27, 27]
const middlePixelIsColor = async (color: [number, number, number]) => { const middlePixelIsColor = async (color: [number, number, number]) => {
return u.getGreatestPixDiff({ x: 600, y: 250 }, color) return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
}
const gizmo = page.locator('[aria-label*=gizmo]')
await test.step(`Load an empty file`, async () => {
await page.addInitScript(async () => {
localStorage.setItem('persistCode', '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await u.closeKclCodePanel()
})
await test.step(`Zoom out until you can't see the default planes`, async () => {
await expect
.poll(async () => middlePixelIsColor(planeColor), {
timeout: 5000,
message: 'Plane color is visible',
})
.toBeLessThan(15)
let maxZoomOuts = 10
let middlePixelIsBackgroundColor =
(await middlePixelIsColor(bgColor)) < 10
while (!middlePixelIsBackgroundColor && maxZoomOuts > 0) {
await page.keyboard.down('Control')
await page.mouse.move(600, 460)
await page.mouse.down({ button: 'right' })
await page.mouse.move(600, 50, { steps: 20 })
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Control')
await page.waitForTimeout(100)
maxZoomOuts--
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 10
} }
const gizmo = page.locator('[aria-label*=gizmo]')
expect(middlePixelIsBackgroundColor, { await test.step(`Load an empty file`, async () => {
message: 'We no longer the default planes', await page.addInitScript(async () => {
}).toBeTruthy() localStorage.setItem('persistCode', '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await u.closeKclCodePanel()
})
await test.step(`Zoom out until you can't see the default planes`, async () => {
await expect
.poll(async () => middlePixelIsColor(planeColor), {
timeout: 5000,
message: 'Plane color is visible',
})
.toBeLessThan(15)
let maxZoomOuts = 10
let middlePixelIsBackgroundColor =
(await middlePixelIsColor(bgColor)) < 10
while (!middlePixelIsBackgroundColor && maxZoomOuts > 0) {
await page.keyboard.down('Control')
await page.mouse.move(600, 460)
await page.mouse.down({ button: 'right' })
await page.mouse.move(600, 50, { steps: 20 })
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Control')
await page.waitForTimeout(100)
maxZoomOuts--
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 10
}
expect(middlePixelIsBackgroundColor, {
message: 'We no longer the default planes',
}).toBeTruthy()
})
await test.step(`Check that the gizmo is still visible`, async () => {
await expect(gizmo).toBeVisible()
})
}) })
await test.step(`Check that the gizmo is still visible`, async () => {
await expect(gizmo).toBeVisible()
}) })
}) })
async function clickExportButton(page: Page) { async function clickExportButton(page: Page) {

View File

@ -3,190 +3,200 @@ import { test, expect } from './zoo-test'
import { commonPoints, getUtils } from './test-utils' import { commonPoints, getUtils } from './test-utils'
test.describe('Test network and connection issues', () => { test.describe('Test network and connection issues', () => {
test('simulate network down and network little widget', async ({ page, homePage }) => { const u = await getUtils(page) test('simulate network down and network little widget', async ({
await page.setBodyDimensions({ width: 1200, height: 500 }) page,
homePage,
}) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
const networkToggle = page.getByTestId('network-toggle') const networkToggle = page.getByTestId('network-toggle')
// This is how we wait until the stream is online // This is how we wait until the stream is online
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 }) ).not.toBeDisabled({ timeout: 15000 })
const networkWidget = page.locator('[data-testid="network-toggle"]') const networkWidget = page.locator('[data-testid="network-toggle"]')
await expect(networkWidget).toBeVisible() await expect(networkWidget).toBeVisible()
await networkWidget.hover() await networkWidget.hover()
const networkPopover = page.locator('[data-testid="network-popover"]') const networkPopover = page.locator('[data-testid="network-popover"]')
await expect(networkPopover).not.toBeVisible() await expect(networkPopover).not.toBeVisible()
// (First check) Expect the network to be up // (First check) Expect the network to be up
await expect(networkToggle).toContainText('Connected') await expect(networkToggle).toContainText('Connected')
// Click the network widget // Click the network widget
await networkWidget.click() await networkWidget.click()
// Check the modal opened. // Check the modal opened.
await expect(networkPopover).toBeVisible() await expect(networkPopover).toBeVisible()
// Click off the modal. // Click off the modal.
await page.mouse.click(100, 100) await page.mouse.click(100, 100)
await expect(networkPopover).not.toBeVisible() await expect(networkPopover).not.toBeVisible()
// Turn off the network // Turn off the network
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
offline: true, offline: true,
// values of 0 remove any active throttling. crbug.com/456324#c9 // values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0, latency: 0,
downloadThroughput: -1, downloadThroughput: -1,
uploadThroughput: -1, uploadThroughput: -1,
})
// Expect the network to be down
await expect(networkToggle).toContainText('Problem')
// Click the network widget
await networkWidget.click()
// Check the modal opened.
await expect(networkPopover).toBeVisible()
// Click off the modal.
await page.mouse.click(0, 0)
await expect(networkPopover).not.toBeVisible()
// Turn back on the network
await u.emulateNetworkConditions({
offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 })
// (Second check) expect the network to be up
await expect(networkToggle).toContainText('Connected')
}) })
// Expect the network to be down test('Engine disconnect & reconnect in sketch mode', async ({
await expect(networkToggle).toContainText('Problem') page,
browserName,
homePage,
}) => {
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
const networkToggle = page.getByTestId('network-toggle')
// Click the network widget const u = await getUtils(page)
await networkWidget.click() await page.setBodyDimensions({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
// Check the modal opened. await homePage.goToModelingScene()
await expect(networkPopover).toBeVisible() await u.waitForPageLoad()
// Click off the modal. await u.openDebugPanel()
await page.mouse.click(0, 0) // click on "Start Sketch" button
await expect(networkPopover).not.toBeVisible() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
// Turn back on the network // select a plane
await u.emulateNetworkConditions({ await page.mouse.click(700, 200)
offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
await expect( await expect(page.locator('.cm-content')).toHaveText(
page.getByRole('button', { name: 'Start Sketch' }) `sketch001 = startSketchOn('XZ')`
).not.toBeDisabled({ timeout: 15000 }) )
await u.closeDebugPanel()
// (Second check) expect the network to be up await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await expect(networkToggle).toContainText('Connected') })
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 const startXPx = 600
const networkToggle = page.getByTestId('network-toggle') await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
const u = await getUtils(page) .toHaveText(`sketch001 = startSketchOn('XZ')
await page.setBodyDimensions({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await homePage.goToModelingScene()
await u.waitForPageLoad()
await u.openDebugPanel()
// 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(
`sketch001 = startSketchOn('XZ')`
)
await u.closeDebugPanel()
await page.waitForTimeout(500) // 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(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)`) |> startProfileAt(${commonPoints.startAt}, %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %) |> startProfileAt(${commonPoints.startAt}, %)
|> xLine(${commonPoints.num1}, %)`) |> xLine(${commonPoints.num1}, %)`)
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await expect(networkToggle).toContainText('Connected')
// simulate network down // simulate network down
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
offline: true, offline: true,
// values of 0 remove any active throttling. crbug.com/456324#c9 // values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0, latency: 0,
downloadThroughput: -1, downloadThroughput: -1,
uploadThroughput: -1, uploadThroughput: -1,
}) })
// Expect the network to be down // Expect the network to be down
await expect(networkToggle).toContainText('Problem') await expect(networkToggle).toContainText('Problem')
// Ensure we are not in sketch mode // Ensure we are not in sketch mode
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).not.toBeVisible() ).not.toBeVisible()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible() ).toBeVisible()
// simulate network up // simulate network up
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
offline: false, offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9 // values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0, latency: 0,
downloadThroughput: -1, downloadThroughput: -1,
uploadThroughput: -1, uploadThroughput: -1,
}) })
// Wait for the app to be ready for use // Wait for the app to be ready for use
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 }) ).not.toBeDisabled({ timeout: 15000 })
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await expect(networkToggle).toContainText('Connected')
await expect(page.getByTestId('loading-stream')).not.toBeAttached() await expect(page.getByTestId('loading-stream')).not.toBeAttached()
// Click off the code pane. // Click off the code pane.
await page.mouse.click(100, 100) await page.mouse.click(100, 100)
// select a line // select a line
await page.getByText(`startProfileAt(${commonPoints.startAt}, %)`).click() await page.getByText(`startProfileAt(${commonPoints.startAt}, %)`).click()
// enter sketch again // enter sketch again
await u.doAndWaitForCmd( await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Edit Sketch' }).click(), () => page.getByRole('button', { name: 'Edit Sketch' }).click(),
'default_camera_get_settings' 'default_camera_get_settings'
) )
await page.waitForTimeout(150) await page.waitForTimeout(150)
// Click the line tool // Click the line tool
await page.getByRole('button', { name: 'line Line', exact: true }).click() await page.getByRole('button', { name: 'line Line', exact: true }).click()
await page.waitForTimeout(150) await page.waitForTimeout(150)
// Ensure we can continue sketching // Ensure we can continue sketching
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect.poll(u.normalisedEditorCode) await expect.poll(u.normalisedEditorCode)
.toBe(`sketch001 = startSketchOn('XZ') .toBe(`sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> xLine(12.34, %) |> xLine(12.34, %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
`) `)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20) await page.mouse.click(startXPx, 500 - PUR * 20)
await expect.poll(u.normalisedEditorCode) await expect.poll(u.normalisedEditorCode)
.toBe(`sketch001 = startSketchOn('XZ') .toBe(`sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> xLine(12.34, %) |> xLine(12.34, %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
@ -194,19 +204,20 @@ test.describe('Test network and connection issues', () => {
`) `)
// Unequip line tool // Unequip line tool
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
// Make sure we didn't pop out of sketch mode. // Make sure we didn't pop out of sketch mode.
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).toBeVisible() ).toBeVisible()
await expect( await expect(
page.getByRole('button', { name: 'line Line', exact: true }) page.getByRole('button', { name: 'line Line', exact: true })
).not.toHaveAttribute('aria-pressed', 'true') ).not.toHaveAttribute('aria-pressed', 'true')
// Exit sketch // Exit sketch
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).not.toBeVisible() }) ).not.toBeVisible()
})
}) })

View File

@ -1157,3 +1157,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)
}

View File

@ -1,29 +1,117 @@
import { test, expect } from './zoo-test' import { test, expect } from './zoo-test'
import { EngineCommand } from 'lang/std/artifactGraph' import { EngineCommand } from 'lang/std/artifactGraph'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { getUtils, } from './test-utils' import { getUtils } from './test-utils'
test.describe('Testing Camera Movement', () => { test.describe('Testing Camera Movement', () => {
test('Can move camera reliably', async ({ page, context, homePage }) => { test('Can move camera reliably', async ({ page, context, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.closeKclCodePanel() await u.closeKclCodePanel()
const camPos: [number, number, number] = [0, 85, 85] const camPos: [number, number, number] = [0, 85, 85]
const bakeInRetries = async ( const bakeInRetries = async (
mouseActions: any, mouseActions: any,
xyz: [number, number, number], xyz: [number, number, number],
cnt = 0 cnt = 0
) => { ) => {
// hack that we're implemented our own retry instead of using retries built into playwright. // 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 // 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. // 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 // 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 // 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. // for the stream to start, so it can be good to bundle related things together.
const camCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
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' })
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
expect(appLogoBBox).not.toBeNull()
if (!appLogoBBox) throw new Error('app logo not found')
await page.mouse.move(
appLogoBBox.x + appLogoBBox.width / 2,
appLogoBBox.y + appLogoBBox.height / 2
)
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')
}, [-19, -85, -85])
const camCommand: EngineCommand = { const camCommand: EngineCommand = {
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
@ -47,417 +135,282 @@ test.describe('Testing Camera Movement', () => {
await u.sendCustomCmd(updateCamCommand) await u.sendCustomCmd(updateCamCommand)
await page.waitForTimeout(100) await page.waitForTimeout(100)
// rotate await u.clearCommandLogs()
await u.closeDebugPanel() await u.closeDebugPanel()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100) await page.waitForTimeout(200)
// const yo = page.getByTestId('cam-x-position').inputValue()
// zoom
await u.doAndWaitForImageDiff(async () => { await u.doAndWaitForImageDiff(async () => {
await mouseActions() 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.openAndClearDebugPanel() await u.openDebugPanel()
await page.waitForTimeout(300)
await u.clearCommandLogs()
await u.closeDebugPanel() await u.closeDebugPanel()
await page.waitForTimeout(100)
}, 300) }, 300)
// zoom with scroll
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await page.getByTestId('cam-x-position').isVisible() // 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)
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.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' })
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
expect(appLogoBBox).not.toBeNull()
if (!appLogoBBox) throw new Error('app logo not found')
await page.mouse.move(
appLogoBBox.x + appLogoBBox.width / 2,
appLogoBBox.y + appLogoBBox.height / 2
)
await page.mouse.move(600, 303)
await page.mouse.up({ button: 'right' })
}, [4, -10.5, -120])
await bakeInRetries(async () => { await bakeInRetries(async () => {
await page.mouse.move(700, 400)
await page.mouse.wheel(0, -100)
}, [0, -85, -85])
})
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
// than again for sketching
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
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, 325)
let code = `sketch001 = startSketchOn('XY')`
await expect(u.codeLocator).toHaveText(code)
await u.closeDebugPanel()
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
// move the camera slightly
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await page.mouse.move(600, 200) await page.mouse.move(700, 300)
await page.mouse.down({ button: 'right' }) await page.mouse.down({ button: 'right' })
await page.mouse.move(700, 200, { steps: 2 }) await page.mouse.move(800, 200)
await page.mouse.up({ button: 'right' }) await page.mouse.up({ button: 'right' })
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
}, [-19, -85, -85])
const camCommand: EngineCommand = { let y = 350,
type: 'modeling_cmd_req', x = 948
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
await page.waitForTimeout(100)
await u.clearCommandLogs() await u.canvasLocator.click({ position: { x: 783, y } })
await u.closeDebugPanel() code += `\n |> startProfileAt([8.12, -12.98], %)`
// await expect(u.codeLocator).toHaveText(code)
await u.canvasLocator.click({ position: { x, y } })
code += `\n |> line([11.18, 0], %)`
// await expect(u.codeLocator).toHaveText(code)
await u.canvasLocator.click({ position: { x, y: 275 } })
code += `\n |> line([0, 6.99], %)`
// await expect(u.codeLocator).toHaveText(code)
await page.getByRole('button', { name: 'Start Sketch' }).click() // click the line button
await page.waitForTimeout(200) await page.getByRole('button', { name: 'line Line', exact: true }).click()
// zoom const hoverOverNothing = async () => {
await u.doAndWaitForImageDiff(async () => { // await u.canvasLocator.hover({position: {x: 700, y: 325}})
await page.keyboard.down('Control') await page.mouse.move(700, 325)
await page.mouse.move(700, 400) await page.waitForTimeout(100)
await page.mouse.down({ button: 'right' }) await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
await page.mouse.move(700, 300) timeout: 10_000,
await page.mouse.up({ button: 'right' }) })
await page.keyboard.up('Control') }
await u.openDebugPanel() await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.waitForTimeout(300)
await u.clearCommandLogs()
await u.closeDebugPanel() await page.waitForTimeout(200)
}, 300) // hover over horizontal line
await u.canvasLocator.hover({ position: { x: 800, y } })
// zoom with scroll await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
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)
}, [0, -85, -85]) })
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
// than again for sketching
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
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, 325)
let code = `sketch001 = startSketchOn('XY')`
await expect(u.codeLocator).toHaveText(code)
await u.closeDebugPanel()
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
// move the camera slightly
await page.keyboard.down('Shift')
await page.mouse.move(700, 300)
await page.mouse.down({ button: 'right' })
await page.mouse.move(800, 200)
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Shift')
let y = 350,
x = 948
await u.canvasLocator.click({ position: { x: 783, y } })
code += `\n |> startProfileAt([8.12, -12.98], %)`
// await expect(u.codeLocator).toHaveText(code)
await u.canvasLocator.click({ position: { x, y } })
code += `\n |> line([11.18, 0], %)`
// await expect(u.codeLocator).toHaveText(code)
await u.canvasLocator.click({ position: { x, y: 275 } })
code += `\n |> line([0, 6.99], %)`
// await expect(u.codeLocator).toHaveText(code)
// click the line button
await page.getByRole('button', { name: 'line Line', exact: true }).click()
const hoverOverNothing = async () => {
// await u.canvasLocator.hover({position: {x: 700, y: 325}})
await page.mouse.move(700, 325)
await page.waitForTimeout(100)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
timeout: 10_000, timeout: 10_000,
}) })
} await page.waitForTimeout(200)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible() await hoverOverNothing()
await page.waitForTimeout(200)
// hover over vertical line
await u.canvasLocator.hover({ position: { x, y: 325 } })
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
await page.waitForTimeout(200) await hoverOverNothing()
// hover over horizontal line
await u.canvasLocator.hover({ position: { x: 800, y } })
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
await page.waitForTimeout(200)
await hoverOverNothing() // click exit sketch
await page.waitForTimeout(200) await page.getByRole('button', { name: 'Exit Sketch' }).click()
// hover over vertical line await page.waitForTimeout(400)
await u.canvasLocator.hover({ position: { x, y: 325 } })
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
await hoverOverNothing() await hoverOverNothing()
await page.waitForTimeout(200)
// hover over horizontal line
await page.mouse.move(858, y, { steps: 5 })
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
// click exit sketch await hoverOverNothing()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await page.waitForTimeout(400)
await hoverOverNothing() // hover over vertical line
await page.waitForTimeout(200) await page.mouse.move(x, 325)
// hover over horizontal line await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
await page.mouse.move(858, y, { steps: 5 }) timeout: 10_000,
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({ })
timeout: 10_000,
})
await hoverOverNothing() await hoverOverNothing()
// hover over vertical line // hover over vertical line
await page.mouse.move(x, 325) await page.mouse.move(857, y)
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({ await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000, timeout: 10_000,
}) })
// now click it
await page.mouse.click(857, y)
await hoverOverNothing() await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await hoverOverNothing()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
// hover over vertical line await page.waitForTimeout(400)
await page.mouse.move(857, y)
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
// now click it
await page.mouse.click(857, y)
await expect( x = 975
page.getByRole('button', { name: 'Edit Sketch' }) y = 468
).toBeVisible()
await hoverOverNothing()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400) await page.waitForTimeout(100)
await page.mouse.move(x, 419, { steps: 5 })
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
x = 975 await hoverOverNothing()
y = 468
await page.waitForTimeout(100) await page.mouse.move(855, y)
await page.mouse.move(x, 419, { steps: 5 }) await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({ timeout: 10_000,
timeout: 10_000, })
})
await hoverOverNothing() await hoverOverNothing()
await page.mouse.move(855, y) await page.getByRole('button', { name: 'Exit Sketch' }).click()
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({ await page.waitForTimeout(200)
timeout: 10_000,
})
await hoverOverNothing() await hoverOverNothing()
await page.waitForTimeout(200)
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.mouse.move(x, 419)
await page.waitForTimeout(200) await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
})
await hoverOverNothing() await hoverOverNothing()
await page.waitForTimeout(200)
await page.mouse.move(x, 419) await page.mouse.move(855, y)
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({ await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000, timeout: 10_000,
})
await hoverOverNothing()
await page.mouse.move(855, y)
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
timeout: 10_000,
}) })
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,
* until the engine supports unifying multiple camera movements.
* This verifies that scrollCallback's guard is working as expected.
*/
const u = await getUtils(page)
// Constants and locators
const settingsLink = page.getByTestId('settings-link')
const settingsDialogHeading = page.getByRole('heading', {
name: 'Settings',
exact: true,
})
const userSettingsTab = page.getByRole('radio', { name: 'User' })
const mouseControlsSetting = page
.locator('#mouseControls')
.getByRole('combobox')
const mouseControlSuccesToast = page.getByText(
'Set mouse controls to "Solidworks"'
)
const settingsCloseButton = page.getByTestId('settings-close-button')
const gizmo = page.locator('[aria-label*=gizmo]')
const resetCameraButton = page.getByRole('button', { name: 'Reset view' })
const orbitMouseStart = { x: 800, y: 130 }
const orbitMouseEnd = { x: 0, y: 130 }
const mid = (v1: number, v2: number) => v1 + (v2 - v1) / 2
type Point = { x: number; y: number }
const midPoint = (p1: Point, p2: Point) => ({
x: mid(p1.x, p2.x),
y: mid(p1.y, p2.y),
})
const orbitMouseStepOne = midPoint(orbitMouseStart, orbitMouseEnd)
const expectedStartCamZPosition = 64.0
const expectedZoomCamZPosition = 32.0
const expectedOrbitCamZPosition = 64.0
await test.step(`Test setup`, async () => {
await homePage.goToModelingScene()
await u.closeKclCodePanel()
// This test requires the mouse controls to be set to Solidworks
await u.openDebugPanel()
await test.step(`Set mouse controls setting to Solidworks`, async () => {
await settingsLink.click()
await expect(settingsDialogHeading).toBeVisible()
await userSettingsTab.click()
await mouseControlsSetting.selectOption({ label: 'Solidworks' })
await expect(mouseControlSuccesToast).toBeVisible()
await settingsCloseButton.click()
}) })
}) })
await test.step(`Test scrolling zoom works`, async () => { test(`Zoom by scroll should not fire while orbiting`, async ({
await resetCamera() page,
await page.mouse.move(orbitMouseStart.x, orbitMouseStart.y) homePage,
await page.mouse.wheel(0, -100) }) => {
await test.step(`Force a refresh of the camera position`, async () => { /**
await u.openAndClearDebugPanel() * Currently we only allow zooming by scroll when no other camera movement is happening,
await u.sendCustomCmd({ * set within cameraMouseDragGuards in cameraControls.ts,
type: 'modeling_cmd_req', * until the engine supports unifying multiple camera movements.
cmd_id: uuidv4(), * This verifies that scrollCallback's guard is working as expected.
cmd: { */
type: 'default_camera_get_settings', const u = await getUtils(page)
},
}) // Constants and locators
await u.waitForCmdReceive('default_camera_get_settings') const settingsLink = page.getByTestId('settings-link')
const settingsDialogHeading = page.getByRole('heading', {
name: 'Settings',
exact: true,
}) })
const userSettingsTab = page.getByRole('radio', { name: 'User' })
await expect const mouseControlsSetting = page
.poll(getCameraZValue, { .locator('#mouseControls')
message: 'Camera should be at expected position after zooming', .getByRole('combobox')
}) const mouseControlSuccesToast = page.getByText(
.toEqual(expectedZoomCamZPosition) 'Set mouse controls to "Solidworks"'
}) )
const settingsCloseButton = page.getByTestId('settings-close-button')
await test.step(`Test orbiting works`, async () => { const gizmo = page.locator('[aria-label*=gizmo]')
await doOrbitWith() const resetCameraButton = page.getByRole('button', { name: 'Reset view' })
}) const orbitMouseStart = { x: 800, y: 130 }
const orbitMouseEnd = { x: 0, y: 130 }
await test.step(`Test scrolling while orbiting doesn't zoom`, async () => { const mid = (v1: number, v2: number) => v1 + (v2 - v1) / 2
await doOrbitWith(async () => { type Point = { x: number; y: number }
await page.mouse.wheel(0, -100) const midPoint = (p1: Point, p2: Point) => ({
x: mid(p1.x, p2.x),
y: mid(p1.y, p2.y),
}) })
}) const orbitMouseStepOne = midPoint(orbitMouseStart, orbitMouseEnd)
const expectedStartCamZPosition = 64.0
const expectedZoomCamZPosition = 32.0
const expectedOrbitCamZPosition = 64.0
// Helper functions await test.step(`Test setup`, async () => {
async function resetCamera() { await homePage.goToModelingScene()
await test.step(`Reset camera`, async () => { await u.closeKclCodePanel()
// This test requires the mouse controls to be set to Solidworks
await u.openDebugPanel() await u.openDebugPanel()
await u.clearCommandLogs() await test.step(`Set mouse controls setting to Solidworks`, async () => {
await u.doAndWaitForCmd(async () => { await settingsLink.click()
await gizmo.click({ button: 'right' }) await expect(settingsDialogHeading).toBeVisible()
await resetCameraButton.click() await userSettingsTab.click()
}, 'zoom_to_fit') await mouseControlsSetting.selectOption({ label: 'Solidworks' })
await expect await expect(mouseControlSuccesToast).toBeVisible()
.poll(getCameraZValue, { await settingsCloseButton.click()
message: 'Camera Z should be at expected position after reset', })
})
.toEqual(expectedStartCamZPosition)
}) })
}
async function getCameraZValue() { await test.step(`Test scrolling zoom works`, async () => {
return page await resetCamera()
.getByTestId('cam-z-position')
.inputValue()
.then((value) => parseFloat(value))
}
async function doOrbitWith(callback = async () => {}) {
await resetCamera()
await test.step(`Perform orbit`, async () => {
await page.mouse.move(orbitMouseStart.x, orbitMouseStart.y) await page.mouse.move(orbitMouseStart.x, orbitMouseStart.y)
await page.mouse.down({ button: 'middle' }) await page.mouse.wheel(0, -100)
await page.mouse.move(orbitMouseStepOne.x, orbitMouseStepOne.y, { await test.step(`Force a refresh of the camera position`, async () => {
steps: 3, await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await u.waitForCmdReceive('default_camera_get_settings')
}) })
await callback()
await page.mouse.move(orbitMouseEnd.x, orbitMouseEnd.y, {
steps: 3,
})
})
await test.step(`Verify orbit`, async () => {
await expect await expect
.poll(getCameraZValue, { .poll(getCameraZValue, {
message: 'Camera should be at expected position after orbiting', message: 'Camera should be at expected position after zooming',
}) })
await callback() await callback()
await page.mouse.move(orbitMouseEnd.x, orbitMouseEnd.y, { await page.mouse.move(orbitMouseEnd.x, orbitMouseEnd.y, {

File diff suppressed because it is too large Load Diff

View File

@ -52,15 +52,105 @@ test.describe('Testing Gizmo', () => {
} of cases) { } of cases) {
test(`check ${testDescription}`, async ({ page, homePage }) => { test(`check ${testDescription}`, async ({ page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript((TEST_CODE_GIZMO) => {
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
}, TEST_CODE_GIZMO)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(100)
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: {
x: 3000,
y: 3000,
z: 3000,
},
center: {
x: 800,
y: -152,
z: 26,
},
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.clearCommandLogs()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await u.waitForCmdReceive('default_camera_get_settings')
await u.clearCommandLogs()
await page.mouse.move(clickPosition.x, clickPosition.y)
await page.waitForTimeout(100)
await page.mouse.click(clickPosition.x, clickPosition.y)
await page.mouse.move(0, 0)
await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await u.waitForCmdReceive('default_camera_get_settings')
await Promise.all([
// position
expect(page.getByTestId('cam-x-position')).toHaveValue(
expectedCameraPosition.x.toString()
),
expect(page.getByTestId('cam-y-position')).toHaveValue(
expectedCameraPosition.y.toString()
),
expect(page.getByTestId('cam-z-position')).toHaveValue(
expectedCameraPosition.z.toString()
),
// target
expect(page.getByTestId('cam-x-target')).toHaveValue(
expectedCameraTarget.x.toString()
),
expect(page.getByTestId('cam-y-target')).toHaveValue(
expectedCameraTarget.y.toString()
),
expect(page.getByTestId('cam-z-target')).toHaveValue(
expectedCameraTarget.z.toString()
),
])
})
}
test('Context menu and popover menu', async ({ page, homePage }) => {
const testCase = {
testDescription: 'Right view',
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
expectedCameraTarget: { x: 800, y: -152, z: 26 },
}
// Test prelude taken from the above test
const u = await getUtils(page)
await page.addInitScript((TEST_CODE_GIZMO) => { await page.addInitScript((TEST_CODE_GIZMO) => {
localStorage.setItem('persistCode', TEST_CODE_GIZMO) localStorage.setItem('persistCode', TEST_CODE_GIZMO)
}, TEST_CODE_GIZMO) }, TEST_CODE_GIZMO)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(100) await page.waitForTimeout(100)
// wait for execution done // wait for execution done
await u.openDebugPanel() await u.openDebugPanel()
@ -94,13 +184,20 @@ test.describe('Testing Gizmo', () => {
}) })
await u.waitForCmdReceive('default_camera_get_settings') await u.waitForCmdReceive('default_camera_get_settings')
// Now find and select the correct
// view from the context menu
await u.clearCommandLogs() await u.clearCommandLogs()
await page.mouse.move(clickPosition.x, clickPosition.y) const gizmo = page.locator('[aria-label*=gizmo]')
await page.waitForTimeout(100) await gizmo.click({ button: 'right' })
await page.mouse.click(clickPosition.x, clickPosition.y) const buttonToTest = page.getByRole('button', {
await page.mouse.move(0, 0) name: testCase.testDescription,
})
await expect(buttonToTest).toBeVisible()
await buttonToTest.click()
// Now assert we've moved to the correct view
// Taken from the above test
await u.waitForCmdReceive('default_camera_look_at') await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs()
await u.sendCustomCmd({ await u.sendCustomCmd({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
@ -110,135 +207,41 @@ test.describe('Testing Gizmo', () => {
}, },
}) })
await u.waitForCmdReceive('default_camera_get_settings') await u.waitForCmdReceive('default_camera_get_settings')
await page.waitForTimeout(400)
await Promise.all([ await Promise.all([
// position // position
expect(page.getByTestId('cam-x-position')).toHaveValue( expect(page.getByTestId('cam-x-position')).toHaveValue(
expectedCameraPosition.x.toString() testCase.expectedCameraPosition.x.toString()
), ),
expect(page.getByTestId('cam-y-position')).toHaveValue( expect(page.getByTestId('cam-y-position')).toHaveValue(
expectedCameraPosition.y.toString() testCase.expectedCameraPosition.y.toString()
), ),
expect(page.getByTestId('cam-z-position')).toHaveValue( expect(page.getByTestId('cam-z-position')).toHaveValue(
expectedCameraPosition.z.toString() testCase.expectedCameraPosition.z.toString()
), ),
// target // target
expect(page.getByTestId('cam-x-target')).toHaveValue( expect(page.getByTestId('cam-x-target')).toHaveValue(
expectedCameraTarget.x.toString() testCase.expectedCameraTarget.x.toString()
), ),
expect(page.getByTestId('cam-y-target')).toHaveValue( expect(page.getByTestId('cam-y-target')).toHaveValue(
expectedCameraTarget.y.toString() testCase.expectedCameraTarget.y.toString()
), ),
expect(page.getByTestId('cam-z-target')).toHaveValue( expect(page.getByTestId('cam-z-target')).toHaveValue(
expectedCameraTarget.z.toString() testCase.expectedCameraTarget.z.toString()
), ),
]) }) ])
}
test('Context menu and popover menu', async ({ page, homePage }) => { const testCase = { // Now test the popover menu.
testDescription: 'Right view', // It has the same click handlers, so we can just
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 }, // test that it opens and contains the same content.
expectedCameraTarget: { x: 800, y: -152, z: 26 }, const gizmoPopoverButton = page.getByRole('button', {
} name: 'view settings',
})
// Test prelude taken from the above test await expect(gizmoPopoverButton).toBeVisible()
const u = await getUtils(page) await gizmoPopoverButton.click()
await page.addInitScript((TEST_CODE_GIZMO) => { await expect(buttonToTest).toBeVisible()
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
}, TEST_CODE_GIZMO)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await page.waitForTimeout(100)
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: {
x: 3000,
y: 3000,
z: 3000,
},
center: {
x: 800,
y: -152,
z: 26,
},
up: { x: 0, y: 0, z: 1 },
},
}) })
await page.waitForTimeout(100)
await u.clearCommandLogs()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await u.waitForCmdReceive('default_camera_get_settings')
// Now find and select the correct
// view from the context menu
await u.clearCommandLogs()
const gizmo = page.locator('[aria-label*=gizmo]')
await gizmo.click({ button: 'right' })
const buttonToTest = page.getByRole('button', {
name: testCase.testDescription,
})
await expect(buttonToTest).toBeVisible()
await buttonToTest.click()
// Now assert we've moved to the correct view
// Taken from the above test
await u.waitForCmdReceive('default_camera_look_at')
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await u.waitForCmdReceive('default_camera_get_settings')
await page.waitForTimeout(400)
await Promise.all([
// position
expect(page.getByTestId('cam-x-position')).toHaveValue(
testCase.expectedCameraPosition.x.toString()
),
expect(page.getByTestId('cam-y-position')).toHaveValue(
testCase.expectedCameraPosition.y.toString()
),
expect(page.getByTestId('cam-z-position')).toHaveValue(
testCase.expectedCameraPosition.z.toString()
),
// target
expect(page.getByTestId('cam-x-target')).toHaveValue(
testCase.expectedCameraTarget.x.toString()
),
expect(page.getByTestId('cam-y-target')).toHaveValue(
testCase.expectedCameraTarget.y.toString()
),
expect(page.getByTestId('cam-z-target')).toHaveValue(
testCase.expectedCameraTarget.z.toString()
),
])
// Now test the popover menu.
// It has the same click handlers, so we can just
// test that it opens and contains the same content.
const gizmoPopoverButton = page.getByRole('button', {
name: 'view settings',
})
await expect(gizmoPopoverButton).toBeVisible()
await gizmoPopoverButton.click()
await expect(buttonToTest).toBeVisible() })
}) })
test.describe(`Testing gizmo, fixture-based`, () => { test.describe(`Testing gizmo, fixture-based`, () => {
@ -252,7 +255,9 @@ test.describe(`Testing gizmo, fixture-based`, () => {
scene, scene,
}) => { }) => {
await context.addInitScript(() => { await context.addInitScript(() => {
localStorage.setItem('persistCode', ` localStorage.setItem(
'persistCode',
`
const sketch002 = startSketchOn('XZ') const sketch002 = startSketchOn('XZ')
|> startProfileAt([-108.83, -57.48], %) |> startProfileAt([-108.83, -57.48], %)
|> angledLine([0, 105.13], %, $rectangleSegmentA001) |> angledLine([0, 105.13], %, $rectangleSegmentA001)
@ -271,7 +276,8 @@ test.describe(`Testing gizmo, fixture-based`, () => {
radius: 182.8 radius: 182.8
}, %) }, %)
|> extrude(50, %) |> extrude(50, %)
`) `
)
}) })
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })

View File

@ -1,18 +1,10 @@
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 { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
import * as TOML from '@iarna/toml' 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('Test toggling perspective', () => { test.describe('Test toggling perspective', () => {
test('via command palette and toggle', async ({ page }) => { test('via command palette and toggle', async ({ page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
// Locators and constants // Locators and constants
@ -40,8 +32,8 @@ test.describe('Test toggling perspective', () => {
}) })
await test.step('Setup', async () => { await test.step('Setup', async () => {
await page.setViewportSize({ width: screenWidth, height: screenHeight }) await page.setBodyDimensions({ width: screenWidth, height: screenHeight })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await u.closeKclCodePanel() await u.closeKclCodePanel()
await expect await expect
.poll(async () => locationToHaveColor(backgroundColor), { .poll(async () => locationToHaveColor(backgroundColor), {
@ -87,7 +79,7 @@ test.describe('Test toggling perspective', () => {
} }
) )
await page.reload() await page.reload()
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await expect await expect
.poll(async () => locationToHaveColor(xzPlaneColor), { .poll(async () => locationToHaveColor(xzPlaneColor), {
timeout: 5000, timeout: 5000,

View File

@ -7,7 +7,8 @@ import packageJson from '../package.json'
import { MachinesListing } from 'components/MachineManagerProvider' import { MachinesListing } from 'components/MachineManagerProvider'
import chokidar from 'chokidar' import chokidar from 'chokidar'
const resizeWindow = (width: number, height: number) => ipcRenderer.invoke('app.resizeWindow', [width, height]) const resizeWindow = (width: number, height: number) =>
ipcRenderer.invoke('app.resizeWindow', [width, height])
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args) const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
const save = (args: any) => ipcRenderer.invoke('dialog.showSaveDialog', args) const save = (args: any) => ipcRenderer.invoke('dialog.showSaveDialog', args)
const openExternal = (url: any) => ipcRenderer.invoke('shell.openExternal', url) const openExternal = (url: any) => ipcRenderer.invoke('shell.openExternal', url)