* Add ability to pick default plane in feature tree in 'Sketch no face' mode * add ability to select deoffset plane where starting a new sketch * use selectDefaultSketchPlane * refactor: remove some duplication * warning cleanups * feature tree items selectable depedngin on no face sketch mode * lint * fix small jump because of border:none when going into and back from 'No face sketch' mode * grey out items other than offset planes in 'No face sketch' mode * start sketching on plane in context menu * sketch on offset plane with context menu * add ability to right click on default plane and start sketch on it * default planes in feature tree should be selectable because of right click context menu * add right click Start sketch option for selected plane on the canvas * selectDefaultSketchPlane returns error now * circular deps * move select functions to lib/selections.ts to avoid circular deps * add test for clicking on feature tree after starting a new sketch * graphite suggestion * fix bug of not being able to create offset plane using another offset plane with command bar * add ability to select default plane on feature when going through the Offset plane command bar flow
3886 lines
125 KiB
TypeScript
3886 lines
125 KiB
TypeScript
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import type { Page } from '@playwright/test'
|
|
import { roundOff, uuidv4 } from '@src/lib/utils'
|
|
|
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
|
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
|
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
|
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
|
import {
|
|
PERSIST_MODELING_CONTEXT,
|
|
TEST_COLORS,
|
|
getMovementUtils,
|
|
getUtils,
|
|
} from '@e2e/playwright/test-utils'
|
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
|
|
|
test.describe('Sketch tests', () => {
|
|
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
|
page,
|
|
context,
|
|
homePage,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
const selectionsSnippets = {
|
|
startProfileAt1:
|
|
'|> startProfile(at = [-width / 4 + screwRadius, height / 2])',
|
|
startProfileAt2: '|> startProfile(at = [-width / 2, 0])',
|
|
startProfileAt3: '|> startProfile(at = [0, thickness])',
|
|
}
|
|
await context.addInitScript(
|
|
async ({ startProfileAt1, startProfileAt2, startProfileAt3 }: any) => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`
|
|
width = 20
|
|
height = 10
|
|
thickness = 5
|
|
screwRadius = 3
|
|
wireRadius = 2
|
|
wireOffset = 0.5
|
|
|
|
screwHole = startSketchOn(XY)
|
|
${startProfileAt1}
|
|
|> arc(angleStart = 0, angleEnd = 360, radius = screwRadius)
|
|
|
|
part001 = startSketchOn(XY)
|
|
${startProfileAt2}
|
|
|> xLine(length = width * .5)
|
|
|> yLine(length = height)
|
|
|> xLine(length = -width * .5)
|
|
|> close()
|
|
|> subtract2d(tool = screwHole)
|
|
|> extrude(length = thickness)
|
|
|
|
part002 = startSketchOn(-XZ)
|
|
${startProfileAt3}
|
|
|> xLine(length = width / 4)
|
|
|> tangentialArc(endAbsolute = [width / 2, 0])
|
|
|> xLine(length = -width / 4 + wireRadius)
|
|
|> yLine(length = wireOffset)
|
|
|> arc(angleStart = 0, angleEnd = 180, radius = wireRadius)
|
|
|> yLine(length = -wireOffset)
|
|
|> xLine(length = -width / 4)
|
|
|> close()
|
|
|> extrude(length = -height)
|
|
`
|
|
)
|
|
},
|
|
selectionsSnippets
|
|
)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
// wait for execution done
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
await page.getByText(selectionsSnippets.startProfileAt1).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
|
|
await page.getByText(selectionsSnippets.startProfileAt2).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
|
|
await page.getByText(selectionsSnippets.startProfileAt3).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
})
|
|
test('Can delete most of a sketch and the line tool will still work', async ({
|
|
page,
|
|
scene,
|
|
homePage,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [2.61, -4.01])
|
|
|> xLine(length = 8.73)
|
|
|> tangentialArc(endAbsolute = [8.33, -1.31])`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 587, y: 270 }, 15)
|
|
|
|
await expect(async () => {
|
|
await page.mouse.click(700, 200)
|
|
await page.getByText('tangentialArc(endAbsolute = [8.33, -1.31])').click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeEnabled({ timeout: 2000 })
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
|
|
|
await page.waitForTimeout(600) // wait for animation
|
|
|
|
await page.getByText('tangentialArc(endAbsolute = [8.33, -1.31])').click()
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.down('Shift')
|
|
await page.keyboard.press('ArrowUp')
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.up('Shift')
|
|
await page.keyboard.press('Backspace')
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
|
await page.waitForTimeout(100)
|
|
|
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
|
await page.waitForTimeout(500)
|
|
// click start profileAt handle to continue profile
|
|
await page.mouse.click(702, 406, { delay: 500 })
|
|
await page.waitForTimeout(100)
|
|
await page.mouse.move(800, 150)
|
|
|
|
await expect(async () => {
|
|
// click to add segment
|
|
await page.mouse.click(700, 200)
|
|
|
|
await expect
|
|
.poll(u.normalisedEditorCode, { timeout: 1000 })
|
|
.toBe(`@settings(defaultLengthUnit = in)
|
|
|
|
|
|
sketch002 = startSketchOn(XZ)
|
|
sketch001 = startProfile(sketch002, at = [12.34, -12.34])
|
|
|> yLine(length = 12.34)
|
|
|
|
`)
|
|
}).toPass({ timeout: 5_000, intervals: [1_000] })
|
|
})
|
|
|
|
test('Can exit selection of face', async ({ page, homePage }) => {
|
|
// Load the app with the code panes
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem('persistCode', ``)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Exit Sketch' })
|
|
).toBeVisible()
|
|
|
|
await expect(page.getByText('select a plane or face')).toBeVisible()
|
|
|
|
await page.keyboard.press('Escape')
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).toBeVisible()
|
|
})
|
|
|
|
test('Can select planes in Feature Tree after Start Sketch', async ({
|
|
page,
|
|
homePage,
|
|
toolbar,
|
|
editor,
|
|
}) => {
|
|
// Load the app with empty code
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`plane001 = offsetPlane(XZ, offset = 5)`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
await test.step('Click Start Sketch button', async () => {
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Exit Sketch' })
|
|
).toBeVisible()
|
|
await expect(page.getByText('select a plane or face')).toBeVisible()
|
|
})
|
|
|
|
await test.step('Open feature tree and select Front plane (XZ)', async () => {
|
|
await toolbar.openFeatureTreePane()
|
|
|
|
await page.getByRole('button', { name: 'Front plane' }).click()
|
|
|
|
await page.waitForTimeout(600)
|
|
|
|
await expect(toolbar.lineBtn).toBeEnabled()
|
|
await editor.expectEditor.toContain('startSketchOn(XZ)')
|
|
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).toBeVisible()
|
|
})
|
|
|
|
await test.step('Click Start Sketch button again', async () => {
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Exit Sketch' })
|
|
).toBeVisible()
|
|
})
|
|
|
|
await test.step('Select the offset plane', async () => {
|
|
await toolbar.openFeatureTreePane()
|
|
|
|
await page.getByRole('button', { name: 'Offset plane' }).click()
|
|
|
|
await page.waitForTimeout(600)
|
|
|
|
await expect(toolbar.lineBtn).toBeEnabled()
|
|
await editor.expectEditor.toContain('startSketchOn(plane001)')
|
|
})
|
|
})
|
|
|
|
test('Can edit segments by dragging their handles', () => {
|
|
const doEditSegmentsByDraggingHandle = async (
|
|
page: Page,
|
|
homePage: HomePageFixture,
|
|
openPanes: string[],
|
|
scene: SceneFixture,
|
|
toolbar: ToolbarFixture,
|
|
cmdBar: CmdBarFixture
|
|
) => {
|
|
// Load the app with the code panes
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [4.61, -14.01])
|
|
|> line(end = [12.73, -0.09])
|
|
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
|
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|
|
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|
|
|> close()`
|
|
)
|
|
})
|
|
|
|
const u = await getUtils(page)
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.waitForTimeout(100)
|
|
await u.openAndClearDebugPanel()
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_look_at',
|
|
vantage: { x: 0, y: -1250, z: 580 },
|
|
center: { x: 0, y: 0, z: 0 },
|
|
up: { x: 0, y: 0, z: 1 },
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_get_settings',
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await u.closeDebugPanel()
|
|
|
|
// If we have the code pane open, we should see the code.
|
|
if (openPanes.includes('code')) {
|
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [4.61, -14.01])
|
|
|> line(end = [12.73, -0.09])
|
|
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
|
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|
|
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|
|
|> close()
|
|
`)
|
|
} else {
|
|
// Ensure we don't see the code.
|
|
await expect(u.codeLocator).not.toBeVisible()
|
|
}
|
|
|
|
const startPX = [665, 458]
|
|
|
|
const dragPX = 30
|
|
let prevContent = ''
|
|
|
|
if (openPanes.includes('code')) {
|
|
await page.getByText('startProfile(at = [4.61, -14.01])').click()
|
|
} else {
|
|
// Wait for the render.
|
|
await page.waitForTimeout(1000)
|
|
// Select the sketch
|
|
await page.mouse.click(700, 370)
|
|
}
|
|
await toolbar.editSketch()
|
|
if (openPanes.includes('code')) {
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
}
|
|
|
|
const step5 = { steps: 5 }
|
|
|
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
|
|
|
|
// drag startProfileAt handle
|
|
await page.mouse.move(startPX[0], startPX[1])
|
|
await page.mouse.down()
|
|
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
|
await page.mouse.up()
|
|
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
}
|
|
|
|
// drag line handle
|
|
await page.waitForTimeout(100)
|
|
|
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
|
await page.mouse.move(lineEnd.x - 5, lineEnd.y)
|
|
await page.mouse.down()
|
|
await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
}
|
|
|
|
// drag tangentialArc handle
|
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
|
await page.mouse.move(tangentEnd.x, tangentEnd.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
}
|
|
|
|
// drag arcTo interiorAbsolute handle (three point arc)
|
|
const arcToHandle = await u.getBoundingBox('[data-overlay-index="2"]')
|
|
await page.mouse.move(arcToHandle.x, arcToHandle.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
arcToHandle.x - dragPX,
|
|
arcToHandle.y + dragPX,
|
|
step5
|
|
)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
}
|
|
|
|
// drag arcTo end handle (three point arc)
|
|
const arcToEndHandle = await u.getBoundingBox('[data-overlay-index="3"]')
|
|
await page.mouse.move(arcToEndHandle.x, arcToEndHandle.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
arcToEndHandle.x - dragPX,
|
|
arcToEndHandle.y + dragPX,
|
|
step5
|
|
)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
}
|
|
|
|
// drag arc radius handle
|
|
const arcRadiusHandle = await u.getBoundingBox('[data-overlay-index="4"]')
|
|
await page.mouse.move(arcRadiusHandle.x, arcRadiusHandle.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
arcRadiusHandle.x - dragPX,
|
|
arcRadiusHandle.y + dragPX,
|
|
step5
|
|
)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
}
|
|
|
|
// drag arc center handle (we'll have to hardcode the position because it doesn't have a overlay near the handle)
|
|
const arcCenterHandle = { x: 745, y: 214 }
|
|
await page.mouse.move(arcCenterHandle.x, arcCenterHandle.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
arcCenterHandle.x - dragPX,
|
|
arcCenterHandle.y + dragPX,
|
|
step5
|
|
)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
if (openPanes.includes('code')) {
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
}
|
|
|
|
// Open the code pane
|
|
await u.openKclCodePanel()
|
|
|
|
// expect the code to have changed
|
|
await expect(
|
|
page.locator('.cm-content')
|
|
).toHaveText(`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [6.44, -12.07])
|
|
|> line(end = [14.72, 1.97])
|
|
|> tangentialArc(endAbsolute = [26.92, -3.32])
|
|
|> arc(interiorAbsolute = [18.11, -3.73], endAbsolute = [9.77, -3.19])
|
|
|> arc(angleStart = -58.29, angleEnd = 161.17, radius = 3.75)
|
|
|> close()
|
|
`)
|
|
}
|
|
test('code pane open at start-handles', async ({
|
|
page,
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
// Load the app with the code panes
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'store',
|
|
JSON.stringify({
|
|
state: {
|
|
openPanes: ['code'],
|
|
},
|
|
version: 0,
|
|
})
|
|
)
|
|
})
|
|
await doEditSegmentsByDraggingHandle(
|
|
page,
|
|
homePage,
|
|
['code'],
|
|
scene,
|
|
toolbar,
|
|
cmdBar
|
|
)
|
|
})
|
|
|
|
test('code pane closed at start-handles', async ({
|
|
page,
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
// Load the app with the code panes
|
|
await page.addInitScript(async (persistModelingContext) => {
|
|
localStorage.setItem(
|
|
persistModelingContext,
|
|
JSON.stringify({ openPanes: [] })
|
|
)
|
|
}, PERSIST_MODELING_CONTEXT)
|
|
await doEditSegmentsByDraggingHandle(
|
|
page,
|
|
homePage,
|
|
[],
|
|
scene,
|
|
toolbar,
|
|
cmdBar
|
|
)
|
|
})
|
|
})
|
|
|
|
test('Can edit a circle center and radius by dragging its handles', async ({
|
|
page,
|
|
editor,
|
|
homePage,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit=in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> circle(center = [4.61, -5.01], radius = 8)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.waitForTimeout(100)
|
|
await u.openAndClearDebugPanel()
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_look_at',
|
|
vantage: { x: 0, y: -1250, z: 580 },
|
|
center: { x: 0, y: 0, z: 0 },
|
|
up: { x: 0, y: 0, z: 1 },
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_get_settings',
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
|
|
const startPX = [667, 325]
|
|
|
|
const dragPX = 40
|
|
|
|
await page.getByText('circle(center = [4.61, -5.01], radius = 8)').click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(400)
|
|
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
|
|
|
|
await test.step('drag circle center handle', async () => {
|
|
await page.dragAndDrop('#stream', '#stream', {
|
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX },
|
|
})
|
|
await page.waitForTimeout(100)
|
|
|
|
await editor.expectEditor.not.toContain(prevContent)
|
|
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
})
|
|
|
|
await test.step('drag circle radius handle', async () => {
|
|
await page.waitForTimeout(100)
|
|
|
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
|
await page.waitForTimeout(100)
|
|
await page.dragAndDrop('#stream', '#stream', {
|
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
|
targetPosition: { x: lineEnd.x + dragPX * 2, y: lineEnd.y + dragPX },
|
|
})
|
|
await editor.expectEditor.not.toContain(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
})
|
|
|
|
// expect the code to have changed
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> circle(center = [7.26, -2.37], radius = 11.44)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
|
page,
|
|
homePage,
|
|
editor,
|
|
toolbar,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit=in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [4.61, -10.01])
|
|
|> line(end = [12.73, -0.09])
|
|
|> tangentialArc(endAbsolute = [24.95, -0.38])
|
|
|> close()
|
|
|> extrude(length = 5)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
|
await scene.settled(cmdBar)
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled({ timeout: 10_000 })
|
|
|
|
await page.waitForTimeout(100)
|
|
await u.openAndClearDebugPanel()
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_look_at',
|
|
vantage: { x: 0, y: -1250, z: 580 },
|
|
center: { x: 0, y: 0, z: 0 },
|
|
up: { x: 0, y: 0, z: 1 },
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_get_settings',
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
|
|
const startPX = [665, 397]
|
|
|
|
const dragPX = 40
|
|
|
|
await page.getByText('startProfile(at = [4.61, -10.01])').click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(400)
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
|
|
|
// drag startProfileAt handle
|
|
await page.dragAndDrop('#stream', '#stream', {
|
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag line handle
|
|
await page.waitForTimeout(100)
|
|
|
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
|
await page.dragAndDrop('#stream', '#stream', {
|
|
sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
|
|
targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag tangentialArc handle
|
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
|
await page.dragAndDrop('#stream', '#stream', {
|
|
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
|
targetPosition: {
|
|
x: tangentEnd.x,
|
|
y: tangentEnd.y - 15,
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
|
|
// expect the code to have changed
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [7.12, -12.68])
|
|
|> line(end = [12.68, -1.09])
|
|
|> tangentialArc(endAbsolute = [24.95, -0.38])
|
|
|> close()
|
|
|> extrude(length = 5)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
|
|
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
|
page,
|
|
homePage,
|
|
scene,
|
|
editor,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit=in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [4.61, -14.01])
|
|
|> line(end = [12.73, -0.09])
|
|
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
|
|> close()
|
|
|> revolve(axis = X)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await page.waitForTimeout(100)
|
|
await u.openAndClearDebugPanel()
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_look_at',
|
|
vantage: { x: 0, y: -1250, z: 580 },
|
|
center: { x: 0, y: 0, z: 0 },
|
|
up: { x: 0, y: 0, z: 1 },
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
await u.sendCustomCmd({
|
|
type: 'modeling_cmd_req',
|
|
cmd_id: uuidv4(),
|
|
cmd: {
|
|
type: 'default_camera_get_settings',
|
|
},
|
|
})
|
|
await page.waitForTimeout(100)
|
|
|
|
const startPX = [665, 458]
|
|
|
|
const dragPX = 30
|
|
|
|
await page.getByText('startProfile(at = [4.61, -14.01])').click()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Edit Sketch' })
|
|
).toBeVisible()
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(400)
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
const step5 = { steps: 5 }
|
|
|
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
|
|
|
// drag startProfileAt handle
|
|
await page.mouse.move(startPX[0], startPX[1])
|
|
await page.mouse.down()
|
|
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
|
await page.mouse.up()
|
|
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag line handle
|
|
await page.waitForTimeout(100)
|
|
|
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
|
await page.mouse.move(lineEnd.x - 5, lineEnd.y)
|
|
await page.mouse.down()
|
|
await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
// drag tangentialArc handle
|
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
|
await page.mouse.move(tangentEnd.x, tangentEnd.y - 5)
|
|
await page.mouse.down()
|
|
await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5)
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
|
|
// expect the code to have changed
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [8.41, -9.97])
|
|
|> line(end = [12.73, -0.09])
|
|
|> line(end = [1.99, 2.06])
|
|
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
|
|> close()
|
|
|> revolve(axis = X)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
test('Can add multiple sketches', async ({ page, homePage }) => {
|
|
const u = await getUtils(page)
|
|
|
|
const viewportSize = { width: 1200, height: 500 }
|
|
await page.setBodyDimensions(viewportSize)
|
|
|
|
await homePage.goToModelingScene()
|
|
await u.openDebugPanel()
|
|
|
|
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
|
|
const { toSU, toU, click00r } = getMovementUtils({ center, page })
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).toBeVisible()
|
|
|
|
// click on "Start Sketch" button
|
|
await u.clearCommandLogs()
|
|
await u.doAndWaitForImageDiff(
|
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
|
200
|
|
)
|
|
|
|
let codeStr =
|
|
'@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XY)'
|
|
|
|
await page.mouse.click(center.x, viewportSize.height * 0.55)
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
await u.closeDebugPanel()
|
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
|
|
|
await click00r(0, 0)
|
|
codeStr += `profile001 = startProfile(sketch001, at = ${toSU([0, 0])})`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(50, 0)
|
|
await page.waitForTimeout(100)
|
|
codeStr += ` |> xLine(length = ${toU(50, 0)[0]})`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(0, 50)
|
|
codeStr += ` |> yLine(length = ${toU(0, 50)[1]})`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(-50, 0)
|
|
codeStr += ` |> xLine(length = ${toU(-50, 0)[0]})`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
// exit the sketch, reset relative clicker
|
|
await click00r(undefined, undefined)
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await page.waitForTimeout(250)
|
|
await u.clearCommandLogs()
|
|
|
|
// start a new sketch
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
// when exiting the sketch above the camera is still looking down at XY,
|
|
// so selecting the plane again is a bit easier.
|
|
await page.mouse.click(center.x + 200, center.y + 100)
|
|
await page.waitForTimeout(600) // TODO detect animation ending, or disable animation
|
|
codeStr += 'sketch002 = startSketchOn(XY)'
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
await u.closeDebugPanel()
|
|
|
|
await click00r(30, 0)
|
|
codeStr += `profile002 = startProfile(sketch002, at = [2.03, 0])`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
// TODO: I couldn't use `toSU` here because of some rounding error causing
|
|
// it to be off by 0.01
|
|
await click00r(30, 0)
|
|
codeStr += ` |> xLine(length = 2.04)`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(0, 30)
|
|
codeStr += ` |> yLine(length = -2.03)`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(-30, 0)
|
|
codeStr += ` |> xLine(length = -2.04)`
|
|
await expect(u.codeLocator).toHaveText(codeStr)
|
|
|
|
await click00r(undefined, undefined)
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.updateCamPosition([100, 100, 100])
|
|
await u.clearCommandLogs()
|
|
})
|
|
test.describe('Snap to close works (at any scale)', () => {
|
|
const doSnapAtDifferentScales = async (
|
|
page: any,
|
|
camPos: [number, number, number],
|
|
scale = 1
|
|
) => {
|
|
const u = await getUtils(page)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await u.openDebugPanel()
|
|
|
|
const code = `@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(-XZ)
|
|
profile001 = startProfile(sketch001, at = [${roundOff(scale * 69.6)}, ${roundOff(
|
|
scale * 34.8
|
|
)}])
|
|
|> xLine(length = ${roundOff(scale * 139.19)})
|
|
|> yLine(length = -${roundOff(scale * 139.2)})
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).toBeVisible()
|
|
|
|
await u.clearCommandLogs()
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.waitForTimeout(100)
|
|
|
|
await u.openAndClearDebugPanel()
|
|
await u.updateCamPosition(camPos)
|
|
await u.closeDebugPanel()
|
|
|
|
await page.mouse.move(0, 0)
|
|
|
|
// select a plane
|
|
await page.mouse.move(700, 200, { steps: 10 })
|
|
await page.mouse.click(700, 200, { delay: 200 })
|
|
await expect(page.locator('.cm-content')).toHaveText(
|
|
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(-XZ)`
|
|
)
|
|
|
|
let prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
const pointA = [700, 200]
|
|
const pointB = [900, 200]
|
|
const pointC = [900, 400]
|
|
|
|
// draw three lines
|
|
await page.waitForTimeout(500)
|
|
await page.mouse.move(pointA[0], pointA[1], { steps: 10 })
|
|
await page.mouse.click(pointA[0], pointA[1], { delay: 200 })
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.move(pointB[0], pointB[1], { steps: 10 })
|
|
await page.mouse.click(pointB[0], pointB[1], { delay: 200 })
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.move(pointC[0], pointC[1], { steps: 10 })
|
|
await page.mouse.click(pointC[0], pointC[1], { delay: 200 })
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await page.mouse.move(pointA[0] - 12, pointA[1] + 12, { steps: 10 })
|
|
const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7]
|
|
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], {
|
|
steps: 10,
|
|
})
|
|
|
|
await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1], {
|
|
delay: 200,
|
|
})
|
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
|
prevContent = await page.locator('.cm-content').innerText()
|
|
|
|
await expect
|
|
.poll(async () => {
|
|
const text = await page.locator('.cm-content').innerText()
|
|
return text.replace(/\s/g, '')
|
|
})
|
|
.toBe(code.replace(/\s/g, ''))
|
|
|
|
// Assert the tool stays equipped after a profile is closed (ready for the next one)
|
|
await expect(
|
|
page.getByRole('button', { name: 'line Line', exact: true })
|
|
).toHaveAttribute('aria-pressed', 'true')
|
|
|
|
// exit sketch
|
|
await u.openAndClearDebugPanel()
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.removeCurrentCode()
|
|
}
|
|
test('[0, 100, 100]', async ({ page, homePage }) => {
|
|
await homePage.goToModelingScene()
|
|
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
|
|
})
|
|
|
|
test('[0, 10000, 10000]', async ({ page, homePage }) => {
|
|
await homePage.goToModelingScene()
|
|
await doSnapAtDifferentScales(page, [0, 10000, 10000])
|
|
})
|
|
})
|
|
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
|
page,
|
|
homePage,
|
|
cmdBar,
|
|
toolbar,
|
|
}) => {
|
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [-0.45, 0.87])
|
|
|> line(end = [1.32, 0.38])
|
|
|> line(end = [1.02, -1.32], tag = $seg01)
|
|
|> line(end = [-1.01, -0.77])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
`
|
|
)
|
|
})
|
|
|
|
const u = await getUtils(page)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
// wait for execution done
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// click profile in code
|
|
await page.getByText(`startProfile(at = [-0.45, 0.87])`).click()
|
|
await page.waitForTimeout(100)
|
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
|
{ timeout: 10_000 }
|
|
)
|
|
// click edit sketch
|
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
await page.waitForTimeout(600) // wait for animation
|
|
|
|
// exit sketch
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
// expect extrude button to be enabled
|
|
await expect(toolbar.extrudeButton).not.toBeDisabled()
|
|
|
|
// click extrude
|
|
await toolbar.extrudeButton.click()
|
|
|
|
// sketch selection should already have been made.
|
|
// otherwise the cmdbar would be waiting for a selection.
|
|
await cmdBar.progressCmdBar()
|
|
await cmdBar.expectState({
|
|
stage: 'arguments',
|
|
currentArgKey: 'length',
|
|
currentArgValue: '5',
|
|
headerArguments: { Profiles: '1 profile', Length: '' },
|
|
highlightedHeaderArg: 'length',
|
|
commandName: 'Extrude',
|
|
})
|
|
})
|
|
test("Existing sketch with bad code delete user's code", async ({
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [-0.45, 0.87])
|
|
|> line(end = [1.32, 0.38])
|
|
|> line(end = [1.02, -1.32], tag = $seg01)
|
|
|> line(end = [-1.01, -0.77])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(sketch001, length = 5)
|
|
`
|
|
)
|
|
})
|
|
|
|
const u = await getUtils(page)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
// Click the end face of extrude001
|
|
await page.mouse.click(622, 355)
|
|
|
|
// The click should generate a new sketch starting on the end face of extrude001
|
|
// signified by the implicit 'END' tag for that solid.
|
|
await page.waitForTimeout(800)
|
|
await page.getByText(`END)`).click()
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type(' |>', { delay: 100 })
|
|
await page.waitForTimeout(100)
|
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).toBeVisible()
|
|
|
|
await expect((await u.codeLocator.innerText()).replace(/\s/g, '')).toBe(
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [-0.45, 0.87])
|
|
|> line(end = [1.32, 0.38])
|
|
|> line(end = [1.02, -1.32], tag = $seg01)
|
|
|> line(end = [-1.01, -0.77])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(sketch001, length = 5)
|
|
sketch002 = startSketchOn(extrude001, face = END)
|
|
|>
|
|
`.replace(/\s/g, '')
|
|
)
|
|
})
|
|
// TODO: fix after electron migration is merged
|
|
test('empty-scene default-planes act as expected', async ({
|
|
page,
|
|
homePage,
|
|
scene,
|
|
cmdBar,
|
|
editor,
|
|
}) => {
|
|
/**
|
|
* Tests the following things
|
|
* 1) The the planes are there on load because the scene is empty
|
|
* 2) The planes don't changes color when hovered initially
|
|
* 3) Putting something in the scene makes the planes hidden
|
|
* 4) Removing everything from the scene shows the plans again
|
|
* 3) Once "start sketch" is click, the planes do respond to hovers
|
|
* 4) Selecting a plan works as expected, i.e. sketch mode
|
|
* 5) Reloading the scene with something already in the scene means the planes are hidden
|
|
*/
|
|
|
|
const u = await getUtils(page)
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
|
expect(
|
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
).toBeLessThan(8)
|
|
|
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
await page.waitForTimeout(200)
|
|
|
|
// color should not change for having been hovered
|
|
expect(
|
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
).toBeLessThan(8)
|
|
|
|
await u.openAndClearDebugPanel()
|
|
|
|
await u.codeLocator.fill(`sketch001 = startSketchOn(XY)
|
|
|> startProfile(at = [-10, -10])
|
|
|> line(end = [20, 0])
|
|
|> line(end = [0, 20])
|
|
|> xLine(length = -20)
|
|
`)
|
|
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
const noPlanesColor: [number, number, number] = [30, 30, 30]
|
|
expect(
|
|
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
|
).toBeLessThan(3)
|
|
|
|
await u.clearCommandLogs()
|
|
await u.removeCurrentCode()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
|
|
await expect
|
|
.poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), {
|
|
timeout: 5_000,
|
|
})
|
|
.toBeLessThan(8)
|
|
|
|
// click start Sketch
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
|
|
const hoveredColor: [number, number, number] = [93, 93, 127]
|
|
// now that we're expecting the user to select a plan, it does respond to hover
|
|
await expect
|
|
.poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor))
|
|
.toBeLessThan(8)
|
|
|
|
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
|
|
|
// wait for line button to have aria-pressed as proxy for sketch mode
|
|
await expect
|
|
.poll(async () => page.getByTestId('line').getAttribute('aria-pressed'), {
|
|
timeout: 10_000,
|
|
})
|
|
.toBe('true')
|
|
await page.waitForTimeout(200)
|
|
|
|
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
|
await page.waitForTimeout(200)
|
|
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [299.72, 230.82])
|
|
|> line(end = [86.12, -86.13])
|
|
`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [299.72, 230.82])
|
|
|> line(end = [86.12, -86.13])
|
|
`
|
|
)
|
|
})
|
|
await scene.settled(cmdBar)
|
|
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
// expect there to be no planes on load since there's something in the scene
|
|
expect(
|
|
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
|
).toBeLessThan(3)
|
|
})
|
|
|
|
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
|
|
const u = await getUtils(page)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`lugHeadLength = 0.25
|
|
lugDiameter = 0.5
|
|
lugLength = 2
|
|
|
|
fn lug(origin, length, diameter, plane) {
|
|
lugSketch = startSketchOn(plane)
|
|
|> startProfile(at = [origin[0] + lugDiameter / 2, origin[1]])
|
|
|> angledLine(angle = 60, lengthY = lugHeadLength)
|
|
|> xLine(endAbsolute = 0 + .001)
|
|
|> yLine(endAbsolute = 0)
|
|
|> close()
|
|
|> revolve(axis = Y)
|
|
|
|
return lugSketch
|
|
}
|
|
|
|
lug(origin = [0, 0], length = 10, diameter = .5, plane = XY)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
|
|
/***
|
|
* Test Plan
|
|
* Start the sketch mode
|
|
* Click the middle of the screen which should click the top face that is revolved
|
|
* Wait till you see the line tool be enabled
|
|
* Wait till you see the exit sketch enabled
|
|
*
|
|
* This is supposed to test that you are allowed to go into sketch mode to sketch on a revolved face
|
|
*/
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
await expect(async () => {
|
|
await page.mouse.click(600, 250)
|
|
await page.waitForTimeout(1000)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Exit Sketch' })
|
|
).toBeVisible()
|
|
await expect(
|
|
page.getByRole('button', { name: 'line Line', exact: true })
|
|
).toHaveAttribute('aria-pressed', 'true')
|
|
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
|
})
|
|
|
|
test('sketch on face of a boolean works', async ({
|
|
page,
|
|
homePage,
|
|
scene,
|
|
cmdBar,
|
|
toolbar,
|
|
editor,
|
|
}) => {
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = mm)
|
|
|
|
myVar = 50
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = circle(sketch001, center = [myVar, 43.9], radius = 41.05)
|
|
extrude001 = extrude(profile001, length = 200)
|
|
|> translate(x = 3.14, y = 3.14, z = -50.154)
|
|
sketch002 = startSketchOn(XY)
|
|
profile002 = startProfile(sketch002, at = [72.2, -52.05])
|
|
|> angledLine(angle = 0, length = 181.26, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 21.54)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $mySeg)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|
|
|> close()
|
|
|
|
extrude002 = extrude(profile002, length = 151)
|
|
solid001 = subtract([extrude001], tools = [extrude002])
|
|
`
|
|
)
|
|
})
|
|
|
|
const [selectChamferFaceClk] = scene.makeMouseHelpers(705, 234)
|
|
const [circleCenterClk] = scene.makeMouseHelpers(700, 272)
|
|
const [circleRadiusClk] = scene.makeMouseHelpers(694, 264)
|
|
|
|
await test.step('Setup', async () => {
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await scene.moveCameraTo(
|
|
{ x: 180, y: -75, z: 116 },
|
|
{ x: 67, y: -114, z: -15 }
|
|
)
|
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
|
})
|
|
|
|
await test.step('sketch on chamfer face that is part of a boolean', async () => {
|
|
await toolbar.startSketchPlaneSelection()
|
|
await selectChamferFaceClk()
|
|
|
|
await expect
|
|
.poll(async () => {
|
|
const lineBtn = page.getByRole('button', { name: 'line Line' })
|
|
return lineBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
|
|
await editor.expectEditor.toContain(
|
|
'startSketchOn(solid001, face = seg01)'
|
|
)
|
|
})
|
|
|
|
await test.step('verify sketching still works', async () => {
|
|
await toolbar.circleBtn.click()
|
|
await expect
|
|
.poll(async () => {
|
|
const circleBtn = page.getByRole('button', { name: 'circle Circle' })
|
|
return circleBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
|
|
await circleCenterClk()
|
|
await editor.expectEditor.toContain(
|
|
'profile003 = circle(sketch003, center'
|
|
)
|
|
|
|
await circleRadiusClk()
|
|
await editor.expectEditor.toContain(
|
|
'profile003 = circle(sketch003, center = [-25.77, 10.97], radius = 1.85)'
|
|
)
|
|
})
|
|
})
|
|
|
|
test('Can sketch on face when user defined function was used in the sketch', async ({
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
// Checking for a regression that performs a sketch when a user defined function
|
|
// is declared at the top of the file and used in the sketch that is being drawn on.
|
|
// fn in2mm is declared at the top of the file and used rail which does a an extrusion with the function.
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`fn in2mm(@inches) {
|
|
return inches * 25.4
|
|
}
|
|
|
|
railTop = in2mm(.748)
|
|
railSide = in2mm(.024)
|
|
railBaseWidth = in2mm(.612)
|
|
railWideWidth = in2mm(.835)
|
|
railBaseLength = in2mm(.200)
|
|
railClampable = in2mm(.200)
|
|
|
|
rail = startSketchOn(XZ)
|
|
|> startProfile(at = [-railTop / 2, railClampable + railBaseLength])
|
|
|> line(endAbsolute = [
|
|
railTop / 2,
|
|
railClampable + railBaseLength
|
|
])
|
|
|> line(endAbsolute = [
|
|
railWideWidth / 2,
|
|
railClampable / 2 + railBaseLength
|
|
], tag = $seg01)
|
|
|> line(endAbsolute = [railTop / 2, railBaseLength])
|
|
|> line(endAbsolute = [railBaseWidth / 2, railBaseLength])
|
|
|> line(endAbsolute = [railBaseWidth / 2, 0])
|
|
|> line(endAbsolute = [-railBaseWidth / 2, 0])
|
|
|> line(endAbsolute = [-railBaseWidth / 2, railBaseLength])
|
|
|> line(endAbsolute = [-railTop / 2, railBaseLength])
|
|
|> line(endAbsolute = [
|
|
-railWideWidth / 2,
|
|
railClampable / 2 + railBaseLength
|
|
])
|
|
|> line(endAbsolute = [
|
|
-railTop / 2,
|
|
railClampable + railBaseLength
|
|
])
|
|
|> close()
|
|
|> extrude(length = in2mm(2))`
|
|
)
|
|
})
|
|
|
|
const center = { x: 600, y: 250 }
|
|
const rectangleSize = 20
|
|
await homePage.goToModelingScene()
|
|
|
|
// Start a sketch
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
// Click the top face of this rail
|
|
await page.mouse.click(center.x, center.y)
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Draw a rectangle
|
|
// top left
|
|
await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize)
|
|
await page.waitForTimeout(250)
|
|
// top right
|
|
await page.mouse.click(center.x + rectangleSize, center.y - rectangleSize)
|
|
await page.waitForTimeout(250)
|
|
|
|
// bottom right
|
|
await page.mouse.click(center.x + rectangleSize, center.y + rectangleSize)
|
|
await page.waitForTimeout(250)
|
|
|
|
// bottom left
|
|
await page.mouse.click(center.x - rectangleSize, center.y + rectangleSize)
|
|
await page.waitForTimeout(250)
|
|
|
|
// top left
|
|
await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize)
|
|
await page.waitForTimeout(250)
|
|
|
|
// exit sketch
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
// Check execution is done
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
await u.closeDebugPanel()
|
|
})
|
|
|
|
test('Can edit a tangentialArc defined by angle and radius', async ({
|
|
page,
|
|
homePage,
|
|
editor,
|
|
toolbar,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
const viewportSize = { width: 1500, height: 750 }
|
|
await page.setBodyDimensions(viewportSize)
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit=in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [-10, -10])
|
|
|> line(end = [20.0, 10.0])
|
|
|> tangentialArc(angle = 60deg, radius=10.0)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
|
await scene.settled(cmdBar)
|
|
|
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
|
|
|
await page.waitForTimeout(1000)
|
|
|
|
await page.mouse.move(1200, 139)
|
|
await page.mouse.down()
|
|
await page.mouse.move(870, 250)
|
|
await page.mouse.up()
|
|
|
|
await page.waitForTimeout(200)
|
|
|
|
await editor.expectEditor.toContain(
|
|
`tangentialArc(angle = 234.01deg, radius = 4.08)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
|
|
test('Can undo with closed code pane', async ({
|
|
page,
|
|
homePage,
|
|
editor,
|
|
toolbar,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
|
|
const viewportSize = { width: 1500, height: 750 }
|
|
await page.setBodyDimensions(viewportSize)
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit=in)
|
|
sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [-10, -10])
|
|
|> line(end = [20.0, 10.0])
|
|
|> tangentialArc(end = [5.49, 8.37])`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
|
await scene.settled(cmdBar)
|
|
|
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
|
|
|
await page.waitForTimeout(1000)
|
|
|
|
await page.mouse.move(1200, 139)
|
|
await page.mouse.down()
|
|
await page.mouse.move(870, 250)
|
|
await page.mouse.up()
|
|
|
|
await editor.expectEditor.toContain(`tangentialArc(end=[-5.85,4.32])`, {
|
|
shouldNormalise: true,
|
|
})
|
|
|
|
await u.closeKclCodePanel()
|
|
|
|
// Undo the last change
|
|
await page.keyboard.down('Control')
|
|
await page.keyboard.press('KeyZ')
|
|
await page.keyboard.up('Control')
|
|
|
|
await u.openKclCodePanel()
|
|
await editor.expectEditor.toContain(`tangentialArc(end = [5.49, 8.37])`, {
|
|
shouldNormalise: true,
|
|
})
|
|
})
|
|
|
|
test('Can delete a single segment line with keyboard', async ({
|
|
page,
|
|
scene,
|
|
homePage,
|
|
cmdBar,
|
|
editor,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
|
|
const viewportSize = { width: 1100, height: 750 }
|
|
await page.setBodyDimensions(viewportSize)
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = mm)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [0, 0])
|
|
|> xLine(length = 25.0)
|
|
|> yLine(length = 5.0)
|
|
|> line(end = [-22.0, 12.0])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
// Enter sketch mode
|
|
await page.mouse.dblclick(654, 450)
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Select the third line
|
|
await page.mouse.click(918, 90)
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Delete with backspace
|
|
await page.keyboard.press('Delete')
|
|
|
|
// Wait for engine re-execution to complete
|
|
await u.openDebugPanel()
|
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
|
|
|
// Validate the editor code no longer contains the deleted line
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [0, 0])
|
|
|> xLine(length = 25.0)
|
|
|> yLine(length = 5.0)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
})
|
|
|
|
test.describe(`Sketching with offset planes`, () => {
|
|
test(`Can select an offset plane to sketch on`, async ({
|
|
context,
|
|
page,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
homePage,
|
|
}) => {
|
|
// We seed the scene with a single offset plane
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
offsetPlane001 = offsetPlane(XY, offset = 10)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
|
|
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
|
|
|
|
await test.step(`Start sketching on the offset plane`, async () => {
|
|
await toolbar.startSketchPlaneSelection()
|
|
|
|
await test.step(`Hovering should highlight code`, async () => {
|
|
await planeHover()
|
|
await editor.expectState({
|
|
activeLines: [`@settings(defaultLengthUnit = in)`],
|
|
diagnostics: [],
|
|
highlightedCode: 'offsetPlane(XY, offset = 10)',
|
|
})
|
|
})
|
|
|
|
await test.step(`Clicking should select the plane and enter sketch mode`, async () => {
|
|
await planeClick()
|
|
// Have to wait for engine-side animation to finish
|
|
await page.waitForTimeout(600)
|
|
await expect(toolbar.lineBtn).toBeEnabled()
|
|
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
|
await editor.expectState({
|
|
activeLines: [`@settings(defaultLengthUnit = in)`],
|
|
diagnostics: [],
|
|
highlightedCode: '',
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
test.describe('multi-profile sketching', () => {
|
|
test(`test it removes half-finished expressions when changing tools in sketch mode`, async ({
|
|
context,
|
|
page,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
homePage,
|
|
cmdBar,
|
|
}) => {
|
|
// We seed the scene with a single offset plane
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`yo = 5
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [121.52, 168.25])
|
|
|> line(end = [115.04, 113.61])
|
|
|> line(end = [130.87, -97.79])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile002 = startProfile(sketch001, at = [117.2, 56.08])
|
|
|> line(end = [166.82, 25.89])
|
|
|> yLine(length = -107.86)
|
|
|
|
`
|
|
)
|
|
})
|
|
|
|
const [continueProfile2Clk] = scene.makeMouseHelpers(954, 282)
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
|
await page.waitForTimeout(600)
|
|
|
|
const [circlePoint1] = scene.makeMouseHelpers(700, 200)
|
|
|
|
await test.step('equip circle tool and click first point', async () => {
|
|
// await page.waitForTimeout(100)
|
|
await expect
|
|
.poll(async () => {
|
|
await toolbar.circleBtn.click()
|
|
return toolbar.circleBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
await page.waitForTimeout(100)
|
|
await circlePoint1()
|
|
await editor.expectEditor.toContain(
|
|
'profile003 = circle(sketch001, center = ['
|
|
)
|
|
})
|
|
|
|
await test.step('equip line tool and verify circle code is removed', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('profile003 = circle(')
|
|
})
|
|
|
|
const [circle3Point1] = scene.makeMouseHelpers(650, 200)
|
|
const [circle3Point2] = scene.makeMouseHelpers(750, 200)
|
|
// const [circle3Point3] = scene.makeMouseHelpers(700, 150)
|
|
|
|
await test.step('equip three point circle tool and click first two points', async () => {
|
|
await toolbar.selectCircleThreePoint()
|
|
await page.waitForTimeout(100)
|
|
await circle3Point1()
|
|
await page.waitForTimeout(100)
|
|
await circle3Point2()
|
|
await editor.expectEditor.toContain('profile003 = circleThreePoint(')
|
|
})
|
|
|
|
await test.step('equip line tool and verify three-point circle code is removed', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('profile003 = circleThreePoint(')
|
|
})
|
|
|
|
await test.step('equip three-point-arc tool and click first two points', async () => {
|
|
await page.waitForTimeout(200)
|
|
await toolbar.selectThreePointArc()
|
|
await page.waitForTimeout(200)
|
|
await circle3Point1()
|
|
await page.waitForTimeout(200)
|
|
await circle3Point2()
|
|
await editor.expectEditor.toContain('arc(')
|
|
})
|
|
|
|
await test.step('equip line tool and verify three-point-arc code is removed after second click', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('arc(')
|
|
})
|
|
|
|
const [cornerRectPoint1] = scene.makeMouseHelpers(600, 300)
|
|
|
|
await test.step('equip corner rectangle tool and click first point', async () => {
|
|
await toolbar.rectangleBtn.click()
|
|
await page.waitForTimeout(100)
|
|
await cornerRectPoint1()
|
|
await editor.expectEditor.toContain('profile004 = startProfile(')
|
|
})
|
|
|
|
await test.step('equip line tool and verify corner rectangle code is removed', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('profile004 = startProfile(')
|
|
})
|
|
|
|
const [centerRectPoint1] = scene.makeMouseHelpers(700, 300)
|
|
|
|
await test.step('equip center rectangle tool and click first point', async () => {
|
|
await toolbar.selectCenterRectangle()
|
|
await page.waitForTimeout(100)
|
|
await centerRectPoint1()
|
|
await editor.expectEditor.toContain('profile004 = startProfile(')
|
|
})
|
|
|
|
await test.step('equip line tool and verify center rectangle code is removed', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('profile004 = startProfile(')
|
|
})
|
|
|
|
await test.step('continue profile002 with the three point arc tool, and then switch back to the line tool to verify it only removes the last expression in the pipe', async () => {
|
|
await toolbar.selectThreePointArc()
|
|
await page.waitForTimeout(200)
|
|
await continueProfile2Clk()
|
|
await page.waitForTimeout(200)
|
|
await circle3Point1()
|
|
await editor.expectEditor.toContain('arc(')
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.not.toContain('arc(')
|
|
await editor.expectEditor.toContain('profile002')
|
|
})
|
|
})
|
|
test(`snapToProfile start only works for current profile`, async ({
|
|
context,
|
|
page,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
homePage,
|
|
cmdBar,
|
|
}) => {
|
|
// We seed the scene with a single offset plane
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile002 = startProfile(sketch001, at = [40.68, 87.67])
|
|
|> xLine(length = 239.17)
|
|
profile003 = startProfile(sketch001, at = [206.63, -56.73])
|
|
|> xLine(length = -156.32)
|
|
`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [onSegmentClick] = scene.makeMouseHelpers(604, 349)
|
|
const [endOfLowerSegClick, endOfLowerSegMove] = scene.makeMouseHelpers(
|
|
697,
|
|
360
|
|
)
|
|
const [profileStartOfHigherSegClick, profileStartOfHigherSegMove] =
|
|
scene.makeMouseHelpers(677, 78)
|
|
const tanArcLocation = { x: 624, y: 340 } as const
|
|
|
|
await test.step('enter sketch mode', async () => {
|
|
await onSegmentClick({ shouldDbClick: true })
|
|
await page.waitForTimeout(600)
|
|
})
|
|
|
|
const codeFromTangentialArc = ` |> tangentialArc(end = [-10.82, 144.95])`
|
|
await test.step('check that tangential tool does not snap to other profile starts', async () => {
|
|
await toolbar.selectTangentialArc()
|
|
await page.waitForTimeout(1000)
|
|
await endOfLowerSegMove()
|
|
await page.waitForTimeout(1000)
|
|
await endOfLowerSegClick()
|
|
await page.waitForTimeout(1000)
|
|
await profileStartOfHigherSegClick()
|
|
await page.waitForTimeout(1000)
|
|
await editor.expectEditor.toContain(codeFromTangentialArc)
|
|
await editor.expectEditor.not.toContain(
|
|
`[profileStartX(%), profileStartY(%)]`
|
|
)
|
|
})
|
|
|
|
await test.step('remove tangential arc code to reset', async () => {
|
|
await scene.expectPixelColor(TEST_COLORS.WHITE, tanArcLocation, 15)
|
|
await editor.replaceCode(codeFromTangentialArc, '')
|
|
// check pixel is now gray at tanArcLocation to verify code has executed
|
|
await scene.expectPixelColor([26, 26, 26], tanArcLocation, 15)
|
|
await editor.expectEditor.not.toContain(
|
|
`tangentialArc(end = [-10.82, 144.95])`
|
|
)
|
|
})
|
|
|
|
await test.step('check that tangential tool does snap to current profile start', async () => {
|
|
await expect
|
|
.poll(async () => {
|
|
await toolbar.lineBtn.click()
|
|
return toolbar.lineBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
await profileStartOfHigherSegMove()
|
|
await endOfLowerSegMove()
|
|
await endOfLowerSegClick()
|
|
await profileStartOfHigherSegClick()
|
|
await editor.expectEditor.toContain('line(end = [-10.82, 144.95])')
|
|
await editor.expectEditor.not.toContain(
|
|
`[profileStartX(%), profileStartY(%)]`
|
|
)
|
|
})
|
|
})
|
|
test('can enter sketch mode for sketch with no profiles', async ({
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
cmdBar,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XY)
|
|
`
|
|
)
|
|
})
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
// open feature tree and double click the first sketch
|
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
|
await page.waitForTimeout(600)
|
|
|
|
// click in the scene twice to add a segment
|
|
const [startProfile1] = scene.makeMouseHelpers(658, 140)
|
|
const [segment1Clk] = scene.makeMouseHelpers(701, 200)
|
|
|
|
// wait for line to be aria pressed
|
|
await expect
|
|
.poll(async () => toolbar.lineBtn.getAttribute('aria-pressed'))
|
|
.toBe('true')
|
|
|
|
await startProfile1()
|
|
await editor.expectEditor.toContain(`profile001 = startProfile`)
|
|
await segment1Clk()
|
|
await editor.expectEditor.toContain(`|> line(end`)
|
|
})
|
|
test('can delete all profiles in sketch mode and user can still equip a tool and draw something', async ({
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
|
|
|
await toolbar.startSketchPlaneSelection()
|
|
await selectXZPlane()
|
|
// timeout wait for engine animation is unavoidable
|
|
await page.waitForTimeout(600)
|
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn(XZ)`)
|
|
|
|
const [startProfile1] = scene.makeMouseHelpers(568, 70)
|
|
const [segment1Clk] = scene.makeMouseHelpers(701, 78)
|
|
const [segment2Clk] = scene.makeMouseHelpers(745, 189)
|
|
|
|
await test.step('add two segments', async () => {
|
|
await startProfile1()
|
|
await editor.expectEditor.toContain(
|
|
`profile001 = startProfile(sketch001, at = [4.61, 12.21])`
|
|
)
|
|
await segment1Clk()
|
|
await editor.expectEditor.toContain(`|> line(end`)
|
|
await segment2Clk()
|
|
await editor.expectEditor.toContain(`|> line(end = [2.98, -7.52])`)
|
|
})
|
|
|
|
await test.step('delete all profiles', async () => {
|
|
await editor.replaceCode('', 'sketch001 = startSketchOn(XZ)\n')
|
|
await page.waitForTimeout(600) // wait for deferred execution
|
|
})
|
|
|
|
await test.step('equip circle and draw it', async () => {
|
|
await toolbar.circleBtn.click()
|
|
await page.mouse.click(700, 200)
|
|
await page.mouse.click(750, 200)
|
|
await editor.expectEditor.toContain('circle(sketch001, center = [')
|
|
})
|
|
})
|
|
test('Can add multiple profiles to a sketch (all tool types)', async ({
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
|
|
|
const [startProfile1] = scene.makeMouseHelpers(568, 70)
|
|
const [endLineStartTanArc] = scene.makeMouseHelpers(701, 78)
|
|
const [endArcStartLine] = scene.makeMouseHelpers(745, 189)
|
|
|
|
const [startProfile2] = scene.makeMouseHelpers(782, 80)
|
|
const [profile2Point2] = scene.makeMouseHelpers(921, 90)
|
|
const [profile2Point3] = scene.makeMouseHelpers(953, 178)
|
|
|
|
const [circle1Center] = scene.makeMouseHelpers(842, 147)
|
|
const [circle1Radius] = scene.makeMouseHelpers(870, 171)
|
|
|
|
const [circle2Center] = scene.makeMouseHelpers(850, 222)
|
|
const [circle2Radius] = scene.makeMouseHelpers(843, 230)
|
|
|
|
const [crnRect1point1] = scene.makeMouseHelpers(583, 205)
|
|
const [crnRect1point2] = scene.makeMouseHelpers(618, 320)
|
|
|
|
const [crnRect2point1] = scene.makeMouseHelpers(663, 215)
|
|
const [crnRect2point2] = scene.makeMouseHelpers(744, 276)
|
|
|
|
const [cntrRect1point1] = scene.makeMouseHelpers(624, 387)
|
|
const [cntrRect1point2] = scene.makeMouseHelpers(676, 355)
|
|
|
|
const [cntrRect2point1] = scene.makeMouseHelpers(785, 332)
|
|
const [cntrRect2point2] = scene.makeMouseHelpers(808, 286)
|
|
|
|
const [circle3Point1p1, circle3Point1p1Move] = scene.makeMouseHelpers(
|
|
630,
|
|
465
|
|
)
|
|
const [circle3Point1p2, circle3Point1p2Move] = scene.makeMouseHelpers(
|
|
673,
|
|
340
|
|
)
|
|
const [circle3Point1p3, circle3Point1p3Move] = scene.makeMouseHelpers(
|
|
734,
|
|
414
|
|
)
|
|
|
|
const [circle3Point2p1, circle3Point2p1Move] = scene.makeMouseHelpers(
|
|
876,
|
|
351
|
|
)
|
|
const [circle3Point2p2, circle3Point2p2Move] = scene.makeMouseHelpers(
|
|
875,
|
|
279
|
|
)
|
|
const [circle3Point2p3, circle3Point2p3Move] = scene.makeMouseHelpers(
|
|
834,
|
|
306
|
|
)
|
|
|
|
await toolbar.startSketchPlaneSelection()
|
|
await selectXZPlane()
|
|
// timeout wait for engine animation is unavoidable
|
|
await page.waitForTimeout(600)
|
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn(XZ)`)
|
|
await test.step('Create a close profile stopping mid profile to equip the tangential arc, then three-point arc, and then back to the line tool', async () => {
|
|
await startProfile1()
|
|
await editor.expectEditor.toContain(
|
|
`profile001 = startProfile(sketch001, at = [4.61, 12.21])`
|
|
)
|
|
|
|
await endLineStartTanArc()
|
|
await editor.expectEditor.toContain(`|> line(end = [9.02, -0.55])`)
|
|
await toolbar.selectTangentialArc()
|
|
await page.waitForTimeout(300)
|
|
await page.mouse.click(745, 359)
|
|
await page.waitForTimeout(300)
|
|
await endLineStartTanArc({ delay: 544 })
|
|
|
|
await endArcStartLine()
|
|
await editor.expectEditor.toContain(
|
|
`|> tangentialArc(end = [2.98, -7.52])`
|
|
)
|
|
|
|
// Add a three-point arc segment
|
|
await toolbar.selectThreePointArc()
|
|
await page.waitForTimeout(300)
|
|
|
|
// select end of profile again
|
|
await endLineStartTanArc()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Define points for the three-point arc
|
|
const [threePointInterior, threePointInteriorMove] =
|
|
scene.makeMouseHelpers(600, 200)
|
|
const [threePointEnd, threePointEndMove] = scene.makeMouseHelpers(
|
|
590,
|
|
270
|
|
)
|
|
|
|
// Create the three-point arc
|
|
await page.waitForTimeout(300)
|
|
await threePointInteriorMove()
|
|
await threePointInterior()
|
|
await page.waitForTimeout(300)
|
|
await threePointEndMove()
|
|
await threePointEnd()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Verify the three-point arc was created correctly
|
|
await editor.expectEditor.toContain(`arc(`)
|
|
await editor.expectEditor.toContain(`interiorAbsolute`)
|
|
await editor.expectEditor.toContain(`endAbsolute`)
|
|
|
|
// Switch back to line tool to continue
|
|
await toolbar.lineBtn.click()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Continue with the original line segment
|
|
await threePointEnd()
|
|
await page.waitForTimeout(300)
|
|
|
|
await page.mouse.click(572, 110)
|
|
await editor.expectEditor.toContain(`|> line(end = [-1.22, 10.85])`)
|
|
await startProfile1()
|
|
await editor.expectEditor.toContain(
|
|
`|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
await page.waitForTimeout(300)
|
|
})
|
|
|
|
await test.step('Without unequipping from the last step, make another profile, and one that is not closed', async () => {
|
|
await startProfile2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile002 = startProfile(sketch001, at = [19.12, 11.53])`
|
|
)
|
|
await profile2Point2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(`|> line(end = [9.43, -0.68])`)
|
|
await profile2Point3()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(`|> line(end = [2.17, -5.97])`)
|
|
})
|
|
|
|
await test.step('create two circles in a row without unequip', async () => {
|
|
await toolbar.circleBtn.click()
|
|
|
|
await circle1Center()
|
|
await page.waitForTimeout(300)
|
|
await circle1Radius({ delay: 500 })
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile003 = circle(sketch001, center = [23.19, 6.98], radius = 2.5)`
|
|
)
|
|
|
|
await test.step('hover in empty space to wait for overlays to get out of the way', async () => {
|
|
await page.mouse.move(951, 223)
|
|
await page.waitForTimeout(1000)
|
|
})
|
|
|
|
await circle2Center()
|
|
await page.waitForTimeout(300)
|
|
await circle2Radius()
|
|
await editor.expectEditor.toContain(
|
|
`profile004 = circle(sketch001, center = [23.74, 1.9], radius = 0.72)`
|
|
)
|
|
})
|
|
await test.step('create two corner rectangles in a row without unequip', async () => {
|
|
await expect
|
|
.poll(async () => {
|
|
await toolbar.rectangleBtn.click()
|
|
return toolbar.rectangleBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
|
|
await crnRect1point1()
|
|
await editor.expectEditor.toContain(
|
|
`profile005 = startProfile(sketch001, at = [5.63, 3.05])`
|
|
)
|
|
await crnRect1point2()
|
|
await editor.expectEditor.toContain(
|
|
`|> angledLine(angle = 0, length = 2.37, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 7.8)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`.replaceAll('\n', '')
|
|
)
|
|
|
|
await crnRect2point1()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile006 = startProfile(sketch001, at = [11.05, 2.37])`
|
|
)
|
|
await crnRect2point2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`|> angledLine(angle = 0, length = 5.49, tag = $rectangleSegmentA002)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 4.14)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`.replaceAll('\n', '')
|
|
)
|
|
})
|
|
|
|
await test.step('create two center rectangles in a row without unequip', async () => {
|
|
await toolbar.selectCenterRectangle()
|
|
|
|
await cntrRect1point1()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile007 = startProfile(sketch001, at = [8.41, -9.29])`
|
|
)
|
|
await cntrRect1point2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`|> angledLine(angle = 0, length = 7.06, tag = $rectangleSegmentA003)
|
|
|> angledLine(angle = segAng(rectangleSegmentA003) + 90, length = 4.34)
|
|
|> angledLine(angle = segAng(rectangleSegmentA003), length = -segLen(rectangleSegmentA003))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`.replaceAll('\n', '')
|
|
)
|
|
await page.waitForTimeout(300)
|
|
|
|
await cntrRect2point1()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile008 = startProfile(sketch001, at = [19.33, -5.56])`
|
|
)
|
|
await cntrRect2point2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`|> angledLine(angle = 0, length = 3.12, tag = $rectangleSegmentA004)
|
|
|> angledLine(angle = segAng(rectangleSegmentA004) + 90, length = 6.24)
|
|
|> angledLine(angle = segAng(rectangleSegmentA004), length = -segLen(rectangleSegmentA004))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`.replaceAll('\n', '')
|
|
)
|
|
})
|
|
|
|
await test.step('create two circle-three-points in a row without an unequip', async () => {
|
|
await toolbar.selectCircleThreePoint()
|
|
|
|
await circle3Point1p1Move()
|
|
await circle3Point1p1()
|
|
await page.waitForTimeout(300)
|
|
await circle3Point1p2Move()
|
|
await circle3Point1p2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile009 = circleThreePoint(
|
|
sketch001,
|
|
p1 = [8.82, -14.58],
|
|
p2 = [11.73, -6.1],
|
|
p3 = [11.83, -6],
|
|
)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
|
|
await circle3Point1p3Move()
|
|
await circle3Point1p3()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile009 = circleThreePoint(
|
|
sketch001,
|
|
p1 = [8.82, -14.58],
|
|
p2 = [11.73, -6.1],
|
|
p3 = [15.87, -11.12],
|
|
)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
|
|
await circle3Point2p1Move()
|
|
await circle3Point2p1()
|
|
await page.waitForTimeout(300)
|
|
await circle3Point2p2Move()
|
|
await circle3Point2p2()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile010 = circleThreePoint(
|
|
sketch001,
|
|
p1 = [25.5, -6.85],
|
|
p2 = [25.43, -1.97],
|
|
p3 = [25.53, -1.87],
|
|
)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
|
|
await circle3Point2p3Move()
|
|
await circle3Point2p3()
|
|
await page.waitForTimeout(300)
|
|
await editor.expectEditor.toContain(
|
|
`profile010 = circleThreePoint(
|
|
sketch001,
|
|
p1 = [25.5, -6.85],
|
|
p2 = [25.43, -1.97],
|
|
p3 = [22.65, -3.8],
|
|
)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
|
|
await test.step('create three-point arcs in a row without an unequip', async () => {
|
|
// Define points for the first three-point arc
|
|
const [arc1Point1, arc1Point1Move] = scene.makeMouseHelpers(700, 397)
|
|
const [arc1Point2, arc1Point2Move] = scene.makeMouseHelpers(724, 346)
|
|
const [arc1Point3, arc1Point3Move] = scene.makeMouseHelpers(785, 415)
|
|
|
|
// Define points for the second three-point arc
|
|
const [arc2Point1, arc2Point1Move] = scene.makeMouseHelpers(792, 225)
|
|
const [arc2Point2, arc2Point2Move] = scene.makeMouseHelpers(820, 207)
|
|
const [arc2Point3, arc2Point3Move] = scene.makeMouseHelpers(905, 229)
|
|
|
|
// Select the three-point arc tool
|
|
await toolbar.selectThreePointArc()
|
|
|
|
// Create the first three-point arc
|
|
await arc1Point1Move()
|
|
await arc1Point1()
|
|
await page.waitForTimeout(300)
|
|
await arc1Point2Move()
|
|
await arc1Point2()
|
|
await page.waitForTimeout(300)
|
|
await arc1Point3Move()
|
|
await arc1Point3()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Verify the first three-point arc was created correctly
|
|
await editor.expectEditor.toContain(
|
|
`profile011 = startProfile(sketch001, at = [13.56, -9.97])
|
|
|> arc(interiorAbsolute = [15.19, -6.51], endAbsolute = [19.33, -11.19])`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
|
|
// Create the second three-point arc
|
|
await arc2Point1Move()
|
|
await arc2Point1()
|
|
await page.waitForTimeout(300)
|
|
await arc2Point2Move()
|
|
await arc2Point2()
|
|
await page.waitForTimeout(300)
|
|
await arc2Point3Move()
|
|
await arc2Point3()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Verify the second three-point arc was created correctly
|
|
await editor.expectEditor.toContain(
|
|
` |> arc(interiorAbsolute = [19.8, 1.7], endAbsolute = [21.7, 2.92])
|
|
|> arc(interiorAbsolute = [27.47, 1.42], endAbsolute = [27.57, 1.52])`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
|
|
await test.step('double check that three-point arc can be unequipped', async () => {
|
|
// this was tested implicitly for other tools, but not for three-point arc since it's last
|
|
await page.waitForTimeout(300)
|
|
await expect
|
|
.poll(async () => {
|
|
await toolbar.lineBtn.click()
|
|
return toolbar.lineBtn.getAttribute('aria-pressed')
|
|
})
|
|
.toBe('true')
|
|
})
|
|
})
|
|
|
|
test('Can edit a sketch with multiple profiles, dragging segments to edit them, and adding one new profile', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [6.24, 4.54])
|
|
|> line(end = [-0.41, 6.99])
|
|
|> line(end = [8.61, 0.74])
|
|
|> line(end = [10.99, -5.22])
|
|
profile002 = startProfile(sketch001, at = [11.19, 5.02])
|
|
|> angledLine(angle = 0, length = 10.78, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 4.14)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
|
|
profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07], p3 = [18.75, -4.41])
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
// The text to prompt popover gets in the way of pointOnSegment click otherwise
|
|
const moveToClearToolBarPopover = scene.makeMouseHelpers(590, 500)[1]
|
|
|
|
const [pointOnSegment] = scene.makeMouseHelpers(590, 141)
|
|
const [profileEnd] = scene.makeMouseHelpers(970, 105)
|
|
const profileEndMv = scene.makeMouseHelpers(951, 101)[1]
|
|
const [newProfileEnd] = scene.makeMouseHelpers(764, 104)
|
|
const dragSegmentTo = scene.makeMouseHelpers(850, 104)[1]
|
|
|
|
const rectHandle = scene.makeMouseHelpers(901, 150)[1]
|
|
const rectDragTo = scene.makeMouseHelpers(901, 180)[1]
|
|
|
|
const circleEdge = scene.makeMouseHelpers(691, 331)[1]
|
|
const dragCircleTo = scene.makeMouseHelpers(720, 331)[1]
|
|
|
|
const [rectStart] = scene.makeMouseHelpers(794, 322)
|
|
const [rectEnd] = scene.makeMouseHelpers(757, 395)
|
|
|
|
const [circ3PStart] = scene.makeMouseHelpers(854, 332)
|
|
const [circ3PEnd] = scene.makeMouseHelpers(870, 275)
|
|
|
|
await test.step('enter sketch and setup', async () => {
|
|
await moveToClearToolBarPopover()
|
|
await page.waitForTimeout(1000)
|
|
await pointOnSegment({ shouldDbClick: true })
|
|
await page.waitForTimeout(2000)
|
|
|
|
await toolbar.lineBtn.click()
|
|
await page.waitForTimeout(100)
|
|
})
|
|
|
|
await test.step('extend existing profile', async () => {
|
|
await profileEnd()
|
|
await page.waitForTimeout(100)
|
|
await newProfileEnd()
|
|
await editor.expectEditor.toContain(`|> line(end = [-11.35, 0.73])`)
|
|
await toolbar.lineBtn.click()
|
|
await page.waitForTimeout(100)
|
|
})
|
|
|
|
await test.step('edit existing profile', async () => {
|
|
await profileEndMv()
|
|
await page.mouse.down()
|
|
await dragSegmentTo()
|
|
await page.mouse.up()
|
|
await editor.expectEditor.toContain(`line(end = [4.22, -4.49])`)
|
|
})
|
|
|
|
await test.step('edit existing rect', async () => {
|
|
await rectHandle()
|
|
await page.mouse.down()
|
|
await rectDragTo()
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(200)
|
|
await editor.expectEditor.toContain(
|
|
`angledLine(angle = -7, length = 10.27, tag = $rectangleSegmentA001)`
|
|
)
|
|
})
|
|
|
|
await test.step('edit existing circle', async () => {
|
|
await circleEdge()
|
|
await page.mouse.down()
|
|
await dragCircleTo()
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(200)
|
|
await editor.expectEditor.toContain(
|
|
`profile003 = circle(sketch001, center = [6.92, -4.2], radius = 4.81)`
|
|
)
|
|
})
|
|
|
|
await test.step('edit existing circle three point', async () => {
|
|
await circ3PStart()
|
|
await page.mouse.down()
|
|
await circ3PEnd()
|
|
await page.mouse.up()
|
|
await page.waitForTimeout(200)
|
|
await editor.expectEditor.toContain(
|
|
`profile004 = circleThreePoint(
|
|
sketch001,
|
|
p1 = [13.44, -6.8],
|
|
p2 = [13.39, -2.07],
|
|
p3 = [19.73, -1.33],
|
|
)`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
})
|
|
|
|
await test.step('add new profile', async () => {
|
|
await toolbar.rectangleBtn.click()
|
|
await page.waitForTimeout(200)
|
|
await rectStart()
|
|
await editor.expectEditor.toContain(
|
|
`profile005 = startProfile(sketch001, at = [15.68, -3.84])`
|
|
)
|
|
await page.waitForTimeout(100)
|
|
await rectEnd()
|
|
await editor.expectEditor.toContain(
|
|
`|> angledLine(angle = 180, length = 1.97, tag = $rectangleSegmentA002)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002) + 90, length = 3.89)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`.replaceAll('\n', '')
|
|
)
|
|
})
|
|
})
|
|
test('Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added', async ({
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
cmdBar,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [6.24, 4.54])
|
|
|> line(end = [-0.41, 6.99])
|
|
|> line(end = [8.61, 0.74])
|
|
|> line(end = [10.99, -5.22])
|
|
profile002 = startProfile(sketch001, at = [11.19, 5.02])
|
|
|> angledLine(angle = 0, length = 10.78, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 4.14)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
// The text to prompt popover gets in the way of pointOnSegment click otherwise
|
|
const moveToClearToolBarPopover = scene.makeMouseHelpers(590, 500)[1]
|
|
|
|
const [pointOnSegment] = scene.makeMouseHelpers(590, 141)
|
|
const [segment1Click] = scene.makeMouseHelpers(616, 131)
|
|
const sketchIsDrawnProperly = async () => {
|
|
await test.step('check the sketch is still drawn properly', async () => {
|
|
await page.waitForTimeout(200)
|
|
await scene.expectPixelColor([255, 255, 255], { x: 617, y: 163 }, 15)
|
|
await scene.expectPixelColor([255, 255, 255], { x: 629, y: 331 }, 15)
|
|
})
|
|
}
|
|
|
|
await test.step('enter sketch and setup', async () => {
|
|
await moveToClearToolBarPopover()
|
|
await pointOnSegment({ shouldDbClick: true })
|
|
await page.waitForTimeout(600)
|
|
})
|
|
|
|
await test.step('select and delete code for a profile', async () => {})
|
|
await page.getByText('close()').click()
|
|
await page.keyboard.down('Shift')
|
|
for (let i = 0; i < 11; i++) {
|
|
await page.keyboard.press('ArrowUp')
|
|
}
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.up('Shift')
|
|
await page.keyboard.press('Backspace')
|
|
|
|
await sketchIsDrawnProperly()
|
|
|
|
await test.step('add random new var between profiles', async () => {
|
|
await page.keyboard.type('myVar = 5')
|
|
await page.keyboard.press('Enter')
|
|
// If this timeout isn't long enough, the test breaks.
|
|
// TODO: fix https://github.com/KittyCAD/modeling-app/issues/5437
|
|
await page.waitForTimeout(3_000)
|
|
})
|
|
|
|
await sketchIsDrawnProperly()
|
|
|
|
await test.step('Adding a constraint with a variable, and than ctrl-z-ing which will remove the variable again does not break sketch mode', async () => {
|
|
await expect(async () => {
|
|
await segment1Click()
|
|
await editor.expectState({
|
|
diagnostics: [],
|
|
activeLines: ['|>line(end = [-0.41,6.99])'],
|
|
highlightedCode: 'line(end = [-0.41,6.99])',
|
|
})
|
|
}).toPass({ timeout: 30_000, intervals: [1500] })
|
|
|
|
await toolbar.lengthConstraintBtn.click()
|
|
await cmdBar.progressCmdBar()
|
|
await editor.expectEditor.toContain('length001 = 7')
|
|
|
|
// wait for execute defer
|
|
await page.waitForTimeout(600)
|
|
await sketchIsDrawnProperly()
|
|
|
|
await page.keyboard.down('Meta')
|
|
await page.keyboard.press('KeyZ')
|
|
await page.keyboard.up('Meta')
|
|
|
|
await editor.expectEditor.not.toContain('length001 = 7')
|
|
await sketchIsDrawnProperly()
|
|
})
|
|
})
|
|
|
|
test('can enter sketch when there is an extrude', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
page,
|
|
cmdBar,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [-63.43, 193.08])
|
|
|> line(end = [168.52, 149.87])
|
|
|> line(end = [190.29, -39.18])
|
|
|> tangentialArc(endAbsolute = [319.63, 129.65])
|
|
|> line(end = [-217.65, -21.76])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = startProfile(sketch001, at = [16.79, 38.24])
|
|
|> angledLine(angle = 0, length = 182.82, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 105.71)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile004 = circle(
|
|
sketch001,
|
|
center = [280.45, 47.57],
|
|
radius = 55.26
|
|
)
|
|
extrude002 = extrude(profile001, length = 50)
|
|
extrude001 = extrude(profile003, length = 5)
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [pointOnSegment] = scene.makeMouseHelpers(574, 207)
|
|
|
|
await pointOnSegment()
|
|
await toolbar.editSketch()
|
|
// wait for engine animation
|
|
await page.waitForTimeout(600)
|
|
|
|
await test.step('check the sketch is still drawn properly', async () => {
|
|
await Promise.all([
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 596, y: 165 }, 15),
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 641, y: 220 }, 15),
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 763, y: 214 }, 15),
|
|
])
|
|
})
|
|
})
|
|
test('exit new sketch without drawing anything should not be a problem', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
cmdBar,
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
myVar = 5`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
|
|
await page.waitForTimeout(5000)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
|
|
|
await toolbar.startSketchPlaneSelection()
|
|
await selectXZPlane()
|
|
// timeout wait for engine animation is unavoidable
|
|
await page.waitForTimeout(600)
|
|
|
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn(XZ)`)
|
|
await toolbar.exitSketch()
|
|
|
|
await editor.expectEditor.not.toContain(`sketch001 = startSketchOn(XZ)`)
|
|
|
|
await test.step("still renders code, hasn't got into a weird state", async () => {
|
|
await editor.replaceCode(
|
|
'myVar = 5',
|
|
`myVar = 5
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = circle(
|
|
sketch001,
|
|
center = [12.41, 3.87],
|
|
radius = myVar
|
|
)`
|
|
)
|
|
|
|
await scene.settled(cmdBar)
|
|
|
|
await scene.expectPixelColor([255, 255, 255], { x: 633, y: 211 }, 15)
|
|
})
|
|
})
|
|
test('A sketch with only "startProfileAt" and no segments should still be able to be continued', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [85.19, 338.59])
|
|
|> line(end = [213.3, -94.52])
|
|
|> line(end = [-230.09, -55.34])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
sketch002 = startSketchOn(XY)
|
|
profile002 = startProfile(sketch002, at = [85.81, 52.55])
|
|
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [startProfileAt] = scene.makeMouseHelpers(606, 184)
|
|
const [nextPoint] = scene.makeMouseHelpers(763, 130)
|
|
await page.getByText('startProfile(sketch002, at = [85.81, 52.55])').click()
|
|
await toolbar.editSketch(1)
|
|
// timeout wait for engine animation is unavoidable
|
|
await page.waitForTimeout(600)
|
|
|
|
// equip line tool
|
|
await toolbar.lineBtn.click()
|
|
await page.waitForTimeout(100)
|
|
await startProfileAt()
|
|
await page.waitForTimeout(100)
|
|
await nextPoint()
|
|
await editor.expectEditor.toContain(`|> line(end = [126.05, 44.12])`)
|
|
})
|
|
test('old style sketch all in one pipe (with extrude) will break up to allow users to add a new profile to the same sketch', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
thePart = startSketchOn(XZ)
|
|
|> startProfile(at = [7.53, 10.51])
|
|
|> line(end = [12.54, 1.83])
|
|
|> line(end = [6.65, -6.91])
|
|
|> line(end = [-6.31, -8.69])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(thePart, length = 75)
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [objClick] = scene.makeMouseHelpers(565, 343)
|
|
const [profilePoint1] = scene.makeMouseHelpers(609, 289)
|
|
const [profilePoint2] = scene.makeMouseHelpers(714, 389)
|
|
|
|
await test.step('enter sketch and setup', async () => {
|
|
await objClick()
|
|
await toolbar.editSketch()
|
|
// timeout wait for engine animation is unavoidable
|
|
await page.waitForTimeout(600)
|
|
})
|
|
|
|
await test.step('expect code to match initial conditions still', async () => {
|
|
await editor.expectEditor.toContain(
|
|
`thePart = startSketchOn(XZ) |> startProfile(at = [7.53, 10.51])`
|
|
)
|
|
})
|
|
|
|
await test.step('equiping the line tool should break up the pipe expression', async () => {
|
|
await toolbar.lineBtn.click()
|
|
await editor.expectEditor.toContain(
|
|
`sketch001 = startSketchOn(XZ)thePart = startProfile(sketch001, at = [7.53, 10.51])`
|
|
)
|
|
})
|
|
|
|
await test.step('can continue on to add a new profile to this sketch', async () => {
|
|
await profilePoint1()
|
|
await editor.expectEditor.toContain(
|
|
`profile001 = startProfile(sketch001, at = [19.69, -7.05])`
|
|
)
|
|
await profilePoint2()
|
|
await editor.expectEditor.toContain(`|> line(end = [18.97, -18.06])`)
|
|
})
|
|
})
|
|
test('Can enter sketch on sketch of wall and cap for segment, solid2d, extrude-wall, extrude-cap selections', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
cmdBar,
|
|
}) => {
|
|
// TODO this test should include a test for selecting revolve walls and caps
|
|
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [6.71, -3.66])
|
|
|> line(end = [2.65, 9.02], tag = $seg02)
|
|
|> line(end = [3.73, -9.36], tag = $seg01)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(profile001, length = 20)
|
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
|
profile002 = startProfile(sketch002, at = [0.75, 13.46])
|
|
|> line(end = [4.52, 3.79])
|
|
|> line(end = [5.98, -2.81])
|
|
profile003 = startProfile(sketch002, at = [3.19, 13.3])
|
|
|> angledLine(angle = 0, length = 6.64, tag = $rectangleSegmentA001)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 2.81)
|
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile004 = startProfile(sketch002, at = [3.15, 9.39])
|
|
|> xLine(length = 6.92)
|
|
|> line(end = [-7.41, -2.85])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile005 = circle(sketch002, center = [5.15, 4.34], radius = 1.66)
|
|
profile006 = startProfile(sketch002, at = [9.65, 3.82])
|
|
|> line(end = [2.38, 5.62])
|
|
|> line(end = [2.13, -5.57])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
revolve001 = revolve(
|
|
profile004,
|
|
angle = 45,
|
|
axis = getNextAdjacentEdge(seg01)
|
|
)
|
|
extrude002 = extrude(profile006, length = 4)
|
|
sketch003 = startSketchOn(-XZ)
|
|
profile007 = startProfile(sketch003, at = [4.8, 7.55])
|
|
|> line(end = [7.39, 2.58])
|
|
|> line(end = [7.02, -2.85])
|
|
profile008 = startProfile(sketch003, at = [5.54, 5.49])
|
|
|> line(end = [6.34, 2.64])
|
|
|> line(end = [6.33, -2.96])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile009 = startProfile(sketch003, at = [5.23, 1.95])
|
|
|> line(end = [6.8, 2.17])
|
|
|> line(end = [7.34, -2.75])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile010 = circle(
|
|
sketch003,
|
|
center = [7.18, -2.11],
|
|
radius = 2.67
|
|
)
|
|
profile011 = startProfile(sketch003, at = [5.07, -6.39])
|
|
|> angledLine(angle = 0, length = 4.54, tag = $rectangleSegmentA002)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 4.17)
|
|
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude003 = extrude(profile011, length = 2.5)
|
|
// TODO this breaks the test,
|
|
// revolve002 = revolve(profile008, angle = 45, axis = seg02)
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const camPositionForSelectingSketchOnWallProfiles = () =>
|
|
scene.moveCameraTo(
|
|
{ x: 834, y: -680, z: 534 },
|
|
{ x: -54, y: -476, z: 148 }
|
|
)
|
|
const wallSelectionOptions = [
|
|
{
|
|
title: 'select wall segment',
|
|
selectClick: scene.makeMouseHelpers(598, 211)[0],
|
|
},
|
|
{
|
|
title: 'select wall solid 2d',
|
|
selectClick: scene.makeMouseHelpers(677, 236)[0],
|
|
},
|
|
{
|
|
title: 'select wall circle',
|
|
selectClick: scene.makeMouseHelpers(811, 247)[0],
|
|
},
|
|
{
|
|
title: 'select wall extrude wall',
|
|
selectClick: scene.makeMouseHelpers(793, 136)[0],
|
|
},
|
|
{
|
|
title: 'select wall extrude cap',
|
|
selectClick: scene.makeMouseHelpers(836, 103)[0],
|
|
},
|
|
] as const
|
|
|
|
const verifyWallProfilesAreDrawn = async () =>
|
|
test.step('verify wall profiles are drawn', async () => {
|
|
await Promise.all([
|
|
// open polygon
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 599, y: 168 }, 15),
|
|
// closed polygon
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 656, y: 171 }, 15),
|
|
// revolved profile
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 655, y: 264 }, 15),
|
|
// extruded profile
|
|
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 808, y: 396 }, 15),
|
|
// circle (When entering via the circle, it's selected and therefore blue)
|
|
scene.expectPixelColor(
|
|
[TEST_COLORS.WHITE, TEST_COLORS.BLUE],
|
|
{ x: 742, y: 386 },
|
|
15
|
|
),
|
|
])
|
|
})
|
|
|
|
await test.step('select wall profiles', async () => {
|
|
for (const { title, selectClick } of wallSelectionOptions) {
|
|
await test.step(title, async () => {
|
|
await camPositionForSelectingSketchOnWallProfiles()
|
|
await selectClick()
|
|
await toolbar.editSketch(1)
|
|
await page.waitForTimeout(600)
|
|
await verifyWallProfilesAreDrawn()
|
|
await toolbar.exitSketchBtn.click()
|
|
await page.waitForTimeout(100)
|
|
})
|
|
}
|
|
})
|
|
|
|
/* FIXME: the cap part of this test is insanely flaky, and I'm not sure
|
|
* why.
|
|
* await test.step('select cap profiles', async () => {
|
|
for (const { title, selectClick } of capSelectionOptions) {
|
|
await test.step(title, async () => {
|
|
await camPositionForSelectingSketchOnCapProfiles()
|
|
await page.waitForTimeout(100)
|
|
await selectClick()
|
|
await toolbar.editSketch()
|
|
await page.waitForTimeout(600)
|
|
await verifyCapProfilesAreDrawn()
|
|
await toolbar.exitSketchBtn.click()
|
|
await page.waitForTimeout(100)
|
|
})
|
|
}
|
|
}) */
|
|
})
|
|
test('Can enter sketch loft edges, base and continue sketch', async ({
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`@settings(defaultLengthUnit = in)
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [34, 42.66])
|
|
|> line(end = [102.65, 151.99])
|
|
|> line(end = [76, -138.66])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
plane001 = offsetPlane(XZ, offset = 50)
|
|
sketch002 = startSketchOn(plane001)
|
|
profile002 = startProfile(sketch002, at = [39.43, 172.21])
|
|
|> xLine(length = 183.99)
|
|
|> line(end = [-77.95, -145.93])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
|
|
loft([profile001, profile002])
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const [baseProfileEdgeClick] = scene.makeMouseHelpers(621, 292)
|
|
|
|
const [rect1Crn1] = scene.makeMouseHelpers(592, 283)
|
|
const [rect1Crn2] = scene.makeMouseHelpers(797, 268)
|
|
|
|
await baseProfileEdgeClick()
|
|
await toolbar.editSketch()
|
|
await page.waitForTimeout(600)
|
|
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 562, y: 172 }, 15)
|
|
|
|
await toolbar.rectangleBtn.click()
|
|
await page.waitForTimeout(100)
|
|
await rect1Crn1()
|
|
await editor.expectEditor.toContain(
|
|
`profile003 = startProfile(sketch001, at = [50.72, -18.19])`
|
|
)
|
|
await rect1Crn2()
|
|
await editor.expectEditor.toContain(
|
|
`angledLine(angle = 0, length = 113.01, tag = $rectangleSegmentA001)`
|
|
)
|
|
})
|
|
test('Can enter sketch loft edges offsetPlane and continue sketch', async ({
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [34, 42.66])
|
|
|> line(end = [102.65, 151.99])
|
|
|> line(end = [76, -138.66])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
plane001 = offsetPlane(XZ, offset = 50)
|
|
sketch002 = startSketchOn(plane001)
|
|
profile002 = startProfile(sketch002, at = [39.43, 172.21])
|
|
|> xLine(length = 183.99)
|
|
|> line(end = [-77.95, -145.93])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
|
|
loft([profile001, profile002])
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
const topProfileEdgeClickCoords = { x: 602, y: 185 } as const
|
|
const [topProfileEdgeClick] = scene.makeMouseHelpers(
|
|
topProfileEdgeClickCoords.x,
|
|
topProfileEdgeClickCoords.y
|
|
)
|
|
const [sideProfileEdgeClick] = scene.makeMouseHelpers(788, 188)
|
|
|
|
const [rect1Crn1] = scene.makeMouseHelpers(592, 283)
|
|
const [rect1Crn2] = scene.makeMouseHelpers(797, 268)
|
|
|
|
await scene.moveCameraTo(
|
|
{ x: 8171, y: -7740, z: 1624 },
|
|
{ x: 3302, y: -627, z: 2892 }
|
|
)
|
|
|
|
await topProfileEdgeClick()
|
|
await page.waitForTimeout(300)
|
|
await toolbar.editSketch()
|
|
await page.waitForTimeout(600)
|
|
await sideProfileEdgeClick()
|
|
await page.waitForTimeout(300)
|
|
await scene.expectPixelColor(TEST_COLORS.BLUE, { x: 788, y: 188 }, 15)
|
|
|
|
await toolbar.rectangleBtn.click()
|
|
await page.waitForTimeout(100)
|
|
await rect1Crn1()
|
|
await editor.expectEditor.toContain(
|
|
`profile003 = startProfile(plane001, at = [47.76, -17.13])`
|
|
)
|
|
await rect1Crn2()
|
|
await editor.expectEditor.toContain(
|
|
`angledLine(angle = 0, length = 106.42], tag = $rectangleSegmentA001)`
|
|
)
|
|
await page.waitForTimeout(100)
|
|
})
|
|
})
|
|
|
|
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
|
|
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
|
|
test(`Can select a line that reformmed after entering sketch mode`, async ({
|
|
context,
|
|
page,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
homePage,
|
|
cmdBar,
|
|
}) => {
|
|
// We seed the scene with a single offset plane
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [0, 0])
|
|
|> line(end = [3.14, 3.14])
|
|
|> arc(
|
|
interiorAbsolute = [1, 2],
|
|
endAbsolute = [4, 2]
|
|
)`
|
|
)
|
|
})
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
const formattedArc = `arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])`
|
|
await test.step(`format the code`, async () => {
|
|
// doesn't contain condensed version
|
|
await editor.expectEditor.not.toContain(formattedArc)
|
|
// click the code to enter sketch mode
|
|
await page.getByText(`arc`).click()
|
|
// Format the code.
|
|
await page.locator('#code-pane button:first-child').click()
|
|
await page.locator('button:has-text("Format code")').click()
|
|
})
|
|
|
|
await test.step(`Ensure the code reformatted`, async () => {
|
|
await editor.expectEditor.toContain(formattedArc)
|
|
})
|
|
|
|
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
|
|
await test.step('Ensure we can hover the arc', async () => {
|
|
await arcHover()
|
|
|
|
// Check that the code is highlighted
|
|
await editor.expectState({
|
|
activeLines: ['sketch001=startSketchOn(XZ)'],
|
|
diagnostics: [],
|
|
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
|
|
})
|
|
})
|
|
|
|
await test.step('reset the selection', async () => {
|
|
// Move the mouse out of the way
|
|
await page.mouse.move(655, 337)
|
|
|
|
await editor.expectState({
|
|
activeLines: ['sketch001=startSketchOn(XZ)'],
|
|
diagnostics: [],
|
|
highlightedCode: '',
|
|
})
|
|
})
|
|
|
|
await test.step('Ensure we can click the arc', async () => {
|
|
await arcClick()
|
|
|
|
// Check that the code is highlighted
|
|
await editor.expectState({
|
|
activeLines: [],
|
|
diagnostics: [],
|
|
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4372
|
|
test.describe('Redirecting to home page and back to the original file should clear sketch DOM elements', () => {
|
|
test('Can redirect to home page and back to original file and have a cleared DOM', async ({
|
|
context,
|
|
page,
|
|
scene,
|
|
toolbar,
|
|
editor,
|
|
homePage,
|
|
cmdBar,
|
|
}) => {
|
|
// We seed the scene with a single offset plane
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
` sketch001 = startSketchOn(XZ)
|
|
|> startProfile(at = [256.85, 14.41])
|
|
|> line(endAbsolute = [0, 211.07])
|
|
`
|
|
)
|
|
})
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
|
|
const [objClick] = scene.makeMouseHelpers(634, 274)
|
|
await objClick()
|
|
|
|
// Enter sketch mode
|
|
await toolbar.editSketch()
|
|
|
|
await expect(page.getByText('323.49')).toBeVisible()
|
|
|
|
// Open navigation side bar
|
|
await page.getByTestId('project-sidebar-toggle').click()
|
|
const goToHome = page.getByRole('button', {
|
|
name: 'Go to Home',
|
|
})
|
|
|
|
await goToHome.click()
|
|
await homePage.openProject('testDefault')
|
|
await expect(page.getByText('323.49')).not.toBeVisible()
|
|
})
|
|
|
|
test('Straight line snapping to previous tangent', async ({
|
|
page,
|
|
homePage,
|
|
toolbar,
|
|
scene,
|
|
cmdBar,
|
|
context,
|
|
editor,
|
|
}) => {
|
|
await context.addInitScript(() => {
|
|
localStorage.setItem('persistCode', `@settings(defaultLengthUnit = mm)`)
|
|
})
|
|
|
|
const viewportSize = { width: 1200, height: 900 }
|
|
await page.setBodyDimensions(viewportSize)
|
|
await homePage.goToModelingScene()
|
|
|
|
// wait until scene is ready to be interacted with
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
|
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
|
|
// select an axis plane
|
|
await page.mouse.click(700, 200)
|
|
|
|
// Needed as we don't yet have a way to get a signal from the engine that the camera has animated to the sketch plane
|
|
await page.waitForTimeout(3000)
|
|
|
|
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
|
|
const { click00r } = getMovementUtils({ center, page })
|
|
|
|
// Draw line
|
|
await click00r(0, 0)
|
|
await click00r(200, -200)
|
|
|
|
// Draw arc
|
|
await toolbar.selectTangentialArc()
|
|
await click00r(0, 0)
|
|
await click00r(100, 100)
|
|
|
|
// Switch back to line
|
|
await toolbar.selectLine()
|
|
await click00r(0, 0)
|
|
await click00r(-100, 100)
|
|
|
|
// Draw a 3 point arc
|
|
await toolbar.selectThreePointArc()
|
|
await click00r(0, 0)
|
|
await click00r(0, 100)
|
|
await click00r(100, 0)
|
|
|
|
// draw a line to opposite tangent direction of previous arc
|
|
await toolbar.selectLine()
|
|
await click00r(0, 0)
|
|
await click00r(-200, 200)
|
|
|
|
await editor.expectEditor.toContain(
|
|
`@settings(defaultLengthUnit = mm)
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [0, 0])
|
|
|> line(end = [191.39, 191.39])
|
|
|> tangentialArc(end = [95.69, -95.7], tag = $seg01)
|
|
|> angledLine(angle = tangentToEnd(seg01), length = 135.34)
|
|
|> arc(interiorAbsolute = [191.39, -95.69], endAbsolute = [287.08, -95.69], tag = $seg02)
|
|
|> angledLine(angle = tangentToEnd(seg02) + turns::HALF_TURN, length = 270.67)
|
|
`.replaceAll('\n', '')
|
|
)
|
|
})
|
|
})
|
|
|
|
test.describe('manual edits during sketch mode', () => {
|
|
test('Can edit sketch through feature tree with variable modifications', async ({
|
|
page,
|
|
context,
|
|
homePage,
|
|
scene,
|
|
editor,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
const initialCode = `myVar1 = 5
|
|
myVar2 = 6
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [106.68, 89.77])
|
|
|> line(end = [132.34, 157.8])
|
|
|> line(end = [67.65, -460.55], tag = $seg01)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(profile001, length = 500)
|
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
|
profile002 = startProfile(sketch002, at = [83.39, 329.15])
|
|
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|
|
|> angledLine(length = 156.54, angle = -28)
|
|
|> angledLine(
|
|
angle = -151,
|
|
length = 116.27,
|
|
)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
|
|> line(end = [103.55, 33.32])
|
|
|> line(end = [48.8, -153.54])`
|
|
|
|
await context.addInitScript((initialCode) => {
|
|
localStorage.setItem('persistCode', initialCode)
|
|
}, initialCode)
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
|
|
await test.step('Open feature tree and edit second sketch', async () => {
|
|
await toolbar.openFeatureTreePane()
|
|
const sketchButton = await toolbar.getFeatureTreeOperation('Sketch', 1)
|
|
await sketchButton.dblclick()
|
|
await page.waitForTimeout(700) // Wait for engine animation
|
|
})
|
|
|
|
await test.step('Add new variable and wait for re-execution', async () => {
|
|
await page.waitForTimeout(500) // wait for deferred execution
|
|
await editor.replaceCode('myVar2 = 6', 'myVar2 = 6\nmyVar3 = 7')
|
|
await page.waitForTimeout(2000) // wait for deferred execution
|
|
})
|
|
|
|
const handle1Location = { x: 843, y: 235 }
|
|
await test.step('Edit sketch by dragging handle', async () => {
|
|
await page.waitForTimeout(500)
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
await editor.expectEditor.toContain('length = 156.54, angle = -28')
|
|
await page.mouse.move(handle1Location.x, handle1Location.y)
|
|
await page.mouse.down()
|
|
await page.mouse.move(
|
|
handle1Location.x + 50,
|
|
handle1Location.y + 50,
|
|
{
|
|
steps: 5,
|
|
}
|
|
)
|
|
await page.mouse.up()
|
|
await editor.expectEditor.toContain('length = 231.59, angle = -34')
|
|
return true
|
|
},
|
|
{ timeout: 10_000 }
|
|
)
|
|
.toBeTruthy()
|
|
})
|
|
|
|
await test.step('Delete variables and wait for re-execution', async () => {
|
|
await page.waitForTimeout(500)
|
|
await editor.replaceCode('myVar3 = 7', '')
|
|
await page.waitForTimeout(50)
|
|
await editor.replaceCode('myVar2 = 6', '')
|
|
await page.waitForTimeout(2000) // Wait for deferred execution
|
|
})
|
|
|
|
const handle2Location = { x: 872, y: 273 }
|
|
await test.step('Edit sketch again', async () => {
|
|
await editor.expectEditor.toContain('length = 231.59, angle = -34')
|
|
await page.waitForTimeout(500)
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
await page.mouse.move(handle2Location.x, handle2Location.y)
|
|
await page.mouse.down()
|
|
await page.mouse.move(handle2Location.x, handle2Location.y - 50, {
|
|
steps: 5,
|
|
})
|
|
await page.mouse.up()
|
|
await editor.expectEditor.toContain('length = 167.36, angle = -14')
|
|
return true
|
|
},
|
|
{ timeout: 10_000 }
|
|
)
|
|
.toBeTruthy()
|
|
})
|
|
|
|
await test.step('add whole other sketch before current sketch', async () => {
|
|
await page.waitForTimeout(500)
|
|
await editor.replaceCode(
|
|
`myVar1 = 5`,
|
|
`myVar1 = 5
|
|
sketch003 = startSketchOn(XY)
|
|
profile004 = circle(sketch003, center = [143.91, 136.89], radius = 71.63)`
|
|
)
|
|
await page.waitForTimeout(2000) // Wait for deferred execution
|
|
})
|
|
|
|
const handle3Location = { x: 844, y: 212 }
|
|
await test.step('edit sketch again', async () => {
|
|
await page.waitForTimeout(500) // Wait for deferred execution
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
await editor.expectEditor.toContain('length = 167.36, angle = -14')
|
|
await page.mouse.move(handle3Location.x, handle3Location.y)
|
|
await page.mouse.down()
|
|
await page.mouse.move(handle3Location.x, handle3Location.y + 110, {
|
|
steps: 5,
|
|
})
|
|
await page.mouse.up()
|
|
await editor.expectEditor.toContain('length = 219.2, angle = -56')
|
|
return true
|
|
},
|
|
{ timeout: 10_000 }
|
|
)
|
|
.toBeTruthy()
|
|
})
|
|
|
|
// exit sketch and assert whole code
|
|
await test.step('Exit sketch and assert code', async () => {
|
|
await toolbar.exitSketch()
|
|
await editor.expectEditor.toContain(
|
|
`myVar1 = 5
|
|
sketch003 = startSketchOn(XY)
|
|
profile004 = circle(sketch003, center = [143.91, 136.89], radius = 71.63)
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [106.68, 89.77])
|
|
|> line(end = [132.34, 157.8])
|
|
|> line(end = [67.65, -460.55], tag = $seg01)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(profile001, length = 500)
|
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
|
profile002 = startProfile(sketch002, at = [83.39, 329.15])
|
|
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|
|
|> angledLine(length = 219.2, angle = -56)
|
|
|> angledLine(angle = -151, length = 116.27)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
|
|> line(end = [103.55, 33.32])
|
|
|> line(end = [48.8, -153.54])
|
|
`,
|
|
{ shouldNormalise: true }
|
|
)
|
|
await editor.expectState({
|
|
activeLines: [],
|
|
diagnostics: [],
|
|
highlightedCode: '',
|
|
})
|
|
})
|
|
})
|
|
test('Will exit out of sketch mode for some incompatible edits', async ({
|
|
page,
|
|
context,
|
|
homePage,
|
|
scene,
|
|
editor,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
const initialCode = `myVar1 = 5
|
|
myVar2 = 6
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [106.68, 89.77])
|
|
|> line(end = [132.34, 157.8])
|
|
|> line(end = [67.65, -460.55], tag = $seg01)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(profile001, length = 500)
|
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
|
profile002 = startProfile(sketch002, at = [83.39, 329.15])
|
|
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|
|
|> angledLine(length = 156.54, angle = -28)
|
|
|> angledLine(
|
|
angle = -151,
|
|
length = 116.27,
|
|
)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
|
|> line(end = [103.55, 33.32])
|
|
|> line(end = [48.8, -153.54])`
|
|
|
|
await context.addInitScript((initialCode) => {
|
|
localStorage.setItem('persistCode', initialCode)
|
|
}, initialCode)
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
const expectSketchOriginToBeDrawn = async () => {
|
|
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 672, y: 193 }, 15)
|
|
}
|
|
|
|
await test.step('Open feature tree and edit second sketch', async () => {
|
|
await toolbar.openFeatureTreePane()
|
|
const sketchButton = await toolbar.getFeatureTreeOperation('Sketch', 1)
|
|
await sketchButton.dblclick()
|
|
await page.waitForTimeout(700) // Wait for engine animation
|
|
await expectSketchOriginToBeDrawn()
|
|
})
|
|
|
|
await test.step('rename variable of current sketch, sketch002 to changeSketchNamePartWayThrough', async () => {
|
|
await editor.replaceCode('sketch002', 'changeSketchNamePartWayThrough')
|
|
await page.waitForTimeout(100)
|
|
// three times to rename the declaration and it's use
|
|
await editor.replaceCode('sketch002', 'changeSketchNamePartWayThrough')
|
|
await page.waitForTimeout(100)
|
|
await editor.replaceCode('sketch002', 'changeSketchNamePartWayThrough')
|
|
await expect(
|
|
page.getByText('Unable to maintain sketch mode')
|
|
).toBeVisible()
|
|
})
|
|
})
|
|
test('Will exit out of sketch mode when all code is nuked', async ({
|
|
page,
|
|
context,
|
|
homePage,
|
|
scene,
|
|
editor,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
const initialCode = `myVar1 = 5
|
|
myVar2 = 6
|
|
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [106.68, 89.77])
|
|
|> line(end = [132.34, 157.8])
|
|
|> line(end = [67.65, -460.55], tag = $seg01)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(profile001, length = 500)
|
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
|
profile002 = startProfile(sketch002, at = [83.39, 329.15])
|
|
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|
|
|> angledLine(length = 156.54, angle = -28)
|
|
|> angledLine(
|
|
angle = -151,
|
|
length = 116.27,
|
|
)
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
|
|> line(end = [103.55, 33.32])
|
|
|> line(end = [48.8, -153.54])`
|
|
|
|
await context.addInitScript((initialCode) => {
|
|
localStorage.setItem('persistCode', initialCode)
|
|
}, initialCode)
|
|
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
const expectSketchOriginToBeDrawn = async () => {
|
|
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 672, y: 193 }, 15)
|
|
}
|
|
|
|
await test.step('Open feature tree and edit second sketch', async () => {
|
|
await toolbar.openFeatureTreePane()
|
|
const sketchButton = await toolbar.getFeatureTreeOperation('Sketch', 1)
|
|
await sketchButton.dblclick()
|
|
await page.waitForTimeout(700) // Wait for engine animation
|
|
await expectSketchOriginToBeDrawn()
|
|
})
|
|
|
|
await test.step('clear editor content while in sketch mode', async () => {
|
|
await editor.replaceCode('', '')
|
|
await page.waitForTimeout(100)
|
|
await expect(
|
|
page.getByText('Unable to maintain sketch mode')
|
|
).toBeVisible()
|
|
await scene.expectPixelColorNotToBe(
|
|
TEST_COLORS.WHITE,
|
|
{ x: 672, y: 193 },
|
|
15
|
|
)
|
|
})
|
|
})
|
|
test('empty draft sketch is cleaned up properly', async ({
|
|
scene,
|
|
toolbar,
|
|
cmdBar,
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
// This is the sketch used in the original report, but any sketch would work
|
|
await page.addInitScript(async () => {
|
|
localStorage.setItem(
|
|
'persistCode',
|
|
`yRel002 = 200
|
|
lStraight = -200
|
|
yRel001 = -lStraight
|
|
length001 = lStraight
|
|
sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [-102.72, 237.44])
|
|
|> yLine(length = lStraight)
|
|
|> tangentialArc(endAbsolute = [118.9, 23.57])
|
|
|> line(end = [-17.64, yRel002])
|
|
`
|
|
)
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.connectionEstablished()
|
|
await scene.settled(cmdBar)
|
|
|
|
// Ensure start sketch button is enabled
|
|
await expect(
|
|
page.getByRole('button', { name: 'Start Sketch' })
|
|
).not.toBeDisabled()
|
|
|
|
// Start a new sketch
|
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
|
await toolbar.startSketchPlaneSelection()
|
|
await selectXZPlane()
|
|
await page.waitForTimeout(2000) // wait for engine animation
|
|
|
|
// Switch to a different tool (circle)
|
|
await toolbar.circleBtn.click()
|
|
await expect(toolbar.circleBtn).toHaveAttribute('aria-pressed', 'true')
|
|
|
|
// Exit the empty sketch
|
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
|
|
// Ensure the feature tree now shows only one sketch
|
|
await toolbar.openFeatureTreePane()
|
|
await expect(
|
|
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
|
|
).toHaveCount(1)
|
|
await toolbar.closeFeatureTreePane()
|
|
|
|
// Open the first sketch from the feature tree (the existing sketch)
|
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
|
// timeout is a bit longer because when the bug happened, it did go into sketch mode for a split second, but returned
|
|
// automatically, we want to make sure it stays there.
|
|
await page.waitForTimeout(2000)
|
|
|
|
// Validate we are in sketch mode (Exit Sketch button visible)
|
|
await expect(
|
|
page.getByRole('button', { name: 'Exit Sketch' })
|
|
).toBeVisible()
|
|
})
|
|
|
|
// Ensure feature tree is not showing previous file's content when switching to a file with KCL errors.
|
|
test('Feature tree shows correct sketch count per file', async ({
|
|
context,
|
|
homePage,
|
|
scene,
|
|
toolbar,
|
|
cmdBar,
|
|
page,
|
|
}) => {
|
|
const u = await getUtils(page)
|
|
|
|
// Setup project with files.
|
|
const GOOD_KCL = `sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [220.81, 253.8])
|
|
|> line(end = [132.84, -151.31])
|
|
|> line(end = [25.51, 167.15])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
sketch002 = startSketchOn(XZ)
|
|
profile002 = startProfile(sketch002, at = [158.35, -70.82])
|
|
|> line(end = [73.9, -152.19])
|
|
|> line(end = [85.33, 135.48])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()`
|
|
|
|
const ERROR_KCL = `sketch001 = startSketchOn(XZ)
|
|
profile001 = startProfile(sketch001, at = [127.56, 179.02])
|
|
|> line(end = [132.84, -112.6])
|
|
|> line(end = [85.33, 234.01])
|
|
|> line(enfd = [-137.23, -54.55])`
|
|
|
|
await context.folderSetupFn(async (dir) => {
|
|
const projectDir = path.join(dir, 'multi-file-sketch-test')
|
|
await fs.mkdir(projectDir, { recursive: true })
|
|
await Promise.all([
|
|
fs.writeFile(path.join(projectDir, 'good.kcl'), GOOD_KCL, 'utf-8'),
|
|
fs.writeFile(path.join(projectDir, 'error.kcl'), ERROR_KCL, 'utf-8'),
|
|
])
|
|
})
|
|
|
|
await page.setBodyDimensions({ width: 1000, height: 800 })
|
|
|
|
await homePage.openProject('multi-file-sketch-test')
|
|
await scene.connectionEstablished()
|
|
|
|
await u.closeDebugPanel()
|
|
|
|
await toolbar.openFeatureTreePane()
|
|
await toolbar.openPane('files')
|
|
|
|
await toolbar.openFile('good.kcl')
|
|
|
|
await expect(
|
|
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
|
|
).toHaveCount(2)
|
|
|
|
await toolbar.openFile('error.kcl')
|
|
|
|
await expect(
|
|
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
|
|
).toHaveCount(0)
|
|
})
|
|
|
|
test('adding a syntax error, recovers after fixing', async ({
|
|
page,
|
|
homePage,
|
|
context,
|
|
scene,
|
|
editor,
|
|
toolbar,
|
|
cmdBar,
|
|
}) => {
|
|
const file = await fs.readFile(
|
|
path.resolve(
|
|
__dirname,
|
|
'../../',
|
|
'./rust/kcl-lib/e2e/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
|
),
|
|
'utf-8'
|
|
)
|
|
await context.addInitScript((file) => {
|
|
localStorage.setItem('persistCode', file)
|
|
}, file)
|
|
await homePage.goToModelingScene()
|
|
|
|
const [objClick] = scene.makeMouseHelpers(600, 250)
|
|
const arrowHeadLocation = { x: 706, y: 129 } as const
|
|
const arrowHeadWhite = TEST_COLORS.WHITE
|
|
const backgroundGray: [number, number, number] = [28, 28, 28]
|
|
const verifyArrowHeadColor = async (c: [number, number, number]) =>
|
|
scene.expectPixelColor(c, arrowHeadLocation, 15)
|
|
|
|
// wait for scene to load
|
|
await scene.settled(cmdBar)
|
|
|
|
await test.step('check chamfer selection changes cursor position', async () => {
|
|
await expect(async () => {
|
|
// sometimes initial click doesn't register
|
|
await objClick()
|
|
await editor.expectActiveLinesToBe([
|
|
'|> startProfile(at = [75.8, 317.2]) // [$startCapTag, $EndCapTag]',
|
|
])
|
|
}).toPass({ timeout: 15_000, intervals: [500] })
|
|
})
|
|
|
|
await test.step('enter sketch and sanity check segments have been drawn', async () => {
|
|
await toolbar.editSketch()
|
|
// this checks sketch segments have been drawn
|
|
await verifyArrowHeadColor(arrowHeadWhite)
|
|
})
|
|
|
|
await test.step('Make typo and check the segments have Disappeared and there is a syntax error', async () => {
|
|
await editor.replaceCode(
|
|
'line(endAbsolute = [pro',
|
|
'badBadBadFn(endAbsolute = [pro'
|
|
)
|
|
await editor.expectState({
|
|
activeLines: [],
|
|
diagnostics: ['`badBadBadFn`isnotdefined'],
|
|
highlightedCode: '',
|
|
})
|
|
await expect(
|
|
page.getByText(
|
|
"Error in kcl script, sketch cannot be drawn until it's fixed"
|
|
)
|
|
).toBeVisible()
|
|
// this checks sketch segments have failed to be drawn
|
|
await verifyArrowHeadColor(backgroundGray)
|
|
})
|
|
|
|
await test.step('', async () => {
|
|
await editor.replaceCode(
|
|
'badBadBadFn(endAbsolute = [pro',
|
|
'line(endAbsolute = [pro'
|
|
)
|
|
await editor.expectState({
|
|
activeLines: [],
|
|
diagnostics: [],
|
|
highlightedCode: '',
|
|
})
|
|
// this checks sketch segments have been drawn
|
|
await verifyArrowHeadColor(arrowHeadWhite)
|
|
})
|
|
|
|
await test.step('make a change to the code and expect pixel color to change', async () => {
|
|
// defends against a regression where sketch would duplicate in the scene
|
|
// https://github.com/KittyCAD/modeling-app/issues/6345
|
|
await editor.replaceCode(
|
|
'startProfile(at = [75.8, 317.2',
|
|
'startProfile(at = [75.8, 217.2'
|
|
)
|
|
// expect not white anymore
|
|
await scene.expectPixelColorNotToBe(
|
|
TEST_COLORS.WHITE,
|
|
arrowHeadLocation,
|
|
15
|
|
)
|
|
})
|
|
})
|
|
})
|