Users MUST use keyword call syntax now. Closes https://github.com/KittyCAD/modeling-app/issues/4600
		
			
				
	
	
		
			1293 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1293 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import type { Coords2d } from '@src/lang/std/sketch'
 | |
| import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
 | |
| import { uuidv4 } from '@src/lib/utils'
 | |
| 
 | |
| import { commonPoints, getUtils } from '@e2e/playwright/test-utils'
 | |
| import { expect, test } from '@e2e/playwright/zoo-test'
 | |
| 
 | |
| test.describe('Testing selections', () => {
 | |
|   test.setTimeout(90_000)
 | |
|   test('Selections work on fresh and edited sketch', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     toolbar,
 | |
|   }) => {
 | |
|     // tests mapping works on fresh sketch and edited sketch
 | |
|     // tests using hovers which is the same as selections, because if
 | |
|     // source ranges are wrong, hovers won't work
 | |
|     const u = await getUtils(page)
 | |
|     const PUR = 400 / 37.5 //pixeltoUnitRatio
 | |
|     await page.setBodyDimensions({ width: 1200, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await u.openDebugPanel()
 | |
| 
 | |
|     const yAxisClick = () =>
 | |
|       test.step('Click on Y axis', async () => {
 | |
|         await page.mouse.move(600, 200, { steps: 5 })
 | |
|         await page.mouse.click(600, 200)
 | |
|         await page.waitForTimeout(100)
 | |
|       })
 | |
|     const xAxisClickAfterExitingSketch = () =>
 | |
|       test.step(`Click on X axis after exiting sketch, which shifts it at the moment`, async () => {
 | |
|         await page.mouse.click(639, 278)
 | |
|         await page.waitForTimeout(100)
 | |
|       })
 | |
|     const emptySpaceHover = () =>
 | |
|       test.step('Hover over empty space', async () => {
 | |
|         await page.mouse.move(1000, 143, { steps: 5 })
 | |
|         await expect(page.locator('.hover-highlight')).not.toBeVisible()
 | |
|       })
 | |
|     const emptySpaceClick = () =>
 | |
|       test.step(`Click in empty space`, async () => {
 | |
|         await page.mouse.click(1000, 143)
 | |
|         await expect(page.locator('.cm-line').last()).toHaveClass(
 | |
|           /cm-activeLine/
 | |
|         )
 | |
|       })
 | |
|     const topHorzSegmentClick = () =>
 | |
|       page.mouse
 | |
|         .click(startXPx, 500 - PUR * 20)
 | |
|         .then(() => page.waitForTimeout(100))
 | |
|     const bottomHorzSegmentClick = () =>
 | |
|       page.mouse
 | |
|         .click(startXPx + PUR * 10, 500 - PUR * 10)
 | |
|         .then(() => page.waitForTimeout(100))
 | |
| 
 | |
|     await u.clearCommandLogs()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Start Sketch' })
 | |
|     ).not.toBeDisabled()
 | |
|     await page.getByRole('button', { name: 'Start Sketch' }).click()
 | |
| 
 | |
|     // select a plane
 | |
|     await page.mouse.click(700, 200)
 | |
|     await page.waitForTimeout(700) // wait for animation
 | |
| 
 | |
|     const startXPx = 600
 | |
|     await u.closeDebugPanel()
 | |
|     await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
 | |
|     await expect(page.locator('.cm-content')).toHaveText(
 | |
|       `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})`
 | |
|     )
 | |
| 
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
 | |
| 
 | |
|     await expect(
 | |
|       page.locator('.cm-content')
 | |
|     ).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})
 | |
|     |> xLine(length = ${commonPoints.num1})`)
 | |
| 
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
 | |
|     await expect(
 | |
|       page.locator('.cm-content')
 | |
|     ).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${
 | |
|       commonPoints.startAt
 | |
|     })
 | |
|     |> xLine(length = ${commonPoints.num1})
 | |
|     |> yLine(length = ${commonPoints.num1 + 0.01})`)
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(startXPx, 500 - PUR * 20)
 | |
|     await expect(
 | |
|       page.locator('.cm-content')
 | |
|     ).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${
 | |
|       commonPoints.startAt
 | |
|     })
 | |
|     |> xLine(length = ${commonPoints.num1})
 | |
|     |> yLine(length = ${commonPoints.num1 + 0.01})
 | |
|     |> xLine(length = ${commonPoints.num2 * -1})`)
 | |
| 
 | |
|     // deselect line tool
 | |
|     await page.getByRole('button', { name: 'line Line', exact: true }).click()
 | |
| 
 | |
|     await u.closeDebugPanel()
 | |
|     const selectionSequence = async () => {
 | |
|       await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | |
| 
 | |
|       await page.waitForTimeout(100)
 | |
|       await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
 | |
| 
 | |
|       await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | |
|       // bg-yellow-300/70 is more brittle than hover-highlight, but is closer to the user experience
 | |
|       // and will be an easy fix if it breaks because we change the colour
 | |
|       await expect(page.locator('.bg-yellow-300\\/70')).toBeVisible()
 | |
|       // check mousing off, than mousing onto another line
 | |
|       await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
 | |
|       await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | |
|       await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
 | |
|       await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | |
| 
 | |
|       // now check clicking works including axis
 | |
| 
 | |
|       // click a segment hold shift and click an axis, see that a relevant constraint is enabled
 | |
|       const constrainButton = page.getByRole('button', {
 | |
|         name: 'constraints: open menu',
 | |
|       })
 | |
|       const absXButton = page.getByRole('button', { name: 'Absolute X' })
 | |
| 
 | |
|       await test.step(`Select a segment and an axis, see that a relevant constraint is enabled`, async () => {
 | |
|         await topHorzSegmentClick()
 | |
|         await page.keyboard.down('Shift')
 | |
|         await constrainButton.click()
 | |
|         await expect(absXButton).toBeDisabled()
 | |
|         await page.waitForTimeout(100)
 | |
|         await yAxisClick()
 | |
|         await page.keyboard.up('Shift')
 | |
|         await constrainButton.click()
 | |
|         await absXButton.and(page.locator(':not([disabled])')).waitFor()
 | |
|         await expect(absXButton).not.toBeDisabled()
 | |
|       })
 | |
| 
 | |
|       await emptySpaceClick()
 | |
|       await page.waitForTimeout(100)
 | |
| 
 | |
|       await test.step(`Same selection but click the axis first`, async () => {
 | |
|         await yAxisClick()
 | |
|         await constrainButton.click()
 | |
|         await expect(absXButton).toBeDisabled()
 | |
|         await page.keyboard.down('Shift')
 | |
|         await page.waitForTimeout(100)
 | |
|         await topHorzSegmentClick()
 | |
|         await page.waitForTimeout(100)
 | |
| 
 | |
|         await page.keyboard.up('Shift')
 | |
|         await constrainButton.click()
 | |
|         await expect(absXButton).not.toBeDisabled()
 | |
|       })
 | |
| 
 | |
|       // clear selection by clicking on nothing
 | |
|       await emptySpaceClick()
 | |
| 
 | |
|       // check the same selection again by putting cursor in code first then selecting axis
 | |
|       await test.step(`Same selection but code selection then axis`, async () => {
 | |
|         await page
 | |
|           .getByText(`  |> xLine(length = ${commonPoints.num2 * -1})`)
 | |
|           .click()
 | |
|         await page.keyboard.down('Shift')
 | |
|         await constrainButton.click()
 | |
|         await expect(absXButton).toBeDisabled()
 | |
|         await page.waitForTimeout(100)
 | |
|         await yAxisClick()
 | |
|         await page.keyboard.up('Shift')
 | |
|         await constrainButton.click()
 | |
|         await expect(absXButton).not.toBeDisabled()
 | |
|       })
 | |
| 
 | |
|       // clear selection by clicking on nothing
 | |
|       await emptySpaceClick()
 | |
| 
 | |
|       // select segment in editor than another segment in scene and check there are two cursors
 | |
|       // TODO change this back to shift click in the scene, not cmd click in the editor
 | |
|       await bottomHorzSegmentClick()
 | |
| 
 | |
|       await expect(page.locator('.cm-cursor')).toHaveCount(1)
 | |
| 
 | |
|       await page.keyboard.down(
 | |
|         process.platform === 'linux' ? 'Control' : 'Meta'
 | |
|       )
 | |
|       await page.waitForTimeout(100)
 | |
|       await page
 | |
|         .getByText(`  |> xLine(length = ${commonPoints.num2 * -1})`)
 | |
|         .click()
 | |
| 
 | |
|       await expect(page.locator('.cm-cursor')).toHaveCount(2)
 | |
|       await page.waitForTimeout(500)
 | |
|       await page.keyboard.up(process.platform === 'linux' ? 'Control' : 'Meta')
 | |
| 
 | |
|       // clear selection by clicking on nothing
 | |
|       await emptySpaceClick()
 | |
|     }
 | |
| 
 | |
|     await test.step(`Test hovering and selecting on fresh sketch`, async () => {
 | |
|       await selectionSequence()
 | |
|     })
 | |
| 
 | |
|     // hovering in fresh sketch worked, lets try exiting and re-entering
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await page.getByRole('button', { name: 'Exit Sketch' }).click()
 | |
|     await page.waitForTimeout(200)
 | |
|     // wait for execution done
 | |
| 
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]')
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     // select a line, this verifies that sketches in the scene can be selected outside of sketch mode
 | |
|     await topHorzSegmentClick()
 | |
|     await xAxisClickAfterExitingSketch()
 | |
|     await page.waitForTimeout(100)
 | |
|     await emptySpaceHover()
 | |
| 
 | |
|     // enter sketch again
 | |
|     await toolbar.editSketch()
 | |
| 
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await u.sendCustomCmd({
 | |
|       type: 'modeling_cmd_req',
 | |
|       cmd_id: uuidv4(),
 | |
|       cmd: {
 | |
|         type: 'default_camera_look_at',
 | |
|         center: { x: 0, y: 0, z: 0 },
 | |
|         vantage: { x: 0, y: -1378.01, 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 emptySpaceClick()
 | |
| 
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     await test.step(`Test hovering and selecting on edited sketch`, async () => {
 | |
|       await selectionSequence()
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   test('Solids should be select and deletable', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     scene,
 | |
|     cmdBar,
 | |
|   }) => {
 | |
|     test.setTimeout(90_000)
 | |
|     const u = await getUtils(page)
 | |
|     await page.addInitScript(async () => {
 | |
|       localStorage.setItem(
 | |
|         'persistCode',
 | |
|         `@settings(defaultLengthUnit = in)
 | |
| sketch001 = startSketchOn(XZ)
 | |
|   |> startProfile(at = [-79.26, 95.04])
 | |
|   |> line(end = [112.54, 127.64], tag = $seg02)
 | |
|   |> line(end = [170.36, -121.61], tag = $seg01)
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| extrude001 = extrude(sketch001, length = 50)
 | |
| sketch005 = startSketchOn(extrude001, face = 'END')
 | |
|   |> startProfile(at = [23.24, 136.52])
 | |
|   |> line(end = [-8.44, 36.61])
 | |
|   |> line(end = [49.4, 2.05])
 | |
|   |> line(end = [29.69, -46.95])
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| sketch003 = startSketchOn(extrude001, face = seg01)
 | |
|   |> startProfile(at = [21.23, 17.81])
 | |
|   |> line(end = [51.97, 21.32])
 | |
|   |> line(end = [4.07, -22.75])
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| sketch002 = startSketchOn(extrude001, face = seg02)
 | |
|   |> startProfile(at = [-100.54, 16.99])
 | |
|   |> line(end = [0, 20.03])
 | |
|   |> line(end = [62.61, 0], tag = $seg03)
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| extrude002 = extrude(sketch002, length = 50)
 | |
| sketch004 = startSketchOn(extrude002, face = seg03)
 | |
|   |> startProfile(at = [57.07, 134.77])
 | |
|   |> line(end = [-4.72, 22.84])
 | |
|   |> line(end = [28.8, 6.71])
 | |
|   |> line(end = [9.19, -25.33])
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| extrude003 = extrude(sketch004, length = 20)
 | |
| pipeLength = 40
 | |
| pipeSmallDia = 10
 | |
| pipeLargeDia = 20
 | |
| thickness = 0.5
 | |
| part009 = startSketchOn(XY)
 | |
|   |> startProfile(at = [pipeLargeDia - (thickness / 2), 38])
 | |
|   |> line(end = [thickness, 0])
 | |
|   |> line(end = [0, -1])
 | |
|   |> angledLine(angle = 60, endAbsoluteX = pipeSmallDia + thickness)
 | |
|   |> line(end = [0, -pipeLength])
 | |
|   |> angledLine(angle = -60, endAbsoluteX = pipeLargeDia + thickness)
 | |
|   |> line(end = [0, -1])
 | |
|   |> line(end = [-thickness, 0])
 | |
|   |> line(end = [0, 1])
 | |
|   |> angledLine(angle = 120, endAbsoluteX =  pipeSmallDia)
 | |
|   |> line(end = [0, pipeLength])
 | |
|   |> angledLine(angle = 60, endAbsoluteX =  pipeLargeDia)
 | |
|   |> close()
 | |
| rev = revolve(part009, axis = Y)
 | |
| sketch006 = startSketchOn(XY)
 | |
| profile001 = circle(
 | |
|   sketch006,
 | |
|   center = [42.91, -70.42],
 | |
|   radius = 17.96
 | |
| )
 | |
| profile002 = startProfile(sketch006, at = [86.92, -63.81])
 | |
|   |> angledLine(angle = 0, length = 63.81, tag = $rectangleSegmentA001)
 | |
|   |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 17.05)
 | |
|   |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| profile003 = startProfile(sketch006, at = [40.16, -120.48])
 | |
|   |> line(end = [26.95, 24.21])
 | |
|   |> line(end = [20.91, -28.61])
 | |
|   |> line(end = [32.46, 18.71])
 | |
| 
 | |
| `
 | |
|       )
 | |
|     }, KCL_DEFAULT_LENGTH)
 | |
|     await page.setBodyDimensions({ width: 1000, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await scene.connectionEstablished()
 | |
|     await scene.settled(cmdBar)
 | |
| 
 | |
|     const camPosition1 = async () => {
 | |
|       await scene.moveCameraTo(
 | |
|         { x: 1139.49, y: -7053, z: 8597.31 },
 | |
|         { x: -2206.68, y: -1298.36, z: 60 }
 | |
|       )
 | |
|     }
 | |
|     await camPosition1()
 | |
| 
 | |
|     const revolve = { x: 635, y: 253 }
 | |
|     const solid2d = { x: 770, y: 167 }
 | |
|     const individualProfile = { x: 694, y: 432 }
 | |
| 
 | |
|     // DELETE REVOLVE
 | |
|     await page.mouse.click(revolve.x, revolve.y)
 | |
|     await page.waitForTimeout(100)
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|       '|> line(end = [0, -pipeLength])'
 | |
|     )
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await page.keyboard.press('Delete')
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
 | |
|     await page.waitForTimeout(200)
 | |
| 
 | |
|     await expect(u.codeLocator).not.toContainText(
 | |
|       `rev = revolve(part009, axis: Y)`
 | |
|     )
 | |
| 
 | |
|     // FIXME (commented section below), this test would select a wall that had a sketch on it, and delete the underlying extrude
 | |
|     // and replace the sketch on face with a hard coded custom plane, but since there was a sketch on that plane maybe it
 | |
|     // should have delete the sketch? it's broken atm, but not sure if worth fixing since desired behaviour is a little
 | |
|     // vague
 | |
|     // DELETE PARENT EXTRUDE
 | |
|     //   await camPosition2()
 | |
|     //   await page.mouse.click(parentExtrude.x, parentExtrude.y)
 | |
|     //   await page.waitForTimeout(100)
 | |
|     //   await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|     //     '|> line(end = [112.54, 127.64], tag = $seg02)'
 | |
|     //   )
 | |
|     //   await u.clearCommandLogs()
 | |
|     //   await page.keyboard.press('Backspace')
 | |
|     //   await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
 | |
|     //   await page.waitForTimeout(200)
 | |
|     //   await expect(u.codeLocator).not.toContainText(
 | |
|     //     `extrude001 = extrude(sketch001, length = 50)`
 | |
|     //   )
 | |
|     //   await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
 | |
|     //      origin = { x = 0, y = -50, z = 0 },
 | |
|     //      xAxis = { x = 1, y = 0, z = 0 },
 | |
|     //      yAxis = { x = 0, y = 0, z = 1 },
 | |
|     //      zAxis = { x = 0, y = -1, z = 0 }
 | |
|     //  })`)
 | |
|     //   await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
 | |
|     //      origin = { x = 116.53, y = 0, z = 163.25 },
 | |
|     //      xAxis = { x = -0.81, y = 0, z = 0.58 },
 | |
|     //      yAxis = { x = 0, y = -1, z = 0 },
 | |
|     //      zAxis = { x = 0.58, y = 0, z = 0.81 }
 | |
|     //  })`)
 | |
|     //   await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
 | |
|     //      origin = { x = -91.74, y = 0, z = 80.89 },
 | |
|     //      xAxis = { x = -0.66, y = 0, z = -0.75 },
 | |
|     //      yAxis = { x = 0, y = -1, z = 0 },
 | |
|     //      zAxis = { x = -0.75, y = 0, z = 0.66 }
 | |
|     //  })`)
 | |
| 
 | |
|     // DELETE SOLID 2D
 | |
|     await page.mouse.click(solid2d.x, solid2d.y)
 | |
|     await page.waitForTimeout(100)
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|       '|> startProfile(at = [23.24, 136.52])'
 | |
|     )
 | |
|     await u.clearCommandLogs()
 | |
|     await page.keyboard.press('Delete')
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
 | |
|     await page.waitForTimeout(200)
 | |
|     await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
 | |
| 
 | |
|     // Delete a single profile
 | |
|     await page.mouse.click(individualProfile.x, individualProfile.y)
 | |
|     await page.waitForTimeout(100)
 | |
|     const codeToBeDeletedSnippet =
 | |
|       'profile003 = startProfile(sketch006, at = [40.16, -120.48])'
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|       '  |> line(end = [20.91, -28.61])'
 | |
|     )
 | |
|     await u.clearCommandLogs()
 | |
|     await page.keyboard.press('Delete')
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
 | |
|     await page.waitForTimeout(200)
 | |
|     await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
 | |
|   })
 | |
|   test('parent Solid should be select and deletable and uses custom planes to position children', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     scene,
 | |
|     cmdBar,
 | |
|     editor,
 | |
|   }) => {
 | |
|     test.setTimeout(90_000)
 | |
|     const u = await getUtils(page)
 | |
|     await page.addInitScript(async () => {
 | |
|       localStorage.setItem(
 | |
|         'persistCode',
 | |
|         `part001 = startSketchOn(XY)
 | |
| yo = startProfile(part001, at = [4.83, 12.56])
 | |
|   |> line(end = [15.1, 2.48])
 | |
|   |> line(end = [3.15, -9.85], tag = $seg01)
 | |
|   |> line(end = [-15.17, -4.1])
 | |
|   |> angledLine(angle = segAng(seg01), length = 12.35, tag = $seg02)
 | |
|   |> line(end = [-13.02, 10.03])
 | |
|   |> close()
 | |
| yoo = extrude(yo, length = 4)
 | |
| sketch002 = startSketchOn(yoo, face = seg02)
 | |
| sketch001 = startSketchOn(yoo, face = 'END')
 | |
| profile002 = startProfile(sketch002, at = [-11.08, 2.39])
 | |
|   |> line(end = [4.89, 0.9])
 | |
|   |> line(end = [-0.61, -2.41])
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| extrude001 = extrude(profile002, length = 15)
 | |
| profile001 = startProfile(sketch001, at = [7.49, 9.96])
 | |
|   |> angledLine(angle = 0, length = 5.05, tag = $rectangleSegmentA001)
 | |
|   |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 4.81)
 | |
|   |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | |
|   |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|   |> close()
 | |
| 
 | |
| `
 | |
|       )
 | |
|     }, KCL_DEFAULT_LENGTH)
 | |
|     await page.setBodyDimensions({ width: 1000, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await scene.settled(cmdBar)
 | |
| 
 | |
|     const extrudeWall = { x: 575, y: 238 }
 | |
| 
 | |
|     // DELETE with selection on face of parent
 | |
|     await page.mouse.click(extrudeWall.x, extrudeWall.y)
 | |
|     await page.waitForTimeout(100)
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|       '|> line(end = [-15.17, -4.1])'
 | |
|     )
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await page.keyboard.press('Delete')
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
 | |
|     await page.waitForTimeout(200)
 | |
| 
 | |
|     await editor.expectEditor.not.toContain(`yoo = extrude(yo, length = 4)`, {
 | |
|       shouldNormalise: true,
 | |
|     })
 | |
|     await editor.expectEditor.toContain(`startSketchOn({plane={origin`, {
 | |
|       shouldNormalise: true,
 | |
|     })
 | |
|     await editor.snapshot()
 | |
|   })
 | |
|   test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     scene,
 | |
|     cmdBar,
 | |
|   }) => {
 | |
|     const u = await getUtils(page)
 | |
|     await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
 | |
|       localStorage.setItem(
 | |
|         'persistCode',
 | |
|         `@settings(defaultLengthUnit = in)
 | |
| part001 = startSketchOn(XZ)
 | |
|   |> startProfile(at = [20, 0])
 | |
|   |> line(end = [7.13, 4 + 0])
 | |
|   |> angledLine(angle = 3 + 0, length = 3.14 + 0 )
 | |
|   |> line(endAbsolute = [20.14 + 0, -0.14 + 0])
 | |
|   |> xLine(endAbsolute = 29 + 0)
 | |
|   |> yLine(length = -3.14 + 0, tag = $a)
 | |
|   |> xLine(length = 1.63)
 | |
|   |> angledLine(angle = 3 + 0, lengthX = 3.14)
 | |
|   |> angledLine(angle = 30, lengthY = 3 + 0)
 | |
|   |> angledLine(angle = 22.14 + 0, endAbsoluteX =  12)
 | |
|   |> angledLine(angle = 30, endAbsoluteY =  11.14)
 | |
|   |> angledLineThatIntersects(angle = 3.14, intersectTag = a, offset = 0)
 | |
|   |> tangentialArc(endAbsolute = [13.14 + 0, 13.14])
 | |
|   |> close()
 | |
|   |> extrude(length = 5 + 7)
 | |
|     `
 | |
|       )
 | |
|     }, KCL_DEFAULT_LENGTH)
 | |
|     await page.setBodyDimensions({ width: 1000, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
| 
 | |
|     // wait for execution done
 | |
|     await u.openDebugPanel()
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]')
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     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()
 | |
| 
 | |
|     const extrusionTopCap: Coords2d = [800, 240]
 | |
|     const flatExtrusionFace: Coords2d = [960, 160]
 | |
|     const tangentialArc: Coords2d = [840, 160]
 | |
|     const close: Coords2d = [720, 200]
 | |
|     const nothing: Coords2d = [600, 200]
 | |
|     const closeEdge: Coords2d = [744, 233]
 | |
|     const closeAdjacentEdge: Coords2d = [743, 277]
 | |
|     const closeOppositeEdge: Coords2d = [687, 169]
 | |
| 
 | |
|     const tangentialArcEdge: Coords2d = [811, 142]
 | |
|     const tangentialArcOppositeEdge: Coords2d = [820, 180]
 | |
|     const tangentialArcAdjacentEdge: Coords2d = [688, 123]
 | |
| 
 | |
|     const straightSegmentEdge: Coords2d = [819, 369]
 | |
|     const straightSegmentOppositeEdge: Coords2d = [822, 368]
 | |
|     const straightSegmentAdjacentEdge: Coords2d = [893, 165]
 | |
| 
 | |
|     await page.mouse.move(nothing[0], nothing[1])
 | |
|     await page.mouse.click(nothing[0], nothing[1])
 | |
| 
 | |
|     await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | |
|     await page.waitForTimeout(200)
 | |
| 
 | |
|     const checkCodeAtHoverPosition = async (
 | |
|       name = '',
 | |
|       coord: Coords2d,
 | |
|       highlightCode: string,
 | |
|       activeLine = highlightCode
 | |
|     ) => {
 | |
|       await test.step(`test selection for: ${name}`, async () => {
 | |
|         const highlightedLocator = page.getByTestId('hover-highlight')
 | |
|         const activeLineLocator = page.locator('.cm-activeLine')
 | |
| 
 | |
|         await test.step(`hover should highlight correct code, clicking should put the cursor in the right place, and send selection to engine`, async () => {
 | |
|           await expect(async () => {
 | |
|             await page.mouse.move(nothing[0], nothing[1])
 | |
|             await page.mouse.move(coord[0], coord[1])
 | |
|             await expect(highlightedLocator.first()).toBeVisible()
 | |
|             await expect
 | |
|               .poll(async () => {
 | |
|                 let textContents = await highlightedLocator.allTextContents()
 | |
|                 const textContentsStr = textContents
 | |
|                   .join('')
 | |
|                   .replace(/\s+/g, '')
 | |
|                 console.log(textContentsStr)
 | |
|                 return textContentsStr
 | |
|               })
 | |
|               .toBe(highlightCode)
 | |
|             await page.mouse.move(nothing[0], nothing[1])
 | |
|           }).toPass({ timeout: 40_000, intervals: [500] })
 | |
|         })
 | |
|         await test.step(`click should put the cursor in the right place`, async () => {
 | |
|           // await page.mouse.move(nothing[0], nothing[1], { steps: 5 })
 | |
|           // await expect(highlightedLocator.first()).not.toBeVisible()
 | |
|           await page.mouse.click(coord[0], coord[1])
 | |
|           await expect
 | |
|             .poll(async () => {
 | |
|               const activeLines = await activeLineLocator.allInnerTexts()
 | |
|               return activeLines.join('')
 | |
|             })
 | |
|             .toContain(activeLine)
 | |
|           // check pixels near the click location are yellow
 | |
|         })
 | |
|         await test.step(`check the engine agrees with selections`, async () => {
 | |
|           // ultimately the only way we know if the engine agrees with the selection from the FE
 | |
|           // perspective is if it highlights the pixels near where we clicked yellow.
 | |
|           await expect
 | |
|             .poll(async () => {
 | |
|               const RGBs = await u.getPixelRGBs({ x: coord[0], y: coord[1] }, 3)
 | |
|               for (const rgb of RGBs) {
 | |
|                 const [r, g, b] = rgb
 | |
|                 const RGAverage = (r + g) / 2
 | |
|                 const isRedGreenSameIsh = Math.abs(r - g) < 10
 | |
|                 const isBlueLessThanRG = RGAverage - b > 40
 | |
|                 const isYellowy = isRedGreenSameIsh && isBlueLessThanRG
 | |
|                 if (isYellowy) return true
 | |
|               }
 | |
|               return false
 | |
|             })
 | |
|             .toBeTruthy()
 | |
|           await page.mouse.click(nothing[0], nothing[1])
 | |
|         })
 | |
|       })
 | |
|     }
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'extrusionTopCap',
 | |
|       extrusionTopCap,
 | |
|       'startProfile(at=[20,0])',
 | |
|       'startProfile(at = [20, 0])'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'flatExtrusionFace',
 | |
|       flatExtrusionFace,
 | |
|       `angledLineThatIntersects(angle=3.14,intersectTag=a,offset=0)extrude(length=5+7)`,
 | |
|       'angledLineThatIntersects(angle = 3.14, intersectTag = a, offset = 0)'
 | |
|     )
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'tangentialArc',
 | |
|       tangentialArc,
 | |
|       'tangentialArc(endAbsolute=[13.14+0,13.14])extrude(length=5+7)',
 | |
|       'tangentialArc(endAbsolute = [13.14 + 0, 13.14])'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'tangentialArcEdge',
 | |
|       tangentialArcEdge,
 | |
|       `tangentialArc(endAbsolute=[13.14+0,13.14])`,
 | |
|       'tangentialArc(endAbsolute = [13.14 + 0, 13.14])'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'tangentialArcOppositeEdge',
 | |
|       tangentialArcOppositeEdge,
 | |
|       `tangentialArc(endAbsolute=[13.14+0,13.14])`,
 | |
|       'tangentialArc(endAbsolute = [13.14 + 0, 13.14])'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'tangentialArcAdjacentEdge',
 | |
|       tangentialArcAdjacentEdge,
 | |
|       `tangentialArc(endAbsolute=[13.14+0,13.14])`,
 | |
|       'tangentialArc(endAbsolute = [13.14 + 0, 13.14])'
 | |
|     )
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'close',
 | |
|       close,
 | |
|       'close()extrude(length=5+7)',
 | |
|       'close()'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition('closeEdge', closeEdge, `close()`, 'close()')
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'closeAdjacentEdge',
 | |
|       closeAdjacentEdge,
 | |
|       `close()`,
 | |
|       'close()'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'closeOppositeEdge',
 | |
|       closeOppositeEdge,
 | |
|       `close()`,
 | |
|       'close()'
 | |
|     )
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'straightSegmentEdge',
 | |
|       straightSegmentEdge,
 | |
|       `angledLine(angle=30,endAbsoluteY=11.14)`,
 | |
|       'angledLine(angle = 30, endAbsoluteY =  11.14)'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'straightSegmentOppositeEdge',
 | |
|       straightSegmentOppositeEdge,
 | |
|       `angledLine(angle=30,endAbsoluteY=11.14)`,
 | |
|       'angledLine(angle = 30, endAbsoluteY =  11.14)'
 | |
|     )
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'straightSegmentAdjacentEdge',
 | |
|       straightSegmentAdjacentEdge,
 | |
|       `angledLineThatIntersects(angle=3.14,intersectTag=a,offset=0)`,
 | |
|       'angledLineThatIntersects(angle = 3.14, intersectTag = a, offset = 0)'
 | |
|     )
 | |
| 
 | |
|     await page.waitForTimeout(200)
 | |
| 
 | |
|     await u.removeCurrentCode()
 | |
|     await u.codeLocator.fill(`@settings(defaultLengthUnit = in)
 | |
|     sketch001 = startSketchOn(XZ)
 | |
|     |> startProfile(at = [75.8, 317.2]) // [$startCapTag, $EndCapTag]
 | |
|     |> angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001)
 | |
|     |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01)
 | |
|     |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $yo)
 | |
|     |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | |
|     |> close()
 | |
|   extrude001 = extrude(sketch001, length = 100)
 | |
|     |> chamfer(
 | |
|      length = 30,
 | |
|      tags = [
 | |
|        seg01,
 | |
|        getNextAdjacentEdge(yo),
 | |
|        getNextAdjacentEdge(seg02),
 | |
|        getOppositeEdge(seg01)
 | |
|      ],
 | |
|    )
 | |
|   `)
 | |
| 
 | |
|     await scene.settled(cmdBar)
 | |
| 
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await u.sendCustomCmd({
 | |
|       type: 'modeling_cmd_req',
 | |
|       cmd_id: uuidv4(),
 | |
|       cmd: {
 | |
|         type: 'default_camera_look_at',
 | |
|         vantage: { x: 16118, y: -1654, z: 5855 },
 | |
|         center: { x: 4915, y: -3893, z: 4874 },
 | |
|         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()
 | |
| 
 | |
|     await page.mouse.click(nothing[0], nothing[1])
 | |
| 
 | |
|     const oppositeChamfer: Coords2d = [577, 230]
 | |
|     const baseChamfer: Coords2d = [726, 258]
 | |
|     const adjacentChamfer1: Coords2d = [653, 99]
 | |
|     const adjacentChamfer2: Coords2d = [653, 430]
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'oppositeChamfer',
 | |
|       oppositeChamfer,
 | |
|       `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
 | |
|       '   )'
 | |
|     )
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'baseChamfer',
 | |
|       baseChamfer,
 | |
|       `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
 | |
|       '   )'
 | |
|     )
 | |
| 
 | |
|     await u.openAndClearDebugPanel()
 | |
|     await u.sendCustomCmd({
 | |
|       type: 'modeling_cmd_req',
 | |
|       cmd_id: uuidv4(),
 | |
|       cmd: {
 | |
|         type: 'default_camera_look_at',
 | |
|         vantage: { x: -6414, y: 160, z: 6145 },
 | |
|         center: { x: 5919, y: 1236, z: 5251 },
 | |
|         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()
 | |
| 
 | |
|     await page.mouse.click(nothing[0], nothing[1])
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'adjacentChamfer1',
 | |
|       adjacentChamfer1,
 | |
|       `line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
 | |
|       '   )'
 | |
|     )
 | |
| 
 | |
|     await checkCodeAtHoverPosition(
 | |
|       'adjacentChamfer2',
 | |
|       adjacentChamfer2,
 | |
|       `angledLine(angle=segAng(rectangleSegmentA001),length=-segLen(rectangleSegmentA001),tag=$yo)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
 | |
|       '   )'
 | |
|     )
 | |
|   })
 | |
|   test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({
 | |
|     page,
 | |
|     editor,
 | |
|     homePage,
 | |
|   }) => {
 | |
|     const u = await getUtils(page)
 | |
| 
 | |
|     await page.addInitScript(async () => {
 | |
|       localStorage.setItem(
 | |
|         'persistCode',
 | |
|         `sketch001 = startSketchOn(XZ)
 | |
|     |> startProfile(at = [3.29, 7.86])
 | |
|     |> line(end = [2.48, 2.44])
 | |
|     |> line(end = [2.66, 1.17])
 | |
|     |> line(end = [3.75, 0.46])
 | |
|     |> line(end = [4.99, -0.46], tag = $seg01)
 | |
|     |> line(end = [3.3, -2.12])
 | |
|     |> line(end = [2.16, -3.33])
 | |
|     |> line(end = [0.85, -3.08])
 | |
|     |> line(end = [-0.18, -3.36])
 | |
|     |> line(end = [-3.86, -2.73])
 | |
|     |> line(end = [-17.67, 0.85])
 | |
|     |> close()
 | |
|   extrude001 = extrude(sketch001, length = 10)
 | |
|     `
 | |
|       )
 | |
|     })
 | |
| 
 | |
|     await page.setBodyDimensions({ width: 1200, height: 500 })
 | |
|     await homePage.goToModelingScene()
 | |
|     await u.waitForPageLoad()
 | |
| 
 | |
|     // wait for execution done
 | |
|     await u.openDebugPanel()
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]')
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     const selectUnExtrudable = async () => {
 | |
|       await editor.scrollToText(`line(end = [4.99, -0.46], tag = $seg01)`)
 | |
|       await page.getByText(`line(end = [4.99, -0.46], tag = $seg01)`).click()
 | |
|     }
 | |
|     const clickEmpty = () => page.mouse.click(700, 460)
 | |
|     await selectUnExtrudable()
 | |
|     // expect extrude button to be enabled, since we don't guard
 | |
|     // until the extrude button is clicked
 | |
|     await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
 | |
| 
 | |
|     await clickEmpty()
 | |
| 
 | |
|     // expect active line to contain nothing
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText('')
 | |
| 
 | |
|     const codeToAdd = `${await u.codeLocator.allInnerTexts()}
 | |
|   sketch002 = startSketchOn(extrude001, face = $seg01)
 | |
|     |> startProfile(at = [-12.94, 6.6])
 | |
|     |> line(end = [2.45, -0.2])
 | |
|     |> line(end = [-2, -1.25])
 | |
|     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|     |> close()
 | |
|   `
 | |
|     await u.codeLocator.fill(codeToAdd)
 | |
| 
 | |
|     await selectUnExtrudable()
 | |
|     // expect extrude button to be enabled, since we don't guard
 | |
|     // until the extrude button is clicked
 | |
|     await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
 | |
| 
 | |
|     await clickEmpty()
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText('')
 | |
|     // there's not extrudable geometry, so button should be enabled
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Extrude' })
 | |
|     ).not.toBeDisabled()
 | |
|   })
 | |
| 
 | |
|   const removeAfterFirstParenthesis = (inputString: string) => {
 | |
|     const index = inputString.indexOf('(')
 | |
|     if (index !== -1) {
 | |
|       return inputString.substring(0, index)
 | |
|     }
 | |
|     return inputString // return the original string if '(' is not found
 | |
|   }
 | |
| 
 | |
|   test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     scene,
 | |
|     cmdBar,
 | |
|   }) => {
 | |
|     const cases = [
 | |
|       {
 | |
|         pos: [694, 185],
 | |
|         expectedCode: 'line(end = [74.36, 130.4], tag = $seg01)',
 | |
|       },
 | |
|       {
 | |
|         pos: [816, 244],
 | |
|         expectedCode: 'angledLine(angle = segAng(seg01), length = yo)',
 | |
|       },
 | |
|       {
 | |
|         pos: [1107, 161],
 | |
|         expectedCode: 'tangentialArc(endAbsolute = [167.95, -28.85])',
 | |
|       },
 | |
|     ] as const
 | |
|     await page.addInitScript(
 | |
|       async ({ cases }) => {
 | |
|         localStorage.setItem(
 | |
|           'persistCode',
 | |
|           `@settings(defaultLengthUnit = in)
 | |
|   yo = 79
 | |
|   part001 = startSketchOn(XZ)
 | |
|     |> startProfile(at = [-7.54, -26.74])
 | |
|     |> ${cases[0].expectedCode}
 | |
|     |> line(end = [-3.19, -138.43])
 | |
|     |> ${cases[1].expectedCode}
 | |
|     |> line(end = [41.19, 28.97 + 5])
 | |
|     |> ${cases[2].expectedCode}`
 | |
|         )
 | |
|       },
 | |
|       { cases }
 | |
|     )
 | |
|     const u = await getUtils(page)
 | |
|     await page.setBodyDimensions({ width: 1200, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await scene.settled(cmdBar)
 | |
|     await u.openAndClearDebugPanel()
 | |
| 
 | |
|     await u.sendCustomCmd({
 | |
|       type: 'modeling_cmd_req',
 | |
|       cmd_id: uuidv4(),
 | |
|       cmd: {
 | |
|         type: 'default_camera_look_at',
 | |
|         vantage: { x: -449, y: -7503, z: 99 },
 | |
|         center: { x: -449, y: 0, z: 99 },
 | |
|         up: { x: 0, y: 0, z: 1 },
 | |
|       },
 | |
|     })
 | |
|     await u.waitForCmdReceive('default_camera_look_at')
 | |
|     await u.clearAndCloseDebugPanel()
 | |
| 
 | |
|     // end setup, now test hover and selects
 | |
|     for (const { pos, expectedCode } of cases) {
 | |
|       // hover over segment, check it's content
 | |
|       await page.mouse.move(pos[0], pos[1], { steps: 5 })
 | |
|       await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | |
|       await expect(page.getByTestId('hover-highlight').first()).toHaveText(
 | |
|         expectedCode
 | |
|       )
 | |
|       // hover over segment, click it and check the cursor has move to the right place
 | |
|       await page.mouse.click(pos[0], pos[1])
 | |
|       await expect(page.locator('.cm-activeLine')).toHaveText(
 | |
|         '|> ' + expectedCode
 | |
|       )
 | |
|     }
 | |
|   })
 | |
|   test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
 | |
|     page,
 | |
|     homePage,
 | |
|     scene,
 | |
|     cmdBar,
 | |
|   }) => {
 | |
|     await page.addInitScript(async () => {
 | |
|       localStorage.setItem(
 | |
|         'persistCode',
 | |
|         `@settings(defaultLengthUnit = in)
 | |
|   sketch001 = startSketchOn(XZ)
 | |
|     |> startProfile(at = [-79.26, 95.04])
 | |
|     |> line(end = [112.54, 127.64])
 | |
|     |> line(end = [170.36, -121.61], tag = $seg01)
 | |
|     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | |
|     |> close()
 | |
|   extrude001 = extrude(sketch001, length = 50)
 | |
|       `
 | |
|       )
 | |
|     })
 | |
|     const u = await getUtils(page)
 | |
|     await page.setBodyDimensions({ width: 1200, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await scene.settled(cmdBar)
 | |
|     await u.openAndClearDebugPanel()
 | |
| 
 | |
|     await u.sendCustomCmd({
 | |
|       type: 'modeling_cmd_req',
 | |
|       cmd_id: uuidv4(),
 | |
|       cmd: {
 | |
|         type: 'default_camera_look_at',
 | |
|         vantage: { x: 6615, y: -9505, z: 10344 },
 | |
|         center: { x: 1579, y: -635, z: 4035 },
 | |
|         up: { x: 0, y: 0, z: 1 },
 | |
|       },
 | |
|     })
 | |
|     await u.waitForCmdReceive('default_camera_look_at')
 | |
|     await u.clearAndCloseDebugPanel()
 | |
| 
 | |
|     await page.waitForTimeout(1000)
 | |
| 
 | |
|     let noHoverColor: [number, number, number] = [92, 92, 92]
 | |
|     let hoverColor: [number, number, number] = [127, 127, 127]
 | |
|     let selectColor: [number, number, number] = [155, 155, 105]
 | |
| 
 | |
|     const extrudeWall = { x: 670, y: 275 }
 | |
|     const extrudeText = `line(end = [170.36, -121.61], tag = $seg01)`
 | |
| 
 | |
|     const cap = { x: 594, y: 283 }
 | |
|     const capText = `startProfile(at = [-79.26, 95.04])`
 | |
| 
 | |
|     const nothing = { x: 946, y: 229 }
 | |
| 
 | |
|     await expect
 | |
|       .poll(() => u.getGreatestPixDiff(extrudeWall, noHoverColor))
 | |
|       .toBeLessThan(15)
 | |
|     await page.mouse.move(nothing.x, nothing.y)
 | |
|     await page.waitForTimeout(1000)
 | |
|     await page.mouse.move(extrudeWall.x, extrudeWall.y)
 | |
|     await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | |
|     await expect(page.getByTestId('hover-highlight').first()).toContainText(
 | |
|       removeAfterFirstParenthesis(extrudeText)
 | |
|     )
 | |
|     await page.waitForTimeout(1000)
 | |
|     await expect(
 | |
|       await u.getGreatestPixDiff(extrudeWall, hoverColor)
 | |
|     ).toBeLessThan(15)
 | |
|     await page.mouse.click(extrudeWall.x, extrudeWall.y)
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${extrudeText}`)
 | |
|     await page.waitForTimeout(1000)
 | |
|     await expect(
 | |
|       await u.getGreatestPixDiff(extrudeWall, selectColor)
 | |
|     ).toBeLessThan(15)
 | |
|     await page.waitForTimeout(1000)
 | |
|     // check color stays there, i.e. not overridden (this was a bug previously)
 | |
|     await expect(
 | |
|       await u.getGreatestPixDiff(extrudeWall, selectColor)
 | |
|     ).toBeLessThan(15)
 | |
| 
 | |
|     await page.mouse.move(nothing.x, nothing.y)
 | |
|     await page.waitForTimeout(1000)
 | |
|     await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | |
| 
 | |
|     // because of shading, color is not exact everywhere on the face
 | |
|     noHoverColor = [115, 115, 115]
 | |
|     hoverColor = [145, 145, 145]
 | |
|     selectColor = [168, 168, 120]
 | |
| 
 | |
|     await expect(await u.getGreatestPixDiff(cap, noHoverColor)).toBeLessThan(15)
 | |
|     await page.mouse.move(cap.x, cap.y)
 | |
|     await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | |
|     await expect(page.getByTestId('hover-highlight').first()).toContainText(
 | |
|       removeAfterFirstParenthesis(capText)
 | |
|     )
 | |
|     await page.waitForTimeout(1000)
 | |
|     await expect(await u.getGreatestPixDiff(cap, hoverColor)).toBeLessThan(15)
 | |
|     await page.mouse.click(cap.x, cap.y)
 | |
|     await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${capText}`)
 | |
|     await page.waitForTimeout(1000)
 | |
|     await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15)
 | |
|     await page.waitForTimeout(1000)
 | |
|     // check color stays there, i.e. not overridden (this was a bug previously)
 | |
|     await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15)
 | |
|   })
 | |
|   test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
 | |
|     page,
 | |
|     homePage,
 | |
|   }) => {
 | |
|     const u = await getUtils(page)
 | |
|     const selectionsSnippets = {
 | |
|       extrudeAndEditBlocked: '|> startProfile(at = [10.81, 32.99])',
 | |
|       extrudeAndEditBlockedInFunction: '|> startProfile(at = pos)',
 | |
|       extrudeAndEditAllowed: '|> startProfile(at = [15.72, 4.7])',
 | |
|       editOnly: '|> startProfile(at = [15.79, -14.6])',
 | |
|     }
 | |
|     await page.addInitScript(
 | |
|       async ({
 | |
|         extrudeAndEditBlocked,
 | |
|         extrudeAndEditBlockedInFunction,
 | |
|         extrudeAndEditAllowed,
 | |
|         editOnly,
 | |
|       }: any) => {
 | |
|         localStorage.setItem(
 | |
|           'persistCode',
 | |
|           `part001 = startSketchOn(XZ)
 | |
|   ${extrudeAndEditBlocked}
 | |
|   |> line(end = [25.96, 2.93])
 | |
|   |> line(end = [5.25, -5.72])
 | |
|   |> line(end = [-2.01, -10.35])
 | |
|   |> line(end = [-27.65, -2.78])
 | |
|   |> close()
 | |
|   |> extrude(length = 5)
 | |
|     sketch002 = startSketchOn(XZ)
 | |
|   ${extrudeAndEditAllowed}
 | |
|   |> line(end = [10.32, 6.47])
 | |
|   |> line(end = [9.71, -6.16])
 | |
|   |> line(end = [-3.08, -9.86])
 | |
|   |> line(end = [-12.02, -1.54])
 | |
|   |> close()
 | |
|     sketch003 = startSketchOn(XZ)
 | |
|   ${editOnly}
 | |
|   |> line(end = [27.55, -1.65])
 | |
|   |> line(end = [4.95, -8])
 | |
|   |> line(end = [-20.38, -10.12])
 | |
|   |> line(end = [-15.79, 17.08])
 | |
| 
 | |
|     fn yohey(@pos) {
 | |
|   sketch004 = startSketchOn(XZ)
 | |
|   ${extrudeAndEditBlockedInFunction}
 | |
|   |> line(end = [27.55, -1.65])
 | |
|   |> line(end = [4.95, -10.53])
 | |
|   |> line(end = [-20.38, -8])
 | |
|   |> line(end = [-15.79, 17.08])
 | |
|   return ''
 | |
|     }
 | |
| 
 | |
|     yohey([15.79, -34.6])
 | |
|     `
 | |
|         )
 | |
|       },
 | |
|       selectionsSnippets
 | |
|     )
 | |
|     await page.setBodyDimensions({ width: 1200, height: 1000 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
| 
 | |
|     // wait for execution done
 | |
|     await u.openDebugPanel()
 | |
|     await u.expectCmdLog('[data-message-type="execution-done"]')
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     // wait for start sketch as a proxy for the stream being ready
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Start Sketch' })
 | |
|     ).not.toBeDisabled()
 | |
| 
 | |
|     await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
 | |
|     // expect extrude button to be enabled, since we don't guard
 | |
|     // until the extrude button is clicked
 | |
|     await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
 | |
| 
 | |
|     await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Extrude' })
 | |
|     ).not.toBeDisabled()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Edit Sketch' })
 | |
|     ).not.toBeDisabled()
 | |
| 
 | |
|     await page.getByText(selectionsSnippets.editOnly).click()
 | |
|     // expect extrude button to be enabled, since we don't guard
 | |
|     // until the extrude button is clicked
 | |
|     await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Edit Sketch' })
 | |
|     ).not.toBeDisabled()
 | |
| 
 | |
|     await page
 | |
|       .getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
 | |
|       .click()
 | |
|     // expect extrude button to be enabled, since we don't guard
 | |
|     // until the extrude button is clicked
 | |
|     await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Edit Sketch' })
 | |
|     ).not.toBeVisible()
 | |
|   })
 | |
| 
 | |
|   test('Deselecting line tool should mean nothing happens on click', async ({
 | |
|     page,
 | |
|     homePage,
 | |
|   }) => {
 | |
|     /**
 | |
|      * If the line tool is clicked when the state is 'No Points' it will exit Sketch mode.
 | |
|      * This is the same exact workflow as pressing ESC.
 | |
|      *
 | |
|      * To continue to test this workflow, we now enter sketch mode and place a single point before exiting the line tool.
 | |
|      */
 | |
|     const u = await getUtils(page)
 | |
|     await page.setBodyDimensions({ width: 1200, height: 500 })
 | |
| 
 | |
|     await homePage.goToModelingScene()
 | |
|     await u.openDebugPanel()
 | |
| 
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Start Sketch' })
 | |
|     ).not.toBeDisabled()
 | |
|     await expect(
 | |
|       page.getByRole('button', { name: 'Start Sketch' })
 | |
|     ).toBeVisible()
 | |
| 
 | |
|     // click on "Start Sketch" button
 | |
|     await u.clearCommandLogs()
 | |
|     await u.doAndWaitForImageDiff(
 | |
|       () => page.getByRole('button', { name: 'Start Sketch' }).click(),
 | |
|       200
 | |
|     )
 | |
| 
 | |
|     // Clicks the XZ Plane in the page
 | |
|     await page.mouse.click(700, 200)
 | |
| 
 | |
|     await expect(page.locator('.cm-content')).toHaveText(
 | |
|       `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)`
 | |
|     )
 | |
| 
 | |
|     await page.waitForTimeout(600)
 | |
| 
 | |
|     const firstClickCoords = { x: 650, y: 200 } as const
 | |
|     // Place a point because the line tool will exit if no points are pressed
 | |
|     await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
 | |
|     await page.waitForTimeout(600)
 | |
| 
 | |
|     // Code before exiting the tool
 | |
|     let previousCodeContent = (
 | |
|       await page.locator('.cm-content').innerText()
 | |
|     ).replace(/\s+/g, '')
 | |
| 
 | |
|     // deselect the line tool by clicking it
 | |
|     await page.getByRole('button', { name: 'line Line', exact: true }).click()
 | |
| 
 | |
|     await page.mouse.click(700, 200)
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(700, 250)
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(750, 200)
 | |
|     await page.waitForTimeout(100)
 | |
| 
 | |
|     await expect
 | |
|       .poll(async () => {
 | |
|         let str = await page.locator('.cm-content').innerText()
 | |
|         str = str.replace(/\s+/g, '')
 | |
|         return str
 | |
|       })
 | |
|       .toBe(previousCodeContent)
 | |
| 
 | |
|     // select line tool again
 | |
|     await page.getByRole('button', { name: 'line Line', exact: true }).click()
 | |
| 
 | |
|     await u.closeDebugPanel()
 | |
| 
 | |
|     // Click to continue profile
 | |
|     await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
 | |
|     await page.waitForTimeout(100)
 | |
| 
 | |
|     // line tool should work as expected again
 | |
|     await page.mouse.click(700, 200)
 | |
|     await expect(page.locator('.cm-content')).not.toHaveText(
 | |
|       previousCodeContent
 | |
|     )
 | |
|     previousCodeContent = await page.locator('.cm-content').innerText()
 | |
| 
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(700, 300)
 | |
|     await expect(page.locator('.cm-content')).not.toHaveText(
 | |
|       previousCodeContent
 | |
|     )
 | |
|     previousCodeContent = await page.locator('.cm-content').innerText()
 | |
| 
 | |
|     await page.waitForTimeout(100)
 | |
|     await page.mouse.click(750, 300)
 | |
|     await expect(page.locator('.cm-content')).not.toHaveText(
 | |
|       previousCodeContent
 | |
|     )
 | |
|     previousCodeContent = await page.locator('.cm-content').innerText()
 | |
|   })
 | |
| })
 |