Compare commits
2 Commits
ben/involu
...
jtran/json
Author | SHA1 | Date | |
---|---|---|---|
4a5ef17c92 | |||
83220a7255 |
@ -19,11 +19,6 @@
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-array-constructor": "error",
|
||||
@ -66,7 +61,6 @@
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"react/no-unknown-property": "error",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
|
11
.github/ci-cd-scripts/playwright-electron.sh
vendored
11
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -26,8 +26,8 @@ max_retries=1
|
||||
# Retry failed tests, doing our own retries because using inbuilt Playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retries ]]; do
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
status=$(jq -r '.status' test-results/.last-run.json)
|
||||
if [[ "$status" == "failed" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$3" == *ubuntu* ]]; then
|
||||
@ -56,11 +56,10 @@ done
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
status=$(jq -r '.status' test-results/.last-run.json)
|
||||
if [[ "$status" == "failed" ]]; then
|
||||
# If it still fails after retries, then fail the job
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
# If it still fails after 3 retries, then fail the job
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
13
.github/workflows/e2e-tests.yml
vendored
13
.github/workflows/e2e-tests.yml
vendored
@ -285,10 +285,21 @@ jobs:
|
||||
# TODO: enable namespace-profile-windows-latest once available
|
||||
os:
|
||||
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||
- namespace-profile-macos-8-cores
|
||||
- namespace-profile-macos-6-cores
|
||||
- windows-latest-8-cores
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
# Disable macos and windows tests on hourly e2e tests since we only care
|
||||
# about server side changes.
|
||||
# Technique from https://github.com/joaomcteixeira/python-project-skeleton/pull/31/files
|
||||
isScheduled:
|
||||
- ${{ github.event_name == 'schedule' }}
|
||||
exclude:
|
||||
- os: namespace-profile-macos-6-cores
|
||||
isScheduled: true
|
||||
- os: windows-latest-8-cores
|
||||
isScheduled: true
|
||||
# TODO: add ref here for main and latest release tag
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -5,6 +5,6 @@ command = "vscode-eslint-language-server"
|
||||
[[language]]
|
||||
name = "typescript"
|
||||
auto-format = true
|
||||
formatter = { command = "./node_modules/@biomejs/biome/bin/biome", args = ["format", "--write", "--stdin-file-path=foo.ts"] }
|
||||
# language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]
|
||||
formatter = { command = "node_modules/.bin/prettier", args = ["--parser", "typescript"] }
|
||||
language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
npm run fmt
|
4
.husky/pre-push
Executable file
4
.husky/pre-push
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm run fmt-check
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
45
docs/kcl/arcTo.md
Normal file
45
docs/kcl/arcTo.md
Normal file
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
@ -10,10 +10,8 @@ Draw a smooth, continuous, curved line segment from the current origin to the de
|
||||
|
||||
```js
|
||||
bezierCurve(
|
||||
data: BezierData,
|
||||
sketch: Sketch,
|
||||
control1: [number],
|
||||
control2: [number],
|
||||
end: [number],
|
||||
tag?: TagDeclarator,
|
||||
): Sketch
|
||||
```
|
||||
@ -23,11 +21,9 @@ bezierCurve(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `control1` | [`[number]`](/docs/kcl/types/number) | First control point for the cubic | Yes |
|
||||
| `control2` | [`[number]`](/docs/kcl/types/number) | Second control point for the cubic | Yes |
|
||||
| `end` | [`[number]`](/docs/kcl/types/number) | How far away (along the X and Y axes) should this line go? | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
|
||||
| `data` | [`BezierData`](/docs/kcl/types/BezierData) | Data to draw a bezier curve. | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -40,7 +36,11 @@ bezierCurve(
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [0, 10])
|
||||
|> bezierCurve(control1 = [5, 0], control2 = [5, 10], end = [10, 10])
|
||||
|> bezierCurve({
|
||||
to = [10, 10],
|
||||
control1 = [5, 0],
|
||||
control2 = [5, 10]
|
||||
}, %)
|
||||
|> line(endAbsolute = [10, 0])
|
||||
|> close()
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -42,10 +42,18 @@ extrude(
|
||||
example = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [10, 0])
|
||||
|> arc(angleStart = 120, angleEnd = 0, radius = 5)
|
||||
|> arc({
|
||||
angleStart = 120,
|
||||
angleEnd = 0,
|
||||
radius = 5
|
||||
}, %)
|
||||
|> line(end = [5, 0])
|
||||
|> line(end = [0, 10])
|
||||
|> bezierCurve(control1 = [-10, 0], control2 = [2, 10], end = [-5, 10])
|
||||
|> bezierCurve({
|
||||
control1 = [-10, 0],
|
||||
control2 = [2, 10],
|
||||
to = [-5, 10]
|
||||
}, %)
|
||||
|> line(end = [-5, -2])
|
||||
|> close()
|
||||
|> extrude(length = 10)
|
||||
@ -56,10 +64,18 @@ example = startSketchOn(XZ)
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([-10, 0], %)
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc({
|
||||
angleStart = 120,
|
||||
angleEnd = -60,
|
||||
radius = 5
|
||||
}, %)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
|> bezierCurve({
|
||||
control1 = [-3, 0],
|
||||
control2 = [2, 10],
|
||||
to = [-5, 10]
|
||||
}, %)
|
||||
|> line(end = [-4, 10])
|
||||
|> line(end = [-5, -2])
|
||||
|> close()
|
||||
@ -72,10 +88,18 @@ example = extrude(exampleSketch, length = 10)
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([-10, 0], %)
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc({
|
||||
angleStart = 120,
|
||||
angleEnd = -60,
|
||||
radius = 5
|
||||
}, %)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
|> bezierCurve({
|
||||
control1 = [-3, 0],
|
||||
control2 = [2, 10],
|
||||
to = [-5, 10]
|
||||
}, %)
|
||||
|> line(end = [-4, 10])
|
||||
|> line(end = [-5, -2])
|
||||
|> close()
|
||||
@ -88,10 +112,18 @@ example = extrude(exampleSketch, length = 20, symmetric = true)
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([-10, 0], %)
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc({
|
||||
angleStart = 120,
|
||||
angleEnd = -60,
|
||||
radius = 5
|
||||
}, %)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
|> bezierCurve({
|
||||
control1 = [-3, 0],
|
||||
control2 = [2, 10],
|
||||
to = [-5, 10]
|
||||
}, %)
|
||||
|> line(end = [-4, 10])
|
||||
|> line(end = [-5, -2])
|
||||
|> close()
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from centimeters to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from centimeters to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42cm`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in centimeters.
|
||||
|
||||
For example, if the current file uses inches, `fromCm(1)` will return `0.393701`. If the current file uses millimeters, `fromCm(1)` will return `10`. If the current file uses centimeters, `fromCm(1)` will return `1`.
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from feet to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from feet to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42ft`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in feet.
|
||||
|
||||
For example, if the current file uses inches, `fromFt(1)` will return `12`. If the current file uses millimeters, `fromFt(1)` will return `304.8`. If the current file uses feet, `fromFt(1)` will return `1`.
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from inches to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from inches to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42inch`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in inches.
|
||||
|
||||
For example, if the current file uses inches, `fromInches(1)` will return `1`. If the current file uses millimeters, `fromInches(1)` will return `25.4`.
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from meters to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from meters to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42m`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in meters.
|
||||
|
||||
For example, if the current file uses inches, `fromM(1)` will return `39.3701`. If the current file uses millimeters, `fromM(1)` will return `1000`. If the current file uses meters, `fromM(1)` will return `1`.
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from mm to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from mm to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42mm`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in millimeters.
|
||||
|
||||
For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`. If the current file uses millimeters, `fromMm(1)` will return `1`.
|
||||
|
@ -4,12 +4,8 @@ excerpt: "Converts a number from yards to the current default unit."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Converts a number from yards to the current default unit.
|
||||
|
||||
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42yd`) or the `to...` conversion functions.
|
||||
|
||||
No matter what units the current file uses, this function will always return a number equivalent to the input in yards.
|
||||
|
||||
For example, if the current file uses inches, `fromYd(1)` will return `36`. If the current file uses millimeters, `fromYd(1)` will return `914.4`. If the current file uses yards, `fromYd(1)` will return `1`.
|
||||
|
@ -9,7 +9,7 @@ Get the next adjacent edge to the edge given.
|
||||
|
||||
|
||||
```js
|
||||
getNextAdjacentEdge(edge: TagIdentifier): Uuid
|
||||
getNextAdjacentEdge(tag: TagIdentifier): Uuid
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ getNextAdjacentEdge(edge: TagIdentifier): Uuid
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the next adjacent edge of. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Get the opposite edge to the edge given.
|
||||
|
||||
|
||||
```js
|
||||
getOppositeEdge(edge: TagIdentifier): Uuid
|
||||
getOppositeEdge(tag: TagIdentifier): Uuid
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ getOppositeEdge(edge: TagIdentifier): Uuid
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the opposite edge of. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Get the previous adjacent edge to the edge given.
|
||||
|
||||
|
||||
```js
|
||||
getPreviousAdjacentEdge(edge: TagIdentifier): Uuid
|
||||
getPreviousAdjacentEdge(tag: TagIdentifier): Uuid
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ getPreviousAdjacentEdge(edge: TagIdentifier): Uuid
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the previous adjacent edge of. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -22,8 +22,18 @@ layout: manual
|
||||
* [`string`](kcl/types/string)
|
||||
* [`tag`](kcl/types/tag)
|
||||
* **std**
|
||||
* [`Axis2d`](kcl/types/Axis2d)
|
||||
* [`Axis3d`](kcl/types/Axis3d)
|
||||
* [`END`](kcl/consts/std-END)
|
||||
* [`Edge`](kcl/types/Edge)
|
||||
* [`Face`](kcl/types/Face)
|
||||
* [`Helix`](kcl/types/Helix)
|
||||
* [`Plane`](kcl/types/Plane)
|
||||
* [`Point2d`](kcl/types/Point2d)
|
||||
* [`Point3d`](kcl/types/Point3d)
|
||||
* [`START`](kcl/consts/std-START)
|
||||
* [`Sketch`](kcl/types/Sketch)
|
||||
* [`Solid`](kcl/types/Solid)
|
||||
* [`X`](kcl/consts/std-X)
|
||||
* [`XY`](kcl/consts/std-XY)
|
||||
* [`XZ`](kcl/consts/std-XZ)
|
||||
@ -32,24 +42,36 @@ layout: manual
|
||||
* [`Z`](kcl/consts/std-Z)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
* [`angleToMatchLengthY`](kcl/angleToMatchLengthY)
|
||||
* [`angledLine`](kcl/angledLine)
|
||||
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
||||
* [`appearance`](kcl/appearance)
|
||||
* [`arc`](kcl/arc)
|
||||
* [`arcTo`](kcl/arcTo)
|
||||
* [`asin`](kcl/asin)
|
||||
* [`assert`](kcl/assert)
|
||||
* [`assertIs`](kcl/assertIs)
|
||||
* [`assertEqual`](kcl/assertEqual)
|
||||
* [`assertGreaterThan`](kcl/assertGreaterThan)
|
||||
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
|
||||
* [`assertLessThan`](kcl/assertLessThan)
|
||||
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
||||
* [`atan`](kcl/atan)
|
||||
* [`atan2`](kcl/atan2)
|
||||
* [`bezierCurve`](kcl/bezierCurve)
|
||||
* [`ceil`](kcl/ceil)
|
||||
* [`chamfer`](kcl/chamfer)
|
||||
* [`circleThreePoint`](kcl/circleThreePoint)
|
||||
* [`clone`](kcl/clone)
|
||||
* [`close`](kcl/close)
|
||||
* [`extrude`](kcl/extrude)
|
||||
* [`fillet`](kcl/fillet)
|
||||
* [`floor`](kcl/floor)
|
||||
* [`fromCm`](kcl/fromCm)
|
||||
* [`fromFt`](kcl/fromFt)
|
||||
* [`fromInches`](kcl/fromInches)
|
||||
* [`fromM`](kcl/fromM)
|
||||
* [`fromMm`](kcl/fromMm)
|
||||
* [`fromYd`](kcl/fromYd)
|
||||
* [`getCommonEdge`](kcl/getCommonEdge)
|
||||
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||
@ -73,7 +95,7 @@ layout: manual
|
||||
* [`map`](kcl/map)
|
||||
* [`max`](kcl/max)
|
||||
* [`min`](kcl/min)
|
||||
* [`offsetPlane`](kcl/std-offsetPlane)
|
||||
* [`offsetPlane`](kcl/offsetPlane)
|
||||
* [`patternCircular2d`](kcl/patternCircular2d)
|
||||
* [`patternCircular3d`](kcl/patternCircular3d)
|
||||
* [`patternLinear2d`](kcl/patternLinear2d)
|
||||
@ -109,14 +131,8 @@ layout: manual
|
||||
* [`sweep`](kcl/sweep)
|
||||
* [`tangentToEnd`](kcl/tangentToEnd)
|
||||
* [`tangentialArc`](kcl/tangentialArc)
|
||||
* [`toCentimeters`](kcl/std-toCentimeters)
|
||||
* [`toDegrees`](kcl/std-toDegrees)
|
||||
* [`toFeet`](kcl/std-toFeet)
|
||||
* [`toInches`](kcl/std-toInches)
|
||||
* [`toMeters`](kcl/std-toMeters)
|
||||
* [`toMillimeters`](kcl/std-toMillimeters)
|
||||
* [`toRadians`](kcl/std-toRadians)
|
||||
* [`toYards`](kcl/std-toYards)
|
||||
* [`toDegrees`](kcl/toDegrees)
|
||||
* [`toRadians`](kcl/toRadians)
|
||||
* [`translate`](kcl/translate)
|
||||
* [`union`](kcl/union)
|
||||
* [`xLine`](kcl/xLine)
|
||||
@ -137,14 +153,3 @@ layout: manual
|
||||
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
|
||||
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
|
||||
* **std::types**
|
||||
* [`Axis2d`](kcl/types/Axis2d)
|
||||
* [`Axis3d`](kcl/types/Axis3d)
|
||||
* [`Edge`](kcl/types/Edge)
|
||||
* [`Face`](kcl/types/Face)
|
||||
* [`Helix`](kcl/types/Helix)
|
||||
* [`Plane`](kcl/types/Plane)
|
||||
* [`Point2d`](kcl/types/Point2d)
|
||||
* [`Point3d`](kcl/types/Point3d)
|
||||
* [`Sketch`](kcl/types/Sketch)
|
||||
* [`Solid`](kcl/types/Solid)
|
||||
|
@ -34,7 +34,7 @@ int(num: number): number
|
||||
|
||||
```js
|
||||
n = int(ceil(5 / 2))
|
||||
assert(n, isEqualTo = 3, error = "5/2 = 2.5, rounded up makes 3")
|
||||
assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
|
||||
// Draw n cylinders.
|
||||
startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 2)
|
||||
|
@ -22,5 +22,3 @@ once fixed in engine will just start working here with no language changes.
|
||||
chamfer cases work currently.
|
||||
|
||||
- **Appearance**: Changing the appearance on a loft does not work.
|
||||
|
||||
- **CSG Booleans**: Coplanar (bodies that share a plane) unions, subtractions, and intersections are not currently supported.
|
||||
|
@ -24,8 +24,8 @@ legAngX(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,7 +35,7 @@ legAngX(
|
||||
### Examples
|
||||
|
||||
```js
|
||||
legAngX(hypotenuse = 5, leg = 3)
|
||||
legAngX(5, 3)
|
||||
```
|
||||
|
||||
|
||||
|
@ -24,8 +24,8 @@ legAngY(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,7 +35,7 @@ legAngY(
|
||||
### Examples
|
||||
|
||||
```js
|
||||
legAngY(hypotenuse = 5, leg = 3)
|
||||
legAngY(5, 3)
|
||||
```
|
||||
|
||||
|
||||
|
@ -24,8 +24,8 @@ legLen(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
||||
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,7 +35,7 @@ legLen(
|
||||
### Examples
|
||||
|
||||
```js
|
||||
legLen(hypotenuse = 5, leg = 3)
|
||||
legLen(5, 3)
|
||||
```
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ isolated from other files as a separate module.
|
||||
When you define a function, you can use `export` before it to make it available
|
||||
to other modules.
|
||||
|
||||
```kcl
|
||||
```
|
||||
// util.kcl
|
||||
export fn increment(x) {
|
||||
return x + 1
|
||||
@ -31,11 +31,11 @@ Imported files _must_ be in the same project so that units are uniform across
|
||||
modules. This means that it must be in the same directory.
|
||||
|
||||
Import statements must be at the top-level of a file. It is not allowed to have
|
||||
an `import` statement inside a function or in the body of an if‑else.
|
||||
an `import` statement inside a function or in the body of an if-else.
|
||||
|
||||
Multiple functions can be exported in a file.
|
||||
|
||||
```kcl
|
||||
```
|
||||
// util.kcl
|
||||
export fn increment(x) {
|
||||
return x + 1
|
||||
@ -58,211 +58,6 @@ Imported symbols can be renamed for convenience or to avoid name collisions.
|
||||
import increment as inc, decrement as dec from "util.kcl"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Functions vs `clone`
|
||||
|
||||
There are two common patterns for re‑using geometry:
|
||||
|
||||
1. **Wrap the construction in a function** – flexible and fully parametric.
|
||||
2. **Duplicate an existing object with `clone`** – lightning‑fast, but an exact
|
||||
duplicate.
|
||||
|
||||
### Parametric function example
|
||||
|
||||
```kcl
|
||||
fn cube(center) {
|
||||
return startSketchOn(XY)
|
||||
|> startProfileAt([center[0] - 10, center[1] - 10], %)
|
||||
|> line(endAbsolute = [center[0] + 10, center[1] - 10])
|
||||
|> line(endAbsolute = [center[0] + 10, center[1] + 10])
|
||||
|> line(endAbsolute = [center[0] - 10, center[1] + 10])
|
||||
|> close()
|
||||
|> extrude(length = 10)
|
||||
}
|
||||
|
||||
myCube = cube([0, 0])
|
||||
```
|
||||
|
||||
*Pros*
|
||||
- Any argument can be a parameter – size, position, appearance, etc.
|
||||
- Works great inside loops, arrays, or optimisation sweeps.
|
||||
|
||||
*Cons*
|
||||
- Every invocation rebuilds the entire feature tree.
|
||||
- **Slower** than a straight duplicate – each call is its own render job.
|
||||
|
||||
### `clone` example
|
||||
|
||||
```kcl
|
||||
sketch001 = startSketchOn(-XZ)
|
||||
|> circle(center = [0, 0], radius = 10)
|
||||
|> extrude(length = 5)
|
||||
|> appearance(color = "#ff0000", metalness = 90, roughness = 90)
|
||||
|
||||
sketch002 = clone(sketch001) // ✓ instant copy
|
||||
```
|
||||
|
||||
*Pros*
|
||||
- Roughly an O(1) operation – we just duplicate the underlying engine handle.
|
||||
- Perfect when you need ten identical bolts or two copies of the same imported STEP file.
|
||||
|
||||
*Cons*
|
||||
- **Not parametric** – the clone is exactly the same shape as the source.
|
||||
- If you need to tweak dimensions per‑instance, you’re back to a function.
|
||||
|
||||
> **Rule of thumb** – Reach for `clone` when the geometry is already what you want. Reach for a function when you need customisation.
|
||||
|
||||
---
|
||||
|
||||
## Module‑level parallelism
|
||||
|
||||
Under the hood, the Design Studio runs **every module in parallel** where it can. This means:
|
||||
|
||||
- The top‑level code of `foo.kcl`, `bar.kcl`, and `baz.kcl` all start executing immediately and concurrently.
|
||||
- Imports that read foreign files (STEP/OBJ/…) overlap their I/O and background render.
|
||||
- CPU‑bound calculations in separate modules get their own worker threads.
|
||||
|
||||
### Why modules beat one‑big‑file
|
||||
|
||||
If you shoe‑horn everything into `main.kcl`, each statement runs sequentially:
|
||||
|
||||
```norun
|
||||
import "big.step" as gizmo // blocks main while reading
|
||||
|
||||
gizmo |> translate(x=50) // blocks again while waiting for render
|
||||
```
|
||||
|
||||
Split `gizmo` into its own file and the read/render can overlap whatever else `main.kcl` is doing.
|
||||
|
||||
```norun
|
||||
// gizmo.kcl (worker A)
|
||||
import "big.step"
|
||||
|
||||
// main.kcl (worker B)
|
||||
import "gizmo.kcl" as gizmo // non‑blocking
|
||||
|
||||
// ... other setup ...
|
||||
|
||||
gizmo |> translate(x=50) // only blocks here
|
||||
```
|
||||
|
||||
### Gotcha: defining but **not** calling functions
|
||||
|
||||
Defining a function inside a module is instantaneous – we just record the byte‑code. The heavy lifting happens when the function is **called**. So:
|
||||
|
||||
```norun
|
||||
// util.kcl
|
||||
export fn makeBolt(size) { /* … expensive CAD … */ }
|
||||
```
|
||||
|
||||
If `main.kcl` waits until the very end to call `makeBolt`, *none* of that work was parallelised – you’ve pushed the cost back onto the serial tail of your script.
|
||||
|
||||
**Better:** call it early or move the invocation into another module.
|
||||
|
||||
```norun
|
||||
// bolt_instance.kcl
|
||||
import makeBolt from "util.kcl"
|
||||
bolt = makeBolt(5) // executed in parallel
|
||||
bolt
|
||||
```
|
||||
|
||||
Now `main.kcl` can `import "bolt_instance.kcl" as bolt` and get the result that was rendered while it was busy doing other things.
|
||||
|
||||
---
|
||||
|
||||
## Whole module import
|
||||
|
||||
You can also import the whole module. This is useful if you want to use the
|
||||
result of a module as a variable, like a part.
|
||||
|
||||
```norun
|
||||
import "tests/inputs/cube.kcl" as cube
|
||||
cube
|
||||
|> translate(x=10)
|
||||
```
|
||||
|
||||
This imports the whole module and makes it available as `cube`. You can then
|
||||
use it like any other object. The `cube` variable is now a reference to the
|
||||
result of the module. This means that if you change the module, the `cube`
|
||||
variable will change as well.
|
||||
|
||||
In `cube.kcl`, you cannot have multiple objects. It has to be a single part. If
|
||||
you have multiple objects, you will get an error. This is because the module is
|
||||
expected to return a single object that can be used as a variable.
|
||||
|
||||
You also cannot assign that object to a variable. This is because the module is
|
||||
expected to return a single object that can be used as a variable.
|
||||
|
||||
So for example, this is not allowed:
|
||||
|
||||
```norun
|
||||
... a bunch of code to create cube and cube2 ...
|
||||
|
||||
myUnion = union([cube, cube2])
|
||||
```
|
||||
|
||||
What you need to do instead is:
|
||||
|
||||
```norun
|
||||
... a bunch of code to create cube and cube2 ...
|
||||
|
||||
union([cube, cube2])
|
||||
```
|
||||
|
||||
That way the last line will return the union of the two objects.
|
||||
|
||||
Or what you could do instead is:
|
||||
|
||||
```norun
|
||||
... a bunch of code to create cube and cube2 ...
|
||||
|
||||
myUnion = union([cube, cube2])
|
||||
myUnion
|
||||
```
|
||||
|
||||
This will return the union of the two objects, but it will not be assigned to a
|
||||
variable. This is because the module is expected to return a single object that
|
||||
can be used as a variable.
|
||||
|
||||
---
|
||||
|
||||
## Multiple instances of the same import
|
||||
|
||||
Whether you are importing a file from another CAD system or a KCL file, that
|
||||
file represents object(s) in memory. If you import the same file multiple times,
|
||||
it will only be rendered once.
|
||||
|
||||
If you want to have multiple instances of the same object, you can use the
|
||||
[`clone`](/docs/kcl/clone) function. This will render a new instance of the object in memory.
|
||||
|
||||
```norun
|
||||
import cube from "tests/inputs/cube.kcl"
|
||||
|
||||
cube
|
||||
|> translate(x=10)
|
||||
clone(cube)
|
||||
|> translate(x=20)
|
||||
```
|
||||
|
||||
In the sample above, the `cube` object is imported from a KCL file. The first
|
||||
instance is translated 10 units in the x direction. The second instance is
|
||||
cloned and translated 20 units in the x direction. The two instances are now
|
||||
separate objects in memory, and can be manipulated independently.
|
||||
|
||||
Here is an example with a file from another CAD system:
|
||||
|
||||
```kcl
|
||||
import "tests/inputs/cube.step" as cube
|
||||
|
||||
cube
|
||||
|> translate(x=10)
|
||||
clone(cube)
|
||||
|> translate(x=20)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Importing files from other CAD systems
|
||||
|
||||
`import` can also be used to import files from other CAD systems. The format of the statement is the
|
||||
@ -274,17 +69,25 @@ import "tests/inputs/cube.obj"
|
||||
// Use `cube` just like a KCL object.
|
||||
```
|
||||
|
||||
```kcl
|
||||
import "tests/inputs/cube.sldprt" as cube
|
||||
```norun
|
||||
import "tests/inputs/cube-2.sldprt" as cube
|
||||
|
||||
// Use `cube` just like a KCL object.
|
||||
```
|
||||
|
||||
You can make the file format explicit using a format attribute (useful if using a different
|
||||
extension), e.g.,
|
||||
|
||||
```norun
|
||||
@(format = obj)
|
||||
import "tests/inputs/cube"
|
||||
```
|
||||
|
||||
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
|
||||
unit of measurement is millimeters. Alternatively you may specify the unit
|
||||
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
|
||||
by using an attirbute. Likewise, you can also specify a coordinate system. E.g.,
|
||||
|
||||
```kcl
|
||||
```norun
|
||||
@(unitLength = ft, coords = opengl)
|
||||
import "tests/inputs/cube.obj"
|
||||
```
|
||||
@ -307,55 +110,97 @@ Coordinate systems:
|
||||
- `opengl`, forward: +Z, up: +Y, handedness: right
|
||||
- `vulkan`, forward: +Z, up: -Y, handedness: left
|
||||
|
||||
---
|
||||
### Performance
|
||||
|
||||
## Performance deep‑dive for foreign‑file imports
|
||||
|
||||
Parallelized foreign‑file imports now let you overlap file reads, initialization,
|
||||
Parallelized foreign-file imports now let you overlap file reads, initialization,
|
||||
and rendering. To maximize throughput, you need to understand the three distinct
|
||||
stages—reading, initializing (background render start), and invocation (blocking)
|
||||
—and structure your code to defer blocking operations until the end.
|
||||
|
||||
### Foreign import execution stages
|
||||
#### Foreign Import Execution Stages
|
||||
|
||||
1. **Import (Read / Initialization) Stage**
|
||||
```kcl
|
||||
1. **Import (Read) Stage**
|
||||
```norun
|
||||
import "tests/inputs/cube.step" as cube
|
||||
```
|
||||
- Reads the file from disk and makes its API available.
|
||||
- Starts engine rendering but **does not block** your script.
|
||||
- This kick‑starts the render pipeline while you keep executing other code.
|
||||
- **Does _not_** start Engine rendering or block your script.
|
||||
|
||||
2. **Invocation (Blocking) Stage**
|
||||
```kcl
|
||||
2. **Initialization (Background Render) Stage**
|
||||
```norun
|
||||
import "tests/inputs/cube.step" as cube
|
||||
|
||||
cube
|
||||
|> translate(z=10) // ← blocks here only
|
||||
myCube = cube // <- This line starts background rendering
|
||||
```
|
||||
- Invoking the imported symbol (assignment or plain call) triggers Engine rendering _in the background_.
|
||||
- This kick‑starts the render pipeline but doesn’t block—you can continue other work while the Engine processes the model.
|
||||
|
||||
3. **Invocation (Blocking) Stage**
|
||||
```norun
|
||||
import "tests/inputs/cube.step" as cube
|
||||
|
||||
myCube = cube
|
||||
|
||||
myCube
|
||||
|> translate(z=10) // <- This line blocks
|
||||
```
|
||||
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
|
||||
- This is the only point where your script will block.
|
||||
|
||||
### Best practices
|
||||
> **Nuance:** Foreign imports differ from pure KCL modules—calling the same import symbol multiple times (e.g., `screw` twice) starts background rendering twice.
|
||||
|
||||
#### 1. Defer blocking calls
|
||||
#### Best Practices
|
||||
|
||||
```kcl
|
||||
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
|
||||
##### 1. Defer Blocking Calls
|
||||
Initialize early but delay all transformations until after your heavy computation:
|
||||
```norun
|
||||
import "tests/inputs/cube.step" as cube // 1) Read
|
||||
|
||||
myCube = cube // 2) Background render starts
|
||||
|
||||
|
||||
// --- perform other operations and calculations here ---
|
||||
// --- perform other operations and calculations or setup here ---
|
||||
|
||||
|
||||
cube
|
||||
|> translate(z=10) // 2) Blocks only here
|
||||
myCube
|
||||
|> translate(z=10) // 3) Blocks only here
|
||||
```
|
||||
|
||||
#### 2. Split heavy work into separate modules
|
||||
##### 2. Encapsulate Imports in Modules
|
||||
Keep `main.kcl` free of reads and initialization; wrap them:
|
||||
|
||||
Place computationally expensive or IO‑heavy work into its own module so it can render in parallel while `main.kcl` continues.
|
||||
|
||||
#### Future improvements
|
||||
|
||||
Upcoming releases will auto‑analyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.
|
||||
```norun
|
||||
// imports.kcl
|
||||
import "tests/inputs/cube.step" as cube // Read only
|
||||
|
||||
|
||||
export myCube = cube // Kick off rendering
|
||||
```
|
||||
|
||||
```norun
|
||||
// main.kcl
|
||||
import myCube from "imports.kcl" // Import the initialized object
|
||||
|
||||
|
||||
// ... computations ...
|
||||
|
||||
|
||||
myCube
|
||||
|> translate(z=10) // Blocking call at the end
|
||||
```
|
||||
|
||||
##### 3. Avoid Immediate Method Calls
|
||||
|
||||
```norun
|
||||
import "tests/inputs/cube.step" as cube
|
||||
|
||||
cube
|
||||
|> translate(z=10) // Blocks immediately, negating parallelism
|
||||
```
|
||||
|
||||
Both calling methods right on `cube` immediately or leaving an implicit import without assignment introduce blocking.
|
||||
|
||||
#### Future Improvements
|
||||
|
||||
Upcoming releases will auto‑analyze dependencies and only block when truly necessary. Until then, explicit deferral and modular wrapping give you the best performance.
|
||||
|
||||
|
121
docs/kcl/offsetPlane.md
Normal file
121
docs/kcl/offsetPlane.md
Normal file
File diff suppressed because one or more lines are too long
@ -174,7 +174,7 @@ t = 0.005 // taper factor [0-1)
|
||||
// Defines how to modify each layer of the vase.
|
||||
// Each replica is shifted up the Z axis, and has a smoothly-varying radius
|
||||
fn transform(replicaId) {
|
||||
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8: number(rad)))
|
||||
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))
|
||||
return {
|
||||
translate = [0, 0, replicaId * 10],
|
||||
scale = [scale, scale, 0]
|
||||
@ -205,12 +205,12 @@ fn transform(i) {
|
||||
}
|
||||
startSketchOn(XY)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> polygon(
|
||||
|> polygon({
|
||||
radius = 10,
|
||||
numSides = 4,
|
||||
center = [0, 0],
|
||||
inscribed = false,
|
||||
)
|
||||
inscribed = false
|
||||
}, %)
|
||||
|> extrude(length = 4)
|
||||
|> patternTransform(instances = 3, transform = transform)
|
||||
```
|
||||
|
@ -10,11 +10,9 @@ Create a regular polygon with the specified number of sides that is either inscr
|
||||
|
||||
```js
|
||||
polygon(
|
||||
data: PolygonData,
|
||||
sketchSurfaceOrGroup: SketchOrSurface,
|
||||
radius: number,
|
||||
numSides: u64,
|
||||
center: [number],
|
||||
inscribed?: bool,
|
||||
tag?: TagDeclarator,
|
||||
): Sketch
|
||||
```
|
||||
|
||||
@ -23,11 +21,9 @@ polygon(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on | Yes |
|
||||
| `radius` | [`number`](/docs/kcl/types/number) | The radius of the polygon | Yes |
|
||||
| `numSides` | `u64` | The number of sides in the polygon | Yes |
|
||||
| `center` | [`[number]`](/docs/kcl/types/number) | The center point of the polygon | Yes |
|
||||
| `inscribed` | [`bool`](/docs/kcl/types/bool) | Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius | No |
|
||||
| `data` | [`PolygonData`](/docs/kcl/types/PolygonData) | Data for drawing a polygon | Yes |
|
||||
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -39,12 +35,12 @@ polygon(
|
||||
```js
|
||||
// Create a regular hexagon inscribed in a circle of radius 10
|
||||
hex = startSketchOn(XY)
|
||||
|> polygon(
|
||||
|> polygon({
|
||||
radius = 10,
|
||||
numSides = 6,
|
||||
center = [0, 0],
|
||||
inscribed = true,
|
||||
)
|
||||
inscribed = true
|
||||
}, %)
|
||||
|
||||
example = extrude(hex, length = 5)
|
||||
```
|
||||
@ -54,12 +50,12 @@ example = extrude(hex, length = 5)
|
||||
```js
|
||||
// Create a square circumscribed around a circle of radius 5
|
||||
square = startSketchOn(XY)
|
||||
|> polygon(
|
||||
|> polygon({
|
||||
radius = 5.0,
|
||||
numSides = 4,
|
||||
center = [10, 10],
|
||||
inscribed = false,
|
||||
)
|
||||
inscribed = false
|
||||
}, %)
|
||||
example = extrude(square, length = 5)
|
||||
```
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin value.
|
||||
|
||||
|
||||
```js
|
||||
profileStart(profile: Sketch): [number]
|
||||
profileStart(sketch: Sketch): [number]
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ profileStart(profile: Sketch): [number]
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'x' value.
|
||||
|
||||
|
||||
```js
|
||||
profileStartX(profile: Sketch): number
|
||||
profileStartX(sketch: Sketch): number
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ profileStartX(profile: Sketch): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'y' value.
|
||||
|
||||
|
||||
```js
|
||||
profileStartY(profile: Sketch): number
|
||||
profileStartY(sketch: Sketch): number
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ profileStartY(profile: Sketch): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
|
||||
### 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
@ -26,7 +26,7 @@ helix(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `revolutions` | `number(_)` | Number of revolutions. | Yes |
|
||||
| `angleStart` | `number(Angle)` | Start angle. | Yes |
|
||||
| `angleStart` | `number(Angle)` | Start angle (in degrees). | Yes |
|
||||
| `ccw` | [`bool`](/docs/kcl/types/bool) | Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||
| `radius` | `number(Length)` | Radius of the helix. | No |
|
||||
| `axis` | `Axis3d | Edge` | Axis to use for the helix. | No |
|
||||
|
@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine(
|
||||
angle = 30,
|
||||
length = 3 / cos(30deg),
|
||||
length = 3 / cos(toRadians(30)),
|
||||
)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
@ -11,7 +11,7 @@ cartesian (x/y/z grid) coordinates.
|
||||
|
||||
```js
|
||||
polar(
|
||||
angle: number(rad),
|
||||
angle: number(Angle),
|
||||
length: number(Length),
|
||||
): Point2d
|
||||
```
|
||||
@ -21,7 +21,7 @@ polar(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `angle` | `number(rad)` | | Yes |
|
||||
| `angle` | `number(Angle)` | | Yes |
|
||||
| `length` | `number(Length)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine(
|
||||
angle = 50,
|
||||
length = 15 / sin(135deg),
|
||||
length = 15 / sin(toRadians(135)),
|
||||
)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine(
|
||||
angle = 50,
|
||||
length = 50 * tan((1/2): number(rad)),
|
||||
length = 50 * tan(1/2),
|
||||
)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toCentimeters"
|
||||
excerpt: "Convert a number to centimeters from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Convert a number to centimeters from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toCentimeters(@num: number(cm)): number(cm)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(cm)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(cm)`
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toFeet"
|
||||
excerpt: "Convert a number to feet from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Convert a number to feet from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toFeet(@num: number(ft)): number(ft)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(ft)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(ft)`
|
||||
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toInches"
|
||||
excerpt: "Convert a number to inches from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Convert a number to inches from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toInches(@num: number(in)): number(in)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(in)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(in)`
|
||||
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toMeters"
|
||||
excerpt: "Convert a number to meters from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Convert a number to meters from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toMeters(@num: number(m)): number(m)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(m)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(m)`
|
||||
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toMillimeters"
|
||||
excerpt: "Convert a number to millimeters from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Convert a number to millimeters from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toMillimeters(@num: number(mm)): number(mm)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(mm)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(mm)`
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: "std::toYards"
|
||||
excerpt: "Converts a number to yards from its current units."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Converts a number to yards from its current units.
|
||||
|
||||
|
||||
|
||||
```js
|
||||
toYards(@num: number(yd)): number(yd)
|
||||
```
|
||||
|
||||
|
||||
### Arguments
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `num` | `number(yd)` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
`number(yd)`
|
||||
|
||||
|
||||
|
68822
docs/kcl/std.json
68822
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -73,7 +73,7 @@ rectangleExtrude = extrude(rectangleSketch, length = 10)
|
||||
```js
|
||||
bottom = startSketchOn(XY)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> arc(endAbsolute = [10, 10], interiorAbsolute = [5, 1], tag = $arc1)
|
||||
|> arcTo({ end = [10, 10], interior = [5, 1] }, %, $arc1)
|
||||
|> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
|> close()
|
||||
```
|
||||
|
45
docs/kcl/toDegrees.md
Normal file
45
docs/kcl/toDegrees.md
Normal file
File diff suppressed because one or more lines are too long
45
docs/kcl/toRadians.md
Normal file
45
docs/kcl/toRadians.md
Normal file
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Axis2d"
|
||||
title: "std::Axis2d"
|
||||
excerpt: "An infinite line in 2d space."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Axis3d"
|
||||
title: "std::Axis3d"
|
||||
excerpt: "An infinite line in 3d space."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Edge"
|
||||
title: "std::Edge"
|
||||
excerpt: "The edge of a solid."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -51,26 +51,6 @@ An extruded arc.
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
An extruded involute.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `extrudeInvolute`| | No |
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude surface. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
Geometry metadata.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Face"
|
||||
title: "std::Face"
|
||||
excerpt: "A face."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -1,84 +0,0 @@
|
||||
---
|
||||
title: "GeometryWithImportedGeometry"
|
||||
excerpt: "A geometry including an imported geometry."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A geometry including an imported geometry.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Sketch`](/docs/kcl/types/Sketch)| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the sketch (this will change when the engine's reference to it changes). | No |
|
||||
| `paths` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||
| `originalId` |[`string`](/docs/kcl/types/string)| | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Solid`](/docs/kcl/types/Solid)| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the solid. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
||||
| `height` |[`number`](/docs/kcl/types/number)| The height of the solid. | No |
|
||||
| `startCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion start cap | No |
|
||||
| `endCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion end cap | No |
|
||||
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| The units of the solid. | No |
|
||||
| `sectional` |`boolean`| Is this a sectional solid? | No |
|
||||
|
||||
|
||||
----
|
||||
Data for an imported geometry.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ImportedGeometry`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
|
||||
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Helix"
|
||||
title: "std::Helix"
|
||||
excerpt: "A helix."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -141,8 +141,8 @@ A base path.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ArcThreePoint`| | No |
|
||||
| `p1` |`[number, number]`| Point 1 of the arc (base on the end of previous segment) | No |
|
||||
| `p2` |`[number, number]`| Point 2 of the arc (interiorAbsolute kwarg) | No |
|
||||
| `p3` |`[number, number]`| Point 3 of the arc (endAbsolute kwarg) | No |
|
||||
| `p2` |`[number, number]`| Point 2 of the arc (interior kwarg) | No |
|
||||
| `p3` |`[number, number]`| Point 3 of the arc (end kwarg) | No |
|
||||
| `from` |`[number, number]`| The from point. | No |
|
||||
| `to` |`[number, number]`| The to point. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
@ -241,31 +241,6 @@ A circular arc, not necessarily tangential to the current point.
|
||||
|
||||
|
||||
----
|
||||
An involute of a circle of start_radius ending at end_radius
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `CircularInvolute`| | No |
|
||||
| `start_radius` |[`number`](/docs/kcl/types/number)| The radius of the base circle of the involute | No |
|
||||
| `end_radius` |[`number`](/docs/kcl/types/number)| The radius that the involute ends at | No |
|
||||
| `angle` |[`number`](/docs/kcl/types/number)| Angle about which the whole involute is rotated | No |
|
||||
| `reverse` |`boolean`| If true, the path segment starts at the end radius and goes towards the start radius | No |
|
||||
| `from` |`[number, number]`| The from point. | No |
|
||||
| `to` |`[number, number]`| The to point. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
|
||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Plane"
|
||||
title: "std::Plane"
|
||||
excerpt: "A plane."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Point2d"
|
||||
title: "std::Point2d"
|
||||
excerpt: "A point in two dimensional space."
|
||||
layout: manual
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "std::types::Point3d"
|
||||
title: "std::Point3d"
|
||||
excerpt: "A point in three dimensional space."
|
||||
layout: manual
|
||||
---
|
||||
|
File diff suppressed because one or more lines are too long
@ -30,6 +30,8 @@ A sketch type.
|
||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
|
||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
|
||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -51,6 +53,7 @@ A face.
|
||||
| `value` |[`string`](/docs/kcl/types/string)| The tag of the face. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
|
||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -12,7 +12,7 @@ The syntax for declaring a tag is `$myTag` you would use it in the following
|
||||
way:
|
||||
|
||||
```js
|
||||
startSketchOn(XZ)
|
||||
startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|
||||
|> angledLine(
|
||||
@ -46,7 +46,7 @@ However if the code was written like this:
|
||||
|
||||
```js
|
||||
fn rect(origin) {
|
||||
return startSketchOn(XZ)
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|
||||
|> angledLine(
|
||||
@ -75,7 +75,7 @@ For example the following code works.
|
||||
|
||||
```js
|
||||
fn rect(origin) {
|
||||
return startSketchOn(XZ)
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt(origin, %)
|
||||
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|
||||
|> angledLine(
|
||||
|
@ -29,11 +29,11 @@ test.describe('Electron app header tests', () => {
|
||||
test(
|
||||
'User settings has correct shortcut',
|
||||
{ tag: '@electron' },
|
||||
async ({ page, toolbar }, testInfo) => {
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Open the user sidebar menu.
|
||||
await toolbar.userSidebarButton.click()
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
|
||||
// No space after "User settings" since it's textContent.
|
||||
const text =
|
||||
|
@ -1,70 +0,0 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
// test file is for testing auth functionality
|
||||
test.describe('Authentication tests', () => {
|
||||
test(
|
||||
`The user can sign out and back in`,
|
||||
{ tag: ['@electron'] },
|
||||
async ({ page, homePage, signInPage, toolbar, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.projectSection.waitFor()
|
||||
|
||||
// This is only needed as an override to test-utils' setup() for this test
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', '')
|
||||
})
|
||||
|
||||
await test.step('Click on sign out and expect sign in page', async () => {
|
||||
await toolbar.userSidebarButton.click()
|
||||
await toolbar.signOutButton.click()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step("Refresh doesn't log the user back in", async () => {
|
||||
await page.reload()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Click on sign in and cancel, click again and expect different code', async () => {
|
||||
await signInPage.signInButton.click()
|
||||
await expect(signInPage.userCode).toBeVisible()
|
||||
const firstUserCode = await signInPage.userCode.textContent()
|
||||
await signInPage.cancelSignInButton.click()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
|
||||
await signInPage.signInButton.click()
|
||||
await expect(signInPage.userCode).toBeVisible()
|
||||
const secondUserCode = await signInPage.userCode.textContent()
|
||||
expect(secondUserCode).not.toEqual(firstUserCode)
|
||||
await signInPage.cancelSignInButton.click()
|
||||
})
|
||||
|
||||
await test.step('Press back button and remain on home page', async () => {
|
||||
await page.goBack()
|
||||
await expect(homePage.projectSection).not.toBeVisible()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Sign in, activate, and expect home page', async () => {
|
||||
await signInPage.signInButton.click()
|
||||
await expect(signInPage.userCode).toBeVisible()
|
||||
const userCode = await signInPage.userCode.textContent()
|
||||
expect(userCode).not.toBeNull()
|
||||
await signInPage.verifyAndConfirmAuth(userCode!)
|
||||
|
||||
// Longer timeout than usual here for the wait on home page
|
||||
await expect(homePage.projectSection).toBeVisible({ timeout: 10000 })
|
||||
})
|
||||
|
||||
await test.step('Click on sign out and expect sign in page', async () => {
|
||||
await toolbar.userSidebarButton.click()
|
||||
await toolbar.signOutButton.click()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
@ -155,7 +155,7 @@ async function doBasicSketch(
|
||||
|> xLine(length = -segLen(seg01))`)
|
||||
}
|
||||
|
||||
test.describe('Basic sketch', () => {
|
||||
test.describe('Basic sketch', { tag: ['@skipWin'] }, () => {
|
||||
test('code pane open at start', async ({ page, homePage }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
await doBasicSketch(page, homePage, ['code'])
|
||||
|
@ -8,7 +8,10 @@ import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
test.describe(
|
||||
'Can create sketches on all planes and their back sides',
|
||||
{ tag: ['@skipWin'] },
|
||||
() => {
|
||||
const sketchOnPlaneAndBackSideTest = async (
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
@ -130,4 +133,5 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Code pane and errors', () => {
|
||||
test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||
page,
|
||||
homePage,
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Command bar tests', () => {
|
||||
test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
test('Extrude from command bar selects extrude line after', async ({
|
||||
page,
|
||||
homePage,
|
||||
@ -179,10 +179,10 @@ test.describe('Command bar tests', () => {
|
||||
await expect(commandLevelArgButton).toHaveText('level: project')
|
||||
})
|
||||
|
||||
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
test(
|
||||
'Command bar keybinding works from code editor and can change a setting',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
@ -217,10 +217,9 @@ test.describe('Command bar tests', () => {
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
|
||||
'data-headlessui-state',
|
||||
'active'
|
||||
)
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'system' })
|
||||
).toHaveAttribute('data-headlessui-state', 'active')
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Check the toast appeared
|
||||
@ -229,7 +228,8 @@ test.describe('Command bar tests', () => {
|
||||
).toBeVisible()
|
||||
// Check that the theme changed
|
||||
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Can extrude from the command bar', async ({
|
||||
page,
|
||||
|
@ -10,7 +10,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test(
|
||||
'export works on the first try',
|
||||
{ tag: ['@electron', '@macos', '@windows', '@skipLocalEngine'] },
|
||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
||||
async ({ page, context, scene, tronApp, cmdBar }, testInfo) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Editor tests', () => {
|
||||
test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
@ -989,11 +989,10 @@ sketch001 = startSketchOn(XZ)
|
||||
|> close()`)
|
||||
})
|
||||
|
||||
test('Can undo a sketch modification with ctrl+z', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
}) => {
|
||||
test(
|
||||
'Can undo a sketch modification with ctrl+z',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage, editor }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -1144,7 +1143,8 @@ sketch001 = startSketchOn(XZ)
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Can import a local OBJ file`,
|
||||
|
@ -430,17 +430,5 @@ profile003 = startProfileAt([0, -4.93], sketch001)
|
||||
await editor.expectEditor.not.toContain('sketch001 =')
|
||||
await editor.expectEditor.not.toContain('profile002 = ')
|
||||
})
|
||||
|
||||
await test.step(`Delete the remaining plane via feature tree`, async () => {
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Offset Plane',
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
|
||||
// Verify the plane code is gone, and https://github.com/KittyCAD/modeling-app/issues/5988 is fixed.
|
||||
await editor.expectEditor.not.toContain('plane001 =')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -29,7 +29,7 @@ test.describe('integrations tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
const [clickObj] = scene.makeMouseHelpers(726, 272)
|
||||
const [clickObj] = await scene.makeMouseHelpers(726, 272)
|
||||
|
||||
await test.step('setup test', async () => {
|
||||
await homePage.expectState({
|
||||
@ -73,7 +73,7 @@ test.describe('integrations tests', () => {
|
||||
})
|
||||
await test.step('setup for next assertion', async () => {
|
||||
await toolbar.openFile('main.kcl')
|
||||
await page.waitForTimeout(2000)
|
||||
await page.waitForTimeout(1000)
|
||||
await clickObj()
|
||||
await page.waitForTimeout(1000)
|
||||
await scene.moveNoWhere()
|
||||
|
@ -18,7 +18,6 @@ import type { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||
import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import { SignInPageFixture } from '@e2e/playwright/fixtures/signInPageFixture'
|
||||
import { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
|
||||
@ -67,7 +66,6 @@ export interface Fixtures {
|
||||
toolbar: ToolbarFixture
|
||||
scene: SceneFixture
|
||||
homePage: HomePageFixture
|
||||
signInPage: SignInPageFixture
|
||||
}
|
||||
|
||||
export class ElectronZoo {
|
||||
@ -389,9 +387,6 @@ const fixturesBasedOnProcessEnvPlatform = {
|
||||
homePage: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new HomePageFixture(page))
|
||||
},
|
||||
signInPage: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new SignInPageFixture(page))
|
||||
},
|
||||
}
|
||||
|
||||
if (process.env.PLATFORM === 'web') {
|
||||
|
@ -1,48 +0,0 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import { secrets } from '@e2e/playwright/secrets'
|
||||
|
||||
export class SignInPageFixture {
|
||||
public page: Page
|
||||
|
||||
signInButton!: Locator
|
||||
cancelSignInButton!: Locator
|
||||
userCode!: Locator
|
||||
|
||||
apiBaseUrl!: string
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
|
||||
this.signInButton = this.page.getByTestId('sign-in-button')
|
||||
this.cancelSignInButton = this.page.getByTestId('cancel-sign-in-button')
|
||||
this.userCode = this.page.getByTestId('sign-in-user-code')
|
||||
|
||||
// TODO: set this thru env var
|
||||
this.apiBaseUrl = 'https://api.dev.zoo.dev'
|
||||
}
|
||||
|
||||
async verifyAndConfirmAuth(userCode: string) {
|
||||
// Device flow: stolen from the tauri days
|
||||
// https://github.com/KittyCAD/modeling-app/blob/d916c7987452e480719004e6d11fd2e595c7d0eb/e2e/tauri/specs/app.spec.ts#L19
|
||||
const headers = {
|
||||
Authorization: `Bearer ${secrets.token}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
const verifyUrl = `${this.apiBaseUrl}/oauth2/device/verify?user_code=${userCode}`
|
||||
console.log(`GET ${verifyUrl}`)
|
||||
const vr = await fetch(verifyUrl, { headers })
|
||||
console.log(vr.status)
|
||||
|
||||
// Device flow: confirm
|
||||
const confirmUrl = `${this.apiBaseUrl}/oauth2/device/confirm`
|
||||
const data = JSON.stringify({ user_code: userCode })
|
||||
console.log(`POST ${confirmUrl} ${data}`)
|
||||
const cr = await fetch(confirmUrl, {
|
||||
headers,
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})
|
||||
console.log(cr.status)
|
||||
}
|
||||
}
|
@ -46,9 +46,6 @@ export class ToolbarFixture {
|
||||
gizmo!: Locator
|
||||
gizmoDisabled!: Locator
|
||||
loadButton!: Locator
|
||||
/** User button for the user sidebar menu */
|
||||
userSidebarButton!: Locator
|
||||
signOutButton!: Locator
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
@ -85,9 +82,6 @@ export class ToolbarFixture {
|
||||
// element or two different elements can represent these states.
|
||||
this.gizmo = page.getByTestId('gizmo')
|
||||
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
|
||||
|
||||
this.userSidebarButton = page.getByTestId('user-sidebar-toggle')
|
||||
this.signOutButton = page.getByTestId('user-sidebar-sign-out')
|
||||
}
|
||||
|
||||
get logoLink() {
|
||||
@ -174,13 +168,6 @@ export class ToolbarFixture {
|
||||
openFile = async (fileName: string) => {
|
||||
await this.filePane.getByText(fileName).click()
|
||||
}
|
||||
selectTangentialArc = async () => {
|
||||
await this.page.getByRole('button', { name: 'caret down arcs:' }).click()
|
||||
await expect(
|
||||
this.page.getByTestId('dropdown-three-point-arc')
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-tangential-arc').click()
|
||||
}
|
||||
selectCenterRectangle = async () => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down rectangles:' })
|
||||
|
@ -1,25 +1,6 @@
|
||||
import type {
|
||||
Reporter,
|
||||
TestCase,
|
||||
TestResult,
|
||||
FullResult,
|
||||
} from '@playwright/test/reporter'
|
||||
import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter'
|
||||
|
||||
class MyAPIReporter implements Reporter {
|
||||
private pendingRequests: Promise<void>[] = []
|
||||
private allResults: Record<string, any>[] = []
|
||||
private blockingResults: Record<string, any>[] = []
|
||||
|
||||
async onEnd(result: FullResult): Promise<void> {
|
||||
await Promise.all(this.pendingRequests)
|
||||
if (this.allResults.length > 0 && this.blockingResults.length === 0) {
|
||||
result.status = 'passed'
|
||||
if (!process.env.CI) {
|
||||
console.error('TAB API - Marked failures as non-blocking')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTestEnd(test: TestCase, result: TestResult): void {
|
||||
if (!process.env.TAB_API_URL || !process.env.TAB_API_KEY) {
|
||||
return
|
||||
@ -39,9 +20,8 @@ class MyAPIReporter implements Reporter {
|
||||
platform: process.env.RUNNER_OS || process.platform,
|
||||
// Extra test and result data
|
||||
annotations: test.annotations.map((a) => a.type), // e.g. 'fail' or 'fixme'
|
||||
id: test.id, // computed file/test/project ID used for reruns
|
||||
retry: result.retry,
|
||||
tags: test.tags, // e.g. '@snapshot' or '@skipLocalEngine'
|
||||
tags: test.tags, // e.g. '@snapshot' or '@skipWin'
|
||||
// Extra environment variables
|
||||
CI_COMMIT_SHA: process.env.CI_COMMIT_SHA || null,
|
||||
CI_PR_NUMBER: process.env.CI_PR_NUMBER || null,
|
||||
@ -55,7 +35,7 @@ class MyAPIReporter implements Reporter {
|
||||
RUNNER_ARCH: process.env.RUNNER_ARCH || null,
|
||||
}
|
||||
|
||||
const request = (async () => {
|
||||
void (async () => {
|
||||
try {
|
||||
const response = await fetch(`${process.env.TAB_API_URL}/api/results`, {
|
||||
method: 'POST',
|
||||
@ -66,27 +46,18 @@ class MyAPIReporter implements Reporter {
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
this.allResults.push(result)
|
||||
if (result.block) {
|
||||
this.blockingResults.push(result)
|
||||
if (!response.ok && !process.env.CI) {
|
||||
console.error(
|
||||
'TAB API - Failed to send test result:',
|
||||
await response.text()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const error = await response.json()
|
||||
} catch {
|
||||
if (!process.env.CI) {
|
||||
console.error('TAB API - Failed to send test result:', error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
if (!process.env.CI) {
|
||||
console.error('TAB API - Unable to send test result:', message)
|
||||
console.error('TAB API - Unable to send test result')
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
this.pendingRequests.push(request)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,18 +63,6 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
'e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts XY',
|
||||
project: 'Google Chrome',
|
||||
},
|
||||
{
|
||||
name: 'fE',
|
||||
message:
|
||||
'engine: Failed to wait for promise from send modeling command: JsValue("no connection to send on")',
|
||||
stack: `fE: engine: Failed to wait for promise from send modeling command: JsValue("no connection to send on")
|
||||
at ET (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:49875:12)
|
||||
at WGe.clearSceneAndBustCache (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:91886:19)
|
||||
at async a.onEngineConnectionOpened (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:91483:9)`,
|
||||
foundInSpec:
|
||||
'e2e/playwright/testing-constraints.spec.ts is frequent but could be any spec',
|
||||
project: 'Google Chrome',
|
||||
},
|
||||
{
|
||||
name: 'RangeError',
|
||||
message: 'Position 160 is out of range for changeset of length 0',
|
||||
|
@ -6,10 +6,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
* Not all menu actions are tested. Some are default electron menu actions.
|
||||
* Test file menu actions that trigger something in the frontend
|
||||
*/
|
||||
test.describe(
|
||||
'Native file menu',
|
||||
{ tag: ['@electron', '@macos', '@windows'] },
|
||||
() => {
|
||||
test.describe('Native file menu', { tag: ['@electron'] }, () => {
|
||||
test.skip() // TODO: Reimplement native file menu tests
|
||||
test.describe('Home page', () => {
|
||||
test.describe('File role', () => {
|
||||
@ -24,9 +21,8 @@ test.describe(
|
||||
if (!app || !app.applicationMenu) {
|
||||
return false
|
||||
}
|
||||
const newProject = app.applicationMenu.getMenuItemById(
|
||||
'File.Create project'
|
||||
)
|
||||
const newProject =
|
||||
app.applicationMenu.getMenuItemById('File.New project')
|
||||
if (!newProject) return false
|
||||
newProject.click()
|
||||
return true
|
||||
@ -164,11 +160,7 @@ test.describe(
|
||||
const defaultUnit = settings.locator('#defaultUnit')
|
||||
await expect(defaultUnit).toBeVisible()
|
||||
})
|
||||
test('Home.File.Preferences.Theme', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
}) => {
|
||||
test('Home.File.Preferences.Theme', async ({ tronApp, cmdBar, page }) => {
|
||||
if (!tronApp) fail()
|
||||
// Run electron snippet to find the Menu!
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
@ -372,11 +364,7 @@ test.describe(
|
||||
})
|
||||
})
|
||||
test.describe('Help role', () => {
|
||||
test('Home.Help.Show all commands', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
}) => {
|
||||
test('Home.Help.Show all commands', async ({ tronApp, cmdBar, page }) => {
|
||||
if (!tronApp) fail()
|
||||
// Run electron snippet to find the Menu!
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
@ -400,11 +388,7 @@ test.describe(
|
||||
const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search')
|
||||
await expect(actual).toBeVisible()
|
||||
})
|
||||
test('Home.Help.KCL code samples', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
}) => {
|
||||
test('Home.Help.KCL code samples', async ({ tronApp, cmdBar, page }) => {
|
||||
if (!tronApp) fail()
|
||||
// Run electron snippet to find the Menu!
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
@ -424,7 +408,11 @@ test.describe(
|
||||
)
|
||||
.toBe(true)
|
||||
})
|
||||
test('Home.Help.Report a bug', async ({ tronApp, cmdBar, page }) => {
|
||||
test('Home.Help.Refresh and report a bug', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
}) => {
|
||||
if (!tronApp) fail()
|
||||
// Run electron snippet to find the Menu!
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
@ -435,8 +423,9 @@ test.describe(
|
||||
if (!app || !app.applicationMenu) {
|
||||
return false
|
||||
}
|
||||
const menu =
|
||||
app.applicationMenu.getMenuItemById('Help.Report a bug')
|
||||
const menu = app.applicationMenu.getMenuItemById(
|
||||
'Help.Refresh and report a bug'
|
||||
)
|
||||
if (!menu) return false
|
||||
menu.click()
|
||||
return true
|
||||
@ -450,11 +439,7 @@ test.describe(
|
||||
)
|
||||
await expect(actual).toBeVisible()
|
||||
})
|
||||
test('Home.Help.Reset onboarding', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
}) => {
|
||||
test('Home.Help.Reset onboarding', async ({ tronApp, cmdBar, page }) => {
|
||||
if (!tronApp) fail()
|
||||
// Run electron snippet to find the Menu!
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
@ -499,9 +484,8 @@ test.describe(
|
||||
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
|
||||
await tronApp.electron.evaluate(async ({ app }) => {
|
||||
if (!app || !app.applicationMenu) fail()
|
||||
const newProject = app.applicationMenu.getMenuItemById(
|
||||
'File.Create project'
|
||||
)
|
||||
const newProject =
|
||||
app.applicationMenu.getMenuItemById('File.New project')
|
||||
if (!newProject) fail()
|
||||
newProject.click()
|
||||
})
|
||||
@ -624,7 +608,7 @@ test.describe(
|
||||
const expected = 'Export'
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
test('Modeling.File.Share part via Zoo link', async ({
|
||||
test('Modeling.File.Share current part (via Zoo link)', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
@ -645,10 +629,10 @@ test.describe(
|
||||
throw new Error('app or app.applicationMenu is missing')
|
||||
}
|
||||
const openProject = app.applicationMenu.getMenuItemById(
|
||||
'File.Share part via Zoo link'
|
||||
'File.Share current part (via Zoo link)'
|
||||
)
|
||||
if (!openProject) {
|
||||
throw new Error('File.Share part via Zoo link')
|
||||
throw new Error('File.Share current part (via Zoo link)')
|
||||
}
|
||||
openProject.click()
|
||||
})
|
||||
@ -2305,7 +2289,7 @@ test.describe(
|
||||
if (!menu) fail()
|
||||
})
|
||||
})
|
||||
test('Modeling.Help.Report a bug', async ({
|
||||
test('Modeling.Help.Refresh and report a bug', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
@ -2329,8 +2313,9 @@ test.describe(
|
||||
async () =>
|
||||
await tronApp.electron.evaluate(async ({ app }) => {
|
||||
if (!app || !app.applicationMenu) return false
|
||||
const menu =
|
||||
app.applicationMenu.getMenuItemById('Help.Report a bug')
|
||||
const menu = app.applicationMenu.getMenuItemById(
|
||||
'Help.Refresh and report a bug'
|
||||
)
|
||||
if (!menu) return false
|
||||
menu.click()
|
||||
return true
|
||||
@ -2373,5 +2358,4 @@ test.describe(
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -63,7 +63,7 @@ test.describe('Onboarding tests', () => {
|
||||
{
|
||||
tag: '@electron',
|
||||
},
|
||||
async ({ page, tronApp, scene }) => {
|
||||
async ({ page, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
@ -72,6 +72,7 @@ test.describe('Onboarding tests', () => {
|
||||
onboarding_status: '',
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
@ -79,7 +80,7 @@ test.describe('Onboarding tests', () => {
|
||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||
await createProject({ name: 'project-link', page })
|
||||
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
|
||||
await scene.connectionEstablished()
|
||||
await u.waitForPageLoad()
|
||||
})
|
||||
})
|
||||
|
||||
@ -106,10 +107,12 @@ test.describe('Onboarding tests', () => {
|
||||
)
|
||||
|
||||
test('Code resets after confirmation', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
@ -273,8 +276,6 @@ test.describe('Onboarding tests', () => {
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
editor,
|
||||
toolbar,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
@ -288,6 +289,7 @@ test.describe('Onboarding tests', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
@ -297,19 +299,18 @@ test.describe('Onboarding tests', () => {
|
||||
.poll(() => page.url())
|
||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
|
||||
// Check the code got reset on load
|
||||
await toolbar.openPane('code')
|
||||
await editor.expectEditor.toContain(bracket, {
|
||||
shouldNormalise: true,
|
||||
await expect(page.locator('#code-pane')).toBeVisible()
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
// Mess with the code again
|
||||
await editor.replaceCode('', badCode)
|
||||
await editor.expectEditor.toContain(badCode, {
|
||||
shouldNormalise: true,
|
||||
timeout: 10_000,
|
||||
})
|
||||
await u.codeLocator.selectText()
|
||||
await u.codeLocator.fill(badCode)
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
@ -319,10 +320,7 @@ test.describe('Onboarding tests', () => {
|
||||
})
|
||||
|
||||
// Check that the code has been reset
|
||||
await editor.expectEditor.toContain(bracket, {
|
||||
shouldNormalise: true,
|
||||
timeout: 10_000,
|
||||
})
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
})
|
||||
|
||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
||||
@ -331,7 +329,6 @@ test.describe('Onboarding tests', () => {
|
||||
test('Avatar text updates depending on image load success', async ({
|
||||
context,
|
||||
page,
|
||||
toolbar,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
@ -363,7 +360,7 @@ test.describe('Onboarding tests', () => {
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const avatarLocator = toolbar.userSidebarButton.locator('img')
|
||||
const avatarLocator = page.getByTestId('user-sidebar-toggle').locator('img')
|
||||
const onboardingOverlayLocator = page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
@ -405,7 +402,6 @@ test.describe('Onboarding tests', () => {
|
||||
test("Avatar text doesn't mention avatar when no avatar", async ({
|
||||
context,
|
||||
page,
|
||||
toolbar,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
@ -437,7 +433,7 @@ test.describe('Onboarding tests', () => {
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const sidebar = toolbar.userSidebarButton
|
||||
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||
const avatar = sidebar.locator('img')
|
||||
const onboardingOverlayLocator = page
|
||||
.getByTestId('onboarding-content')
|
||||
@ -466,7 +462,6 @@ test.describe('Onboarding tests', () => {
|
||||
test('Restarting onboarding on desktop takes one attempt', async ({
|
||||
context,
|
||||
page,
|
||||
toolbar,
|
||||
tronApp,
|
||||
}) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
@ -505,7 +500,7 @@ test('Restarting onboarding on desktop takes one attempt', async ({
|
||||
.filter({ hasText: 'Tutorial Project 00' })
|
||||
const tutorialModalText = page.getByText('Welcome to Design Studio!')
|
||||
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
|
||||
const userMenuButton = toolbar.userSidebarButton
|
||||
const userMenuButton = page.getByTestId('user-sidebar-toggle')
|
||||
const userMenuSettingsButton = page.getByRole('button', {
|
||||
name: 'User settings',
|
||||
})
|
||||
|
@ -6,7 +6,6 @@ import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import {
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
kclSamplesPath,
|
||||
testsInputPath,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
@ -144,45 +143,28 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
|
||||
await test.step('Insert a second time with the same name and expect error', async () => {
|
||||
await toolbar.insertButton.click()
|
||||
await cmdBar.selectOption({ name: 'bracket.kcl' }).click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'localName',
|
||||
currentArgValue: '',
|
||||
headerArguments: { Path: 'bracket.kcl', LocalName: '' },
|
||||
highlightedHeaderArg: 'localName',
|
||||
commandName: 'Insert',
|
||||
})
|
||||
await page.keyboard.insertText('bracket')
|
||||
await cmdBar.progressCmdBar()
|
||||
await expect(
|
||||
page.getByText('This variable name is already in use')
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Insert a second time with a different name and expect error', async () => {
|
||||
await page.keyboard.insertText('2')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: { Path: 'bracket.kcl', LocalName: 'bracket2' },
|
||||
commandName: 'Insert',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await test.step('Insert a second time and expect error', async () => {
|
||||
// TODO: revisit once we have clone with #6209
|
||||
await insertPartIntoAssembly(
|
||||
'bracket.kcl',
|
||||
'bracket',
|
||||
toolbar,
|
||||
cmdBar,
|
||||
page
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
`
|
||||
import "cylinder.kcl" as cylinder
|
||||
import "bracket.kcl" as bracket
|
||||
import "bracket.kcl" as bracket2
|
||||
import "bracket.kcl" as bracket
|
||||
cylinder
|
||||
bracket
|
||||
bracket2
|
||||
bracket
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
// TODO: update once we have clone() with #6209
|
||||
await scene.settled(cmdBar)
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
})
|
||||
}
|
||||
)
|
||||
@ -279,7 +261,7 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
highlightedHeaderArg: 'x',
|
||||
commandName: 'Translate',
|
||||
})
|
||||
await page.keyboard.insertText('100')
|
||||
await page.keyboard.insertText('5')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('0.1')
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -288,7 +270,7 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
X: '100',
|
||||
X: '5',
|
||||
Y: '0.1',
|
||||
Z: '0.2',
|
||||
},
|
||||
@ -300,7 +282,7 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`
|
||||
bracket
|
||||
|> translate(x = 100, y = 0.1, z = 0.2)
|
||||
|> translate(x = 5, y = 0.1, z = 0.2)
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
@ -349,7 +331,7 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`
|
||||
bracket
|
||||
|> translate(x = 100, y = 0.1, z = 0.2)
|
||||
|> translate(x = 5, y = 0.1, z = 0.2)
|
||||
|> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3)
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
@ -473,94 +455,4 @@ test.describe('Point-and-click assemblies tests', () => {
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Assembly gets reexecuted when imported models are updated externally',
|
||||
{ tag: ['@electron'] },
|
||||
async ({ context, page, homePage, scene, toolbar, cmdBar, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
const midPoint = { x: 500, y: 250 }
|
||||
const washerPoint = { x: 645, y: 250 }
|
||||
const partColor: [number, number, number] = [120, 120, 120]
|
||||
const redPartColor: [number, number, number] = [200, 0, 0]
|
||||
const bgColor: [number, number, number] = [30, 30, 30]
|
||||
const tolerance = 50
|
||||
const projectName = 'assembly'
|
||||
|
||||
await test.step('Setup parts and expect imported model', async () => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const projectDir = path.join(dir, projectName)
|
||||
await fsp.mkdir(projectDir, { recursive: true })
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('cube.kcl'),
|
||||
path.join(projectDir, 'cube.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
kclSamplesPath(
|
||||
path.join(
|
||||
'pipe-flange-assembly',
|
||||
'mcmaster-parts',
|
||||
'98017a257-washer.step'
|
||||
)
|
||||
),
|
||||
path.join(projectDir, 'foreign.step')
|
||||
),
|
||||
fsp.writeFile(
|
||||
path.join(projectDir, 'main.kcl'),
|
||||
`
|
||||
import "cube.kcl" as cube
|
||||
import "foreign.step" as foreign
|
||||
cube
|
||||
foreign
|
||||
|> translate(x = 40, z = 10)`
|
||||
),
|
||||
])
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.openProject(projectName)
|
||||
await scene.settled(cmdBar)
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor(partColor, midPoint, tolerance)
|
||||
})
|
||||
|
||||
await test.step('Change imported kcl file and expect change', async () => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// Append appearance to the cube.kcl file
|
||||
await fsp.appendFile(
|
||||
path.join(dir, projectName, 'cube.kcl'),
|
||||
`\n |> appearance(color = "#ff0000")`
|
||||
)
|
||||
})
|
||||
await scene.settled(cmdBar)
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor(redPartColor, midPoint, tolerance)
|
||||
await scene.expectPixelColor(partColor, washerPoint, tolerance)
|
||||
})
|
||||
|
||||
await test.step('Change imported step file and expect change', async () => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
// Replace the washer with a pipe
|
||||
await fsp.copyFile(
|
||||
kclSamplesPath(
|
||||
path.join(
|
||||
'pipe-flange-assembly',
|
||||
'mcmaster-parts',
|
||||
'1120t74-pipe.step'
|
||||
)
|
||||
),
|
||||
path.join(dir, projectName, 'foreign.step')
|
||||
)
|
||||
})
|
||||
await scene.settled(cmdBar)
|
||||
await toolbar.closePane('code')
|
||||
// Expect pipe to take over the red cube but leave some space where the washer was
|
||||
await scene.expectPixelColor(partColor, midPoint, tolerance)
|
||||
await scene.expectPixelColor(bgColor, washerPoint, tolerance)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -18,7 +18,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test(
|
||||
'projects reload if a new one is created, deleted, or renamed externally',
|
||||
{ tag: ['@electron', '@macos', '@windows'] },
|
||||
{ tag: '@electron' },
|
||||
async ({ context, page }, testInfo) => {
|
||||
let externalCreatedProjectName = 'external-created-project'
|
||||
|
||||
@ -400,6 +400,11 @@ test(
|
||||
await expect(page.getByText('broken-code')).toBeVisible()
|
||||
await page.getByText('broken-code').click()
|
||||
|
||||
// Gotcha: You can not use scene.settled() since the KCL code is going to fail
|
||||
await expect(
|
||||
page.getByTestId('model-state-indicator-playing')
|
||||
).toBeAttached()
|
||||
|
||||
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
|
||||
await editor.scrollToText(
|
||||
"|> line(end = [0, wallMountL], tag = 'outerEdge')"
|
||||
@ -774,9 +779,7 @@ test.describe(`Project management commands`, () => {
|
||||
// Constants and locators
|
||||
const projectHomeLink = page.getByTestId('project-link')
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', {
|
||||
name: 'rename project',
|
||||
})
|
||||
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const projectRenamedName = `untitled`
|
||||
// const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||
@ -836,9 +839,7 @@ test.describe(`Project management commands`, () => {
|
||||
// Constants and locators
|
||||
const projectHomeLink = page.getByTestId('project-link')
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', {
|
||||
name: 'delete project',
|
||||
})
|
||||
const commandOption = page.getByRole('option', { name: 'delete project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
@ -890,9 +891,7 @@ test.describe(`Project management commands`, () => {
|
||||
// Constants and locators
|
||||
const projectHomeLink = page.getByTestId('project-link')
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', {
|
||||
name: 'rename project',
|
||||
})
|
||||
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const projectRenamedName = `untitled`
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
@ -948,9 +947,7 @@ test.describe(`Project management commands`, () => {
|
||||
// Constants and locators
|
||||
const projectHomeLink = page.getByTestId('project-link')
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', {
|
||||
name: 'delete project',
|
||||
})
|
||||
const commandOption = page.getByRole('option', { name: 'delete project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
@ -1815,8 +1812,8 @@ test(
|
||||
'basic_fillet_cube_next_adjacent.kcl',
|
||||
'basic_fillet_cube_previous_adjacent.kcl',
|
||||
'basic_fillet_cube_start.kcl',
|
||||
'broken-code-test.kcl',
|
||||
'circular_pattern3d_a_pattern.kcl',
|
||||
'big_number_angle_to_match_length_x.kcl',
|
||||
'big_number_angle_to_match_length_y.kcl',
|
||||
'close_arc.kcl',
|
||||
'computed_var.kcl',
|
||||
'cube-embedded.gltf',
|
||||
@ -1965,13 +1962,13 @@ test(
|
||||
test(
|
||||
'Settings persist across restarts',
|
||||
{ tag: '@electron' },
|
||||
async ({ page, toolbar }, testInfo) => {
|
||||
async ({ page, scene, cmdBar }, testInfo) => {
|
||||
await test.step('We can change a user setting like theme', async () => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await toolbar.userSidebarButton.click()
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
|
||||
await page.getByTestId('user-settings').click()
|
||||
|
||||
@ -1998,7 +1995,7 @@ test(
|
||||
test(
|
||||
'Original project name persist after onboarding',
|
||||
{ tag: '@electron' },
|
||||
async ({ page, toolbar }, testInfo) => {
|
||||
async ({ page }, testInfo) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
@ -2010,7 +2007,7 @@ test(
|
||||
})
|
||||
|
||||
await test.step('Should go through onboarding', async () => {
|
||||
await toolbar.userSidebarButton.click()
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
await page.getByTestId('user-settings').click()
|
||||
await page.getByRole('button', { name: 'Replay Onboarding' }).click()
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
/* eslint-disable jest/no-conditional-expect */
|
||||
@ -29,7 +30,7 @@ sketch003 = startSketchOn(XY)
|
||||
extrude003 = extrude(sketch003, length = 20)
|
||||
`
|
||||
|
||||
test.describe('Prompt-to-edit tests', () => {
|
||||
test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
test.describe('Check the happy path, for basic changing color', () => {
|
||||
const cases = [
|
||||
{
|
||||
@ -50,6 +51,7 @@ test.describe('Prompt-to-edit tests', () => {
|
||||
page,
|
||||
scene,
|
||||
}) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
@ -198,6 +200,7 @@ test.describe('Prompt-to-edit tests', () => {
|
||||
page,
|
||||
scene,
|
||||
}) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const body1CapCoords = { x: 571, y: 311 }
|
||||
|
||||
await context.addInitScript((file) => {
|
||||
@ -257,6 +260,7 @@ test.describe('Prompt-to-edit tests', () => {
|
||||
page,
|
||||
scene,
|
||||
}) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const body1CapCoords = { x: 571, y: 311 }
|
||||
const body2WallCoords = { x: 620, y: 152 }
|
||||
const [clickBody1Cap] = scene.makeMouseHelpers(
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Regression tests', () => {
|
||||
test.describe('Regression tests', { tag: ['@skipWin'] }, () => {
|
||||
// bugs we found that don't fit neatly into other categories
|
||||
test('bad model has inline error #3251', async ({
|
||||
context,
|
||||
@ -239,11 +239,10 @@ extrude001 = extrude(sketch001, length = 50)
|
||||
await expect(zooLogo).not.toHaveAttribute('href')
|
||||
})
|
||||
|
||||
test('Position _ Is Out Of Range... regression test', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
test(
|
||||
'Position _ Is Out Of Range... regression test',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
@ -316,7 +315,8 @@ extrude001 = extrude(sketch001, length = 50)
|
||||
thing: "blah"`)
|
||||
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'window resize updates should reconfigure the stream',
|
||||
@ -486,10 +486,10 @@ extrude002 = extrude(profile002, length = 150)
|
||||
}
|
||||
)
|
||||
// We updated this test such that you can have multiple exports going at once.
|
||||
test('ensure you CAN export while an export is already going', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
test(
|
||||
'ensure you CAN export while an export is already going',
|
||||
{ tag: ['@skipLinux', '@skipWin'] },
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step('Set up the code and durations', async () => {
|
||||
await page.addInitScript(
|
||||
@ -560,7 +560,8 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await expect(successToastMessage).toHaveCount(2)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Network health indicator only appears in modeling view`,
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Sketch tests', () => {
|
||||
test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
||||
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
||||
page,
|
||||
context,
|
||||
@ -45,7 +45,11 @@ test.describe('Sketch tests', () => {
|
||||
|
||||
screwHole = startSketchOn(XY)
|
||||
${startProfileAt1}
|
||||
|> arc(angleStart = 0, angleEnd = 360, radius = screwRadius)
|
||||
|> arc({
|
||||
radius = screwRadius,
|
||||
angleStart = 0,
|
||||
angleEnd = 360
|
||||
}, %)
|
||||
|
||||
part001 = startSketchOn(XY)
|
||||
${startProfileAt2}
|
||||
@ -62,7 +66,11 @@ test.describe('Sketch tests', () => {
|
||||
|> tangentialArc(endAbsolute = [width / 2, 0])
|
||||
|> xLine(length = -width / 4 + wireRadius)
|
||||
|> yLine(length = wireOffset)
|
||||
|> arc(angleStart = 0, angleEnd = 180, radius = wireRadius)
|
||||
|> arc({
|
||||
radius = wireRadius,
|
||||
angleStart = 0,
|
||||
angleEnd = 180
|
||||
}, %)
|
||||
|> yLine(length = -wireOffset)
|
||||
|> xLine(length = -width / 4)
|
||||
|> close()
|
||||
@ -206,8 +214,15 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
||||
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|
||||
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|
||||
|> arcTo({
|
||||
interior = [20.18, -1.7],
|
||||
end = [11.82, -1.16]
|
||||
}, %)
|
||||
|> arc({
|
||||
radius = 5.92,
|
||||
angleStart = -89.36,
|
||||
angleEnd = 135.81
|
||||
}, %)
|
||||
|> close()`
|
||||
)
|
||||
})
|
||||
@ -249,8 +264,15 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArc(endAbsolute = [24.95, -5.38])
|
||||
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|
||||
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|
||||
|> arcTo({
|
||||
interior = [20.18, -1.7],
|
||||
end = [11.82, -1.16]
|
||||
}, %)
|
||||
|> arc({
|
||||
radius = 5.92,
|
||||
angleStart = -89.36,
|
||||
angleEnd = 135.81
|
||||
}, %)
|
||||
|> close()
|
||||
`)
|
||||
} else {
|
||||
@ -316,7 +338,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
}
|
||||
|
||||
// drag arcTo interiorAbsolute handle (three point arc)
|
||||
// drag arcTo interior handle (three point arc)
|
||||
const arcToHandle = await u.getBoundingBox('[data-overlay-index="2"]')
|
||||
await page.mouse.move(arcToHandle.x, arcToHandle.y - 5)
|
||||
await page.mouse.down()
|
||||
@ -388,18 +410,22 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> startProfileAt([6.44, -12.07], %)
|
||||
|> line(end = [14.72, 1.97])
|
||||
|> tangentialArc(endAbsolute = [26.92, -3.32])
|
||||
|> arc(interiorAbsolute = [18.11, -3.73], endAbsolute = [9.77, -3.19])
|
||||
|> arc(angleStart = -58.29, angleEnd = 161.17, radius = 3.75)
|
||||
|> arcTo({
|
||||
interior = [18.11, -3.73],
|
||||
end = [9.77, -3.19]
|
||||
}, %)
|
||||
|> arc({
|
||||
radius = 3.75,
|
||||
angleStart = -58.29,
|
||||
angleEnd = 161.17
|
||||
}, %)
|
||||
|> close()
|
||||
`)
|
||||
}
|
||||
test('code pane open at start-handles', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
test(
|
||||
'code pane open at start-handles',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -420,15 +446,13 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
toolbar,
|
||||
cmdBar
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('code pane closed at start-handles', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
test(
|
||||
'code pane closed at start-handles',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async (persistModelingContext) => {
|
||||
localStorage.setItem(
|
||||
@ -444,7 +468,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
toolbar,
|
||||
cmdBar
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test('Can edit a circle center and radius by dragging its handles', async ({
|
||||
@ -923,9 +948,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
|
||||
await page.mouse.move(pointA[0] - 12, pointA[1] + 12, { steps: 10 })
|
||||
const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7]
|
||||
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], {
|
||||
steps: 10,
|
||||
})
|
||||
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], { steps: 10 })
|
||||
|
||||
await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1], {
|
||||
delay: 200,
|
||||
@ -1393,15 +1416,10 @@ offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
})
|
||||
|
||||
test.describe('multi-profile sketching', () => {
|
||||
test(`test it removes half-finished expressions when changing tools in sketch mode`, async ({
|
||||
context,
|
||||
page,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
test(
|
||||
`test it removes half-finished expressions when changing tools in sketch mode`,
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ context, page, scene, toolbar, editor, homePage, cmdBar }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
// We seed the scene with a single offset plane
|
||||
await context.addInitScript(() => {
|
||||
@ -1470,7 +1488,9 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
|
||||
await test.step('equip line tool and verify three-point circle code is removed', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('profile003 = circleThreePoint(')
|
||||
await editor.expectEditor.not.toContain(
|
||||
'profile003 = circleThreePoint('
|
||||
)
|
||||
})
|
||||
|
||||
await test.step('equip three-point-arc tool and click first two points', async () => {
|
||||
@ -1480,12 +1500,12 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
await circle3Point1()
|
||||
await page.waitForTimeout(200)
|
||||
await circle3Point2()
|
||||
await editor.expectEditor.toContain('arc(')
|
||||
await editor.expectEditor.toContain('arcTo({')
|
||||
})
|
||||
|
||||
await test.step('equip line tool and verify three-point-arc code is removed after second click', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('arc(')
|
||||
await editor.expectEditor.not.toContain('arcTo({')
|
||||
})
|
||||
|
||||
const [cornerRectPoint1] = scene.makeMouseHelpers(600, 300)
|
||||
@ -1522,21 +1542,17 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
await continueProfile2Clk()
|
||||
await page.waitForTimeout(200)
|
||||
await circle3Point1()
|
||||
await editor.expectEditor.toContain('arc(')
|
||||
await editor.expectEditor.toContain('arcTo({')
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('arc(')
|
||||
await editor.expectEditor.not.toContain('arcTo({')
|
||||
await editor.expectEditor.toContain('profile002')
|
||||
})
|
||||
})
|
||||
test(`snapToProfile start only works for current profile`, async ({
|
||||
context,
|
||||
page,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
}
|
||||
)
|
||||
test(
|
||||
`snapToProfile start only works for current profile`,
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ context, page, scene, toolbar, editor, homePage, cmdBar }) => {
|
||||
// We seed the scene with a single offset plane
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
@ -1575,7 +1591,7 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
|
||||
const codeFromTangentialArc = ` |> tangentialArc(endAbsolute = [39.49, 88.22])`
|
||||
await test.step('check that tangential tool does not snap to other profile starts', async () => {
|
||||
await toolbar.selectTangentialArc()
|
||||
await toolbar.tangentialArcBtn.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await endOfLowerSegMove()
|
||||
await page.waitForTimeout(1000)
|
||||
@ -1615,7 +1631,8 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
`[profileStartX(%), profileStartY(%)]`
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
test('can enter sketch mode for sketch with no profiles', async ({
|
||||
scene,
|
||||
toolbar,
|
||||
@ -1787,7 +1804,7 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
|
||||
await endLineStartTanArc()
|
||||
await editor.expectEditor.toContain(`|> line(end = [9.02, -0.55])`)
|
||||
await toolbar.selectTangentialArc()
|
||||
await toolbar.tangentialArcBtn.click()
|
||||
await page.waitForTimeout(300)
|
||||
await page.mouse.click(745, 359)
|
||||
await page.waitForTimeout(300)
|
||||
@ -1824,9 +1841,7 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Verify the three-point arc was created correctly
|
||||
await editor.expectEditor.toContain(`arc(`)
|
||||
await editor.expectEditor.toContain(`interiorAbsolute`)
|
||||
await editor.expectEditor.toContain(`endAbsolute`)
|
||||
await editor.expectEditor.toContain(`|> arcTo(`)
|
||||
|
||||
// Switch back to line tool to continue
|
||||
await toolbar.lineBtn.click()
|
||||
@ -2046,7 +2061,10 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
// Verify the first three-point arc was created correctly
|
||||
await editor.expectEditor.toContain(
|
||||
`profile011 = startProfileAt([13.56, -9.97], sketch001)
|
||||
|> arc(interiorAbsolute = [15.19, -6.51], endAbsolute = [19.33, -11.19])`,
|
||||
|> arcTo({
|
||||
interior = [15.19, -6.51],
|
||||
end = [19.33, -11.19]
|
||||
}, %)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|
||||
@ -2063,8 +2081,14 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
|
||||
// Verify the second three-point arc was created correctly
|
||||
await editor.expectEditor.toContain(
|
||||
` |> arc(interiorAbsolute = [19.8, 1.7], endAbsolute = [21.7, 2.92])
|
||||
|> arc(interiorAbsolute = [27.47, 1.42], endAbsolute = [27.57, 1.52])`,
|
||||
` |> arcTo({
|
||||
interior = [19.8, 1.7],
|
||||
end = [21.7, 2.92]
|
||||
}, %)
|
||||
|> arcTo({
|
||||
interior = [27.47, 1.42],
|
||||
end = [27.57, 1.52]
|
||||
}, %)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
@ -2081,13 +2105,10 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
})
|
||||
})
|
||||
|
||||
test('Can edit a sketch with multiple profiles, dragging segments to edit them, and adding one new profile', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
page,
|
||||
}) => {
|
||||
test(
|
||||
'Can edit a sketch with multiple profiles, dragging segments to edit them, and adding one new profile',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, editor, page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2217,15 +2238,12 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
})
|
||||
test('Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added', async ({
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
cmdBar,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
}
|
||||
)
|
||||
test(
|
||||
'Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added',
|
||||
{ tag: ['@skipWin', '@skipLinux'] },
|
||||
async ({ scene, toolbar, editor, cmdBar, page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2319,15 +2337,13 @@ profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
|
||||
await editor.expectEditor.not.toContain('length001 = 7')
|
||||
await sketchIsDrawnProperly()
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('can enter sketch when there is an extrude', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
page,
|
||||
cmdBar,
|
||||
}) => {
|
||||
test(
|
||||
'can enter sketch when there is an extrude',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, page, cmdBar }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2379,7 +2395,8 @@ extrude001 = extrude(profile003, length = 5)
|
||||
scene.expectPixelColor(TEST_COLORS.WHITE, { x: 763, y: 214 }, 15),
|
||||
])
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
test('exit new sketch without drawing anything should not be a problem', async ({
|
||||
homePage,
|
||||
scene,
|
||||
@ -2433,13 +2450,10 @@ extrude001 = extrude(profile003, length = 5)
|
||||
await scene.expectPixelColor([255, 255, 255], { x: 633, y: 211 }, 15)
|
||||
})
|
||||
})
|
||||
test('A sketch with only "startProfileAt" and no segments should still be able to be continued', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
page,
|
||||
}) => {
|
||||
test(
|
||||
'A sketch with only "startProfileAt" and no segments should still be able to be continued',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, editor, page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2477,14 +2491,12 @@ profile002 = startProfileAt([85.81, 52.55], sketch002)
|
||||
await page.waitForTimeout(100)
|
||||
await nextPoint()
|
||||
await editor.expectEditor.toContain(`|> line(end = [126.05, 44.12])`)
|
||||
})
|
||||
test('old style sketch all in one pipe (with extrude) will break up to allow users to add a new profile to the same sketch', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
page,
|
||||
}) => {
|
||||
}
|
||||
)
|
||||
test(
|
||||
'old style sketch all in one pipe (with extrude) will break up to allow users to add a new profile to the same sketch',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, editor, page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2539,15 +2551,12 @@ extrude001 = extrude(thePart, length = 75)
|
||||
await profilePoint2()
|
||||
await editor.expectEditor.toContain(`|> line(end = [18.97, -18.06])`)
|
||||
})
|
||||
})
|
||||
test('Can enter sketch on sketch of wall and cap for segment, solid2d, extrude-wall, extrude-cap selections', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
page,
|
||||
cmdBar,
|
||||
}) => {
|
||||
}
|
||||
)
|
||||
test(
|
||||
'Can enter sketch on sketch of wall and cap for segment, solid2d, extrude-wall, extrude-cap selections',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, editor, page, cmdBar }) => {
|
||||
// TODO this test should include a test for selecting revolve walls and caps
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
@ -2706,14 +2715,12 @@ extrude003 = extrude(profile011, length = 2.5)
|
||||
})
|
||||
}
|
||||
}) */
|
||||
})
|
||||
test('Can enter sketch loft edges, base and continue sketch', async ({
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
editor,
|
||||
page,
|
||||
}) => {
|
||||
}
|
||||
)
|
||||
test(
|
||||
'Can enter sketch loft edges, base and continue sketch',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ homePage, scene, toolbar, editor, page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -2763,7 +2770,8 @@ loft([profile001, profile002])
|
||||
await editor.expectEditor.toContain(
|
||||
`angledLine(angle = 0, length = 113.01, tag = $rectangleSegmentA001)`
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
test('Can enter sketch loft edges offsetPlane and continue sketch', async ({
|
||||
scene,
|
||||
toolbar,
|
||||
@ -2855,29 +2863,33 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [3.14, 3.14])
|
||||
|> arc(
|
||||
interiorAbsolute = [1, 2],
|
||||
endAbsolute = [4, 2]
|
||||
)`
|
||||
|> arcTo({
|
||||
end = [4, 2],
|
||||
interior = [1, 2]
|
||||
}, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
const formattedArc = `arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])`
|
||||
await test.step(`format the code`, async () => {
|
||||
// doesn't contain condensed version
|
||||
await editor.expectEditor.not.toContain(formattedArc)
|
||||
await editor.expectEditor.not.toContain(
|
||||
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
||||
)
|
||||
// click the code to enter sketch mode
|
||||
await page.getByText(`arc`).click()
|
||||
await page.getByText(`arcTo`).click()
|
||||
// Format the code.
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
await page.locator('button:has-text("Format code")').click()
|
||||
})
|
||||
|
||||
await test.step(`Ensure the code reformatted`, async () => {
|
||||
await editor.expectEditor.toContain(formattedArc)
|
||||
await editor.expectEditor.toContain(
|
||||
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
||||
)
|
||||
})
|
||||
|
||||
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
|
||||
@ -2888,7 +2900,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
|
||||
await editor.expectState({
|
||||
activeLines: ['sketch001=startSketchOn(XZ)'],
|
||||
diagnostics: [],
|
||||
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
|
||||
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
||||
})
|
||||
})
|
||||
|
||||
@ -2910,7 +2922,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
|
||||
await editor.expectState({
|
||||
activeLines: [],
|
||||
diagnostics: [],
|
||||
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
|
||||
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -2996,7 +3008,7 @@ test.describe('Redirecting to home page and back to the original file should cle
|
||||
await click00r(200, -200)
|
||||
|
||||
// Draw arc
|
||||
await toolbar.selectTangentialArc()
|
||||
await toolbar.tangentialArcBtn.click()
|
||||
await click00r(0, 0)
|
||||
await click00r(100, 100)
|
||||
|
||||
@ -3024,7 +3036,10 @@ profile001 = startProfileAt([0, 0], sketch001)
|
||||
|> line(end = [191.39, 191.39])
|
||||
|> tangentialArc(endAbsolute = [287.08, 95.69], tag = $seg01)
|
||||
|> angledLine(angle = tangentToEnd(seg01), length = 135.34)
|
||||
|> arc(interiorAbsolute = [191.39, -95.69], endAbsolute = [287.08, -95.69], tag = $seg02)
|
||||
|> arcTo({
|
||||
interior = [191.39, -95.69],
|
||||
end = [287.08, -95.69]
|
||||
}, %, $seg02)
|
||||
|> angledLine(angle = tangentToEnd(seg02) + turns::HALF_TURN, length = 270.67)
|
||||
`.replaceAll('\n', '')
|
||||
)
|
||||
@ -3271,15 +3286,10 @@ profile003 = startProfileAt([-201.08, 254.17], sketch002)
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
test('adding a syntax error, recovers after fixing', async ({
|
||||
page,
|
||||
homePage,
|
||||
context,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
test(
|
||||
'adding a syntax error, recovers after fixing',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage, context, scene, editor, toolbar, cmdBar }) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
@ -3326,7 +3336,7 @@ profile003 = startProfileAt([-201.08, 254.17], sketch002)
|
||||
)
|
||||
await editor.expectState({
|
||||
activeLines: [],
|
||||
diagnostics: ['`badBadBadFn`isnotdefined'],
|
||||
diagnostics: ['memoryitemkey`badBadBadFn`isnotdefined'],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await expect(
|
||||
@ -3366,5 +3376,6 @@ profile003 = startProfileAt([-201.08, 254.17], sketch002)
|
||||
15
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -47,8 +47,9 @@ test.setTimeout(60_000)
|
||||
// up with another PR if we want this back.
|
||||
test(
|
||||
'exports of each format should work',
|
||||
{ tag: ['@snapshot'] },
|
||||
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
||||
async ({ page, context, scene, cmdBar, tronApp }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
@ -376,6 +377,8 @@ test.describe(
|
||||
'extrude on default planes should be stable',
|
||||
{ tag: '@snapshot' },
|
||||
() => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
test('XY', async ({ page, context, cmdBar, scene }) => {
|
||||
await extrudeDefaultPlane(context, page, cmdBar, scene, 'XY')
|
||||
})
|
||||
@ -406,6 +409,7 @@ test(
|
||||
'Draft segments should look right',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, scene, toolbar }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
@ -464,7 +468,9 @@ test(
|
||||
|> xLine(length = 184.3)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||
|
||||
await toolbar.selectTangentialArc()
|
||||
await page
|
||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||
.click()
|
||||
|
||||
// click on the end of the profile to continue it
|
||||
await page.waitForTimeout(500)
|
||||
@ -528,6 +534,8 @@ test(
|
||||
'Draft rectangles should look right',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
@ -573,6 +581,7 @@ test(
|
||||
'Draft circle should look right',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
@ -619,7 +628,9 @@ test.describe(
|
||||
'Client side scene scale should match engine scale',
|
||||
{ tag: '@snapshot' },
|
||||
() => {
|
||||
test('Inch scale', async ({ page, cmdBar, scene, toolbar }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
test('Inch scale', async ({ page, cmdBar, scene }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
@ -653,7 +664,9 @@ test.describe(
|
||||
|> xLine(length = 184.3)`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
await toolbar.selectTangentialArc()
|
||||
await page
|
||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||
.click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// click to continue profile
|
||||
@ -667,8 +680,9 @@ test.describe(
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
// click tangential arc tool again to unequip it
|
||||
// it will be available directly in the toolbar since it was last equipped
|
||||
await toolbar.tangentialArcBtn.click()
|
||||
await page
|
||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||
.click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// screen shot should show the sketch
|
||||
@ -691,13 +705,7 @@ test.describe(
|
||||
})
|
||||
})
|
||||
|
||||
test('Millimeter scale', async ({
|
||||
page,
|
||||
context,
|
||||
cmdBar,
|
||||
scene,
|
||||
toolbar,
|
||||
}) => {
|
||||
test('Millimeter scale', async ({ page, context, cmdBar, scene }) => {
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
@ -750,7 +758,9 @@ test.describe(
|
||||
|> xLine(length = 184.3)`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
await toolbar.selectTangentialArc()
|
||||
await page
|
||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||
.click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// click to continue profile
|
||||
@ -763,7 +773,9 @@ test.describe(
|
||||
|> tangentialArc(endAbsolute = [551.2, -62.01])`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
await toolbar.tangentialArcBtn.click()
|
||||
await page
|
||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||
.click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// screen shot should show the sketch
|
||||
@ -793,6 +805,8 @@ test(
|
||||
'Sketch on face with none z-up',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||
localStorage.setItem(
|
||||
@ -851,6 +865,8 @@ test(
|
||||
'Zoom to fit on load - solid 2d',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -887,6 +903,8 @@ test(
|
||||
'Zoom to fit on load - solid 3d',
|
||||
{ tag: '@snapshot' },
|
||||
async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -926,6 +944,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||
cmdBar,
|
||||
scene,
|
||||
}) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const u = await getUtils(page)
|
||||
const stream = page.getByTestId('stream')
|
||||
|
||||
@ -985,6 +1004,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||
})
|
||||
|
||||
test('Grid turned off', async ({ page, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const u = await getUtils(page)
|
||||
const stream = page.getByTestId('stream')
|
||||
|
||||
@ -1006,6 +1026,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||
})
|
||||
|
||||
test('Grid turned on', async ({ page, context, cmdBar, scene }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
@ -1115,6 +1136,7 @@ test('theme persists', async ({ page, context }) => {
|
||||
|
||||
test.describe('code color goober', { tag: '@snapshot' }, () => {
|
||||
test('code color goober', async ({ page, context, scene, cmdBar }) => {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -1132,7 +1154,11 @@ sweepPath = startSketchOn(XZ)
|
||||
|
||||
sweepSketch = startSketchOn(XY)
|
||||
|> startProfileAt([2, 0], %)
|
||||
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|
||||
|> arc({
|
||||
angleEnd = 360,
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep(path = sweepPath)
|
||||
|> appearance(
|
||||
color = "#bb00ff",
|
||||
@ -1177,7 +1203,11 @@ sweepPath = startSketchOn(XZ)
|
||||
|
||||
sweepSketch = startSketchOn(XY)
|
||||
|> startProfileAt([2, 0], %)
|
||||
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|
||||
|> arc({
|
||||
angleEnd = 360,
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep(path = sweepPath)
|
||||
|> appearance(
|
||||
color = "#bb00ff",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 55 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: 58 KiB After Width: | Height: | Size: 55 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user