merge main and modeling-api

This commit is contained in:
benjamaan476
2025-06-25 18:01:04 +01:00
716 changed files with 77772 additions and 1285 deletions

View File

@ -280,6 +280,9 @@ Assign someone to each section of the manual checklist generated by the issue te
Follow the instructions [here](./rust/README.md) to publish new crates. Follow the instructions [here](./rust/README.md) to publish new crates.
This ensures that the KCL accepted by the app is also accepted by the CLI. This ensures that the KCL accepted by the app is also accepted by the CLI.
If there are documentation changes, merge the corresponding Dependabot PRs [here](https://github.com/KittyCAD/website/pulls/app%2Fdependabot) for the website.
You can trigger Dependabot to check for updates [here](https://github.com/KittyCAD/website/network/updates/17261214/jobs).
#### 5. Publish the release #### 5. Publish the release
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well. Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well.

View File

@ -62,7 +62,10 @@ else
endif endif
public/kcl-samples/manifest.json: $(KCL_SOURCES) public/kcl-samples/manifest.json: $(KCL_SOURCES)
ifndef WINDOWS
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
@ touch $@
endif
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES) .vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
npm run tronb:vite:dev npm run tronb:vite:dev

View File

@ -83,6 +83,13 @@ Allow orbiting in sketch mode.
Whether to show the debug panel, which lets you see various states of the app to aid in development. Whether to show the debug panel, which lets you see various states of the app to aid in development.
**Default:** None
##### fixed_size_grid
If true, the grid cells will be fixed-size, where the width is your default length unit. If false, the grid will get larger as you zoom out, and smaller as you zoom in.
**Default:** None **Default:** None

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,9 +10,11 @@ Extend the current sketch with a new involute circular curve.
```kcl ```kcl
involuteCircular( involuteCircular(
@sketch: Sketch, @sketch: Sketch,
startRadius: number(Length),
endRadius: number(Length),
angle: number(Angle), angle: number(Angle),
startRadius?: number(Length),
endRadius?: number(Length),
startDiameter?: number(Length),
endDiameter?: number(Length),
reverse?: bool, reverse?: bool,
tag?: TagDecl, tag?: TagDecl,
): Sketch ): Sketch
@ -25,9 +27,11 @@ involuteCircular(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes | | `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes | | `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No | | `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No | | `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@ revolved around the same axis.
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes | | `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes | | `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No | | `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the revolve operation. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No | | `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No | | `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No | | `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@ will smoothly blend the transition.
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes | | `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes | | `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes | | `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance for this fillet | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No | | `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

View File

@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes | | `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the intersection operation. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -27,7 +27,7 @@ and complex multi-body part modeling.
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes | | `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes | | `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the subtraction operation. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
### Returns ### Returns

View File

@ -21,7 +21,7 @@ union(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | `[Solid; 2+]` | The solids to union. | Yes | | `solids` | `[Solid; 2+]` | The solids to union. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the union operation. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -265,6 +265,8 @@ middle(0)
}) })
await expect( await expect(
page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't
Backtrace:
assert() assert()
check() check()
middle()`) middle()`)

View File

@ -307,7 +307,7 @@ test.describe('Command bar tests', () => {
) )
const continueButton = page.getByRole('button', { name: 'Continue' }) const continueButton = page.getByRole('button', { name: 'Continue' })
const submitButton = page.getByRole('button', { name: 'Submit command' }) const submitButton = page.getByTestId('command-bar-submit')
await continueButton.click() await continueButton.click()
// Review step and argument hotkeys // Review step and argument hotkeys

View File

@ -54,9 +54,7 @@ test(
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export') await cmdBar.submit()
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Expect it to succeed // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
@ -119,9 +117,7 @@ test(
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export') await cmdBar.submit()
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Look out for the toast message // Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`) const exportingToastMessage = page.getByText(`Exporting...`)

View File

@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers must be lowerCamelCase').first() page.getByText('Identifiers should be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
await page.locator('#code-pane button:first-child').click() await page.locator('#code-pane button:first-child').click()
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers must be lowerCamelCase').first() page.getByText('Identifiers should be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
}) })
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers must be lowerCamelCase').first() page.getByText('Identifiers should be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
// focus the editor // focus the editor
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers must be lowerCamelCase').first() page.getByText('Identifiers should be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
}) })
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers must be lowerCamelCase').first() page.getByText('Identifiers should be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
// select the line that's causing the error and delete it // select the line that's causing the error and delete it
@ -1617,4 +1617,33 @@ sketch001 = startSketchOn(XZ)
// Verify error is still visible // Verify error is still visible
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1) await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
}) })
test('Core dump hotkey', async ({ page, scene, cmdBar, homePage }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
`
)
})
const viewportSize = { width: 1200, height: 800 }
await page.setBodyDimensions(viewportSize)
await homePage.goToModelingScene()
await scene.connectionEstablished()
await scene.settled(cmdBar)
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'
await page.keyboard.press(`${modifier}+Shift+.`)
const toast1 = page.getByText('Starting core dump...')
await expect(toast1).toBeVisible()
const toast2 = page.getByText('Core dump completed')
await expect(toast2).toBeVisible()
})
}) })

View File

@ -229,11 +229,12 @@ test.describe('Feature Tree pane', () => {
const initialCode = `sketch001 = startSketchOn(XZ) const initialCode = `sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 5) |> circle(center = [0, 0], radius = 5)
renamedExtrude = extrude(sketch001, length = ${initialInput})` renamedExtrude = extrude(sketch001, length = ${initialInput})`
const newConstantName = 'length001' const newParameterName = 'length001'
const expectedCode = `${newConstantName} = 23 const expectedCode = `${newParameterName} = 23
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 5) |> circle(center = [0, 0], radius = 5)
renamedExtrude = extrude(sketch001, length = ${newConstantName})` renamedExtrude = extrude(sketch001, length = ${newParameterName})`
const editedParameterValue = '23 * 2'
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const testDir = join(dir, 'test-sample') const testDir = join(dir, 'test-sample')
@ -279,7 +280,7 @@ test.describe('Feature Tree pane', () => {
}) })
}) })
await test.step('Add a named constant for distance argument and submit', async () => { await test.step('Add a parameter for distance argument and submit', async () => {
await expect(cmdBar.currentArgumentInput).toBeVisible() await expect(cmdBar.currentArgumentInput).toBeVisible()
await cmdBar.variableCheckbox.click() await cmdBar.variableCheckbox.click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -296,13 +297,43 @@ test.describe('Feature Tree pane', () => {
highlightedCode: '', highlightedCode: '',
diagnostics: [], diagnostics: [],
activeLines: [ activeLines: [
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`, `renamedExtrude = extrude(sketch001, length = ${newParameterName})`,
], ],
}) })
await editor.expectEditor.toContain(expectedCode, { await editor.expectEditor.toContain(expectedCode, {
shouldNormalise: true, shouldNormalise: true,
}) })
}) })
await test.step('Edit the parameter via the feature tree', async () => {
const parameter = await toolbar.getFeatureTreeOperation('Parameter', 0)
await parameter.dblclick()
await cmdBar.expectState({
commandName: 'Edit parameter',
currentArgKey: 'value',
currentArgValue: '23',
headerArguments: {
Name: newParameterName,
Value: '23',
},
stage: 'arguments',
highlightedHeaderArg: 'value',
})
await cmdBar.argumentInput
.locator('[contenteditable]')
.fill(editedParameterValue)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
commandName: 'Edit parameter',
headerArguments: {
Name: newParameterName,
Value: '46', // Shows calculated result
},
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(editedParameterValue)
})
}) })
test(`User can edit an offset plane operation from the feature tree`, async ({ test(`User can edit an offset plane operation from the feature tree`, async ({
context, context,

View File

@ -118,15 +118,11 @@ export class CmdBarFixture {
return return
} }
const arrowButton = this.page.getByRole('button', { const arrowButton = this.page.getByTestId('command-bar-continue')
name: 'arrow right Continue',
})
if (await arrowButton.isVisible()) { if (await arrowButton.isVisible()) {
await arrowButton.click() await this.continue()
} else { } else {
await this.page await this.submit()
.getByRole('button', { name: 'checkmark Submit command' })
.click()
} }
} }
@ -191,6 +187,13 @@ export class CmdBarFixture {
return this.page.getByRole('option', options) return this.page.getByRole('option', options)
} }
/**
* Select an optional argument from the command bar during review
*/
clickOptionalArgument = async (argName: string) => {
await this.page.getByTestId(`cmd-bar-add-optional-arg-${argName}`).click()
}
/** /**
* Clicks the Create new variable button for kcl input * Clicks the Create new variable button for kcl input
*/ */

View File

@ -183,14 +183,15 @@ export class EditorFixture {
scrollToText(text: string, placeCursor?: boolean) { scrollToText(text: string, placeCursor?: boolean) {
return this.page.evaluate( return this.page.evaluate(
(args: { text: string; placeCursor?: boolean }) => { (args: { text: string; placeCursor?: boolean }) => {
const editorView = window.editorManager.getEditorView()
// error TS2339: Property 'docView' does not exist on type 'EditorView'. // error TS2339: Property 'docView' does not exist on type 'EditorView'.
// Except it does so :shrug: // Except it does so :shrug:
// @ts-ignore // @ts-ignore
let index = window.editorManager._editorView?.docView.view.state.doc const index = editorView?.docView.view.state.doc
.toString() .toString()
.indexOf(args.text) .indexOf(args.text)
window.editorManager._editorView?.focus() editorView?.focus()
window.editorManager._editorView?.dispatch({ editorView?.dispatch({
selection: window.EditorSelection.create([ selection: window.EditorSelection.create([
window.EditorSelection.cursor(index), window.EditorSelection.cursor(index),
]), ]),

View File

@ -5,7 +5,7 @@ import type {
FullResult, FullResult,
} from '@playwright/test/reporter' } from '@playwright/test/reporter'
class MyAPIReporter implements Reporter { class APIReporter implements Reporter {
private pendingRequests: Promise<void>[] = [] private pendingRequests: Promise<void>[] = []
private allResults: Record<string, any>[] = [] private allResults: Record<string, any>[] = []
private blockingResults: Record<string, any>[] = [] private blockingResults: Record<string, any>[] = []
@ -32,7 +32,7 @@ class MyAPIReporter implements Reporter {
'X-API-Key': process.env.TAB_API_KEY || '', 'X-API-Key': process.env.TAB_API_KEY || '',
}), }),
body: JSON.stringify({ body: JSON.stringify({
project: 'https://github.com/KittyCAD/modeling-app', project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
branch: branch:
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '', process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '', commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
@ -60,7 +60,7 @@ class MyAPIReporter implements Reporter {
const payload = { const payload = {
// Required information // Required information
project: 'https://github.com/KittyCAD/modeling-app', project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
suite: process.env.CI_SUITE || 'e2e', suite: process.env.CI_SUITE || 'e2e',
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '', branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '', commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
@ -124,4 +124,4 @@ class MyAPIReporter implements Reporter {
} }
} }
export default MyAPIReporter export default APIReporter

View File

@ -1083,14 +1083,13 @@ openSketch = startSketchOn(XY)
cmdBar, cmdBar,
}) => { }) => {
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 150 } const testPoint = { x: 700, y: 200 }
// TODO: replace the testPoint selection with a feature tree click once that's supported #7544
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)` const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
await homePage.goToModelingScene() await homePage.goToModelingScene()
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue. await scene.settled(cmdBar)
// The engine may not be connected
await page.waitForTimeout(15000)
await test.step(`Look for the blue of the XZ plane`, async () => { await test.step(`Look for the blue of the XZ plane`, async () => {
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME //await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
@ -1611,6 +1610,8 @@ sketch002 = startSketchOn(plane001)
testPoint.y + 80 testPoint.y + 80
) )
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])' const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
const editedLoftDeclaration =
'loft001 = loft([sketch001, sketch002], vDegree = 3)'
await test.step(`Look for the white of the sketch001 shape`, async () => { await test.step(`Look for the white of the sketch001 shape`, async () => {
await scene.expectPixelColor([254, 254, 254], testPoint, 15) await scene.expectPixelColor([254, 254, 254], testPoint, 15)
@ -1682,6 +1683,39 @@ sketch002 = startSketchOn(plane001)
await scene.expectPixelColor([89, 89, 89], testPoint, 15) await scene.expectPixelColor([89, 89, 89], testPoint, 15)
}) })
await test.step('Go through the edit flow via feature tree', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Loft', 0)
await op.dblclick()
await cmdBar.expectState({
stage: 'review',
headerArguments: {},
commandName: 'Loft',
})
await cmdBar.clickOptionalArgument('vDegree')
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'vDegree',
currentArgValue: '',
headerArguments: {
VDegree: '',
},
highlightedHeaderArg: 'vDegree',
commandName: 'Loft',
})
await page.keyboard.insertText('3')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
VDegree: '3',
},
commandName: 'Loft',
})
await cmdBar.submit()
await editor.expectEditor.toContain(editedLoftDeclaration)
})
await test.step('Delete loft via feature tree selection', async () => { await test.step('Delete loft via feature tree selection', async () => {
await editor.closePane() await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0) const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
@ -1692,72 +1726,6 @@ sketch002 = startSketchOn(plane001)
}) })
}) })
// TODO: merge with above test. Right now we're not able to delete a loft
// right after creation via selection for some reason, so we go with a new instance
test('Loft and offset plane deletion via selection', async ({
context,
page,
homePage,
scene,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 30)
plane001 = offsetPlane(XZ, offset = 50)
sketch002 = startSketchOn(plane001)
|> circle(center = [0, 0], radius = 20)
loft001 = loft([sketch001, sketch002])
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnSketch2] = scene.makeMouseHelpers(
testPoint.x,
testPoint.y + 80
)
await test.step(`Delete loft`, async () => {
// Check for loft
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
await clickOnSketch1()
await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle(center = [0, 0], radius = 30)
`)
await page.keyboard.press('Delete')
// Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
})
await test.step('Delete sketch002', async () => {
await page.waitForTimeout(1000)
await clickOnSketch2()
await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle(center = [0, 0], radius = 20)
`)
await page.keyboard.press('Delete')
// Check for plane001
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
})
await test.step('Delete plane001', async () => {
await page.waitForTimeout(1000)
await clickOnSketch2()
await expect(page.locator('.cm-activeLine')).toHaveText(`
plane001 = offsetPlane(XZ, offset = 50)
`)
await page.keyboard.press('Delete')
// Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
})
})
const sweepCases = [ const sweepCases = [
{ {
targetType: 'circle', targetType: 'circle',
@ -1829,7 +1797,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
currentArgKey: 'sketches', currentArgKey: 'sketches',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '', Profiles: '',
Path: '', Path: '',
}, },
@ -1843,7 +1810,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
currentArgKey: 'path', currentArgKey: 'path',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '1 profile', Profiles: '1 profile',
Path: '', Path: '',
}, },
@ -1856,7 +1822,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
currentArgKey: 'path', currentArgKey: 'path',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '1 profile', Profiles: '1 profile',
Path: '', Path: '',
}, },
@ -1869,7 +1834,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
headerArguments: { headerArguments: {
Profiles: '1 profile', Profiles: '1 profile',
Path: '1 segment', Path: '1 segment',
Sectional: '',
}, },
stage: 'review', stage: 'review',
}) })
@ -1894,6 +1858,9 @@ profile002 = startProfile(sketch002, at = [0, 0])
0 0
) )
await operationButton.dblclick({ button: 'left' }) await operationButton.dblclick({ button: 'left' })
await page
.getByRole('button', { name: 'sectional', exact: false })
.click()
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Sweep', commandName: 'Sweep',
currentArgKey: 'sectional', currentArgKey: 'sectional',
@ -1956,6 +1923,7 @@ profile002 = startProfile(sketch002, at = [0, 0])
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
profile001 = ${circleCode}` profile001 = ${circleCode}`
const sweepDeclaration = 'sweep001 = sweep(profile001, path = helix001)' const sweepDeclaration = 'sweep001 = sweep(profile001, path = helix001)'
const editedSweepDeclaration = `sweep001 = sweep(profile001, path = helix001, relativeTo = 'sketchPlane')`
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode) localStorage.setItem('persistCode', initialCode)
@ -1971,7 +1939,6 @@ profile001 = ${circleCode}`
currentArgKey: 'sketches', currentArgKey: 'sketches',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '', Profiles: '',
Path: '', Path: '',
}, },
@ -1986,7 +1953,6 @@ profile001 = ${circleCode}`
currentArgKey: 'path', currentArgKey: 'path',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '1 profile', Profiles: '1 profile',
Path: '', Path: '',
}, },
@ -2000,7 +1966,6 @@ profile001 = ${circleCode}`
currentArgKey: 'path', currentArgKey: 'path',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Sectional: '',
Profiles: '1 profile', Profiles: '1 profile',
Path: '', Path: '',
}, },
@ -2013,7 +1978,6 @@ profile001 = ${circleCode}`
headerArguments: { headerArguments: {
Profiles: '1 profile', Profiles: '1 profile',
Path: '1 helix', Path: '1 helix',
Sectional: '',
}, },
stage: 'review', stage: 'review',
}) })
@ -2021,11 +1985,43 @@ profile001 = ${circleCode}`
await editor.expectEditor.toContain(sweepDeclaration) await editor.expectEditor.toContain(sweepDeclaration)
}) })
await test.step('Go through the edit flow via feature tree', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Sweep', 0)
await op.dblclick()
await cmdBar.expectState({
stage: 'review',
headerArguments: {},
commandName: 'Sweep',
})
await cmdBar.clickOptionalArgument('relativeTo')
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'relativeTo',
currentArgValue: '',
headerArguments: {
RelativeTo: '',
},
highlightedHeaderArg: 'relativeTo',
commandName: 'Sweep',
})
await cmdBar.selectOption({ name: 'sketchPlane' }).click()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
RelativeTo: 'sketchPlane',
},
commandName: 'Sweep',
})
await cmdBar.submit()
await editor.expectEditor.toContain(editedSweepDeclaration)
})
await test.step('Delete sweep via feature tree selection', async () => { await test.step('Delete sweep via feature tree selection', async () => {
const sweep = await toolbar.getFeatureTreeOperation('Sweep', 0) const sweep = await toolbar.getFeatureTreeOperation('Sweep', 0)
await sweep.click() await sweep.click()
await page.keyboard.press('Delete') await page.keyboard.press('Delete')
await editor.expectEditor.not.toContain(sweepDeclaration) await editor.expectEditor.not.toContain(editedSweepDeclaration)
}) })
}) })
@ -3885,6 +3881,8 @@ sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001)
// Edit flow // Edit flow
const newAngle = '270' const newAngle = '270'
const newAngle2 = '5'
const editedCodeToFind = `revolve001 = revolve(sketch003, angle = ${newAngle}, axis = seg01, bidirectionalAngle = ${newAngle2}, )`
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation( const operationButton = await toolbar.getFeatureTreeOperation(
'Revolve', 'Revolve',
@ -3910,11 +3908,33 @@ sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001)
}, },
commandName: 'Revolve', commandName: 'Revolve',
}) })
await cmdBar.clickOptionalArgument('bidirectionalAngle')
await cmdBar.expectState({
commandName: 'Revolve',
currentArgKey: 'bidirectionalAngle',
currentArgValue: '',
headerArguments: {
Angle: newAngle,
BidirectionalAngle: '',
},
highlightedHeaderArg: 'bidirectionalAngle',
stage: 'arguments',
})
await page.keyboard.insertText(newAngle2)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Angle: newAngle,
BidirectionalAngle: newAngle2,
},
commandName: 'Revolve',
})
await cmdBar.submit()
await toolbar.closePane('feature-tree') await toolbar.closePane('feature-tree')
await editor.expectEditor.toContain( await editor.expectEditor.toContain(editedCodeToFind, {
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle) shouldNormalise: true,
) })
}) })
}) })
@ -4734,7 +4754,6 @@ path001 = startProfile(sketch001, at = [0, 0])
headerArguments: { headerArguments: {
Profiles: '', Profiles: '',
Path: '', Path: '',
Sectional: '',
}, },
highlightedHeaderArg: 'Profiles', highlightedHeaderArg: 'Profiles',
commandName: 'Sweep', commandName: 'Sweep',
@ -4747,7 +4766,6 @@ path001 = startProfile(sketch001, at = [0, 0])
headerArguments: { headerArguments: {
Profiles: '2 profiles', Profiles: '2 profiles',
Path: '', Path: '',
Sectional: '',
}, },
highlightedHeaderArg: 'path', highlightedHeaderArg: 'path',
commandName: 'Sweep', commandName: 'Sweep',
@ -4760,7 +4778,6 @@ path001 = startProfile(sketch001, at = [0, 0])
headerArguments: { headerArguments: {
Profiles: '2 profiles', Profiles: '2 profiles',
Path: '1 segment', Path: '1 segment',
Sectional: '',
}, },
commandName: 'Sweep', commandName: 'Sweep',
}) })
@ -4932,4 +4949,154 @@ extrude001 = extrude(profile001 length = 1)`
await editor.expectEditor.toContain(badCode, { shouldNormalise: true }) await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
}) })
}) })
test('Point-and-click extrude with optional args', async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const squareProfileCode = `length001 = 100
sketch001 = startSketchOn(XY)
profile001 = startProfile(sketch001, at = [0, 0])
|> yLine(length = length001)
|> xLine(length = length001)
|> yLine(length = -length001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, squareProfileCode)
await homePage.goToModelingScene()
await scene.settled(cmdBar)
await test.step('Select through code', async () => {
await editor.selectText('startProfile(sketch001, at = [0, 0])')
})
await test.step('Go through command bar flow', async () => {
await toolbar.extrudeButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'sketches',
currentArgValue: '',
headerArguments: {
Profiles: '',
Length: '',
},
highlightedHeaderArg: 'Profiles',
commandName: 'Extrude',
})
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'length',
currentArgValue: '5',
headerArguments: {
Profiles: '1 profile',
Length: '',
},
highlightedHeaderArg: 'length',
commandName: 'Extrude',
})
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Profiles: '1 profile',
Length: '5',
},
commandName: 'Extrude',
})
await cmdBar.clickOptionalArgument('bidirectionalLength')
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'bidirectionalLength',
currentArgValue: '',
headerArguments: {
Profiles: '1 profile',
Length: '5',
BidirectionalLength: '',
},
highlightedHeaderArg: 'bidirectionalLength',
commandName: 'Extrude',
})
await page.keyboard.insertText('10')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Profiles: '1 profile',
Length: '5',
BidirectionalLength: '10',
},
commandName: 'Extrude',
})
await cmdBar.submit()
})
await test.step('Check that the code has changed', async () => {
await scene.settled(cmdBar)
await editor.expectEditor.toContain(
`extrude001 = extrude(profile001, length = 5, bidirectionalLength = 10)`,
{ shouldNormalise: true }
)
})
await test.step('Go through the edit flow via feature tree', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
await op.dblclick()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'length',
currentArgValue: '5',
headerArguments: {
Length: '5',
BidirectionalLength: '10',
},
highlightedHeaderArg: 'length',
commandName: 'Extrude',
})
await page.keyboard.insertText('10')
await cmdBar.progressCmdBar()
await page.getByRole('button', { name: 'BidirectionalLength' }).click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'bidirectionalLength',
currentArgValue: '10',
headerArguments: {
Length: '10',
BidirectionalLength: '10',
},
highlightedHeaderArg: 'bidirectionalLength',
commandName: 'Extrude',
})
await page.keyboard.insertText('20')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Length: '10',
BidirectionalLength: '20',
},
commandName: 'Extrude',
})
await cmdBar.submit()
})
await test.step('Check that the code has changed again', async () => {
await scene.settled(cmdBar)
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`extrude001 = extrude(profile001, length = 10, bidirectionalLength = 20)`,
{ shouldNormalise: true }
)
})
})
}) })

View File

@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
}, },
tronApp.projectDirName, tronApp.projectDirName,
page, page,
cmdBar,
method method
) )
) )
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
const commandContinueButton = page.getByRole('button', { const commandContinueButton = page.getByRole('button', {
name: 'Continue', name: 'Continue',
}) })
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully renamed`) const toastMessage = page.getByText(`Successfully renamed`)
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
await expect(commandContinueButton).toBeVisible() await expect(commandContinueButton).toBeVisible()
await commandContinueButton.click() await commandContinueButton.click()
await expect(commandSubmitButton).toBeVisible() await cmdBar.submit()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
}) })
const projectNameOption = page.getByRole('option', { name: projectName }) const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?') const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully deleted`) const toastMessage = page.getByText(`Successfully deleted`)
const noProjectsMessage = page.getByText('No projects found') const noProjectsMessage = page.getByText('No projects found')
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
await projectNameOption.click() await projectNameOption.click()
await expect(commandWarning).toBeVisible() await expect(commandWarning).toBeVisible()
await expect(commandSubmitButton).toBeVisible() await cmdBar.submit()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
const commandContinueButton = page.getByRole('button', { const commandContinueButton = page.getByRole('button', {
name: 'Continue', name: 'Continue',
}) })
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully renamed`) const toastMessage = page.getByText(`Successfully renamed`)
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
await expect(commandContinueButton).toBeVisible() await expect(commandContinueButton).toBeVisible()
await commandContinueButton.click() await commandContinueButton.click()
await expect(commandSubmitButton).toBeVisible() await cmdBar.submit()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
}) })
const projectNameOption = page.getByRole('option', { name: projectName }) const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?') const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully deleted`) const toastMessage = page.getByText(`Successfully deleted`)
const noProjectsMessage = page.getByText('No projects found') const noProjectsMessage = page.getByText('No projects found')
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
await projectNameOption.click() await projectNameOption.click()
await expect(commandWarning).toBeVisible() await expect(commandWarning).toBeVisible()
await expect(commandSubmitButton).toBeVisible() await cmdBar.submit()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })

View File

@ -1,6 +1,7 @@
import path from 'path' import path from 'path'
import { bracket } from '@e2e/playwright/fixtures/bracket' import { bracket } from '@e2e/playwright/fixtures/bracket'
import type { Page } from '@playwright/test' import type { Page } from '@playwright/test'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { reportRejection } from '@src/lib/trap' import { reportRejection } from '@src/lib/trap'
import * as fsp from 'fs/promises' import * as fsp from 'fs/promises'
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export') await cmdBar.submit()
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await expect(submitButton).toBeVisible() await cmdBar.submit()
await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -482,6 +479,7 @@ extrude002 = extrude(profile002, length = 150)
test('ensure you CAN export while an export is already going', async ({ test('ensure you CAN export while an export is already going', async ({
page, page,
homePage, homePage,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step('Set up the code and durations', async () => { await test.step('Set up the code and durations', async () => {
@ -516,11 +514,11 @@ extrude002 = extrude(profile002, length = 150)
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await test.step('second export', async () => { await test.step('second export', async () => {
await clickExportButton(page) await clickExportButton(page, cmdBar)
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
await clickExportButton(page) await clickExportButton(page, cmdBar)
await test.step('The first export still succeeds', async () => { await test.step('The first export still succeeds', async () => {
await Promise.all([ await Promise.all([
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
await test.step('Successful, unblocked export', async () => { await test.step('Successful, unblocked export', async () => {
// Try exporting again. // Try exporting again.
await clickExportButton(page) await clickExportButton(page, cmdBar)
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -880,7 +878,7 @@ s2 = startSketchOn(XY)
}) })
}) })
async function clickExportButton(page: Page) { async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
await test.step('Running export flow', async () => { await test.step('Running export flow', async () => {
// export the model // export the model
const exportButton = page.getByTestId('export-pane-button') const exportButton = page.getByTestId('export-pane-button')
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export') await cmdBar.submit()
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
}) })
} }

View File

@ -1478,6 +1478,7 @@ sketch001 = startSketchOn(XZ)
await page.mouse.move(1200, 139) await page.mouse.move(1200, 139)
await page.mouse.down() await page.mouse.down()
await page.mouse.move(870, 250) await page.mouse.move(870, 250)
await page.mouse.up()
await page.waitForTimeout(200) await page.waitForTimeout(200)
@ -1487,6 +1488,60 @@ sketch001 = startSketchOn(XZ)
) )
}) })
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 ({ test('Can delete a single segment line with keyboard', async ({
page, page,
scene, scene,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -12,6 +12,7 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
}, },
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
show_debug_panel: true, show_debug_panel: true,
fixed_size_grid: false,
}, },
modeling: { modeling: {
enable_ssao: false, enable_ssao: false,

View File

@ -22,6 +22,7 @@ export const token = process.env.token || ''
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup' import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist' import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
import { test } from '@e2e/playwright/zoo-test' import { test } from '@e2e/playwright/zoo-test'
@ -158,10 +159,10 @@ async function openKclCodePanel(page: Page) {
await page.evaluate(() => { await page.evaluate(() => {
// editorManager is available on the window object. // editorManager is available on the window object.
//@ts-ignore this is in an entirely different context that tsc can't see. //@ts-ignore this is in an entirely different context that tsc can't see.
editorManager._editorView.dispatch({ editorManager.getEditorView().dispatch({
selection: { selection: {
//@ts-ignore this is in an entirely different context that tsc can't see. //@ts-ignore this is in an entirely different context that tsc can't see.
anchor: editorManager._editorView.docView.length, anchor: editorManager.getEditorView().docView.length,
}, },
scrollIntoView: true, scrollIntoView: true,
}) })
@ -737,6 +738,7 @@ export const doExport = async (
output: Models['OutputFormat3d_type'], output: Models['OutputFormat3d_type'],
rootDir: string, rootDir: string,
page: Page, page: Page,
cmdBar: CmdBarFixture,
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown' exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
): Promise<Paths> => { ): Promise<Paths> => {
if (exportFrom === 'dropdown') { if (exportFrom === 'dropdown') {
@ -780,9 +782,7 @@ export const doExport = async (
.click() .click()
await page.locator('#arg-form').waitFor({ state: 'detached' }) await page.locator('#arg-form').waitFor({ state: 'detached' })
} }
await expect(page.getByText('Confirm Export')).toBeVisible() await cmdBar.submit()
await page.getByRole('button', { name: 'Submit command' }).click()
await expect(page.getByText('Exported successfully')).toBeVisible() await expect(page.getByText('Exported successfully')).toBeVisible()
@ -880,6 +880,10 @@ export async function setup(
}, },
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
// Tests were written before this setting existed.
// It's true by default because it's a good user experience, but
// these tests require it to be false.
fixed_size_grid: false,
}, },
project: { project: {
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,

View File

@ -10,7 +10,7 @@ import {
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
test.describe('Testing constraints', () => { test.describe('Testing constraints', () => {
test('Can constrain line length', async ({ page, homePage }) => { test('Can constrain line length', async ({ page, homePage, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.getByTestId('constraint-length').click() await page.getByTestId('constraint-length').click()
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20') await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
await page await cmdBar.continue()
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)` `length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
.getByRole('textbox') .getByRole('textbox')
const cmdBarKclVariableNameInput = const cmdBarKclVariableNameInput =
page.getByPlaceholder('Variable name') page.getByPlaceholder('Variable name')
const cmdBarSubmitButton = page.getByRole('button', {
name: 'arrow right Continue',
})
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(500) await page.waitForTimeout(500)
const [ang, len] = value.split(', ') const [ang, len] = value.split(', ')
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})` const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
await cmdBarSubmitButton.click() await cmdBar.continue()
await expect(page.locator('.cm-content')).toContainText(changedCode) await expect(page.locator('.cm-content')).toContainText(changedCode)
// checking active assures the cursor is where it should be // checking active assures the cursor is where it should be
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(500) await page.waitForTimeout(500)
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10') await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
await page await cmdBar.continue()
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await pollEditorLinesSelectedLength(page, 1) await pollEditorLinesSelectedLength(page, 1)
activeLinesContent = await page.locator('.cm-activeLine').all() activeLinesContent = await page.locator('.cm-activeLine').all()

View File

@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
// We have no more web tests // We have no more web tests
test.fail( test.fail(
'Web: should overwrite current code, cannot create new file', 'Web: should overwrite current code, cannot create new file',
async ({ editor, context, page, homePage }) => { async ({ editor, context, page, homePage, cmdBar }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step(`Test setup`, async () => { await test.step(`Test setup`, async () => {
await context.addInitScript((code) => { await context.addInitScript((code) => {
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
name, name,
}) })
const warningText = page.getByText('Overwrite current file with sample?') const warningText = page.getByText('Overwrite current file with sample?')
const confirmButton = page.getByRole('button', {
name: 'Submit command',
})
await test.step(`Precondition: check the initial code`, async () => { await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel() await u.openKclCodePanel()
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
await expect(commandMethodOption('Create new file')).not.toBeVisible() await expect(commandMethodOption('Create new file')).not.toBeVisible()
await commandMethodOption('Overwrite').click() await commandMethodOption('Overwrite').click()
await expect(warningText).toBeVisible() await expect(warningText).toBeVisible()
await confirmButton.click() await cmdBar.submit()
await editor.expectEditor.toContain('// ' + newSample.title) await editor.expectEditor.toContain('// ' + newSample.title)
}) })

View File

@ -3,6 +3,7 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
import { uuidv4 } from '@src/lib/utils' import { uuidv4 } from '@src/lib/utils'
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils' import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
@ -18,7 +19,7 @@ test.describe('Testing segment overlays', () => {
* @param {number} options.steps - The number of steps to perform * @param {number} options.steps - The number of steps to perform
*/ */
const _clickConstrained = const _clickConstrained =
(page: Page, editor: EditorFixture) => (page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
async ({ async ({
hoverPos, hoverPos,
constraintType, constraintType,
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await page await cmdBar.continue()
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await editor.expectEditor.toContain(expectFinal, { await editor.expectEditor.toContain(expectFinal, {
shouldNormalise: true, shouldNormalise: true,
}) })
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
* @param {number} options.steps - The number of steps to perform * @param {number} options.steps - The number of steps to perform
*/ */
const _clickUnconstrained = const _clickUnconstrained =
(page: Page, editor: EditorFixture) => (page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
async ({ async ({
hoverPos, hoverPos,
constraintType, constraintType,
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await page await cmdBar.continue()
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await editor.expectEditor.toContain(expectAfterUnconstrained, { await editor.expectEditor.toContain(expectAfterUnconstrained, {
shouldNormalise: true, shouldNormalise: true,
}) })
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
await expect(page.getByTestId('segment-overlay')).toHaveCount(14) await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
const clickUnconstrained = _clickUnconstrained(page, editor) const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
const clickConstrained = _clickConstrained(page, editor) const clickConstrained = _clickConstrained(page, editor, cmdBar)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.sendCustomCmd({ await u.sendCustomCmd({
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify the X constraint was added // Verify the X constraint was added
await editor.expectEditor.toContain('center = [xAbs001, 0]', { await editor.expectEditor.toContain('center = [xAbs001, 0]', {
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify the Y constraint was added // Verify the Y constraint was added
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', { await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify all constraints were added // Verify all constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify the constraint was added // Verify the constraint was added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify both constraints were added // Verify both constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify the constraint was added // Verify the constraint was added
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', { await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.getByRole('button', { name: 'arrow right Continue' }).click() await cmdBar.continue()
// Verify all constraints were added // Verify all constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(

View File

@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
test( test(
'Successful export shows a success toast', 'Successful export shows a success toast',
{ tag: '@skipLocalEngine' }, { tag: '@skipLocalEngine' },
async ({ page, homePage, tronApp }) => { async ({ page, homePage, cmdBar, tronApp }) => {
// FYI this test doesn't work with only engine running locally // FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed // And you will need to have the KittyCAD CLI installed
const u = await getUtils(page) const u = await getUtils(page)
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
presentation: 'pretty', presentation: 'pretty',
}, },
tronApp?.projectDirName, tronApp?.projectDirName,
page page,
cmdBar
) )
} }
) )
@ -254,6 +255,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
test('Basic default modeling and sketch hotkeys work', async ({ test('Basic default modeling and sketch hotkeys work', async ({
page, page,
homePage, homePage,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step(`Set up test`, async () => { await test.step(`Set up test`, async () => {
@ -397,11 +399,8 @@ test('Basic default modeling and sketch hotkeys work', async ({
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({ await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
timeout: 20_000, timeout: 20_000,
}) })
await page.getByRole('button', { name: 'Continue' }).click() await cmdBar.continue()
await expect( await cmdBar.submit()
page.getByRole('button', { name: 'Submit command' })
).toBeVisible()
await page.getByRole('button', { name: 'Submit command' }).click()
await expect(page.locator('.cm-content')).toContainText('extrude(') await expect(page.locator('.cm-content')).toContainText('extrude(')
}) })
@ -575,8 +574,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await expect(page.getByText('Confirm Extrude')).toBeVisible() await cmdBar.submit()
await cmdBar.progressCmdBar()
const result2 = result.genNext` const result2 = result.genNext`
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)` const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`

View File

@ -111,7 +111,8 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
PipeSubstitution { "%" } PipeSubstitution { "%" }
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* } // Includes non-whitespace unicode characters.
identifier { $[a-zA-Z_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}] $[a-zA-Z0-9_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}]* }
AnnotationName { "@" identifier? } AnnotationName { "@" identifier? }
PropertyName { identifier } PropertyName { identifier }
TagDeclarator { "$" identifier } TagDeclarator { "$" identifier }

View File

@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight)) helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2)) |> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|> involuteCircular( |> involuteCircular(
startRadius = baseDiameter / 2, startDiameter = baseDiameter,
endRadius = tipDiameter / 2, endDiameter = tipDiameter,
angle = helixCalc, angle = helixCalc,
tag = $seg01, tag = $seg01,
) )
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2)) |> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|> involuteCircular( |> involuteCircular(
startRadius = baseDiameter / 2, startDiameter = baseDiameter,
endRadius = tipDiameter / 2, endDiameter = tipDiameter,
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)), angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
reverse = true, reverse = true,
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Some files were not shown because too many files have changed in this diff Show More