diff --git a/e2e/playwright/flow-tests.spec.ts b/e2e/playwright/flow-tests.spec.ts index 9372a31ef..627fae22c 100644 --- a/e2e/playwright/flow-tests.spec.ts +++ b/e2e/playwright/flow-tests.spec.ts @@ -1,7 +1,7 @@ import { test, expect, Page } from '@playwright/test' import { makeTemplate, getUtils, doExport } from './test-utils' import waitOn from 'wait-on' -import { roundOff, uuidv4 } from 'lib/utils' +import { XOR, roundOff, uuidv4 } from 'lib/utils' import { SaveSettingsPayload } from 'lib/settings/settingsTypes' import { secrets } from './secrets' import { @@ -2440,6 +2440,110 @@ test('Extrude from command bar selects extrude line after', async ({ }) test.describe('Testing constraints', () => { + test.describe('Test distance between constraint', () => { + const cases = [ + { + testName: 'Add variable', + constraint: 'horizontal distance', + value: "segEndX('seg01', %) + xDis001, 61.34", + }, + { + testName: 'No variable', + constraint: 'horizontal distance', + value: "segEndX('seg01', %) + 88.08, 61.34", + }, + { + testName: 'Add variable', + constraint: 'vertical distance', + value: "154.9, segEndY('seg01', %) - yDis001", + }, + { + testName: 'No variable', + constraint: 'vertical distance', + value: "154.9, segEndY('seg01', %) - 42.32", + }, + ] as const + for (const { testName, value, constraint } of cases) { + test(`${constraint} - ${testName}`, async ({ page }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `const yo = 5 +const part001 = startSketchOn('XZ') + |> startProfileAt([-7.54, -26.74], %) + |> line([74.36, 130.4], %) + |> line([78.92, -120.11], %) + |> line([9.16, 77.79], %) + |> line([41.19, 28.97], %) +const part002 = startSketchOn('XZ') + |> startProfileAt([299.05, 231.45], %) + |> xLine(-425.34, %, 'seg-what') + |> yLine(-264.06, %) + |> xLine(segLen('seg-what', %), %) + |> lineTo([profileStartX(%), profileStartY(%)], %)` + ) + }) + const u = await getUtils(page) + await page.setViewportSize({ width: 1200, height: 500 }) + await page.goto('/') + await u.waitForAuthSkipAppStart() + + await page.getByText('line([74.36, 130.4], %)').click() + await page.getByRole('button', { name: 'Edit Sketch' }).click() + + const [line1, line3] = await Promise.all([ + u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`), + u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`), + ]) + + await page.mouse.click(line1.x, line1.y) + await page.keyboard.down('Shift') + await page.mouse.click(line3.x, line3.y) + await page.waitForTimeout(100) // this wait is needed for webkit - not sure why + await page.keyboard.up('Shift') + await page + .getByRole('button', { + name: 'Constrain', + }) + .click() + await page + .getByRole('button', { name: constraint, exact: true }) + .click() + + const createNewVariableCheckbox = page.getByTestId( + 'create-new-variable-checkbox' + ) + const isChecked = await createNewVariableCheckbox.isChecked() + const addVariable = testName === 'Add variable' + XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct + (await createNewVariableCheckbox.click()) + + await page + .getByRole('button', { name: 'Add constraining value' }) + .click() + + // checking activeLines assures the cursors are where they should be + const codeAfter = [ + `|> line([74.36, 130.4], %, 'seg01')`, + `|> lineTo([${value}], %)`, + ] + + const activeLinesContent = await page.locator('.cm-activeLine').all() + await Promise.all( + activeLinesContent.map(async (line, i) => { + await expect(page.locator('.cm-content')).toContainText( + codeAfter[i] + ) + // if the code is an active line then the cursor should be on that line + await expect(line).toHaveText(codeAfter[i]) + }) + ) + + // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state + await expect(page.getByTestId('segment-overlay')).toHaveCount(4) + }) + } + }) test.describe('Test ABS distance constraint', () => { const cases = [ { @@ -2521,7 +2625,7 @@ const part002 = startSketchOn('XZ') 'create-new-variable-checkbox' ) const isChecked = await createNewVariableCheckbox.isChecked() - ;((isChecked && !addVariable) || (!isChecked && addVariable)) && + XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct (await createNewVariableCheckbox.click()) await page @@ -2627,7 +2731,7 @@ const part002 = startSketchOn('XZ') 'create-new-variable-checkbox' ) const isChecked = await createNewVariableCheckbox.isChecked() - ;((isChecked && !addVariable) || (!isChecked && addVariable)) && + XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct (await createNewVariableCheckbox.click()) await page diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx index 88be84869..d9c7bf221 100644 --- a/src/components/ModelingMachineProvider.tsx +++ b/src/components/ModelingMachineProvider.tsx @@ -508,13 +508,26 @@ export const ModelingMachineProvider = ({ }, 'Get horizontal info': async ({ selectionRanges, + sketchDetails, }): Promise => { const { modifiedAst, pathToNodeMap } = await applyConstraintHorzVertDistance({ constraint: 'setHorzDistance', selectionRanges, }) - await kclManager.updateAst(modifiedAst, true) + const _modifiedAst = parse(recast(modifiedAst)) + if (!sketchDetails) throw new Error('No sketch details') + const updatedPathToNode = updatePathToNodeFromMap( + sketchDetails.sketchPathToNode, + pathToNodeMap + ) + await sceneEntitiesManager.updateAstAndRejigSketch( + updatedPathToNode, + _modifiedAst, + sketchDetails.zAxis, + sketchDetails.yAxis, + sketchDetails.origin + ) return { selectionType: 'completeSelection', selection: pathMapToSelections( @@ -522,17 +535,31 @@ export const ModelingMachineProvider = ({ selectionRanges, pathToNodeMap ), + updatedPathToNode, } }, 'Get vertical info': async ({ selectionRanges, + sketchDetails, }): Promise => { const { modifiedAst, pathToNodeMap } = await applyConstraintHorzVertDistance({ constraint: 'setVertDistance', selectionRanges, }) - await kclManager.updateAst(modifiedAst, true) + const _modifiedAst = parse(recast(modifiedAst)) + if (!sketchDetails) throw new Error('No sketch details') + const updatedPathToNode = updatePathToNodeFromMap( + sketchDetails.sketchPathToNode, + pathToNodeMap + ) + await sceneEntitiesManager.updateAstAndRejigSketch( + updatedPathToNode, + _modifiedAst, + sketchDetails.zAxis, + sketchDetails.yAxis, + sketchDetails.origin + ) return { selectionType: 'completeSelection', selection: pathMapToSelections( @@ -540,6 +567,7 @@ export const ModelingMachineProvider = ({ selectionRanges, pathToNodeMap ), + updatedPathToNode, } }, 'Get angle info': async ({ diff --git a/src/components/Toolbar/SetHorzVertDistance.tsx b/src/components/Toolbar/SetHorzVertDistance.tsx index 2f488a7eb..6c7b6202a 100644 --- a/src/components/Toolbar/SetHorzVertDistance.tsx +++ b/src/components/Toolbar/SetHorzVertDistance.tsx @@ -106,7 +106,11 @@ export async function applyConstraintHorzVertDistance({ value: valueUsedInTransform, initialVariableName: constraint === 'setHorzDistance' ? 'xDis' : 'yDis', } as any) - if (segName === tagInfo?.tag && Number(value) === valueUsedInTransform) { + if ( + !variableName && + segName === tagInfo?.tag && + Number(value) === valueUsedInTransform + ) { return { modifiedAst, pathToNodeMap, @@ -126,6 +130,7 @@ export async function applyConstraintHorzVertDistance({ forceValueUsedInTransform: finalValue, }) if (variableName) { + console.log('variableName', variableName) const newBody = [..._modifiedAst.body] newBody.splice( newVariableInsertIndex, @@ -133,6 +138,10 @@ export async function applyConstraintHorzVertDistance({ createVariableDeclaration(variableName, valueNode) ) _modifiedAst.body = newBody + Object.values(pathToNodeMap).forEach((pathToNode) => { + const index = pathToNode.findIndex((a) => a[0] === 'body') + 1 + pathToNode[index][0] = Number(pathToNode[index][0]) + 1 + }) } return { modifiedAst: _modifiedAst, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 009fe4bc9..4201fc81b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -127,3 +127,7 @@ export function isReducedMotion(): boolean { window.matchMedia('(prefers-reduced-motion)').matches ) } + +export function XOR(bool1: boolean, bool2: boolean): boolean { + return (bool1 || bool2) && !(bool1 && bool2) +}