Compare commits

..

11 Commits

Author SHA1 Message Date
c43bf6bb99 add headless option 2024-08-19 20:40:43 +10:00
1d570b9449 try legit google chrome with --enable-gpu 2024-08-19 20:30:31 +10:00
24f2351412 add --enable-gpu launch option 2024-08-19 19:58:52 +10:00
bbc13469c2 Desktop "Chrome" 2024-08-19 18:37:42 +10:00
5c291d2c21 try chromium instead of branded browser 2024-08-19 18:12:54 +10:00
041a83e7c7 disable vector install 2024-08-18 13:01:54 +10:00
5f523278f3 use gh browsers 2024-08-18 12:50:51 +10:00
6ee9105a2c troubleshoot gh failing macos test 2024-08-18 12:34:18 +10:00
cee27abe95 fux
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-17 14:27:17 -07:00
ef2e7d83b0 fix outputs
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-17 14:26:31 -07:00
b3ecaeb7fa updates
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-17 14:16:45 -07:00
20 changed files with 408 additions and 967 deletions

View File

@ -10,11 +10,6 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
actions: read
jobs:
check-format:
runs-on: 'ubuntu-22.04'
@ -61,7 +56,7 @@ jobs:
build-test-web:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
@ -79,37 +74,5 @@ jobs:
- run: yarn build:wasm
- run: yarn simpleserver:ci
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
- name: Install Chromium Browser
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn playwright install chromium --with-deps
- name: run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn test:nowatch
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- name: check for changes
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
id: git-check
run: |
git add src/lang/std/artifactMapGraphs
if git status src/lang/std/artifactMapGraphs | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
# TODO when webkit works on ubuntu remove the os part of the commit message
git commit -am "Look at this (photo)Graph *in the voice of Nickelback*" || true
git push
git push origin ${{ github.head_ref }}
- run: yarn test:nowatch

View File

@ -1,7 +1,7 @@
name: Playwright Tests
on:
push:
branches: [ main ]
branches: [ main, ryanrosello-og/troubleshoot-turn-on-macos ]
pull_request:
branches: [ main ]
@ -34,13 +34,14 @@ jobs:
- 'src/wasm-lib/**'
playwright-chrome:
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 40 }}
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 30 }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
shardIndex: [1]
shardTotal: [1]
os: [macos-14]
# os: [ubuntu-latest, windows-latest, macos-14]
runs-on: ${{ matrix.os }}
needs: check-rust-changes
steps:
@ -55,15 +56,19 @@ jobs:
- name: Install dependencies
shell: bash
run: yarn
- name: Cache Playwright Browsers
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
# - name: Cache Playwright Browsers
# uses: actions/cache@v4
# with:
# path: |
# ~/.cache/ms-playwright/
# key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers
shell: bash
run: yarn playwright install --with-deps
# - name: install chrome from the cask macos
# if: ${{ startsWith(matrix.os, 'macos') }}
# shell: bash
# run: |
# brew install --cask google-chrome
- name: Download Wasm Cache
id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false'
@ -92,28 +97,28 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: install good sed
if: ${{ startsWith(matrix.os, 'macos') }}
shell: bash
run: |
brew install gnu-sed
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- name: Install vector
shell: bash
if: ${{ !startsWith(matrix.os, 'windows') }}
run: |
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
chmod +x /tmp/vector.sh
/tmp/vector.sh -y -no-modify-path
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
# - name: install good sed
# if: ${{ startsWith(matrix.os, 'macos') }}
# shell: bash
# run: |
# brew install gnu-sed
# echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
# - name: Install vector
# shell: bash
# if: ${{ !startsWith(matrix.os, 'windows') }}
# run: |
# curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
# chmod +x /tmp/vector.sh
# /tmp/vector.sh -y -no-modify-path
# mkdir -p /tmp/vector
# cp .github/workflows/vector.toml /tmp/vector.toml
# sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
# sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
# sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
# sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
# sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
# cat /tmp/vector.toml
# ${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
shell: bash
@ -125,111 +130,112 @@ jobs:
- name: build web
run: yarn build:local
shell: bash
- name: Run ubuntu/chrome snapshots
shell: bash
run: |
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
CI: true
NODE_ENV: development
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
VITE_KC_SKIP_AUTH: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
with:
name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true
- name: Clean up test-results
if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true
run: rm -r test-results
- name: check for changes
shell: bash
id: git-check
run: |
git add .
if git status | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: steps.git-check.outputs.modified == 'true'
shell: bash
run: |
git add .
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ${{matrix.os}})" || true
git push
git push origin ${{ github.head_ref }}
# only upload artifacts if there's actually changes
- uses: actions/upload-artifact@v4
if: steps.git-check.outputs.modified == 'true'
with:
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
- uses: actions/download-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true
with:
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
# - name: Run playwright/chrome snapshots
# shell: bash
# run: |
# yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
# env:
# CI: true
# NODE_ENV: development
# VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
# VITE_KC_SKIP_AUTH: true
# token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
# snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
# - uses: actions/upload-artifact@v4
# if: ${{ !cancelled() && (success() || failure()) }}
# with:
# name: playwright-report-${{matrix.os}}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
# path: playwright-report/
# retention-days: 30
# overwrite: true
# - name: Clean up test-results
# if: ${{ !cancelled() && (success() || failure()) }}
# continue-on-error: true
# run: rm -r test-results
# - name: check for changes
# shell: bash
# id: git-check
# run: |
# git add .
# if git status | grep -q "Changes to be committed"
# then echo "modified=true" >> $GITHUB_OUTPUT
# else echo "modified=false" >> $GITHUB_OUTPUT
# fi
# - name: Commit changes, if any
# if: steps.git-check.outputs.modified == 'true'
# shell: bash
# run: |
# git add .
# git config --local user.email "github-actions[bot]@users.noreply.github.com"
# git config --local user.name "github-actions[bot]"
# git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
# git fetch origin
# echo ${{ github.head_ref }}
# git checkout ${{ github.head_ref }}
# git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ${{matrix.os}})" || true
# git push
# git push origin ${{ github.head_ref }}
# # only upload artifacts if there's actually changes
# - uses: actions/upload-artifact@v4
# if: steps.git-check.outputs.modified == 'true'
# with:
# name: playwright-report-${{matrix.os}}-${{ matrix.shardIndex }}-${{ github.sha }}
# path: playwright-report/
# retention-days: 30
# - uses: actions/download-artifact@v4
# if: ${{ !cancelled() && (success() || failure()) }}
# continue-on-error: true
# with:
# name: test-results-${{matrix.os}}-${{ matrix.shardIndex }}-${{ github.sha }}
# path: test-results/
- name: Run playwright/chrome flow (with retries)
id: retry
if: ${{ !cancelled() && (success() || failure()) }}
shell: bash
run: |
if [[ ! -f "test-results/.last-run.json" ]]; then
# if no last run artifact, than run plawright normally
echo "run playwright normally"
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert="@snapshot|@electron" || true
# # send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
fi
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep="@focus" --grep-invert="@snapshot|@electron" || true
# if [[ ! -f "test-results/.last-run.json" ]]; then
# # if no last run artifact, than run plawright normally
# echo "run playwright normally"
# yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert="@snapshot|@electron" || true
# # # send to axiom
# node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
# fi
retry=1
max_retrys=4
# retry=1
# max_retrys=4
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
while [[ $retry -le $max_retrys ]]; do
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --last-failed --grep-invert="@snapshot|@electron" || true
# send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
retry=$((retry + 1))
else
echo "retried=false" >>$GITHUB_OUTPUT
exit 0
fi
else
echo "retried=false" >>$GITHUB_OUTPUT
exit 0
fi
done
# # retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
# while [[ $retry -le $max_retrys ]]; do
# if [[ -f "test-results/.last-run.json" ]]; then
# failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
# if [[ $failed_tests -gt 0 ]]; then
# echo "retried=true" >>$GITHUB_OUTPUT
# echo "run playwright with last failed tests and retry $retry"
# yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --last-failed --grep-invert="@snapshot|@electron" || true
# # send to axiom
# node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
# retry=$((retry + 1))
# else
# echo "retried=false" >>$GITHUB_OUTPUT
# exit 0
# fi
# else
# echo "retried=false" >>$GITHUB_OUTPUT
# exit 0
# fi
# done
echo "retried=false" >>$GITHUB_OUTPUT
# echo "retried=false" >>$GITHUB_OUTPUT
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
# if it still fails after 3 retrys, then fail the job
exit 1
fi
fi
exit 0
# if [[ -f "test-results/.last-run.json" ]]; then
# failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
# if [[ $failed_tests -gt 0 ]]; then
# # if it still fails after 3 retrys, then fail the job
# exit 1
# fi
# fi
# exit 0
env:
CI: true
NODE_ENV: development
@ -244,193 +250,193 @@ jobs:
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
name: test-results-${{matrix.os}}-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
retention-days: 30
overwrite: true
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
name: playwright-report-${{matrix.os}}-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true
playwright-electron:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-14]
timeout-minutes: 30
runs-on: ${{ matrix.os }}
needs: check-rust-changes
steps:
- name: Tune GitHub-hosted runner network
uses: smorimoto/tune-github-hosted-runner-network@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- uses: KittyCAD/action-install-cli@main
- name: Install dependencies
shell: bash
run: yarn
- name: Cache Playwright Browsers
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers
shell: bash
run: yarn playwright install chromium --with-deps
- name: Download Wasm Cache
id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v6
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg
- name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false'
shell: bash
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: OR Cache Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: install good sed
if: ${{ startsWith(matrix.os, 'macos') }}
shell: bash
run: |
brew install gnu-sed
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- name: Install vector
if: ${{ !startsWith(matrix.os, 'windows') }}
shell: bash
run: |
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
chmod +x /tmp/vector.sh
/tmp/vector.sh -y -no-modify-path
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
shell: bash
run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
shell: bash
run: yarn build:wasm
- name: build electron
shell: bash
run: yarn electron:package
- uses: actions/download-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true
with:
name: test-results-ubuntu-${{ github.sha }}
path: test-results/
- name: Run electron tests (with retries)
id: retry
if: ${{ !cancelled() && (success() || failure()) }}
shell: bash
run: |
if [[ ! -f "test-results/.last-run.json" ]]; then
# if no last run artifact, than run plawright normally
echo "run playwright normally"
if [[ "$IS_UBUNTU" == "true" ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
else
yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
fi
# # send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
fi
# playwright-electron:
# strategy:
# fail-fast: false
# matrix:
# os: [ubuntu-latest, windows-latest, macos-14]
# timeout-minutes: 30
# runs-on: ${{ matrix.os }}
# needs: check-rust-changes
# steps:
# - name: Tune GitHub-hosted runner network
# uses: smorimoto/tune-github-hosted-runner-network@v1
# - uses: actions/checkout@v4
# - uses: actions/setup-node@v4
# with:
# node-version-file: '.nvmrc'
# cache: 'yarn'
# - uses: KittyCAD/action-install-cli@main
# - name: Install dependencies
# shell: bash
# run: yarn
# - name: Cache Playwright Browsers
# uses: actions/cache@v4
# with:
# path: |
# ~/.cache/ms-playwright/
# key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
# - name: Install Playwright Browsers
# shell: bash
# run: yarn playwright install chromium --with-deps
# - name: Download Wasm Cache
# id: download-wasm
# if: needs.check-rust-changes.outputs.rust-changed == 'false'
# uses: dawidd6/action-download-artifact@v6
# continue-on-error: true
# with:
# github_token: ${{secrets.GITHUB_TOKEN}}
# name: wasm-bundle
# workflow: build-and-store-wasm.yml
# branch: main
# path: src/wasm-lib/pkg
# - name: copy wasm blob
# if: needs.check-rust-changes.outputs.rust-changed == 'false'
# shell: bash
# run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
# continue-on-error: true
# - name: Setup Rust
# uses: dtolnay/rust-toolchain@stable
# - name: Cache Wasm (because rust diff)
# if: needs.check-rust-changes.outputs.rust-changed == 'true'
# uses: Swatinem/rust-cache@v2
# with:
# workspaces: './src/wasm-lib'
# - name: OR Cache Wasm (because wasm cache failed)
# if: steps.download-wasm.outcome == 'failure'
# uses: Swatinem/rust-cache@v2
# with:
# workspaces: './src/wasm-lib'
# - name: install good sed
# if: ${{ startsWith(matrix.os, 'macos') }}
# shell: bash
# run: |
# brew install gnu-sed
# echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
# - name: Install vector
# if: ${{ !startsWith(matrix.os, 'windows') }}
# shell: bash
# run: |
# curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
# chmod +x /tmp/vector.sh
# /tmp/vector.sh -y -no-modify-path
# mkdir -p /tmp/vector
# cp .github/workflows/vector.toml /tmp/vector.toml
# sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
# sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
# sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
# sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
# sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
# cat /tmp/vector.toml
# ${HOME}/.vector/bin/vector --config /tmp/vector.toml &
# - name: Build Wasm (because rust diff)
# if: needs.check-rust-changes.outputs.rust-changed == 'true'
# shell: bash
# run: yarn build:wasm
# - name: OR Build Wasm (because wasm cache failed)
# if: steps.download-wasm.outcome == 'failure'
# shell: bash
# run: yarn build:wasm
# - name: build electron
# shell: bash
# run: yarn electron:package
# - uses: actions/download-artifact@v4
# if: ${{ !cancelled() && (success() || failure()) }}
# continue-on-error: true
# with:
# name: test-results-${{matrix.os}}-${{ github.sha }}
# path: test-results/
# - name: Run electron tests (with retries)
# id: retry
# if: ${{ !cancelled() && (success() || failure()) }}
# shell: bash
# run: |
# if [[ ! -f "test-results/.last-run.json" ]]; then
# # if no last run artifact, than run plawright normally
# echo "run playwright normally"
# if [[ "$IS_UBUNTU" == "true" ]]; then
# xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
# else
# yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
# fi
# # # send to axiom
# node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
# fi
retry=1
max_retrys=2
# retry=1
# max_retrys=2
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
while [[ $retry -le $max_retrys ]]; do
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
if [[ "$IS_UBUNTU" == "true" ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
else
yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
fi
# send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
retry=$((retry + 1))
else
echo "retried=false" >>$GITHUB_OUTPUT
exit 0
fi
else
echo "retried=false" >>$GITHUB_OUTPUT
exit 0
fi
done
# # retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
# while [[ $retry -le $max_retrys ]]; do
# if [[ -f "test-results/.last-run.json" ]]; then
# failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
# if [[ $failed_tests -gt 0 ]]; then
# echo "retried=true" >>$GITHUB_OUTPUT
# echo "run playwright with last failed tests and retry $retry"
# if [[ "$IS_UBUNTU" == "true" ]]; then
# xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
# else
# yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
# fi
# # send to axiom
# node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
# retry=$((retry + 1))
# else
# echo "retried=false" >>$GITHUB_OUTPUT
# exit 0
# fi
# else
# echo "retried=false" >>$GITHUB_OUTPUT
# exit 0
# fi
# done
echo "retried=false" >>$GITHUB_OUTPUT
# echo "retried=false" >>$GITHUB_OUTPUT
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
# if it still fails after 3 retrys, then fail the job
exit 1
fi
fi
exit 0
env:
CI: true
NODE_ENV: development
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
VITE_KC_SKIP_AUTH: true
IS_UBUNTU: ${{ startsWith(matrix.os, 'ubuntu') && 'true' || 'false' }}
#DEBUG: 'pw:browser*'
- name: send to axiom
if: ${{ !cancelled() && (success() || failure()) && !startsWith(matrix.os, 'windows') }}
shell: bash
run: |
node playwrightProcess.mjs | tee /tmp/github-actions.log
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
with:
name: test-results-electron-${{ github.sha }}
path: test-results/
retention-days: 30
overwrite: true
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
with:
name: playwright-report-electron-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true
# if [[ -f "test-results/.last-run.json" ]]; then
# failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
# if [[ $failed_tests -gt 0 ]]; then
# # if it still fails after 3 retrys, then fail the job
# exit 1
# fi
# fi
# exit 0
# env:
# CI: true
# NODE_ENV: development
# VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
# VITE_KC_SKIP_AUTH: true
# IS_UBUNTU: ${{ startsWith(matrix.os, 'ubuntu') && 'true' || 'false' }}
# #DEBUG: 'pw:browser*'
# - name: send to axiom
# if: ${{ !cancelled() && (success() || failure()) && !startsWith(matrix.os, 'windows') }}
# shell: bash
# run: |
# node playwrightProcess.mjs | tee /tmp/github-actions.log
# - uses: actions/upload-artifact@v4
# if: ${{ !cancelled() && (success() || failure()) }}
# with:
# name: test-results-electron-${{ github.sha }}
# path: test-results/
# retention-days: 30
# overwrite: true
# - uses: actions/upload-artifact@v4
# if: ${{ !cancelled() && (success() || failure()) }}
# with:
# name: playwright-report-electron-${{ github.sha }}
# path: playwright-report/
# retention-days: 30
# overwrite: true

View File

@ -131,7 +131,7 @@ test.describe('Onboarding tests', () => {
await expect(page.url()).not.toContain('onboarding')
})
test('Onboarding redirects and code updating', async ({ page }) => {
test('Onboarding redirects and code updating @focus', async ({ page }) => {
const u = await getUtils(page)
// Override beforeEach test setup

View File

@ -1,11 +1,5 @@
import { test, expect } from '@playwright/test'
import {
doExport,
getUtils,
Paths,
setupElectron,
tearDown,
} from './test-utils'
import { getUtils, setupElectron, tearDown } from './test-utils'
import fsp from 'fs/promises'
import fs from 'fs'
import { join } from 'path'
@ -14,94 +8,6 @@ test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test(
'Can export from electron app',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
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 u = await getUtils(page)
page.on('console', console.log)
await electronApp.context().addInitScript(async () => {
;(window as any).playwrightSkipFilePicker = true
})
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
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.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [75, 75, 75]), {
timeout: 10_000,
})
.toBeLessThan(10)
})
const exportLocations: Array<Paths> = []
await test.step('export the model as a glTF', async () => {
exportLocations.push(
await doExport(
{
type: 'gltf',
storage: 'embedded',
presentation: 'pretty',
},
page,
true
)
)
})
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 electronApp.close()
}
)
test(
'Rename and delete projects, also spam arrow keys when renaming',
{ tag: '@electron' },
@ -160,10 +66,10 @@ test(
await page.waitForTimeout(100)
// type "updated project name"
await page.keyboard.press('Backspace')
await page.keyboard.type('updated project name')
// spam arrow keys to make sure it doesn't impact the text
for (let i = 0; i < 10; i++) {
await page.keyboard.press('ArrowRight')
}
@ -389,101 +295,6 @@ test.fixme(
await electronApp.close()
}
)
test(
'Deleting projects, can delete individual project, can still create projects after deleting all',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
})
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
const createProjectAndRenameIt = async (name: string) =>
test.step(`Create and rename project ${name}`, async () => {
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 "updated project name"
await page.keyboard.press('Backspace')
await page.keyboard.type(name)
await page.getByLabel('checkmark').last().click()
})
// we need to create the folders so that the order is correct
// creating them ahead of time with fs tools means they all have the same timestamp
await createProjectAndRenameIt('router-template-slate')
// await createProjectAndRenameIt('focusrite_scarlett_mounting_braket')
await createProjectAndRenameIt('bracket')
await createProjectAndRenameIt('lego')
await test.step('delete the middle project, i.e. the bracket project', async () => {
const project = page.getByText('bracket')
await project.hover()
await project.focus()
await page
.locator('[data-edit-buttons-for="bracket"]')
.getByLabel('trash')
.click()
await expect(page.getByText('This will permanently delete')).toBeVisible()
await page.getByTestId('delete-confirmation').click()
await expect(page.getByText('Successfully deleted')).toBeVisible()
await expect(page.getByText('Successfully deleted')).not.toBeVisible()
await expect(page.getByText('bracket')).not.toBeVisible()
})
await test.step('Now that the middle project is deleted, check the other projects are still there', async () => {
await expect(page.getByText('router-template-slate')).toBeVisible()
await expect(page.getByText('lego')).toBeVisible()
})
await test.step('delete other two projects', async () => {
await page
.locator('[data-edit-buttons-for="router-template-slate"]')
.getByLabel('trash')
.click()
await page.getByTestId('delete-confirmation').click()
await page
.locator('[data-edit-buttons-for="lego"]')
.getByLabel('trash')
.click()
await page.getByTestId('delete-confirmation').click()
})
await test.step('Check that the home page is empty', async () => {
await expect(page.getByText('No Projects found')).toBeVisible()
})
await test.step('Check we can still create a project', async () => {
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 electronApp.close()
}
)
test(
'Can sort projects on home page',
{ tag: '@electron' },
@ -691,7 +502,7 @@ const extrude001 = extrude(200, sketch001)`)
)
test(
'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
'Check you can go home with two different methods, and that switching between projects does not harm the stream',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({

View File

@ -1,6 +1,6 @@
import { test, expect, Page } from '@playwright/test'
import * as fsp from 'fs/promises'
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { getUtils, setup, tearDown } from './test-utils'
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
import { bracket } from 'lib/exampleKcl'
@ -415,52 +415,6 @@ const sketch001 = startSketchAt([-0, -0])
await expect(successToastMessage).toBeVisible()
})
})
test(
`Network health indicator only appears in modeling view`,
{ tag: '@electron' },
async ({ browserName: _ }, testInfo) => {
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 u = await getUtils(page)
// Locators
const projectsHeading = page.getByRole('heading', {
name: 'Your projects',
})
const projectLink = page.getByRole('link', { name: 'bracket' })
const networkHealthIndicator = page.getByTestId('network-toggle')
await test.step('Check the home page', async () => {
await expect(projectsHeading).toBeVisible()
await expect(projectLink).toBeVisible()
await expect(networkHealthIndicator).not.toBeVisible()
})
await test.step('Open the project', async () => {
await projectLink.click()
})
await test.step('Check the modeling view', async () => {
await expect(networkHealthIndicator).toBeVisible()
await expect(networkHealthIndicator).toContainText('Problem')
await u.waitForPageLoad()
await expect(networkHealthIndicator).toContainText('Connected')
})
await electronApp.close()
}
)
})
async function clickExportButton(page: Page) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -538,19 +538,14 @@ export interface Paths {
export const doExport = async (
output: Models['OutputFormat_type'],
page: Page,
isElectron = false
page: Page
): Promise<Paths> => {
if (!isElectron) {
await page.getByRole('button', { name: APP_NAME }).click()
const exportMenuButton = page.getByRole('button', {
name: 'Export current part',
})
await expect(exportMenuButton).toBeVisible()
await exportMenuButton.click()
} else {
await page.getByTestId('export-pane-button').click()
}
await page.getByRole('button', { name: APP_NAME }).click()
const exportMenuButton = page.getByRole('button', {
name: 'Export current part',
})
await expect(exportMenuButton).toBeVisible()
await exportMenuButton.click()
await expect(page.getByTestId('command-bar')).toBeVisible()
// Go through export via command bar
@ -577,21 +572,13 @@ export const doExport = async (
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
let downloadCnt = 0
if (!isElectron)
page.on('download', async (download) => {
if (downloadCnt === 0) {
downloadResolve1(download)
}
downloadCnt++
})
await page.getByRole('button', { name: 'Submit command' }).click()
if (isElectron) {
return {
modelPath: '',
imagePath: '',
outputType: output.type,
page.on('download', async (download) => {
if (downloadCnt === 0) {
downloadResolve1(download)
}
}
downloadCnt++
})
await page.getByRole('button', { name: 'Submit command' }).click()
// Handle download
const download = await downloadPromise1

View File

@ -1,10 +1,9 @@
import { test, expect } from '@playwright/test'
import * as fsp from 'fs/promises'
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { getUtils, setup, tearDown } from './test-utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates'
import * as TOML from '@iarna/toml'
import { APP_NAME } from 'lib/constants'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
@ -188,74 +187,4 @@ test.describe('Testing settings', () => {
await expect(themeColorSetting).toHaveValue(settingValues.default)
})
})
test(
`Project settings override user settings on desktop`,
{ tag: '@electron' },
async ({ browser: _ }, testInfo) => {
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 u = await getUtils(page)
page.on('console', console.log)
// Selectors and constants
const userThemeColor = '120'
const projectThemeColor = '50'
const settingsOpenButton = page.getByRole('link', {
name: 'settings Settings',
})
const themeColorSetting = page.locator('#themeColor').getByRole('slider')
const projectSettingsTab = page.getByRole('radio', { name: 'Project' })
const userSettingsTab = page.getByRole('radio', { name: 'User' })
const settingsCloseButton = page.getByTestId('settings-close-button')
const projectLink = page.getByText('bracket')
const logoLink = page.getByTestId('app-logo')
// Open the app and set the user theme color
await test.step('Set user theme color on home', async () => {
await expect(settingsOpenButton).toBeVisible()
await settingsOpenButton.click()
// The user tab should be selected by default on home
await expect(userSettingsTab).toBeChecked()
await themeColorSetting.fill(userThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
await settingsCloseButton.click()
})
await test.step('Set project theme color', async () => {
// Open the project
await projectLink.click()
await settingsOpenButton.click()
// The project tab should be selected by default within a project
await expect(projectSettingsTab).toBeChecked()
await themeColorSetting.fill(projectThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
})
await test.step('Refresh the application and see project setting applied', async () => {
await page.reload()
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
await settingsCloseButton.click()
})
await test.step(`Navigate back to the home view and see user setting applied`, async () => {
await logoLink.click()
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
})
await electronApp.close()
}
)
})

View File

@ -31,13 +31,15 @@ test.describe('Text-to-CAD tests', () => {
)
await expect(submittingToastMessage).toBeVisible()
await page.waitForTimeout(5000)
const generatingToastMessage = page.getByText(
`Generating parametric model...`
)
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await expect(generatingToastMessage).toBeVisible()
const successToastMessage = page.getByText(`Text-to-CAD successful`)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(successToastMessage).toBeVisible()
await expect(page.getByText('Copied')).not.toBeVisible()
@ -99,13 +101,15 @@ test.describe('Text-to-CAD tests', () => {
)
await expect(submittingToastMessage).toBeVisible()
await page.waitForTimeout(5000)
const generatingToastMessage = page.getByText(
`Generating parametric model...`
)
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await expect(generatingToastMessage).toBeVisible()
const successToastMessage = page.getByText(`Text-to-CAD successful`)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(successToastMessage).toBeVisible()
await expect(page.getByText('Copied')).not.toBeVisible()
@ -117,12 +121,13 @@ test.describe('Text-to-CAD tests', () => {
// Find the toast.
// Look out for the toast message
await expect(submittingToastMessage).toBeVisible()
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await page.waitForTimeout(5000)
await expect(generatingToastMessage).toBeVisible()
// Expect 2 success toasts.
await expect(successToastMessage).toHaveCount(2, {
timeout: 15000,
})
await expect(successToastMessage).toHaveCount(2)
await expect(page.getByText('a 2x4 lego')).toBeVisible()
await expect(page.getByText('a 2x6 lego')).toBeVisible()
})
@ -145,13 +150,15 @@ test.describe('Text-to-CAD tests', () => {
)
await expect(submittingToastMessage).toBeVisible()
await page.waitForTimeout(5000)
const generatingToastMessage = page.getByText(
`Generating parametric model...`
)
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await expect(generatingToastMessage).toBeVisible()
const successToastMessage = page.getByText(`Text-to-CAD successful`)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(successToastMessage).toBeVisible()
// Hit copy to clipboard.
const rejectButton = page.getByRole('button', { name: 'Reject' })
@ -312,9 +319,11 @@ test.describe('Text-to-CAD tests', () => {
// Look out for the toast message
await expect(submittingToastMessage).toBeVisible()
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await page.waitForTimeout(5000)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(generatingToastMessage).toBeVisible()
await expect(successToastMessage).toBeVisible()
})
test('sending a bad prompt fails, can ignore toast, can start over from command bar', async ({
@ -344,7 +353,7 @@ test.describe('Text-to-CAD tests', () => {
const prompt = page.getByText('Prompt')
await expect(prompt.first()).toBeVisible()
const badPrompt = 'akjsndladflajbhflauweyf15;'
const badPrompt = 'akjsndladf lajbhflauweyfa;wieufjn---4;'
// Type the prompt.
await page.keyboard.type(badPrompt)
@ -382,9 +391,11 @@ test.describe('Text-to-CAD tests', () => {
// Look out for the toast message
await expect(submittingToastMessage).toBeVisible()
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await page.waitForTimeout(5000)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(generatingToastMessage).toBeVisible()
await expect(successToastMessage).toBeVisible()
await expect(page.getByText('Copied')).not.toBeVisible()
@ -437,13 +448,16 @@ test.describe('Text-to-CAD tests', () => {
)
await expect(submittingToastMessage).toBeVisible()
await page.waitForTimeout(1000)
const generatingToastMessage = page.getByText(
`Generating parametric model...`
)
await expect(generatingToastMessage).toBeVisible({ timeout: 10000 })
await expect(generatingToastMessage).toBeVisible()
await page.waitForTimeout(5000)
const successToastMessage = page.getByText(`Text-to-CAD successful`)
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
await expect(successToastMessage).toBeVisible()
await expect(page.getByText(promptWithNewline)).toBeVisible()
})
@ -451,8 +465,6 @@ test.describe('Text-to-CAD tests', () => {
test('can do many at once and get many prompts back, and interact with many', async ({
page,
}) => {
// Let this test run longer since we've seen it timeout.
test.setTimeout(180_000)
// skip on windows
test.skip(
process.platform === 'win32',
@ -481,13 +493,11 @@ test.describe('Text-to-CAD tests', () => {
const generatingToastMessage = page.getByText(
`Generating parametric model...`
)
await expect(generatingToastMessage.first()).toBeVisible({
timeout: 10_000,
})
await expect(generatingToastMessage.first()).toBeVisible({ timeout: 10000 })
const successToastMessage = page.getByText(`Text-to-CAD successful`)
// We should have three success toasts.
await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 })
await expect(successToastMessage).toHaveCount(3, { timeout: 15000 })
await expect(page.getByText('Copied')).not.toBeVisible()
@ -529,7 +539,12 @@ test.describe('Text-to-CAD tests', () => {
await expect(page.locator('.cm-content')).toContainText(`2x8`)
// Find the toast close button.
const closeButton = page.locator('[data-negative-button="close"]').first()
const closeButton = page
.getByRole('status')
.locator('div')
.filter({ hasText: 'Text-to-CAD successfulPrompt' })
.first()
.getByRole('button', { name: 'Close' })
await expect(closeButton).toBeVisible()
await closeButton.click()
@ -682,7 +697,7 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
// Type the prompt.
await page.keyboard.type(promptStr)
await page.waitForTimeout(200)
await page.waitForTimeout(1000)
await page.keyboard.press('Enter')
})
}

View File

@ -42,6 +42,9 @@ export default defineConfig({
/* Chromium is the only one with these permission types */
permissions: ['clipboard-write', 'clipboard-read'],
},
launchOptions: {
args: process.env.CI ? ['--headless', '--enable-gpu'] : [],
},
}, // or 'chrome-beta'
},
{

View File

@ -49,7 +49,7 @@ export const FileMachineProvider = ({
if (event.data && 'name' in event.data) {
commandBarSend({ type: 'Close' })
navigate(
`..${PATHS.FILE}/${encodeURIComponent(
`${PATHS.FILE}/${encodeURIComponent(
context.selectedDirectory +
window.electron.path.sep +
event.data.name
@ -61,7 +61,7 @@ export const FileMachineProvider = ({
event.data.path.endsWith(FILE_EXT)
) {
// Don't navigate to newly created directories
navigate(`..${PATHS.FILE}/${encodeURIComponent(event.data.path)}`)
navigate(`${PATHS.FILE}/${encodeURIComponent(event.data.path)}`)
}
},
addFileToRenamingQueue: assign({
@ -100,7 +100,7 @@ export const FileMachineProvider = ({
let createdPath: string
if (event.data.makeDir) {
let { name, path } = getNextDirName({
let { name, path } = await getNextDirName({
entryName: createdName,
baseDir: context.selectedDirectory.path,
})
@ -108,13 +108,16 @@ export const FileMachineProvider = ({
createdPath = path
await window.electron.mkdir(createdPath)
} else {
const { name, path } = getNextFileName({
const { name, path } = await getNextFileName({
entryName: createdName,
baseDir: context.selectedDirectory.path,
})
createdName = name
createdPath = path
await window.electron.writeFile(createdPath, event.data.content ?? '')
await window.electron.mkdir(createdPath)
if (event.data.content) {
await window.electron.writeFile(createdPath, event.data.content)
}
}
return {
@ -127,7 +130,7 @@ export const FileMachineProvider = ({
let createdPath: string
if (event.data.makeDir) {
let { name, path } = getNextDirName({
let { name, path } = await getNextDirName({
entryName: createdName,
baseDir: context.selectedDirectory.path,
})
@ -135,13 +138,16 @@ export const FileMachineProvider = ({
createdPath = path
await window.electron.mkdir(createdPath)
} else {
const { name, path } = getNextFileName({
const { name, path } = await getNextFileName({
entryName: createdName,
baseDir: context.selectedDirectory.path,
})
createdName = name
createdPath = path
await window.electron.writeFile(createdPath, event.data.content ?? '')
await window.electron.mkdir(createdPath)
if (event.data.content) {
await window.electron.writeFile(createdPath, '')
}
}
return {
@ -174,13 +180,13 @@ export const FileMachineProvider = ({
const currentFilePath = window.electron.path.join(file.path, file.name)
if (oldPath === currentFilePath && project?.path) {
// If we just renamed the current file, navigate to the new path
navigate(`..${PATHS.FILE}/${encodeURIComponent(newPath)}`)
navigate(PATHS.FILE + '/' + encodeURIComponent(newPath))
} else if (file?.path.includes(oldPath)) {
// If we just renamed a directory that the current file is in, navigate to the new path
navigate(
`..${PATHS.FILE}/${encodeURIComponent(
file.path.replace(oldPath, newDirPath)
)}`
PATHS.FILE +
'/' +
encodeURIComponent(file.path.replace(oldPath, newDirPath))
)
}
@ -215,7 +221,7 @@ export const FileMachineProvider = ({
file?.path.includes(event.data.path)) &&
project?.path
) {
navigate(`../${PATHS.FILE}/${encodeURIComponent(project.path)}`)
navigate(PATHS.FILE + '/' + encodeURIComponent(project.path))
}
return `Successfully deleted ${isDir ? 'folder' : 'file'} "${

View File

@ -107,9 +107,7 @@ export function LowerRightControls({
</Tooltip>
</Link>
<NetworkMachineIndicator className={linkOverrideClassName} />
{!location.pathname.startsWith(PATHS.HOME) && (
<NetworkHealthIndicator />
)}
<NetworkHealthIndicator />
<HelpMenu />
</menu>
</section>

View File

@ -120,10 +120,7 @@ function ProjectCard({
</div>
</Link>
{!isEditing && (
<div
className="absolute z-10 flex items-center gap-1 opacity-0 bottom-2 right-2 group-hover:opacity-100 group-focus-within:opacity-100"
data-edit-buttons-for={project.name?.replace(FILE_EXT, '')}
>
<div className="absolute z-10 flex items-center gap-1 opacity-0 bottom-2 right-2 group-hover:opacity-100 group-focus-within:opacity-100">
<ActionButton
Element="button"
iconStart={{

View File

@ -5,7 +5,7 @@ import { isDesktop } from 'lib/isDesktop'
import { PATHS } from 'lib/paths'
import toast from 'react-hot-toast'
import { TextToCad_type } from '@kittycad/lib/dist/types/src/models'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import {
Box3,
Color,
@ -121,40 +121,10 @@ export function ToastTextToCadSuccess({
}) {
const wrapperRef = useRef<HTMLDivElement | null>(null)
const canvasRef = useRef<HTMLCanvasElement | null>(null)
const animationRequestRef = useRef<number>()
const [hasCopied, setHasCopied] = useState(false)
const [showCopiedUi, setShowCopiedUi] = useState(false)
const modelId = data.id
const animate = useCallback(
({
renderer,
scene,
camera,
controls,
isFirstRender = false,
}: {
renderer: WebGLRenderer
scene: Scene
camera: OrthographicCamera
controls: OrbitControls
isFirstRender?: boolean
}) => {
if (
!wrapperRef.current ||
!(isFirstRender || animationRequestRef.current)
)
return
animationRequestRef.current = requestAnimationFrame(() =>
animate({ renderer, scene, camera, controls })
)
// required if controls.enableDamping or controls.autoRotate are set to true
controls.update()
renderer.render(scene, camera)
},
[]
)
useEffect(() => {
if (!canvasRef.current) return
@ -162,6 +132,7 @@ export function ToastTextToCadSuccess({
const renderer = new WebGLRenderer({ canvas, antialias: true, alpha: true })
renderer.setSize(CANVAS_SIZE, CANVAS_SIZE)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.setAnimationLoop(animate)
const scene = new Scene()
const ambientLight = new DirectionalLight(new Color('white'), 8.0)
@ -184,6 +155,13 @@ export function ToastTextToCadSuccess({
return
}
function animate() {
requestAnimationFrame(animate)
// required if controls.enableDamping or controls.autoRotate are set to true
controls.update()
renderer.render(scene, camera)
}
loader.parse(
buffer,
'',
@ -234,8 +212,6 @@ export function ToastTextToCadSuccess({
camera.updateProjectionMatrix()
controls.update()
// render the scene once...
renderer.render(scene, camera)
},
// called when loading has errors
function (error) {
@ -245,26 +221,8 @@ export function ToastTextToCadSuccess({
}
)
// ...and set a mouseover listener on the canvas to enable the orbit controls
canvasRef.current.addEventListener('mouseover', () => {
renderer.setAnimationLoop(() =>
animate({ renderer, scene, camera, controls, isFirstRender: true })
)
})
canvasRef.current.addEventListener('mouseout', () => {
renderer.setAnimationLoop(null)
if (animationRequestRef.current) {
cancelAnimationFrame(animationRequestRef.current)
animationRequestRef.current = undefined
}
})
return () => {
renderer.dispose()
if (animationRequestRef.current) {
cancelAnimationFrame(animationRequestRef.current)
animationRequestRef.current = undefined
}
}
}, [])
@ -293,7 +251,6 @@ export function ToastTextToCadSuccess({
iconStart={{
icon: 'close',
}}
data-negative-button={hasCopied ? 'close' : 'reject'}
name={hasCopied ? 'Close' : 'Reject'}
onClick={() => {
if (!hasCopied) {

View File

@ -14,15 +14,6 @@ const save_ = async (file: ModelingAppFile) => {
extensions.push(extension)
}
if (!(window as any).playwrightSkipFilePicker) {
// skip file picker, save to default location
await window.electron.writeFile(
file.name,
new Uint8Array(file.contents)
)
return
}
// Open a dialog to save the file.
const filePathMeta = await window.electron.save({
defaultPath: file.name,

View File

@ -112,7 +112,7 @@ const Home = () => {
).trim()
if (doesProjectNameNeedInterpolated(name)) {
const nextIndex = getNextProjectIndex(name, projects)
const nextIndex = await getNextProjectIndex(name, projects)
name = interpolateProjectNameWithIndex(name, nextIndex)
}

View File

@ -1428,7 +1428,6 @@ dependencies = [
"parse-display",
"pretty_assertions",
"pyo3",
"recursion",
"reqwest",
"ropey",
"schemars",
@ -2149,12 +2148,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "recursion"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f705426858ccd7bbfe19798239d6b6bfd9bf96bde0624a84b92694046e98871"
[[package]]
name = "redox_syscall"
version = "0.2.16"

View File

@ -31,7 +31,6 @@ lazy_static = "1.5.0"
mime_guess = "2.0.5"
parse-display = "0.9.1"
pyo3 = { version = "0.22.2", optional = true }
recursion = "0.5.2"
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
ropey = "1.6.1"
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }

View File

@ -1,3 +1,2 @@
pub mod modify;
mod recursion;
pub mod types;

View File

@ -1,167 +0,0 @@
use recursion::{Collapsible, MappableFrame, PartiallyApplied};
use super::types::{
BinaryExpression, BinaryOperator, BinaryPart, Digest, Expr, FnArgType, Identifier, KclNone, Literal, Parameter,
PipeSubstitution, TagDeclarator,
};
pub enum ExprFrame<A> {
Literal(Box<Literal>),
Identifier(Box<Identifier>),
TagDeclarator(Box<TagDeclarator>),
BinaryExpression(Box<BinaryExpressionFrame<A>>),
FunctionExpression(Box<FunctionExpressionFrame<A>>),
CallExpression(Box<CallExpressionFrame<A>>),
PipeExpression(Box<PipeExpressionFrame<A>>),
PipeSubstitution(Box<PipeSubstitution>),
ArrayExpression(Box<ArrayExpressionFrame<A>>),
ObjectExpression(Box<ObjectExpressionFrame<A>>),
MemberExpression(Box<MemberExpressionFrame<A>>),
UnaryExpression(Box<UnaryExpressionFrame<A>>),
None(KclNone),
}
impl MappableFrame for ExprFrame<PartiallyApplied> {
type Frame<X> = ExprFrame<X>;
fn map_frame<A, B>(input: Self::Frame<A>, mut f: impl FnMut(A) -> B) -> Self::Frame<B> {
match input {
ExprFrame::Literal(x) => ExprFrame::Literal(x),
ExprFrame::Identifier(x) => ExprFrame::Identifier(x),
ExprFrame::TagDeclarator(x) => ExprFrame::TagDeclarator(x),
ExprFrame::BinaryExpression(x) => ExprFrame::BinaryExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::FunctionExpression(x) => ExprFrame::FunctionExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::CallExpression(x) => ExprFrame::CallExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::PipeExpression(x) => ExprFrame::PipeExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::PipeSubstitution(x) => ExprFrame::PipeSubstitution(x),
ExprFrame::ArrayExpression(x) => ExprFrame::ArrayExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::ObjectExpression(x) => ExprFrame::ObjectExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::MemberExpression(x) => ExprFrame::MemberExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::UnaryExpression(x) => ExprFrame::UnaryExpression(MappableFrame::map_frame(x, &mut f)),
ExprFrame::None(x) => ExprFrame::None(x),
}
}
}
impl<'a> Collapsible for &'a Expr {
type FrameToken = ExprFrame<PartiallyApplied>;
fn into_frame(self) -> <Self::FrameToken as MappableFrame>::Frame<Self> {
match self {
Expr::Literal(x) => ExprFrame::Literal(x.clone()),
Expr::Identifier(x) => ExprFrame::Identifier(x.clone()),
Expr::TagDeclarator(x) => ExprFrame::TagDeclarator(x.clone()),
Expr::BinaryExpression(x) => ExprFrame::BinaryExpression(Box::new(x.into_frame())),
Expr::FunctionExpression(x) => ExprFrame::FunctionExpression(Box::new(x.into_frame())),
Expr::CallExpression(x) => ExprFrame::CallExpression(Box::new(x.into_frame())),
Expr::PipeExpression(x) => ExprFrame::PipeExpression(Box::new(x.into_frame())),
Expr::PipeSubstitution(x) => ExprFrame::PipeSubstitution(x.clone()),
Expr::ArrayExpression(x) => ExprFrame::ArrayExpression(Box::new(x.into_frame())),
Expr::ObjectExpression(x) => ExprFrame::ObjectExpression(Box::new(x.into_frame())),
Expr::MemberExpression(x) => ExprFrame::MemberExpression(Box::new(x.into_frame())),
Expr::UnaryExpression(x) => ExprFrame::UnaryExpression(Box::new(x.into_frame())),
Expr::None(x) => ExprFrame::None(x.clone()),
}
}
}
pub struct BinaryExpressionFrame<A> {
pub start: usize,
pub end: usize,
pub operator: BinaryOperator,
pub left: BinaryPartFrame<A>,
pub right: BinaryPartFrame<A>,
pub digest: Option<Digest>,
}
impl MappableFrame for BinaryExpressionFrame<PartiallyApplied> {
type Frame<X> = BinaryExpressionFrame<X>;
fn map_frame<A, B>(input: Self::Frame<A>, mut f: impl FnMut(A) -> B) -> Self::Frame<B> {
BinaryExpressionFrame::<B> {
start: input.start,
end: input.end,
operator: input.operator,
left: <BinaryPartFrame<PartiallyApplied> as MappableFrame>::map_frame(input.left, &mut f),
right: <BinaryPartFrame<PartiallyApplied> as MappableFrame>::map_frame(input.right, &mut f),
digest: input.digest,
}
}
}
impl<'a> Collapsible for &'a BinaryExpression {
type FrameToken = BinaryExpressionFrame<PartiallyApplied>;
fn into_frame(self) -> <Self::FrameToken as MappableFrame>::Frame<Self> {
BinaryExpressionFrame::<BinaryExpression> {
start: self.start,
end: self.end,
operator: self.operator.clone(),
left: self.left.into_frame(),
right: self.right.into_frame(),
digest: self.digest,
}
}
}
pub enum BinaryPartFrame<A> {
Literal(Box<Literal>),
Identifier(Box<Identifier>),
BinaryExpression(Box<BinaryExpressionFrame<A>>),
CallExpression(Box<CallExpressionFrame<A>>),
UnaryExpression(Box<UnaryExpressionFrame<A>>),
MemberExpression(Box<MemberExpressionFrame<A>>),
}
impl MappableFrame for BinaryPartFrame<PartiallyApplied> {
type Frame<X> = BinaryPartFrame<X>;
fn map_frame<A, B>(input: Self::Frame<A>, mut f: impl FnMut(A) -> B) -> Self::Frame<B> {
match input {
BinaryPartFrame::Literal(x) => BinaryPartFrame::Literal(x),
BinaryPartFrame::Identifier(x) => BinaryPartFrame::Identifier(x),
BinaryPartFrame::BinaryExpression(x) => BinaryPartFrame::BinaryExpression(MappableFrame::map_frame(x, f)),
BinaryPartFrame::CallExpression(x) => BinaryPartFrame::CallExpression(MappableFrame::map_frame(x, f)),
BinaryPartFrame::UnaryExpression(x) => BinaryPartFrame::UnaryExpression(MappableFrame::map_frame(x, f)),
BinaryPartFrame::MemberExpression(x) => BinaryPartFrame::MemberExpression(MappableFrame::map_frame(x, f)),
}
}
}
impl<'a> Collapsible for &'a BinaryPart {
type FrameToken = BinaryPartFrame<PartiallyApplied>;
fn into_frame(self) -> <Self::FrameToken as MappableFrame>::Frame<Self> {
match self {
BinaryPart::Literal(x) => BinaryPartFrame::Literal(x.clone()),
BinaryPart::Identifier(x) => BinaryPartFrame::Identifier(x.clone()),
BinaryPart::BinaryExpression(x) => BinaryPartFrame::BinaryExpression(x.into_frame()),
BinaryPart::CallExpression(x) => BinaryPartFrame::CallExpression(x.into_frame()),
BinaryPart::UnaryExpression(x) => BinaryPartFrame::UnaryExpression(x.into_frame()),
BinaryPart::MemberExpression(x) => BinaryPartFrame::MemberExpression(x.into_frame()),
}
}
}
pub struct FunctionExpressionFrame<A> {
pub start: usize,
pub end: usize,
pub params: Vec<Parameter>,
pub body: ProgramFrame<A>,
pub return_type: Option<FnArgType>,
pub digest: Option<Digest>,
}
pub struct CallExpressionFrame<A> {
pub start: usize,
pub end: usize,
pub callee: Identifier,
pub arguments: Vec<ExprFrame<A>>,
pub optional: bool,
pub digest: Option<Digest>,
}
// More...