Compare commits
68 Commits
andrewvarg
...
nadro/adho
Author | SHA1 | Date | |
---|---|---|---|
53a646d12a | |||
85c721fb49 | |||
27af2d08a3 | |||
fb8b975b5e | |||
62d8d45a58 | |||
ae3440df0a | |||
af658c909d | |||
7ec11d23c8 | |||
30000a1eac | |||
cb3b45747c | |||
fe66310f2d | |||
fefb6cfe87 | |||
0f8375cbb4 | |||
107adc77b3 | |||
4356885aa2 | |||
6a2027cd51 | |||
f49cf8281c | |||
7de27c648f | |||
344fb6f84d | |||
df808b3e58 | |||
e1ab6bbc48 | |||
0a1f35b89a | |||
78278d6889 | |||
6f1a539e83 | |||
0ad619e1d2 | |||
8d876a806e | |||
c7f0a6c2a0 | |||
e4941cb524 | |||
1b687a82a6 | |||
1bb882acf8 | |||
478bf34f2b | |||
dbc87292e4 | |||
bb3a74076f | |||
0cd6031aae | |||
1e1bdbd6e7 | |||
631b63b1b6 | |||
eabcf86436 | |||
7ce0ef770a | |||
599ab33e40 | |||
c584d942d4 | |||
35b8872678 | |||
2f245fe445 | |||
4b95980e9e | |||
53d6613d0d | |||
416d0b37a2 | |||
5f2a10ec7e | |||
24edb66b3c | |||
903ba33c46 | |||
c5bf6ad42d | |||
eeaa71142a | |||
0d1fc1b513 | |||
d510e58ebc | |||
23a01e86e6 | |||
9dd6e3e852 | |||
9eaacc2a51 | |||
de6e0f6b18 | |||
d02a9f59ae | |||
92f930dfc0 | |||
e651e0c2cf | |||
6358eed7e4 | |||
e35bcf2f11 | |||
fe581ff1d2 | |||
b301fbba22 | |||
0c702e4bab | |||
939d5ef3f7 | |||
8d19a955af | |||
625394d587 | |||
e6dd628736 |
@ -9,6 +9,7 @@ VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||
#VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port"
|
||||
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
||||
#token="required token for playwright. TODO: clean up env vars in #3973"
|
||||
|
||||
|
11
.github/ISSUE_TEMPLATE/release.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Release
|
||||
about: Create a new release for the Zoo Design Studio
|
||||
title: "Cut release v1.?.?"
|
||||
labels: [release]
|
||||
labels: [meta/release]
|
||||
---
|
||||
|
||||
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
|
||||
@ -19,7 +19,8 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
||||
## macOS via ???
|
||||
|
||||
@ -27,7 +28,8 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
||||
## Linux via ???
|
||||
|
||||
@ -35,4 +37,5 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
4
.github/workflows/build-apps.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -362,7 +362,7 @@ jobs:
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
6
.github/workflows/cargo-fmt.yml
vendored
@ -31,15 +31,15 @@ jobs:
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
cp .github/workflows/nightly-rust-toolchain.toml rust-toolchain.toml
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache-workspaces: rust
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
- name: Run nightly cargo fmt
|
||||
run: |
|
||||
cd rust
|
||||
cargo fmt -- --check
|
||||
cargo +nightly fmt -- --check
|
||||
shell: bash
|
||||
|
12
.github/workflows/cargo-test.yml
vendored
@ -25,8 +25,8 @@ jobs:
|
||||
- runner=8cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -149,8 +149,8 @@ jobs:
|
||||
partitionIndex: [1, 2, 3, 4, 5, 6]
|
||||
partitionTotal: [6]
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -207,8 +207,8 @@ jobs:
|
||||
- runner=32cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
|
6
.github/workflows/e2e-tests.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
- name: Download Wasm cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -230,7 +230,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
|
2
.github/workflows/generate-website-docs.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
# required
|
||||
|
2
.github/workflows/kcl-language-server.yml
vendored
@ -328,7 +328,7 @@ jobs:
|
||||
mkdir -p releases/language-server/${{ env.TAG }}
|
||||
cp -r build/* releases/language-server/${{ env.TAG }}
|
||||
- name: "Authenticate to Google Cloud"
|
||||
uses: "google-github-actions/auth@v2.1.8"
|
||||
uses: "google-github-actions/auth@v2.1.10"
|
||||
with:
|
||||
credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}"
|
||||
- name: Set up Cloud SDK
|
||||
|
6
.github/workflows/kcl-python-bindings.yml
vendored
@ -113,7 +113,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
- uses: taiki-e/install-action@just
|
||||
- name: Run tests
|
||||
@ -130,7 +130,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: Install codespell
|
||||
run: |
|
||||
uv venv .venv
|
||||
@ -161,7 +161,7 @@ jobs:
|
||||
with:
|
||||
path: rust/kcl-python-bindings
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: do uv things
|
||||
run: |
|
||||
cd rust/kcl-python-bindings
|
||||
|
3
.github/workflows/nightly-rust-toolchain.toml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
components = ["rustfmt"]
|
2
.github/workflows/publish-apps-release.yml
vendored
@ -108,7 +108,7 @@ jobs:
|
||||
run: npm run files:set-notes
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
@ -280,6 +280,9 @@ Assign someone to each section of the manual checklist generated by the issue te
|
||||
Follow the instructions [here](./rust/README.md) to publish new crates.
|
||||
This ensures that the KCL accepted by the app is also accepted by the CLI.
|
||||
|
||||
If there are documentation changes, merge the corresponding Dependabot PRs [here](https://github.com/KittyCAD/website/pulls/app%2Fdependabot) for the website.
|
||||
You can trigger Dependabot to check for updates [here](https://github.com/KittyCAD/website/network/updates/17261214/jobs).
|
||||
|
||||
#### 5. Publish the release
|
||||
|
||||
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well.
|
||||
|
3
Makefile
@ -62,7 +62,10 @@ else
|
||||
endif
|
||||
|
||||
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
||||
ifndef WINDOWS
|
||||
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||
@ touch $@
|
||||
endif
|
||||
|
||||
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
npm run tronb:vite:dev
|
||||
|
@ -83,6 +83,13 @@ Allow orbiting in sketch mode.
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### fixed_size_grid
|
||||
|
||||
If true, the grid cells will be fixed-size, where the width is your default length unit. If false, the grid will get larger as you zoom out, and smaller as you zoom in.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
|
@ -10,9 +10,11 @@ Extend the current sketch with a new involute circular curve.
|
||||
```kcl
|
||||
involuteCircular(
|
||||
@sketch: Sketch,
|
||||
startRadius: number(Length),
|
||||
endRadius: number(Length),
|
||||
angle: number(Angle),
|
||||
startRadius?: number(Length),
|
||||
endRadius?: number(Length),
|
||||
startDiameter?: number(Length),
|
||||
endDiameter?: number(Length),
|
||||
reverse?: bool,
|
||||
tag?: TagDecl,
|
||||
): Sketch
|
||||
@ -25,9 +27,11 @@ involuteCircular(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
|
||||
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
|
||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
|
||||
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
|
||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |
|
||||
|
||||
|
@ -38,7 +38,7 @@ revolved around the same axis.
|
||||
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
|
||||
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
|
||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the revolve operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
|
||||
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
|
||||
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |
|
||||
|
@ -28,7 +28,7 @@ will smoothly blend the transition.
|
||||
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
|
||||
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
|
||||
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance for this fillet | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
|
||||
|
||||
### Returns
|
||||
|
@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the intersection operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -27,7 +27,7 @@ and complex multi-body part modeling.
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
|
||||
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the subtraction operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -21,7 +21,7 @@ union(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | `[Solid; 2+]` | The solids to union. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the union operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
code: 'subtract(extrude001, tools = extrude006)',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
@ -81,6 +81,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
} else {
|
||||
await cmdBar.progressCmdBar()
|
||||
}
|
||||
|
||||
// Select second object
|
||||
@ -103,8 +105,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
Solids: '1 path',
|
||||
Tools: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
|
@ -5,6 +5,7 @@ import { uuidv4 } from '@src/lib/utils'
|
||||
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
@ -14,13 +15,18 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
homePage: HomePageFixture,
|
||||
scene: SceneFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
cmdBar: CmdBarFixture,
|
||||
plane: string,
|
||||
clickCoords: { x: number; y: number }
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
// await page.addInitScript(() => {
|
||||
// localStorage.setItem('persistCode', '@settings(defaultLengthUnit = in)')
|
||||
// })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
// await scene.settled(cmdBar)
|
||||
const XYPlanRed: [number, number, number] = [98, 50, 51]
|
||||
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
|
||||
|
||||
@ -119,12 +125,166 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
]
|
||||
|
||||
for (const config of planeConfigs) {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar }) => {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
config.plane,
|
||||
config.coords
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
test.describe('Can create sketches on offset planes and their back sides', () => {
|
||||
const sketchOnPlaneAndBackSideTest = async (
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
scene: SceneFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
cmdbar: CmdBarFixture,
|
||||
plane: string,
|
||||
clickCoords: { x: number; y: number }
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
xyPlane = offsetPlane(XY, offset = 0.05)
|
||||
xzPlane = offsetPlane(XZ, offset = 0.05)
|
||||
yzPlane = offsetPlane(YZ, offset = 0.05)
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
// await scene.settled(cmdbar)
|
||||
const XYPlanRed: [number, number, number] = [74, 74, 74]
|
||||
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
const coord =
|
||||
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
|
||||
const camCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
vantage: { x: coord, y: coord, z: coord },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
}
|
||||
const updateCamCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
}
|
||||
|
||||
const prefix = plane.length === 3 ? '-' : ''
|
||||
const planeName = plane
|
||||
.slice(plane.length === 3 ? 1 : 0)
|
||||
.toLocaleLowerCase()
|
||||
|
||||
const codeLine1 = `sketch001 = startSketchOn(${prefix}${planeName}Plane)`
|
||||
const codeLine2 = `profile001 = startProfile(sketch001, at = [${0.91 + (plane[0] === '-' ? 0.01 : 0)}, -${1.21 + (plane[0] === '-' ? 0.01 : 0)}])`
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
|
||||
await u.sendCustomCmd(camCommand)
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd(updateCamCommand)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await toolbar.openFeatureTreePane()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('XY').click()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('XZ').click()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('YZ').click()
|
||||
await expect(
|
||||
toolbar
|
||||
.getDefaultPlaneVisibilityButton('YZ')
|
||||
.locator('[aria-label="eye crossed out"]')
|
||||
).toBeVisible()
|
||||
|
||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||
await page.waitForTimeout(600) // wait for animation
|
||||
|
||||
await toolbar.waitUntilSketchingReady()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'line Line', exact: true })
|
||||
).toBeVisible()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(707, 393)
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(codeLine1)
|
||||
await expect(page.locator('.cm-content')).toContainText(codeLine2)
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'line Line', exact: true })
|
||||
.first()
|
||||
.click()
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await u.removeCurrentCode()
|
||||
}
|
||||
|
||||
const planeConfigs = [
|
||||
{
|
||||
plane: 'XY',
|
||||
coords: { x: 600, y: 388 },
|
||||
description: 'red plane',
|
||||
},
|
||||
{
|
||||
plane: 'YZ',
|
||||
coords: { x: 700, y: 250 },
|
||||
description: 'green plane',
|
||||
},
|
||||
{
|
||||
plane: 'XZ',
|
||||
coords: { x: 684, y: 427 },
|
||||
description: 'blue plane',
|
||||
},
|
||||
{
|
||||
plane: '-XY',
|
||||
coords: { x: 600, y: 118 },
|
||||
description: 'back of red plane',
|
||||
},
|
||||
{
|
||||
plane: '-YZ',
|
||||
coords: { x: 700, y: 219 },
|
||||
description: 'back of green plane',
|
||||
},
|
||||
{
|
||||
plane: '-XZ',
|
||||
coords: { x: 700, y: 80 },
|
||||
description: 'back of blue plane',
|
||||
},
|
||||
]
|
||||
|
||||
for (const config of planeConfigs) {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
config.plane,
|
||||
config.coords
|
||||
)
|
||||
|
@ -265,6 +265,8 @@ middle(0)
|
||||
})
|
||||
await expect(
|
||||
page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't
|
||||
|
||||
Backtrace:
|
||||
assert()
|
||||
check()
|
||||
middle()`)
|
||||
|
@ -307,7 +307,7 @@ test.describe('Command bar tests', () => {
|
||||
)
|
||||
|
||||
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const submitButton = page.getByTestId('command-bar-submit')
|
||||
await continueButton.click()
|
||||
|
||||
// Review step and argument hotkeys
|
||||
@ -525,7 +525,9 @@ test.describe('Command bar tests', () => {
|
||||
const projectName = 'test'
|
||||
const beforeKclCode = `a = 5
|
||||
b = a * a
|
||||
c = 3 + a`
|
||||
c = 3 + a
|
||||
theta = 45deg
|
||||
`
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testProject = join(dir, projectName)
|
||||
await fsp.mkdir(testProject, { recursive: true })
|
||||
@ -615,9 +617,45 @@ c = 3 + a`
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
await test.step(`Edit a parameter with explicit units via command bar`, async () => {
|
||||
await cmdBar.cmdBarOpenBtn.click()
|
||||
await cmdBar.chooseCommand('edit parameter')
|
||||
await cmdBar
|
||||
.selectOption({
|
||||
name: 'theta',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
commandName: 'Edit parameter',
|
||||
currentArgKey: 'value',
|
||||
currentArgValue: '45deg',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '',
|
||||
},
|
||||
highlightedHeaderArg: 'value',
|
||||
})
|
||||
await cmdBar.argumentInput
|
||||
.locator('[contenteditable]')
|
||||
.fill('45deg + 1deg')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
commandName: 'Edit parameter',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '46deg',
|
||||
},
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + a`
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + atheta = 45deg + 1deg`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -54,9 +54,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Expect it to succeed
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
@ -119,9 +117,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Look out for the toast message
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
|
@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// focus the editor
|
||||
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// select the line that's causing the error and delete it
|
||||
@ -1617,4 +1617,33 @@ sketch001 = startSketchOn(XZ)
|
||||
// Verify error is still visible
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('Core dump hotkey', async ({ page, scene, cmdBar, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
const viewportSize = { width: 1200, height: 800 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||
|
||||
await page.keyboard.press(`${modifier}+Shift+.`)
|
||||
|
||||
const toast1 = page.getByText('Starting core dump...')
|
||||
await expect(toast1).toBeVisible()
|
||||
|
||||
const toast2 = page.getByText('Core dump completed')
|
||||
await expect(toast2).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
@ -229,11 +229,12 @@ test.describe('Feature Tree pane', () => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 5)
|
||||
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||
const newConstantName = 'length001'
|
||||
const expectedCode = `${newConstantName} = 23
|
||||
const newParameterName = 'length001'
|
||||
const expectedCode = `${newParameterName} = 23
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 5)
|
||||
renamedExtrude = extrude(sketch001, length = ${newConstantName})`
|
||||
renamedExtrude = extrude(sketch001, length = ${newParameterName})`
|
||||
const editedParameterValue = '23 * 2'
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testDir = join(dir, 'test-sample')
|
||||
@ -279,7 +280,7 @@ test.describe('Feature Tree pane', () => {
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Add a named constant for distance argument and submit', async () => {
|
||||
await test.step('Add a parameter for distance argument and submit', async () => {
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.variableCheckbox.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -296,13 +297,43 @@ test.describe('Feature Tree pane', () => {
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
activeLines: [
|
||||
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
|
||||
`renamedExtrude = extrude(sketch001, length = ${newParameterName})`,
|
||||
],
|
||||
})
|
||||
await editor.expectEditor.toContain(expectedCode, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Edit the parameter via the feature tree', async () => {
|
||||
const parameter = await toolbar.getFeatureTreeOperation('Parameter', 0)
|
||||
await parameter.dblclick()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Edit parameter',
|
||||
currentArgKey: 'value',
|
||||
currentArgValue: '23',
|
||||
headerArguments: {
|
||||
Name: newParameterName,
|
||||
Value: '23',
|
||||
},
|
||||
stage: 'arguments',
|
||||
highlightedHeaderArg: 'value',
|
||||
})
|
||||
await cmdBar.argumentInput
|
||||
.locator('[contenteditable]')
|
||||
.fill(editedParameterValue)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
commandName: 'Edit parameter',
|
||||
headerArguments: {
|
||||
Name: newParameterName,
|
||||
Value: '46', // Shows calculated result
|
||||
},
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await editor.expectEditor.toContain(editedParameterValue)
|
||||
})
|
||||
})
|
||||
test(`User can edit an offset plane operation from the feature tree`, async ({
|
||||
context,
|
||||
|
@ -118,15 +118,11 @@ export class CmdBarFixture {
|
||||
return
|
||||
}
|
||||
|
||||
const arrowButton = this.page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
const arrowButton = this.page.getByTestId('command-bar-continue')
|
||||
if (await arrowButton.isVisible()) {
|
||||
await arrowButton.click()
|
||||
await this.continue()
|
||||
} else {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'checkmark Submit command' })
|
||||
.click()
|
||||
await this.submit()
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +187,13 @@ export class CmdBarFixture {
|
||||
return this.page.getByRole('option', options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an optional argument from the command bar during review
|
||||
*/
|
||||
clickOptionalArgument = async (argName: string) => {
|
||||
await this.page.getByTestId(`cmd-bar-add-optional-arg-${argName}`).click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the Create new variable button for kcl input
|
||||
*/
|
||||
|
@ -183,14 +183,15 @@ export class EditorFixture {
|
||||
scrollToText(text: string, placeCursor?: boolean) {
|
||||
return this.page.evaluate(
|
||||
(args: { text: string; placeCursor?: boolean }) => {
|
||||
const editorView = window.editorManager.getEditorView()
|
||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||
// Except it does so :shrug:
|
||||
// @ts-ignore
|
||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||
const index = editorView?.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(args.text)
|
||||
window.editorManager._editorView?.focus()
|
||||
window.editorManager._editorView?.dispatch({
|
||||
editorView?.focus()
|
||||
editorView?.dispatch({
|
||||
selection: window.EditorSelection.create([
|
||||
window.EditorSelection.cursor(index),
|
||||
]),
|
||||
|
@ -274,6 +274,13 @@ export class ToolbarFixture {
|
||||
.nth(operationIndex)
|
||||
}
|
||||
|
||||
getDefaultPlaneVisibilityButton(plane: 'XY' | 'XZ' | 'YZ' = 'XY') {
|
||||
const index = plane === 'XZ' ? 0 : plane === 'XY' ? 1 : 2
|
||||
return this.featureTreePane
|
||||
.getByTestId('feature-tree-visibility-toggle')
|
||||
.nth(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* View source on a specific operation in the Feature Tree pane.
|
||||
* @param operationName The name of the operation type
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
FullResult,
|
||||
} from '@playwright/test/reporter'
|
||||
|
||||
class MyAPIReporter implements Reporter {
|
||||
class APIReporter implements Reporter {
|
||||
private pendingRequests: Promise<void>[] = []
|
||||
private allResults: Record<string, any>[] = []
|
||||
private blockingResults: Record<string, any>[] = []
|
||||
@ -32,7 +32,7 @@ class MyAPIReporter implements Reporter {
|
||||
'X-API-Key': process.env.TAB_API_KEY || '',
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
project: 'https://github.com/KittyCAD/modeling-app',
|
||||
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||
branch:
|
||||
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||
@ -60,7 +60,7 @@ class MyAPIReporter implements Reporter {
|
||||
|
||||
const payload = {
|
||||
// Required information
|
||||
project: 'https://github.com/KittyCAD/modeling-app',
|
||||
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||
suite: process.env.CI_SUITE || 'e2e',
|
||||
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||
@ -124,4 +124,4 @@ class MyAPIReporter implements Reporter {
|
||||
}
|
||||
}
|
||||
|
||||
export default MyAPIReporter
|
||||
export default APIReporter
|
||||
|
@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
|
||||
},
|
||||
tronApp.projectDirName,
|
||||
page,
|
||||
cmdBar,
|
||||
method
|
||||
)
|
||||
)
|
||||
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully deleted`)
|
||||
const noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully deleted`)
|
||||
const noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import * as fsp from 'fs/promises'
|
||||
|
||||
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -482,6 +479,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
test('ensure you CAN export while an export is already going', async ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step('Set up the code and durations', async () => {
|
||||
@ -516,11 +514,11 @@ extrude002 = extrude(profile002, length = 150)
|
||||
const successToastMessage = page.getByText(`Exported successfully`)
|
||||
|
||||
await test.step('second export', async () => {
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await test.step('The first export still succeeds', async () => {
|
||||
await Promise.all([
|
||||
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await test.step('Successful, unblocked export', async () => {
|
||||
// Try exporting again.
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -575,7 +573,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
name: 'Projects',
|
||||
})
|
||||
const projectLink = page.getByRole('link', { name: 'bracket' })
|
||||
const networkHealthIndicator = page.getByTestId('network-toggle')
|
||||
const networkHealthIndicator = page.getByTestId(/network-toggle/)
|
||||
|
||||
await test.step('Check the home page', async () => {
|
||||
await expect(projectsHeading).toBeVisible()
|
||||
@ -880,7 +878,7 @@ s2 = startSketchOn(XY)
|
||||
})
|
||||
})
|
||||
|
||||
async function clickExportButton(page: Page) {
|
||||
async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
|
||||
await test.step('Running export flow', async () => {
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
})
|
||||
}
|
||||
|
@ -1478,6 +1478,7 @@ sketch001 = startSketchOn(XZ)
|
||||
await page.mouse.move(1200, 139)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(870, 250)
|
||||
await page.mouse.up()
|
||||
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
@ -1487,6 +1488,60 @@ sketch001 = startSketchOn(XZ)
|
||||
)
|
||||
})
|
||||
|
||||
test('Can undo with closed code pane', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1500, height: 750 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfile(at = [-10, -10])
|
||||
|> line(end = [20.0, 10.0])
|
||||
|> tangentialArc(end = [5.49, 8.37])`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await toolbar.waitForFeatureTreeToBeBuilt()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await page.mouse.move(1200, 139)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(870, 250)
|
||||
await page.mouse.up()
|
||||
|
||||
await editor.expectEditor.toContain(`tangentialArc(end=[-5.85,4.32])`, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
// Undo the last change
|
||||
await page.keyboard.down('Control')
|
||||
await page.keyboard.press('KeyZ')
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await u.openKclCodePanel()
|
||||
await editor.expectEditor.toContain(`tangentialArc(end = [5.49, 8.37])`, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('Can delete a single segment line with keyboard', async ({
|
||||
page,
|
||||
scene,
|
||||
|
@ -798,7 +798,7 @@ test('theme persists', async ({ page, context, homePage }) => {
|
||||
|
||||
await page.getByTestId('settings-close-button').click()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
|
||||
// simulate network down
|
||||
await u.emulateNetworkConditions({
|
||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
@ -12,6 +12,7 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
|
||||
},
|
||||
onboarding_status: 'dismissed',
|
||||
show_debug_panel: true,
|
||||
fixed_size_grid: false,
|
||||
},
|
||||
modeling: {
|
||||
enable_ssao: false,
|
||||
|
@ -24,16 +24,15 @@ test.describe('Test network related behaviors', () => {
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
|
||||
// This is how we wait until the stream is online
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
const networkWidget = page.locator('[data-testid="network-toggle"]')
|
||||
await expect(networkWidget).toBeVisible()
|
||||
await networkWidget.hover()
|
||||
await expect(networkToggle).toBeVisible()
|
||||
await networkToggle.hover()
|
||||
|
||||
const networkPopover = page.locator('[data-testid="network-popover"]')
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
@ -44,7 +43,7 @@ test.describe('Test network related behaviors', () => {
|
||||
).toBeVisible()
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
await networkToggle.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
@ -65,8 +64,8 @@ test.describe('Test network related behaviors', () => {
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Network health (Offline)')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
// Click the network toggle
|
||||
await networkToggle.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
@ -99,7 +98,7 @@ test.describe('Test network related behaviors', () => {
|
||||
'Engine disconnect & reconnect in sketch mode',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, toolbar, scene, cmdBar }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
@ -286,7 +285,7 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34])
|
||||
'Paused stream freezes view frame, unpause reconnect is seamless to user',
|
||||
{ tag: ['@desktop', '@skipLocalEngine'] },
|
||||
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
|
@ -22,6 +22,7 @@ export const token = process.env.token || ''
|
||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||
import { test } from '@e2e/playwright/zoo-test'
|
||||
@ -37,7 +38,7 @@ export const headerMasks = (page: Page) => [
|
||||
]
|
||||
|
||||
export const lowerRightMasks = (page: Page) => [
|
||||
page.getByTestId('network-toggle'),
|
||||
page.getByTestId(/network-toggle/),
|
||||
page.getByTestId('billing-remaining-bar'),
|
||||
]
|
||||
|
||||
@ -158,10 +159,10 @@ async function openKclCodePanel(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
editorManager.getEditorView().dispatch({
|
||||
selection: {
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
anchor: editorManager._editorView.docView.length,
|
||||
anchor: editorManager.getEditorView().docView.length,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
@ -737,6 +738,7 @@ export const doExport = async (
|
||||
output: Models['OutputFormat3d_type'],
|
||||
rootDir: string,
|
||||
page: Page,
|
||||
cmdBar: CmdBarFixture,
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
): Promise<Paths> => {
|
||||
if (exportFrom === 'dropdown') {
|
||||
@ -780,9 +782,7 @@ export const doExport = async (
|
||||
.click()
|
||||
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
||||
}
|
||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
|
||||
@ -880,6 +880,10 @@ export async function setup(
|
||||
},
|
||||
...TEST_SETTINGS.project,
|
||||
onboarding_status: 'dismissed',
|
||||
// Tests were written before this setting existed.
|
||||
// It's true by default because it's a good user experience, but
|
||||
// these tests require it to be false.
|
||||
fixed_size_grid: false,
|
||||
},
|
||||
project: {
|
||||
...TEST_SETTINGS.project,
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Testing constraints', () => {
|
||||
test('Can constrain line length', async ({ page, homePage }) => {
|
||||
test('Can constrain line length', async ({ page, homePage, cmdBar }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await page.getByTestId('constraint-length').click()
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
||||
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
|
||||
.getByRole('textbox')
|
||||
const cmdBarKclVariableNameInput =
|
||||
page.getByPlaceholder('Variable name')
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
const [ang, len] = value.split(', ')
|
||||
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
||||
await cmdBarSubmitButton.click()
|
||||
await cmdBar.continue()
|
||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||
|
||||
// checking active assures the cursor is where it should be
|
||||
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
|
@ -8,37 +8,37 @@ test.describe('Testing Gizmo', () => {
|
||||
const cases = [
|
||||
{
|
||||
testDescription: 'top view',
|
||||
clickPosition: { x: 951, y: 385 },
|
||||
clickPosition: { x: 951, y: 402 },
|
||||
expectedCameraPosition: { x: 800, y: -152, z: 4886.02 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'bottom view',
|
||||
clickPosition: { x: 951, y: 429 },
|
||||
clickPosition: { x: 951, y: 449 },
|
||||
expectedCameraPosition: { x: 800, y: -152, z: -4834.02 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'right view',
|
||||
clickPosition: { x: 929, y: 417 },
|
||||
clickPosition: { x: 929, y: 435 },
|
||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'left view',
|
||||
clickPosition: { x: 974, y: 397 },
|
||||
clickPosition: { x: 974, y: 417 },
|
||||
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'back view',
|
||||
clickPosition: { x: 967, y: 421 },
|
||||
clickPosition: { x: 967, y: 441 },
|
||||
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'front view',
|
||||
clickPosition: { x: 935, y: 393 },
|
||||
clickPosition: { x: 935, y: 413 },
|
||||
expectedCameraPosition: { x: 800, y: -5012.02, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
|
@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
|
||||
// We have no more web tests
|
||||
test.fail(
|
||||
'Web: should overwrite current code, cannot create new file',
|
||||
async ({ editor, context, page, homePage }) => {
|
||||
async ({ editor, context, page, homePage, cmdBar }) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Test setup`, async () => {
|
||||
await context.addInitScript((code) => {
|
||||
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
|
||||
name,
|
||||
})
|
||||
const warningText = page.getByText('Overwrite current file with sample?')
|
||||
const confirmButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
|
||||
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
||||
await commandMethodOption('Overwrite').click()
|
||||
await expect(warningText).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||
})
|
||||
|
@ -3,6 +3,7 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
@ -18,7 +19,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickConstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectFinal, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickUnconstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
|
||||
const clickConstrained = _clickConstrained(page, editor, cmdBar)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the X constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
||||
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the Y constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
||||
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify both constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
||||
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
|
@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
|
||||
test(
|
||||
'Successful export shows a success toast',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, tronApp }) => {
|
||||
async ({ page, homePage, cmdBar, tronApp }) => {
|
||||
// FYI this test doesn't work with only engine running locally
|
||||
// And you will need to have the KittyCAD CLI installed
|
||||
const u = await getUtils(page)
|
||||
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
|
||||
presentation: 'pretty',
|
||||
},
|
||||
tronApp?.projectDirName,
|
||||
page
|
||||
page,
|
||||
cmdBar
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -254,6 +255,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
||||
test('Basic default modeling and sketch hotkeys work', async ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Set up test`, async () => {
|
||||
@ -397,11 +399,8 @@ test('Basic default modeling and sketch hotkeys work', async ({
|
||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||
timeout: 20_000,
|
||||
})
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Submit command' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.continue()
|
||||
await cmdBar.submit()
|
||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||
})
|
||||
|
||||
@ -575,8 +574,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
|
||||
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
|
||||
const result2 = result.genNext`
|
||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||
|
18
flake.lock
generated
@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1745998881,
|
||||
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||
"lastModified": 1750865895,
|
||||
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -36,11 +36,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1745998881,
|
||||
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||
"lastModified": 1750865895,
|
||||
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -78,11 +78,11 @@
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745980514,
|
||||
"narHash": "sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4=",
|
||||
"lastModified": 1750964660,
|
||||
"narHash": "sha256-YQ6EyFetjH1uy5JhdhRdPe6cuNXlYpMAQePFfZj4W7M=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6",
|
||||
"rev": "04f0fcfb1a50c63529805a798b4b5c21610ff390",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -125,18 +125,57 @@ test('Shows a loading spinner when uninitialized credit count', async () => {
|
||||
await expect(queryByTestId('spinner')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
const unKnownTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
const freeTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const proTierData = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const enterpriseTierData = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = unKnownTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -166,17 +205,7 @@ test('Shows the total credits for Unknown subscription', async () => {
|
||||
})
|
||||
|
||||
test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -212,19 +241,7 @@ test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
})
|
||||
})
|
||||
test('Shows infinite credits for Pro subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const data = proTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -255,19 +272,7 @@ test('Shows infinite credits for Pro subscription', async () => {
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
const data = enterpriseTierData
|
||||
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
@ -297,3 +302,58 @@ test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
await expect(queryByTestId('infinity')).toBeVisible()
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
|
||||
test('Show upgrade button if credits are not infinite', async () => {
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return new HttpResponse(403)
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Hide upgrade button if credits are infinite', async () => {
|
||||
const data = enterpriseTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
// Ok finally the first use of an org lol
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return HttpResponse.json(createOrgResponse())
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBe(null)
|
||||
})
|
||||
|
1118
package-lock.json
generated
@ -111,7 +111,8 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
||||
|
||||
PipeSubstitution { "%" }
|
||||
|
||||
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
|
||||
// Includes non-whitespace unicode characters.
|
||||
identifier { $[a-zA-Z_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}] $[a-zA-Z0-9_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}]* }
|
||||
AnnotationName { "@" identifier? }
|
||||
PropertyName { identifier }
|
||||
TagDeclarator { "$" identifier }
|
||||
|
@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
|
||||
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|
||||
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = helixCalc,
|
||||
tag = $seg01,
|
||||
)
|
||||
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
|
||||
reverse = true,
|
||||
)
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 216 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |