Compare commits
70 Commits
jtran/recu
...
kcl-0.2.7
Author | SHA1 | Date | |
---|---|---|---|
9a385fb474 | |||
b740d25bbd | |||
ef350b020b | |||
4d2375faac | |||
22a9f44916 | |||
713a30ed72 | |||
ebed10bc76 | |||
acbe92d717 | |||
e624c9b124 | |||
877eb3ec5e | |||
64500d055a | |||
5df996d877 | |||
84d70751af | |||
3899999465 | |||
9f370fbb56 | |||
f750c4ea8b | |||
e16ecc28a3 | |||
a2d8c5a714 | |||
0bb4586e6d | |||
bbabf04ba6 | |||
37a1208924 | |||
682099c1ad | |||
8f3ad0d43c | |||
be047f5111 | |||
d656a389f8 | |||
682590deea | |||
925f5cc2c2 | |||
a167c174f9 | |||
7f297c13fd | |||
a7e3d83297 | |||
f74c12aa99 | |||
5df9965795 | |||
50d80eb0b6 | |||
96d24065d6 | |||
61dc94b1ee | |||
f14c27e1c4 | |||
c09775f5eb | |||
d14b8f5443 | |||
4a14ca38ab | |||
3543c5f0e7 | |||
a0dc5f4a89 | |||
9d148938a2 | |||
9c6cca2944 | |||
5c472c63d2 | |||
f77b312ecb | |||
8a66d0df76 | |||
b3dc3ff78c | |||
d02df08471 | |||
aac758b396 | |||
0ef6eac239 | |||
c674feb782 | |||
fba3d7c5c1 | |||
8b8fb696d0 | |||
d05f3c00b9 | |||
2541e0c0ea | |||
5e5a204244 | |||
032c2fdd24 | |||
27883e7800 | |||
1ccb810e23 | |||
1c83f148d9 | |||
c7f533b38e | |||
2b711d216f | |||
c67511f67c | |||
d9423219d1 | |||
3f270d8bcf | |||
4c7b72329d | |||
4c060f3d2f | |||
f3afbe8a7b | |||
dad7a84798 | |||
1a560fdc6a |
@ -25,7 +25,9 @@
|
|||||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "warn",
|
"@typescript-eslint/no-floating-promises": "warn",
|
||||||
"testing-library/prefer-screen-queries": "off"
|
"suggest-no-throw/suggest-no-throw": "off",
|
||||||
|
"testing-library/prefer-screen-queries": "off",
|
||||||
|
"jest/valid-expect": "off"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -345,7 +345,7 @@ jobs:
|
|||||||
cat last_download.json
|
cat last_download.json
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.1.3'
|
uses: 'google-github-actions/auth@v2.1.5'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
@ -355,7 +355,7 @@ jobs:
|
|||||||
project_id: kittycadapi
|
project_id: kittycadapi
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.3
|
||||||
with:
|
with:
|
||||||
path: artifact
|
path: artifact
|
||||||
glob: '*/Zoo*'
|
glob: '*/Zoo*'
|
||||||
@ -363,13 +363,13 @@ jobs:
|
|||||||
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.3
|
||||||
with:
|
with:
|
||||||
path: last_update.json
|
path: last_update.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
- name: Upload download endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.3
|
||||||
with:
|
with:
|
||||||
path: last_download.json
|
path: last_download.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
2
.github/workflows/build-test-web.yml
vendored
@ -44,6 +44,8 @@ jobs:
|
|||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
- run: yarn xstate:typegen
|
- run: yarn xstate:typegen
|
||||||
- run: yarn tsc
|
- run: yarn tsc
|
||||||
|
- name: Lint
|
||||||
|
run: yarn eslint --max-warnings 0 src e2e
|
||||||
|
|
||||||
|
|
||||||
check-typos:
|
check-typos:
|
||||||
|
2
.github/workflows/cargo-test.yml
vendored
@ -7,6 +7,7 @@ on:
|
|||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
|
- 'src/wasm-lib/**.kcl'
|
||||||
- .github/workflows/cargo-test.yml
|
- .github/workflows/cargo-test.yml
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -15,6 +16,7 @@ on:
|
|||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
|
- 'src/wasm-lib/**.kcl'
|
||||||
- .github/workflows/cargo-test.yml
|
- .github/workflows/cargo-test.yml
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
|
31
.github/workflows/label-issues.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
name: Label Issues
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
jobs:
|
||||||
|
label:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check if issue opener is ZooSpiritWolf
|
||||||
|
id: check_opener
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const issueOpener = context.payload.issue.user.login;
|
||||||
|
return issueOpener === 'ZooSpiritWolf';
|
||||||
|
|
||||||
|
- name: Add labels
|
||||||
|
if: steps.check_opener.outputs.result == 'true'
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.issue.number,
|
||||||
|
labels: ['bug', 'regression', 'high-priority']
|
||||||
|
});
|
2
.github/workflows/playwright.yml
vendored
@ -346,7 +346,7 @@ jobs:
|
|||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build electron
|
- name: build electron
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn electron:package
|
run: yarn tron:package
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
@ -101,7 +101,7 @@ This will start the application and hot-reload on changed.
|
|||||||
|
|
||||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
||||||
|
|
||||||
To build, run `yarn electron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
## Checking out commits / Bisecting
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
|
BIN
assets/icon.icns
Normal file
BIN
assets/icon.ico
Normal file
After Width: | Height: | Size: 183 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
BIN
assets/icon@2x.icns
Normal file
38
docs/kcl/cm.md
Normal file
38
docs/kcl/ft.md
Normal file
38
docs/kcl/inch.md
Normal file
@ -32,17 +32,20 @@ layout: manual
|
|||||||
* [`chamfer`](kcl/chamfer)
|
* [`chamfer`](kcl/chamfer)
|
||||||
* [`circle`](kcl/circle)
|
* [`circle`](kcl/circle)
|
||||||
* [`close`](kcl/close)
|
* [`close`](kcl/close)
|
||||||
|
* [`cm`](kcl/cm)
|
||||||
* [`cos`](kcl/cos)
|
* [`cos`](kcl/cos)
|
||||||
* [`e`](kcl/e)
|
* [`e`](kcl/e)
|
||||||
* [`extrude`](kcl/extrude)
|
* [`extrude`](kcl/extrude)
|
||||||
* [`fillet`](kcl/fillet)
|
* [`fillet`](kcl/fillet)
|
||||||
* [`floor`](kcl/floor)
|
* [`floor`](kcl/floor)
|
||||||
|
* [`ft`](kcl/ft)
|
||||||
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
|
* [`inch`](kcl/inch)
|
||||||
* [`int`](kcl/int)
|
* [`int`](kcl/int)
|
||||||
* [`lastSegX`](kcl/lastSegX)
|
* [`lastSegX`](kcl/lastSegX)
|
||||||
* [`lastSegY`](kcl/lastSegY)
|
* [`lastSegY`](kcl/lastSegY)
|
||||||
@ -55,8 +58,10 @@ layout: manual
|
|||||||
* [`log`](kcl/log)
|
* [`log`](kcl/log)
|
||||||
* [`log10`](kcl/log10)
|
* [`log10`](kcl/log10)
|
||||||
* [`log2`](kcl/log2)
|
* [`log2`](kcl/log2)
|
||||||
|
* [`m`](kcl/m)
|
||||||
* [`max`](kcl/max)
|
* [`max`](kcl/max)
|
||||||
* [`min`](kcl/min)
|
* [`min`](kcl/min)
|
||||||
|
* [`mm`](kcl/mm)
|
||||||
* [`patternCircular2d`](kcl/patternCircular2d)
|
* [`patternCircular2d`](kcl/patternCircular2d)
|
||||||
* [`patternCircular3d`](kcl/patternCircular3d)
|
* [`patternCircular3d`](kcl/patternCircular3d)
|
||||||
* [`patternLinear2d`](kcl/patternLinear2d)
|
* [`patternLinear2d`](kcl/patternLinear2d)
|
||||||
@ -89,3 +94,4 @@ layout: manual
|
|||||||
* [`xLineTo`](kcl/xLineTo)
|
* [`xLineTo`](kcl/xLineTo)
|
||||||
* [`yLine`](kcl/yLine)
|
* [`yLine`](kcl/yLine)
|
||||||
* [`yLineTo`](kcl/yLineTo)
|
* [`yLineTo`](kcl/yLineTo)
|
||||||
|
* [`yd`](kcl/yd)
|
||||||
|
38
docs/kcl/m.md
Normal file
38
docs/kcl/mm.md
Normal file
@ -55356,7 +55356,7 @@
|
|||||||
"unpublished": false,
|
"unpublished": false,
|
||||||
"deprecated": false,
|
"deprecated": false,
|
||||||
"examples": [
|
"examples": [
|
||||||
"let n = 1.0285\nlet m = 1.0286\nassertEqual(n, m, 0.01, \"n is within the given tolerance for m\")"
|
"let n = 1.0285\nlet o = 1.0286\nassertEqual(n, o, 0.01, \"n is within the given tolerance for o\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -82255,6 +82255,29 @@
|
|||||||
"const exampleSketch = startSketchOn('-XZ')\n |> startProfileAt([0, 0], %)\n |> line([10, 0], %)\n |> line([0, 10], %)\n |> close(%)\n\nconst example = extrude(10, exampleSketch)"
|
"const exampleSketch = startSketchOn('-XZ')\n |> startProfileAt([0, 0], %)\n |> line([10, 0], %)\n |> line([0, 10], %)\n |> close(%)\n\nconst example = extrude(10, exampleSketch)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "cm",
|
||||||
|
"summary": "Centimeters conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to centimeters.\nFor example, if the current project uses inches, this function will return `0.393701`. If the current project uses millimeters, this function will return `10`. If the current project uses centimeters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * cm()` is more readable that your intent is \"I want 10 centimeters\" than `10 * 10`, if the project settings are in millimeters.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * cm()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "cos",
|
"name": "cos",
|
||||||
"summary": "Compute the cosine of a number (in radians).",
|
"summary": "Compute the cosine of a number (in radians).",
|
||||||
@ -98138,6 +98161,29 @@
|
|||||||
"const sketch001 = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> lineTo([12, 10], %)\n |> line([floor(7.02986), 0], %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst extrude001 = extrude(5, sketch001)"
|
"const sketch001 = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> lineTo([12, 10], %)\n |> line([floor(7.02986), 0], %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst extrude001 = extrude(5, sketch001)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ft",
|
||||||
|
"summary": "Feet conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to feet.\nFor example, if the current project uses inches, this function will return `12`. If the current project uses millimeters, this function will return `304.8`. If the current project uses feet, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * ft()` is more readable that your intent is \"I want 10 feet\" than `10 * 304.8`, if the project settings are in millimeters.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * ft()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getNextAdjacentEdge",
|
"name": "getNextAdjacentEdge",
|
||||||
"summary": "Get the next adjacent edge to the edge given.",
|
"summary": "Get the next adjacent edge to the edge given.",
|
||||||
@ -117499,6 +117545,29 @@
|
|||||||
"const model = import(\"tests/inputs/cube.step\")"
|
"const model = import(\"tests/inputs/cube.step\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "inch",
|
||||||
|
"summary": "Inches conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to inches.\nFor example, if the current project uses inches, this function will return `1`. If the current project uses millimeters, this function will return `25.4`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * inch()` is more readable that your intent is \"I want 10 inches\" than `10 * 25.4`, if the project settings are in millimeters.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * inch()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "int",
|
"name": "int",
|
||||||
"summary": "Convert a number to an integer.",
|
"summary": "Convert a number to an integer.",
|
||||||
@ -137886,6 +137955,29 @@
|
|||||||
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> line([log2(100), 0], %)\n |> line([5, 8], %)\n |> line([-10, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> line([log2(100), 0], %)\n |> line([5, 8], %)\n |> line([-10, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "m",
|
||||||
|
"summary": "Meters conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to meters.\nFor example, if the current project uses inches, this function will return `39.3701`. If the current project uses millimeters, this function will return `1000`. If the current project uses meters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * m()` is more readable that your intent is \"I want 10 meters\" than `10 * 1000`, if the project settings are in millimeters.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * m()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "max",
|
"name": "max",
|
||||||
"summary": "Compute the maximum of the given arguments.",
|
"summary": "Compute the maximum of the given arguments.",
|
||||||
@ -137958,6 +138050,29 @@
|
|||||||
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({\n angle: 70,\n length: min(15, 31, 4, 13, 22)\n }, %)\n |> line([20, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({\n angle: 70,\n length: min(15, 31, 4, 13, 22)\n }, %)\n |> line([20, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mm",
|
||||||
|
"summary": "Millimeters conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to millimeters.\nFor example, if the current project uses inches, this function will return `(1/25.4)`. If the current project uses millimeters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * mm()` is more readable that your intent is \"I want 10 millimeters\" than `10 * (1/25.4)`, if the project settings are in inches.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * mm()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "patternCircular2d",
|
"name": "patternCircular2d",
|
||||||
"summary": "Repeat a 2-dimensional sketch some number of times along a partial or",
|
"summary": "Repeat a 2-dimensional sketch some number of times along a partial or",
|
||||||
@ -252170,5 +252285,28 @@
|
|||||||
"examples": [
|
"examples": [
|
||||||
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 50, length: 45 }, %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 50, length: 45 }, %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "yd",
|
||||||
|
"summary": "Yards conversion factor for current projects units.",
|
||||||
|
"description": "No matter what units the current project uses, this function will always return the conversion factor to yards.\nFor example, if the current project uses inches, this function will return `36`. If the current project uses millimeters, this function will return `914.4`. If the current project uses yards, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * yd()` is more readable that your intent is \"I want 10 yards\" than `10 * 914.4`, if the project settings are in millimeters.",
|
||||||
|
"tags": [
|
||||||
|
"units"
|
||||||
|
],
|
||||||
|
"args": [],
|
||||||
|
"returnValue": {
|
||||||
|
"name": "",
|
||||||
|
"type": "number",
|
||||||
|
"schema": {
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"unpublished": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"examples": [
|
||||||
|
"const totalWidth = 10 * yd()"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
38
docs/kcl/yd.md
Normal file
@ -1,8 +1,9 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils, setup, tearDown } from './test-utils'
|
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -83,7 +84,7 @@ test.describe('Code pane and errors', () => {
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
||||||
|
|
||||||
// Close the code pane
|
// Close the code pane
|
||||||
await codePaneButton.click()
|
await codePaneButton.click()
|
||||||
@ -106,7 +107,7 @@ test.describe('Code pane and errors', () => {
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('When error is not in view you can click the badge to scroll to it', async ({
|
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||||
@ -217,3 +218,93 @@ test.describe('Code pane and errors', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Opening multiple panes persists when switching projects',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
|
// Setup multiple projects.
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await Promise.all([
|
||||||
|
fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }),
|
||||||
|
fsp.mkdir(`${dir}/bracket`, { recursive: true }),
|
||||||
|
])
|
||||||
|
await Promise.all([
|
||||||
|
fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/router-template-slate/main.kcl`
|
||||||
|
),
|
||||||
|
fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
|
`${dir}/bracket/main.kcl`
|
||||||
|
),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load', async () => {
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// If they're open by default, we're not actually testing anything.
|
||||||
|
await test.step('Pre-condition: panes are not already visible', async () => {
|
||||||
|
await expect(page.locator('#variables-pane')).not.toBeVisible()
|
||||||
|
await expect(page.locator('#logs-pane')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Open multiple panes', async () => {
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await u.openVariablesPane()
|
||||||
|
await u.openLogsPane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Opening the router-template project should load', async () => {
|
||||||
|
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('router-template-slate').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('All panes opened before should be visible', async () => {
|
||||||
|
await expect(page.locator('#code-pane')).toBeVisible()
|
||||||
|
await expect(page.locator('#variables-pane')).toBeVisible()
|
||||||
|
await expect(page.locator('#logs-pane')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -12,50 +12,47 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Command bar tests', () => {
|
test.describe('Command bar tests', () => {
|
||||||
// TODO fixme: enter is not working in the command bar
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
test.fixme(
|
page,
|
||||||
'Extrude from command bar selects extrude line after',
|
}) => {
|
||||||
async ({ page }) => {
|
await page.addInitScript(async () => {
|
||||||
await page.addInitScript(async () => {
|
localStorage.setItem(
|
||||||
localStorage.setItem(
|
'persistCode',
|
||||||
'persistCode',
|
`const sketch001 = startSketchOn('XY')
|
||||||
`const sketch001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> xLine(-20, %)
|
|> xLine(-20, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// Click the line of code for xLine.
|
|
||||||
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
|
||||||
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// TODO fixme: enter is not working in the command bar
|
const u = await getUtils(page)
|
||||||
test.fixme('Fillet from command bar', async ({ page }) => {
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click the line of code for xLine.
|
||||||
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Fillet from command bar', async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -127,7 +124,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await expect(cmdSearchBar).not.toBeVisible()
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
await expect(cmdSearchBar).toBeFocused()
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
@ -188,7 +185,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
@ -253,7 +250,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
// Search for extrude command and choose it
|
// Search for extrude command and choose it
|
||||||
|
@ -9,6 +9,7 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
test.describe('Copilot ghost text', () => {
|
test.describe('Copilot ghost text', () => {
|
||||||
|
// eslint-disable-next-line jest/valid-title
|
||||||
test.skip(true, 'Needs to get covered again')
|
test.skip(true, 'Needs to get covered again')
|
||||||
|
|
||||||
test('completes code in empty file', async ({ page }) => {
|
test('completes code in empty file', async ({ page }) => {
|
||||||
@ -331,7 +332,6 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -348,10 +348,10 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Going elsewhere in the code should hide the ghost text.
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
@ -367,8 +367,6 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -381,17 +379,17 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
// Ctrl+z
|
// Ctrl+z
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
// Ctrl+shift+z
|
// Ctrl+shift+z
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
@ -410,14 +408,14 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Once for the enter.
|
// Once for the enter.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Once for the text.
|
// Once for the text.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
188
e2e/playwright/desktop-export.spec.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { getUtils, setupElectron, tearDown } from './test-utils'
|
||||||
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(
|
||||||
|
'export works on the first try',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await Promise.all([fsp.mkdir(`${dir}/bracket`, { recursive: true })])
|
||||||
|
await Promise.all([
|
||||||
|
fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/bracket/other.kcl`
|
||||||
|
),
|
||||||
|
fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
|
`${dir}/bracket/main.kcl`
|
||||||
|
),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
await test.step('on open of project', async () => {
|
||||||
|
await expect(page.getByText(`bracket`)).toBeVisible()
|
||||||
|
|
||||||
|
// open the project
|
||||||
|
await page.getByText(`bracket`).click()
|
||||||
|
|
||||||
|
// wait for the project to load
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// expect zero errors in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// export the model
|
||||||
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
|
await expect(exportButton).toBeVisible()
|
||||||
|
|
||||||
|
const gltfOption = page.getByText('glTF')
|
||||||
|
const submitButton = page.getByText('Confirm Export')
|
||||||
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||||
|
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||||
|
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||||
|
|
||||||
|
// Click the export button
|
||||||
|
await exportButton.click()
|
||||||
|
|
||||||
|
await expect(gltfOption).toBeVisible()
|
||||||
|
await expect(page.getByText('STL')).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Click the checkbox
|
||||||
|
await expect(submitButton).toBeVisible()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Find the toast.
|
||||||
|
// Look out for the toast message
|
||||||
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
// Expect it to succeed.
|
||||||
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
|
await expect(successToastMessage).toBeVisible()
|
||||||
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
await test.step('Check the export size', async () => {
|
||||||
|
await expect
|
||||||
|
.poll(
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const outputGltf = await fsp.readFile('output.gltf')
|
||||||
|
return outputGltf.byteLength
|
||||||
|
} catch (e) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timeout: 15_000 }
|
||||||
|
)
|
||||||
|
.toBe(477327)
|
||||||
|
|
||||||
|
// clean up output.gltf
|
||||||
|
await fsp.rm('output.gltf')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('on open of file in file pane', async () => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await u.openFilePanel()
|
||||||
|
|
||||||
|
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
||||||
|
|
||||||
|
// Click the file
|
||||||
|
await otherKclButton.click()
|
||||||
|
|
||||||
|
// Close the file pane
|
||||||
|
await u.closeFilePanel()
|
||||||
|
|
||||||
|
// wait for it to finish executing (todo: make this more robust)
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
// expect zero errors in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// export the model
|
||||||
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
|
await expect(exportButton).toBeVisible()
|
||||||
|
|
||||||
|
const gltfOption = page.getByText('glTF')
|
||||||
|
const submitButton = page.getByText('Confirm Export')
|
||||||
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||||
|
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||||
|
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||||
|
|
||||||
|
// Click the export button
|
||||||
|
await exportButton.click()
|
||||||
|
|
||||||
|
await expect(gltfOption).toBeVisible()
|
||||||
|
await expect(page.getByText('STL')).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Click the checkbox
|
||||||
|
await expect(submitButton).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Find the toast.
|
||||||
|
// Look out for the toast message
|
||||||
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
// Expect it to succeed.
|
||||||
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
|
await expect(successToastMessage).toBeVisible()
|
||||||
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
await test.step('Check the export size', async () => {
|
||||||
|
await expect
|
||||||
|
.poll(
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const outputGltf = await fsp.readFile('output.gltf')
|
||||||
|
return outputGltf.byteLength
|
||||||
|
} catch (e) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timeout: 15_000 }
|
||||||
|
)
|
||||||
|
.toBe(105022)
|
||||||
|
|
||||||
|
// clean up output.gltf
|
||||||
|
await fsp.rm('output.gltf')
|
||||||
|
})
|
||||||
|
await electronApp.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
@ -16,7 +16,6 @@ test.describe('Editor tests', () => {
|
|||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -29,9 +28,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> line([-20, 0], %)
|
|> line([-20, 0], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
|
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -42,9 +41,9 @@ test.describe('Editor tests', () => {
|
|||||||
// |> close(%)`)
|
// |> close(%)`)
|
||||||
|
|
||||||
// uncomment the code
|
// uncomment the code
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -148,9 +147,7 @@ test.describe('Editor tests', () => {
|
|||||||
// Delete all the code.
|
// Delete all the code.
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
// Select all
|
// Select all
|
||||||
await page.keyboard.press('Control+A')
|
await page.keyboard.press('ControlOrMeta+A')
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await page.keyboard.press('Meta+A')
|
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -354,7 +351,7 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
await page.getByText('$ error').click()
|
await page.getByText('$ error').click()
|
||||||
@ -714,17 +711,15 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
|
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||||
// please fix together
|
|
||||||
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`const sketch001 = startSketchOn('XZ')
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([4.61, -14.01], %)
|
|> startProfileAt([4.61, -10.01], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`
|
|> extrude(5, %)`
|
||||||
)
|
)
|
||||||
@ -759,11 +754,11 @@ test.describe('Editor tests', () => {
|
|||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startPX = [665, 458]
|
const startPX = [665, 397]
|
||||||
|
|
||||||
const dragPX = 40
|
const dragPX = 40
|
||||||
|
|
||||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@ -801,7 +796,7 @@ test.describe('Editor tests', () => {
|
|||||||
// drag tangentialArcTo handle
|
// drag tangentialArcTo handle
|
||||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||||
targetPosition: {
|
targetPosition: {
|
||||||
x: tangentEnd.x + dragPX,
|
x: tangentEnd.x + dragPX,
|
||||||
y: tangentEnd.y + dragPX,
|
y: tangentEnd.y + dragPX,
|
||||||
@ -813,12 +808,12 @@ test.describe('Editor tests', () => {
|
|||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -16.82], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([15.4, -2.74], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([27.6, -3.05], %)
|
||||||
|> line([2.65, -2.69], %)
|
|> close(%)
|
||||||
|> close(%)
|
|> extrude(5, %)
|
||||||
|> extrude(5, %)`)
|
`)
|
||||||
|
|
||||||
// Hit undo
|
// Hit undo
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -827,11 +822,11 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -16.82], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([15.4, -2.74], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -840,11 +835,12 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -16.82], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)
|
||||||
|
`)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -854,9 +850,9 @@ test.describe('Editor tests', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([4.61, -14.01], %)
|
|> startProfileAt([4.61, -10.01], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)`)
|
||||||
})
|
})
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
102
e2e/playwright/machines.spec.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { setupElectron, tearDown } from './test-utils'
|
||||||
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(
|
||||||
|
'When machine-api server not found butt is disabled and shows the reason',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
|
`${dir}/bracket/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
const notFoundText = 'Machine API server was not discovered'
|
||||||
|
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
|
||||||
|
|
||||||
|
// Find the make button
|
||||||
|
const makeButton = page.getByRole('button', { name: 'Make' })
|
||||||
|
// Make sure the button is visible but disabled
|
||||||
|
await expect(makeButton).toBeVisible()
|
||||||
|
await expect(makeButton).toBeDisabled()
|
||||||
|
|
||||||
|
// When you hover over the button, the tooltip should show
|
||||||
|
// that the machine-api server is not found
|
||||||
|
await makeButton.hover()
|
||||||
|
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'When machine-api server not found home screen & project status shows the reason',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
|
`${dir}/bracket/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
const notFoundText = 'Machine API server was not discovered'
|
||||||
|
|
||||||
|
await expect(page.getByText(notFoundText)).not.toBeVisible()
|
||||||
|
|
||||||
|
const networkMachineToggle = page.getByTestId('network-machine-toggle')
|
||||||
|
await networkMachineToggle.hover()
|
||||||
|
await expect(page.getByText(notFoundText)).toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
|
||||||
|
|
||||||
|
await networkMachineToggle.hover()
|
||||||
|
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
@ -347,6 +347,10 @@ test(
|
|||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
|
@ -194,7 +194,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
||||||
|
|
||||||
// Okay execution finished, let's start editing text below the error.
|
// Okay execution finished, let's start editing text below the error.
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
@ -236,9 +236,13 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async (code) => {
|
await page.addInitScript(
|
||||||
localStorage.setItem('persistCode', code)
|
async ({ code }) => {
|
||||||
}, TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR)
|
localStorage.setItem('persistCode', code)
|
||||||
|
;(window as any).playwrightSkipFilePicker = true
|
||||||
|
},
|
||||||
|
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
||||||
|
)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
@ -325,7 +329,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
// Expect it to succeed.
|
// Expect it to succeed.
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
@ -337,6 +341,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
}) => {
|
}) => {
|
||||||
// This is being weird on ubuntu and windows.
|
// This is being weird on ubuntu and windows.
|
||||||
test.skip(
|
test.skip(
|
||||||
|
// eslint-disable-next-line jest/valid-title
|
||||||
process.platform === 'linux' || process.platform === 'win32',
|
process.platform === 'linux' || process.platform === 'win32',
|
||||||
'This test is being weird on ubuntu'
|
'This test is being weird on ubuntu'
|
||||||
)
|
)
|
||||||
@ -420,6 +425,10 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
`Network health indicator only appears in modeling view`,
|
`Network health indicator only appears in modeling view`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName: _ }, testInfo) => {
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
@ -472,7 +481,7 @@ async function clickExportButton(page: Page) {
|
|||||||
// Click the export button
|
// Click the export button
|
||||||
await exportButton.click()
|
await exportButton.click()
|
||||||
|
|
||||||
// Click the stl.
|
// Click the gltf.
|
||||||
const gltfOption = page.getByRole('option', { name: 'glTF' })
|
const gltfOption = page.getByRole('option', { name: 'glTF' })
|
||||||
await expect(gltfOption).toBeVisible()
|
await expect(gltfOption).toBeVisible()
|
||||||
|
|
||||||
|
@ -344,111 +344,108 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// failing for the same reason as "Can undo a sketch modification with ctrl+z"
|
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||||
// please fix together
|
page,
|
||||||
test.fixme(
|
}) => {
|
||||||
'Can edit a sketch that has been extruded in the same pipe',
|
const u = await getUtils(page)
|
||||||
async ({ page }) => {
|
await page.addInitScript(async () => {
|
||||||
const u = await getUtils(page)
|
localStorage.setItem(
|
||||||
await page.addInitScript(async () => {
|
'persistCode',
|
||||||
localStorage.setItem(
|
`const sketch001 = startSketchOn('XZ')
|
||||||
'persistCode',
|
|> startProfileAt([4.61, -10.01], %)
|
||||||
`const sketch001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([4.61, -14.01], %)
|
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`
|
|> extrude(5, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_look_at',
|
type: 'default_camera_look_at',
|
||||||
vantage: { x: 0, y: -1250, z: 580 },
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
center: { x: 0, y: 0, z: 0 },
|
center: { x: 0, y: 0, z: 0 },
|
||||||
up: { x: 0, y: 0, z: 1 },
|
up: { x: 0, y: 0, z: 1 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_get_settings',
|
type: 'default_camera_get_settings',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startPX = [665, 458]
|
const startPX = [665, 397]
|
||||||
|
|
||||||
const dragPX = 40
|
const dragPX = 40
|
||||||
|
|
||||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(400)
|
||||||
let prevContent = await page.locator('.cm-content').innerText()
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
|
|
||||||
// drag startProfieAt handle
|
// drag startProfieAt handle
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
// drag line handle
|
// drag line handle
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||||
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
// drag tangentialArcTo handle
|
// drag tangentialArcTo handle
|
||||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||||
targetPosition: {
|
targetPosition: {
|
||||||
x: tangentEnd.x + dragPX,
|
x: tangentEnd.x + dragPX,
|
||||||
y: tangentEnd.y + dragPX,
|
y: tangentEnd.y + dragPX,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
|
||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -16.82], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([15.4, -2.74], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)
|
|> tangentialArcTo([27.6, -3.05], %)
|
||||||
|> line([2.65, -2.69], %)
|
|> close(%)
|
||||||
|> close(%)
|
|> extrude(5, %)
|
||||||
|> extrude(5, %)`)
|
`)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||||
page,
|
page,
|
||||||
@ -602,7 +599,7 @@ test.describe('Sketch tests', () => {
|
|||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
// exit the sketch, reset relative clicker
|
// exit the sketch, reset relative clicker
|
||||||
click00r(undefined, undefined)
|
await click00r(undefined, undefined)
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
@ -53,6 +53,7 @@ test(
|
|||||||
async ({ page, context }) => {
|
async ({ page, context }) => {
|
||||||
// skip on macos and windows.
|
// skip on macos and windows.
|
||||||
test.skip(
|
test.skip(
|
||||||
|
// eslint-disable-next-line jest/valid-title
|
||||||
process.platform === 'darwin' || process.platform === 'win32',
|
process.platform === 'darwin' || process.platform === 'win32',
|
||||||
'Skip on macos and windows'
|
'Skip on macos and windows'
|
||||||
)
|
)
|
||||||
@ -963,3 +964,69 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('theme persists', async ({ page, context }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(10, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}, KCL_DEFAULT_LENGTH)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click()
|
||||||
|
await expect(page.getByTestId('settings-link')).toBeVisible()
|
||||||
|
await page.getByTestId('settings-link').click()
|
||||||
|
|
||||||
|
// open user settingns
|
||||||
|
await page.getByRole('radio', { name: 'person User' }).click()
|
||||||
|
|
||||||
|
await page.getByTestId('app-theme').selectOption('light')
|
||||||
|
|
||||||
|
await page.getByTestId('settings-close-button').click()
|
||||||
|
|
||||||
|
const networkToggle = page.getByTestId('network-toggle')
|
||||||
|
|
||||||
|
// simulate network down
|
||||||
|
await u.emulateNetworkConditions({
|
||||||
|
offline: true,
|
||||||
|
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||||
|
latency: 0,
|
||||||
|
downloadThroughput: -1,
|
||||||
|
uploadThroughput: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disconnect and reconnect to check the theme persists through a reload
|
||||||
|
|
||||||
|
// Expect the network to be down
|
||||||
|
await expect(networkToggle).toContainText('Offline')
|
||||||
|
|
||||||
|
// simulate network up
|
||||||
|
await u.emulateNetworkConditions({
|
||||||
|
offline: false,
|
||||||
|
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||||
|
latency: 0,
|
||||||
|
downloadThroughput: -1,
|
||||||
|
uploadThroughput: -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(networkToggle).toContainText('Connected')
|
||||||
|
|
||||||
|
await expect(page.getByText('building scene')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page, 'expect screenshot to have light theme').toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@ -2,12 +2,13 @@ import {
|
|||||||
expect,
|
expect,
|
||||||
Page,
|
Page,
|
||||||
Download,
|
Download,
|
||||||
TestInfo,
|
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
|
TestInfo,
|
||||||
_electron as electron,
|
_electron as electron,
|
||||||
|
Locator,
|
||||||
|
test,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import os from 'os'
|
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
import fsSync from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
@ -25,6 +26,7 @@ import {
|
|||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
|
import { isArray } from 'lib/utils'
|
||||||
|
|
||||||
type TestColor = [number, number, number]
|
type TestColor = [number, number, number]
|
||||||
export const TEST_COLORS = {
|
export const TEST_COLORS = {
|
||||||
@ -43,6 +45,9 @@ export const commonPoints = {
|
|||||||
num2: 14.44,
|
num2: 14.44,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const editorSelector = '[role="textbox"][data-language="kcl"]'
|
||||||
|
type PaneId = 'variables' | 'code' | 'files' | 'logs'
|
||||||
|
|
||||||
async function waitForPageLoadWithRetry(page: Page) {
|
async function waitForPageLoadWithRetry(page: Page) {
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
@ -72,11 +77,10 @@ async function waitForPageLoad(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function removeCurrentCode(page: Page) {
|
async function removeCurrentCode(page: Page) {
|
||||||
const hotkey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
await page.keyboard.down(hotkey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('a')
|
await page.keyboard.press('a')
|
||||||
await page.keyboard.up(hotkey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
await expect(page.locator('.cm-content')).toHaveText('')
|
await expect(page.locator('.cm-content')).toHaveText('')
|
||||||
}
|
}
|
||||||
@ -94,6 +98,8 @@ async function expectCmdLog(page: Page, locatorStr: string, timeout = 5000) {
|
|||||||
await expect(page.locator(locatorStr).last()).toBeVisible({ timeout })
|
await expect(page.locator(locatorStr).last()).toBeVisible({ timeout })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignoring the lint since I assume someone will want to use this for a test.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function waitForDefaultPlanesToBeVisible(page: Page) {
|
async function waitForDefaultPlanesToBeVisible(page: Page) {
|
||||||
await page.waitForFunction(
|
await page.waitForFunction(
|
||||||
() =>
|
() =>
|
||||||
@ -102,17 +108,21 @@ async function waitForDefaultPlanesToBeVisible(page: Page) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openKclCodePanel(page: Page) {
|
async function openPane(page: Page, testId: string) {
|
||||||
const paneLocator = page.getByTestId('code-pane-button')
|
const locator = page.getByTestId(testId)
|
||||||
const ariaSelected = await paneLocator?.getAttribute('aria-pressed')
|
await expect(locator).toBeVisible()
|
||||||
const isOpen = ariaSelected === 'true'
|
const isOpen = (await locator?.getAttribute('aria-pressed')) === 'true'
|
||||||
|
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
await paneLocator.click()
|
await locator.click()
|
||||||
await expect(paneLocator).toHaveAttribute('aria-pressed', 'true')
|
await expect(locator).toHaveAttribute('aria-pressed', 'true')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openKclCodePanel(page: Page) {
|
||||||
|
await openPane(page, 'code-pane-button')
|
||||||
|
}
|
||||||
|
|
||||||
async function closeKclCodePanel(page: Page) {
|
async function closeKclCodePanel(page: Page) {
|
||||||
const paneLocator = page.getByTestId('code-pane-button')
|
const paneLocator = page.getByTestId('code-pane-button')
|
||||||
const ariaSelected = await paneLocator?.getAttribute('aria-pressed')
|
const ariaSelected = await paneLocator?.getAttribute('aria-pressed')
|
||||||
@ -125,14 +135,7 @@ async function closeKclCodePanel(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openDebugPanel(page: Page) {
|
async function openDebugPanel(page: Page) {
|
||||||
const debugLocator = page.getByTestId('debug-pane-button')
|
await openPane(page, 'debug-pane-button')
|
||||||
await expect(debugLocator).toBeVisible()
|
|
||||||
const isOpen = (await debugLocator?.getAttribute('aria-pressed')) === 'true'
|
|
||||||
|
|
||||||
if (!isOpen) {
|
|
||||||
await debugLocator.click()
|
|
||||||
await expect(debugLocator).toHaveAttribute('aria-pressed', 'true')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeDebugPanel(page: Page) {
|
async function closeDebugPanel(page: Page) {
|
||||||
@ -145,6 +148,28 @@ async function closeDebugPanel(page: Page) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openFilePanel(page: Page) {
|
||||||
|
await openPane(page, 'files-pane-button')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeFilePanel(page: Page) {
|
||||||
|
const fileLocator = page.getByTestId('files-pane-button')
|
||||||
|
await expect(fileLocator).toBeVisible()
|
||||||
|
const isOpen = (await fileLocator?.getAttribute('aria-pressed')) === 'true'
|
||||||
|
if (isOpen) {
|
||||||
|
await fileLocator.click()
|
||||||
|
await expect(fileLocator).not.toHaveAttribute('aria-pressed', 'true')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openVariablesPane(page: Page) {
|
||||||
|
await openPane(page, 'variables-pane-button')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openLogsPane(page: Page) {
|
||||||
|
await openPane(page, 'logs-pane-button')
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForCmdReceive(page: Page, commandType: string) {
|
async function waitForCmdReceive(page: Page, commandType: string) {
|
||||||
return page
|
return page
|
||||||
.locator(`[data-receive-command-type="${commandType}"]`)
|
.locator(`[data-receive-command-type="${commandType}"]`)
|
||||||
@ -171,7 +196,8 @@ export const wiggleMove = async (
|
|||||||
const isElVis = await page.locator(locator).isVisible()
|
const isElVis = await page.locator(locator).isVisible()
|
||||||
if (isElVis) return
|
if (isElVis) return
|
||||||
}
|
}
|
||||||
const [x1, y1] = [0, Math.sin((tau / steps) * j * freq) * amplitude]
|
// x1 is 0.
|
||||||
|
const y1 = Math.sin((tau / steps) * j * freq) * amplitude
|
||||||
const [x2, y2] = [
|
const [x2, y2] = [
|
||||||
Math.cos(-ang * deg) * i - Math.sin(-ang * deg) * y1,
|
Math.cos(-ang * deg) * i - Math.sin(-ang * deg) * y1,
|
||||||
Math.sin(-ang * deg) * i + Math.cos(-ang * deg) * y1,
|
Math.sin(-ang * deg) * i + Math.cos(-ang * deg) * y1,
|
||||||
@ -182,7 +208,7 @@ export const wiggleMove = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const circleMove = async (
|
export const circleMove = async (
|
||||||
page: any,
|
page: Page,
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
steps: number,
|
steps: number,
|
||||||
@ -288,13 +314,19 @@ export function normaliseKclNumbers(code: string, ignoreZero = true): string {
|
|||||||
return replaceNumbers(code)
|
return replaceNumbers(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUtils(page: Page) {
|
export async function getUtils(page: Page, test_?: typeof test) {
|
||||||
|
if (!test) {
|
||||||
|
console.warn(
|
||||||
|
'Some methods in getUtils requires test object as second argument'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Chrome devtools protocol session only works in Chromium
|
// Chrome devtools protocol session only works in Chromium
|
||||||
const browserType = page.context().browser()?.browserType().name()
|
const browserType = page.context().browser()?.browserType().name()
|
||||||
const cdpSession =
|
const cdpSession =
|
||||||
browserType !== 'chromium' ? null : await page.context().newCDPSession(page)
|
browserType !== 'chromium' ? null : await page.context().newCDPSession(page)
|
||||||
|
|
||||||
return {
|
const util = {
|
||||||
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
|
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
|
||||||
waitForPageLoad: () => waitForPageLoad(page),
|
waitForPageLoad: () => waitForPageLoad(page),
|
||||||
waitForPageLoadWithRetry: () => waitForPageLoadWithRetry(page),
|
waitForPageLoadWithRetry: () => waitForPageLoadWithRetry(page),
|
||||||
@ -317,6 +349,10 @@ export async function getUtils(page: Page) {
|
|||||||
closeKclCodePanel: () => closeKclCodePanel(page),
|
closeKclCodePanel: () => closeKclCodePanel(page),
|
||||||
openDebugPanel: () => openDebugPanel(page),
|
openDebugPanel: () => openDebugPanel(page),
|
||||||
closeDebugPanel: () => closeDebugPanel(page),
|
closeDebugPanel: () => closeDebugPanel(page),
|
||||||
|
openFilePanel: () => openFilePanel(page),
|
||||||
|
closeFilePanel: () => closeFilePanel(page),
|
||||||
|
openVariablesPane: () => openVariablesPane(page),
|
||||||
|
openLogsPane: () => openLogsPane(page),
|
||||||
openAndClearDebugPanel: async () => {
|
openAndClearDebugPanel: async () => {
|
||||||
await openDebugPanel(page)
|
await openDebugPanel(page)
|
||||||
return clearCommandLogs(page)
|
return clearCommandLogs(page)
|
||||||
@ -452,9 +488,79 @@ export async function getUtils(page: Page) {
|
|||||||
return page.evaluate('window.tearDown()')
|
return page.evaluate('window.tearDown()')
|
||||||
}
|
}
|
||||||
|
|
||||||
cdpSession?.send('Network.emulateNetworkConditions', networkOptions)
|
return cdpSession?.send(
|
||||||
|
'Network.emulateNetworkConditions',
|
||||||
|
networkOptions
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
toNormalizedCode: (text: string) => {
|
||||||
|
return text.replace(/\s+/g, '')
|
||||||
|
},
|
||||||
|
|
||||||
|
createAndSelectProject: async (hasText: string) => {
|
||||||
|
return test_?.step(
|
||||||
|
`Create and select project with text "${hasText}"`,
|
||||||
|
async () => {
|
||||||
|
await page.getByTestId('home-new-file').click()
|
||||||
|
const projectLinksPost = page.getByTestId('project-link')
|
||||||
|
await projectLinksPost.filter({ hasText }).click()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
editorTextMatches: async (code: string) => {
|
||||||
|
const editor = page.locator(editorSelector)
|
||||||
|
const editorText = await editor.textContent()
|
||||||
|
return expect(util.toNormalizedCode(editorText || '')).toBe(
|
||||||
|
util.toNormalizedCode(code)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
pasteCodeInEditor: async (code: string) => {
|
||||||
|
return test?.step('Paste in KCL code', async () => {
|
||||||
|
const editor = page.locator(editorSelector)
|
||||||
|
await editor.fill(code)
|
||||||
|
await util.editorTextMatches(code)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
clickPane: async (paneId: PaneId) => {
|
||||||
|
return test?.step(`Open ${paneId} pane`, async () => {
|
||||||
|
await page.getByTestId(paneId + '-pane-button').click()
|
||||||
|
await expect(page.locator('#' + paneId + '-pane')).toBeVisible()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
createNewFileAndSelect: async (name: string) => {
|
||||||
|
return test?.step(`Create a file named ${name}, select it`, async () => {
|
||||||
|
await page.getByTestId('create-file-button').click()
|
||||||
|
await page.getByTestId('file-rename-field').fill(name)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page
|
||||||
|
.getByTestId('file-pane-scroll-container')
|
||||||
|
.filter({ hasText: name })
|
||||||
|
.click()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
panesOpen: async (paneIds: PaneId[]) => {
|
||||||
|
return test?.step(`Setting ${paneIds} panes to be open`, async () => {
|
||||||
|
await page.addInitScript(
|
||||||
|
({ PERSIST_MODELING_CONTEXT, paneIds }) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
PERSIST_MODELING_CONTEXT,
|
||||||
|
JSON.stringify({ openPanes: paneIds })
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ PERSIST_MODELING_CONTEXT, paneIds }
|
||||||
|
)
|
||||||
|
await page.reload()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return util
|
||||||
}
|
}
|
||||||
|
|
||||||
type TemplateOptions = Array<number | Array<number>>
|
type TemplateOptions = Array<number | Array<number>>
|
||||||
@ -475,7 +581,7 @@ const _makeTemplate = (
|
|||||||
templateParts: TemplateStringsArray,
|
templateParts: TemplateStringsArray,
|
||||||
...options: TemplateOptions
|
...options: TemplateOptions
|
||||||
) => {
|
) => {
|
||||||
const length = Math.max(...options.map((a) => (Array.isArray(a) ? a[0] : 0)))
|
const length = Math.max(...options.map((a) => (isArray(a) ? a[0] : 0)))
|
||||||
let reExpTemplate = ''
|
let reExpTemplate = ''
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
const currentStr = templateParts.map((str, index) => {
|
const currentStr = templateParts.map((str, index) => {
|
||||||
@ -483,7 +589,7 @@ const _makeTemplate = (
|
|||||||
return (
|
return (
|
||||||
escapeRegExp(str) +
|
escapeRegExp(str) +
|
||||||
String(
|
String(
|
||||||
Array.isArray(currentOptions)
|
isArray(currentOptions)
|
||||||
? currentOptions[i]
|
? currentOptions[i]
|
||||||
: typeof currentOptions === 'number'
|
: typeof currentOptions === 'number'
|
||||||
? currentOptions
|
? currentOptions
|
||||||
@ -539,17 +645,34 @@ export interface Paths {
|
|||||||
export const doExport = async (
|
export const doExport = async (
|
||||||
output: Models['OutputFormat_type'],
|
output: Models['OutputFormat_type'],
|
||||||
page: Page,
|
page: Page,
|
||||||
isElectron = false
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
if (!isElectron) {
|
if (exportFrom === 'dropdown') {
|
||||||
await page.getByRole('button', { name: APP_NAME }).click()
|
await page.getByRole('button', { name: APP_NAME }).click()
|
||||||
const exportMenuButton = page.getByRole('button', {
|
const exportMenuButton = page.getByRole('button', {
|
||||||
name: 'Export current part',
|
name: 'Export current part',
|
||||||
})
|
})
|
||||||
await expect(exportMenuButton).toBeVisible()
|
await expect(exportMenuButton).toBeVisible()
|
||||||
await exportMenuButton.click()
|
await exportMenuButton.click()
|
||||||
} else {
|
} else if (exportFrom === 'sidebarButton') {
|
||||||
|
await expect(page.getByTestId('export-pane-button')).toBeVisible()
|
||||||
await page.getByTestId('export-pane-button').click()
|
await page.getByTestId('export-pane-button').click()
|
||||||
|
} else if (exportFrom === 'commandBar') {
|
||||||
|
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
await expect(commandBarButton).toBeVisible()
|
||||||
|
// Click the command bar button
|
||||||
|
await commandBarButton.click()
|
||||||
|
|
||||||
|
// Wait for the command bar to appear
|
||||||
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
|
const textToCadCommand = page.getByRole('option', {
|
||||||
|
name: 'floppy disk arrow Export',
|
||||||
|
})
|
||||||
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
|
// Click the Text-to-CAD command
|
||||||
|
await textToCadCommand.first().click()
|
||||||
}
|
}
|
||||||
await expect(page.getByTestId('command-bar')).toBeVisible()
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
|
|
||||||
@ -577,7 +700,7 @@ export const doExport = async (
|
|||||||
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
||||||
let downloadCnt = 0
|
let downloadCnt = 0
|
||||||
|
|
||||||
if (!isElectron)
|
if (exportFrom === 'dropdown')
|
||||||
page.on('download', async (download) => {
|
page.on('download', async (download) => {
|
||||||
if (downloadCnt === 0) {
|
if (downloadCnt === 0) {
|
||||||
downloadResolve1(download)
|
downloadResolve1(download)
|
||||||
@ -585,7 +708,7 @@ export const doExport = async (
|
|||||||
downloadCnt++
|
downloadCnt++
|
||||||
})
|
})
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||||
if (isElectron) {
|
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
||||||
return {
|
return {
|
||||||
modelPath: '',
|
modelPath: '',
|
||||||
imagePath: '',
|
imagePath: '',
|
||||||
@ -620,11 +743,6 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the appropriate modifier key for the platform.
|
|
||||||
*/
|
|
||||||
export const metaModifier = os.platform() === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
export async function tearDown(page: Page, testInfo: TestInfo) {
|
export async function tearDown(page: Page, testInfo: TestInfo) {
|
||||||
if (testInfo.status === 'skipped') return
|
if (testInfo.status === 'skipped') return
|
||||||
if (testInfo.status === 'failed') return
|
if (testInfo.status === 'failed') return
|
||||||
@ -649,6 +767,7 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
|||||||
export async function setup(context: BrowserContext, page: Page) {
|
export async function setup(context: BrowserContext, page: Page) {
|
||||||
await context.addInitScript(
|
await context.addInitScript(
|
||||||
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
||||||
|
localStorage.clear()
|
||||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||||
localStorage.setItem('persistCode', ``)
|
localStorage.setItem('persistCode', ``)
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
@ -684,6 +803,9 @@ export async function setup(context: BrowserContext, page: Page) {
|
|||||||
])
|
])
|
||||||
// kill animations, speeds up tests and reduced flakiness
|
// kill animations, speeds up tests and reduced flakiness
|
||||||
await page.emulateMedia({ reducedMotion: 'reduce' })
|
await page.emulateMedia({ reducedMotion: 'reduce' })
|
||||||
|
|
||||||
|
// Trigger a navigation, since loading file:// doesn't.
|
||||||
|
await page.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupElectron({
|
export async function setupElectron({
|
||||||
@ -743,5 +865,50 @@ export async function setupElectron({
|
|||||||
|
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
|
|
||||||
return { electronApp, page }
|
return { electronApp, page, dir: projectDirName }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isOutOfViewInScrollContainer(
|
||||||
|
element: Locator,
|
||||||
|
container: Locator
|
||||||
|
): Promise<boolean> {
|
||||||
|
const elementBox = await element.boundingBox({ timeout: 5_000 })
|
||||||
|
const containerBox = await container.boundingBox({ timeout: 5_000 })
|
||||||
|
|
||||||
|
let isOutOfView = false
|
||||||
|
if (elementBox && containerBox)
|
||||||
|
return (
|
||||||
|
elementBox.y + elementBox.height > containerBox.y + containerBox.height ||
|
||||||
|
elementBox.y < containerBox.y ||
|
||||||
|
elementBox.x + elementBox.width > containerBox.x + containerBox.width ||
|
||||||
|
elementBox.x < containerBox.x
|
||||||
|
)
|
||||||
|
|
||||||
|
return isOutOfView
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createProjectAndRenameIt({
|
||||||
|
name,
|
||||||
|
page,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
page: Page
|
||||||
|
}) {
|
||||||
|
await page.getByRole('button', { name: 'New project' }).click()
|
||||||
|
await expect(page.getByText('Successfully created')).toBeVisible()
|
||||||
|
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.getByText(`project-000`)).toBeVisible()
|
||||||
|
await page.getByText(`project-000`).hover()
|
||||||
|
await page.getByText(`project-000`).focus()
|
||||||
|
|
||||||
|
await page.getByLabel('sketch').first().click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// type the name passed in
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.keyboard.type(name)
|
||||||
|
|
||||||
|
await page.getByLabel('checkmark').last().click()
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ test.describe('Testing constraints', () => {
|
|||||||
page.getByRole('button', { name: 'Exit Sketch' })
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
).not.toBeVisible()
|
).not.toBeVisible()
|
||||||
})
|
})
|
||||||
test(`Test remove constraints`, async ({ page }) => {
|
test(`Remove constraints`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
|
@ -977,10 +977,6 @@ const part001 = startSketchOn('XZ')
|
|||||||
const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
|
const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
|
||||||
await page.mouse.move(0, 0)
|
await page.mouse.move(0, 0)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
let x = 0,
|
|
||||||
y = 0
|
|
||||||
x = hoverPos.x + Math.cos(ang * deg) * 32
|
|
||||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
|
||||||
await page.mouse.move(hoverPos.x, hoverPos.y)
|
await page.mouse.move(hoverPos.x, hoverPos.y)
|
||||||
await wiggleMove(
|
await wiggleMove(
|
||||||
page,
|
page,
|
||||||
|
@ -4,7 +4,6 @@ import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
|||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates'
|
import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
import { APP_NAME } from 'lib/constants'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -73,7 +72,7 @@ test.describe('Testing settings', () => {
|
|||||||
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
|
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
|
||||||
|
|
||||||
// Open the settings modal with the browser keyboard shortcut
|
// Open the settings modal with the browser keyboard shortcut
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||||
|
|
||||||
await expect(headingLocator).toBeVisible()
|
await expect(headingLocator).toBeVisible()
|
||||||
await page.locator('#showDebugPanel').getByText('OffOn').click()
|
await page.locator('#showDebugPanel').getByText('OffOn').click()
|
||||||
@ -83,7 +82,7 @@ test.describe('Testing settings', () => {
|
|||||||
await test.step('Open settings with keyboard shortcut', async () => {
|
await test.step('Open settings with keyboard shortcut', async () => {
|
||||||
await page.getByTestId('settings-close-button').click()
|
await page.getByTestId('settings-close-button').click()
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||||
await expect(headingLocator).toBeVisible()
|
await expect(headingLocator).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -116,8 +115,7 @@ test.describe('Testing settings', () => {
|
|||||||
).not.toBeChecked()
|
).not.toBeChecked()
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO fixme reset doesn't seem to work for color setting
|
test('Project and user settings can be reset', async ({ page }) => {
|
||||||
test.fixme('Project and user settings can be reset', async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
@ -162,6 +160,11 @@ test.describe('Testing settings', () => {
|
|||||||
// Click the reset settings button.
|
// Click the reset settings button.
|
||||||
await resetButton.click()
|
await resetButton.click()
|
||||||
|
|
||||||
|
await expect(page.getByText('Settings restored to default')).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText('Settings restored to default')
|
||||||
|
).not.toBeVisible()
|
||||||
|
|
||||||
// Verify it is now set to the inherited user value
|
// Verify it is now set to the inherited user value
|
||||||
await expect(themeColorSetting).toHaveValue(settingValues.default)
|
await expect(themeColorSetting).toHaveValue(settingValues.default)
|
||||||
|
|
||||||
@ -193,6 +196,10 @@ test.describe('Testing settings', () => {
|
|||||||
`Project settings override user settings on desktop`,
|
`Project settings override user settings on desktop`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
@ -205,7 +212,6 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
@ -258,4 +264,69 @@ test.describe('Testing settings', () => {
|
|||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
`Closing settings modal should go back to the original file being viewed`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browser: _ }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
panesOpen,
|
||||||
|
createAndSelectProject,
|
||||||
|
pasteCodeInEditor,
|
||||||
|
clickPane,
|
||||||
|
createNewFileAndSelect,
|
||||||
|
editorTextMatches,
|
||||||
|
} = await getUtils(page, test)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
await panesOpen([])
|
||||||
|
|
||||||
|
await test.step('Precondition: No projects exist', async () => {
|
||||||
|
await expect(page.getByTestId('home-section')).toBeVisible()
|
||||||
|
const projectLinksPre = page.getByTestId('project-link')
|
||||||
|
await expect(projectLinksPre).toHaveCount(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
await createAndSelectProject('project-000')
|
||||||
|
|
||||||
|
await clickPane('code')
|
||||||
|
const kclCube = await fsp.readFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
await pasteCodeInEditor(kclCube)
|
||||||
|
|
||||||
|
await clickPane('files')
|
||||||
|
await createNewFileAndSelect('2.kcl')
|
||||||
|
|
||||||
|
const kclCylinder = await fsp.readFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
await pasteCodeInEditor(kclCylinder)
|
||||||
|
|
||||||
|
const settingsOpenButton = page.getByRole('link', {
|
||||||
|
name: 'settings Settings',
|
||||||
|
})
|
||||||
|
const settingsCloseButton = page.getByTestId('settings-close-button')
|
||||||
|
|
||||||
|
await test.step('Open and close settings', async () => {
|
||||||
|
await settingsOpenButton.click()
|
||||||
|
await settingsCloseButton.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Postcondition: Same file content is in editor as before settings opened', async () => {
|
||||||
|
await editorTextMatches(kclCylinder)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { test, expect, Page } from '@playwright/test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import * as fsp from 'fs/promises'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -11,8 +9,6 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
test.describe('Text-to-CAD tests', () => {
|
test.describe('Text-to-CAD tests', () => {
|
||||||
test('basic lego happy case', async ({ page }) => {
|
test('basic lego happy case', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -192,7 +188,8 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
await page.keyboard.type('akjsndladf ghgsssswefiuwq22262664')
|
const randomPrompt = `aslkdfja;` + Date.now() + `FFFFEIWJF`
|
||||||
|
await page.keyboard.type(randomPrompt)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
@ -299,9 +296,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await expect(page.locator('textarea')).toContainText(badPrompt)
|
await expect(page.locator('textarea')).toContainText(badPrompt)
|
||||||
|
|
||||||
// Select all and start a new prompt.
|
// Select all and start a new prompt.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.type('a 2x4 lego')
|
await page.keyboard.type('a 2x4 lego')
|
||||||
|
|
||||||
// Submit the new prompt.
|
// Submit the new prompt.
|
||||||
@ -521,9 +518,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
||||||
@ -550,13 +547,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||||
@ -637,9 +634,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||||
|
34
e2e/playwright/user-sidebar-menu-tests.spec.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { setupElectron, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Electron user sidebar menu tests', () => {
|
||||||
|
test(
|
||||||
|
'User settings has correct shortcut',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
// Open the user sidebar menu.
|
||||||
|
await page.getByTestId('user-sidebar-toggle').click()
|
||||||
|
|
||||||
|
// No space after "User settings" since it's textContent.
|
||||||
|
const text =
|
||||||
|
process.platform === 'darwin' ? 'User settings⌘,' : 'User settingsCtrl,'
|
||||||
|
const userSettingsButton = page.getByTestId('user-settings')
|
||||||
|
await expect(userSettingsButton).toBeVisible()
|
||||||
|
await expect(userSettingsButton).toHaveText(text)
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
@ -1,13 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import { doExport, getUtils, makeTemplate, setup, tearDown } from './test-utils'
|
||||||
doExport,
|
|
||||||
getUtils,
|
|
||||||
makeTemplate,
|
|
||||||
metaModifier,
|
|
||||||
setup,
|
|
||||||
tearDown,
|
|
||||||
} from './test-utils'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -17,8 +10,6 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
test('Units menu', async ({ page }) => {
|
test('Units menu', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
@ -157,7 +148,7 @@ test('Paste should not work unless an input is focused', async ({
|
|||||||
|
|
||||||
// Paste without the code pane focused
|
// Paste without the code pane focused
|
||||||
await codeEditorText.blur()
|
await codeEditorText.blur()
|
||||||
await page.keyboard.press(`${metaModifier}+KeyV`)
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
|
|
||||||
// Show that the paste didn't work but typing did
|
// Show that the paste didn't work but typing did
|
||||||
await expect(codeEditorText).not.toContainText(pasteContent)
|
await expect(codeEditorText).not.toContainText(pasteContent)
|
||||||
@ -166,7 +157,7 @@ test('Paste should not work unless an input is focused', async ({
|
|||||||
// Paste with the code editor focused
|
// Paste with the code editor focused
|
||||||
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
||||||
await codeEditorText.focus()
|
await codeEditorText.focus()
|
||||||
await page.keyboard.press(`${metaModifier}+KeyV`)
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
await expect(
|
await expect(
|
||||||
await page.evaluate(
|
await page.evaluate(
|
||||||
() => document.querySelector('.cm-content')?.textContent
|
() => document.querySelector('.cm-content')?.textContent
|
||||||
@ -380,9 +371,9 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
|
|||||||
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
||||||
// Since there's code now, we have to get to the end of the line
|
// Since there's code now, we have to get to the end of the line
|
||||||
await page.locator('.cm-line').last().click()
|
await page.locator('.cm-line').last().click()
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('ArrowRight')
|
await page.keyboard.press('ArrowRight')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type('//')
|
await page.keyboard.type('//')
|
||||||
|
@ -6,6 +6,9 @@ import { MakerRpm } from '@electron-forge/maker-rpm'
|
|||||||
import { VitePlugin } from '@electron-forge/plugin-vite'
|
import { VitePlugin } from '@electron-forge/plugin-vite'
|
||||||
import { FusesPlugin } from '@electron-forge/plugin-fuses'
|
import { FusesPlugin } from '@electron-forge/plugin-fuses'
|
||||||
import { FuseV1Options, FuseVersion } from '@electron/fuses'
|
import { FuseV1Options, FuseVersion } from '@electron/fuses'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
const rootDir = process.cwd()
|
||||||
|
|
||||||
const config: ForgeConfig = {
|
const config: ForgeConfig = {
|
||||||
packagerConfig: {
|
packagerConfig: {
|
||||||
@ -19,13 +22,24 @@ const config: ForgeConfig = {
|
|||||||
}) ||
|
}) ||
|
||||||
undefined,
|
undefined,
|
||||||
executableName: 'zoo-modeling-app',
|
executableName: 'zoo-modeling-app',
|
||||||
|
icon: path.resolve(rootDir, 'assets', 'icon'),
|
||||||
},
|
},
|
||||||
rebuildConfig: {},
|
rebuildConfig: {},
|
||||||
makers: [
|
makers: [
|
||||||
new MakerSquirrel({}),
|
new MakerSquirrel({
|
||||||
|
setupIcon: path.resolve(rootDir, 'assets', 'icon.ico'),
|
||||||
|
}),
|
||||||
new MakerZIP({}, ['darwin']),
|
new MakerZIP({}, ['darwin']),
|
||||||
new MakerRpm({}),
|
new MakerRpm({
|
||||||
new MakerDeb({}),
|
options: {
|
||||||
|
icon: path.resolve(rootDir, 'assets', 'icon.png'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new MakerDeb({
|
||||||
|
options: {
|
||||||
|
icon: path.resolve(rootDir, 'assets', 'icon.png'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
new VitePlugin({
|
new VitePlugin({
|
||||||
|
@ -631,6 +631,7 @@
|
|||||||
"errorno": {
|
"errorno": {
|
||||||
"description": "The error number.",
|
"description": "The error number.",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
"nullable": true,
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"reason": {
|
"reason": {
|
||||||
@ -661,6 +662,7 @@
|
|||||||
"tar_temp": {
|
"tar_temp": {
|
||||||
"description": "The target temperature.",
|
"description": "The target temperature.",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
"nullable": true,
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
@ -671,10 +673,8 @@
|
|||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"command",
|
"command",
|
||||||
"errorno",
|
|
||||||
"result",
|
"result",
|
||||||
"sequence_id",
|
"sequence_id",
|
||||||
"tar_temp",
|
|
||||||
"target"
|
"target"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
@ -1155,6 +1155,59 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"additionalProperties": true,
|
||||||
|
"description": "A gcode file.",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"enum": [
|
||||||
|
"gcode_file"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"param": {
|
||||||
|
"description": "The param.",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"print_type": {
|
||||||
|
"description": "The print type.",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"reason": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Reason"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The reason for the message."
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The result of the command."
|
||||||
|
},
|
||||||
|
"sequence_id": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/SequenceId"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The sequence id."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"command",
|
||||||
|
"reason",
|
||||||
|
"result",
|
||||||
|
"sequence_id"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"additionalProperties": true,
|
"additionalProperties": true,
|
||||||
"description": "Project file.",
|
"description": "Project file.",
|
||||||
|
33
package.json
@ -19,7 +19,7 @@
|
|||||||
"@codemirror/search": "^6.5.6",
|
"@codemirror/search": "^6.5.6",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@csstools/postcss-oklab-function": "^3.0.16",
|
"@csstools/postcss-oklab-function": "^4.0.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.26.1",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"three": "^0.166.1",
|
"three": "^0.166.1",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
@ -81,22 +81,23 @@
|
|||||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||||
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
||||||
"build:wasm": "(cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
|
"build:wasm": "cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||||
"lint": "eslint --fix src",
|
"lint": "eslint --fix src e2e",
|
||||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
||||||
"postinstall": "yarn xstate:typegen",
|
"postinstall": "yarn xstate:typegen",
|
||||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
||||||
"make:dev": "make dev",
|
"make:dev": "make dev",
|
||||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||||
"electron:start": "electron-forge start",
|
"tron:start": "electron-forge start",
|
||||||
"electron:package": "electron-forge package",
|
"tron:package": "electron-forge package",
|
||||||
"electron:make": "electron-forge make",
|
"tron:make": "electron-forge make",
|
||||||
"electron:publish": "electron-forge publish",
|
"tron:publish": "electron-forge publish",
|
||||||
"electron:e2e:local": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron"
|
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
@ -130,17 +131,17 @@
|
|||||||
"@electron/fuses": "^1.8.0",
|
"@electron/fuses": "^1.8.0",
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@lezer/generator": "^1.7.1",
|
"@lezer/generator": "^1.7.1",
|
||||||
"@playwright/test": "^1.45.1",
|
"@playwright/test": "^1.46.1",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^15.0.2",
|
"@testing-library/react": "^15.0.2",
|
||||||
"@types/d3-force": "^3.0.10",
|
"@types/d3-force": "^3.0.10",
|
||||||
"@types/electron": "^1.6.10",
|
"@types/electron": "^1.6.10",
|
||||||
"@types/isomorphic-fetch": "^0.0.39",
|
"@types/isomorphic-fetch": "^0.0.39",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^18.19.31",
|
"@types/node": "^22.5.0",
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react": "^18.3.2",
|
"@types/react": "^18.3.4",
|
||||||
"@types/react-dom": "^18.2.25",
|
"@types/react-dom": "^18.2.25",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.163.0",
|
"@types/three": "^0.163.0",
|
||||||
@ -156,7 +157,7 @@
|
|||||||
"@xstate/cli": "^0.5.17",
|
"@xstate/cli": "^0.5.17",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"d3-force": "^3.0.0",
|
"d3-force": "^3.0.0",
|
||||||
"electron": "^31.2.1",
|
"electron": "^32.0.1",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
@ -164,7 +165,7 @@
|
|||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^14.3.10",
|
"happy-dom": "^14.3.10",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.1.5",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"pixelmatch": "^5.3.0",
|
"pixelmatch": "^5.3.0",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
@ -175,7 +176,7 @@
|
|||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.0.12",
|
"vite": "^5.4.2",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
|
@ -17,6 +17,7 @@ export default defineConfig({
|
|||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: [
|
reporter: [
|
||||||
['dot'],
|
['dot'],
|
||||||
|
['list'],
|
||||||
['json', { outputFile: './test-results/report.json' }],
|
['json', { outputFile: './test-results/report.json' }],
|
||||||
['html'],
|
['html'],
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defineConfig } from '@playwright/test'
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
@ -30,4 +30,24 @@ export default defineConfig({
|
|||||||
actionTimeout: 15_000,
|
actionTimeout: 15_000,
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
},
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'Google Chrome',
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
channel: 'chrome',
|
||||||
|
contextOptions: {
|
||||||
|
/* Chromium is the only one with these permission types */
|
||||||
|
permissions: ['clipboard-write', 'clipboard-read'],
|
||||||
|
},
|
||||||
|
launchOptions: {
|
||||||
|
...(process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
|
||||||
|
? {
|
||||||
|
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
}, // or 'chrome-beta'
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|