Compare commits
2 Commits
kurt-model
...
jtran/recu
Author | SHA1 | Date | |
---|---|---|---|
55a6155400 | |||
d41972ee4d |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock
|
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas,.yarn.lock,**/yarn.lock
|
||||||
|
@ -25,9 +25,7 @@
|
|||||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "warn",
|
"@typescript-eslint/no-floating-promises": "warn",
|
||||||
"suggest-no-throw/suggest-no-throw": "off",
|
"testing-library/prefer-screen-queries": "off"
|
||||||
"testing-library/prefer-screen-queries": "off",
|
|
||||||
"jest/valid-expect": "off"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
414
.github/workflows/build-test-publish-apps.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: build-publish-apps
|
name: build-test-publish-apps
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -21,7 +21,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare-files:
|
prepare-json-files:
|
||||||
runs-on: ubuntu-22.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
runs-on: ubuntu-22.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.export_version.outputs.version }}
|
version: ${{ steps.export_version.outputs.version }}
|
||||||
@ -33,19 +33,6 @@ jobs:
|
|||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
|
|
||||||
- run: yarn install
|
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: './src/wasm-lib'
|
|
||||||
|
|
||||||
# TODO: see if we can fetch from main instead if no diff at src/wasm-lib
|
|
||||||
- name: Run build:wasm
|
|
||||||
run: "yarn build:wasm${{ env.BUILD_RELEASE == 'true' && '-dev' || ''}}"
|
|
||||||
|
|
||||||
- name: Set nightly version
|
- name: Set nightly version
|
||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
run: |
|
run: |
|
||||||
@ -55,50 +42,36 @@ jobs:
|
|||||||
# TODO: see if we ned to add updater test URL here https://dl.zoo.dev/releases/modeling-app/updater-test/last_update.json
|
# TODO: see if we ned to add updater test URL here https://dl.zoo.dev/releases/modeling-app/updater-test/last_update.json
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ github.event_name == 'schedule' || env.CUT_RELEASE_PR == 'true' }}
|
||||||
with:
|
with:
|
||||||
name: prepared-files
|
|
||||||
path: |
|
path: |
|
||||||
package.json
|
package.json
|
||||||
src/wasm-lib/pkg/wasm_lib*
|
|
||||||
|
|
||||||
- id: export_version
|
- id: export_version
|
||||||
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
|
||||||
build-apps:
|
build-test-app-macos:
|
||||||
needs: [prepare-files]
|
needs: [prepare-json-files]
|
||||||
strategy:
|
runs-on: macos-14
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [macos-14, windows-2022, ubuntu-22.04]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
env:
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
CSC_FOR_PULL_REQUEST: true
|
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
||||||
TAURI_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
||||||
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
|
||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
name: prepared-files
|
if: github.event_name == 'schedule'
|
||||||
|
|
||||||
- name: Copy prepared files
|
- name: Copy updated .json files
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
run: |
|
run: |
|
||||||
ls -R prepared-files
|
ls -l artifact
|
||||||
cp prepared-files/package.json package.json
|
cp artifact/package.json package.json
|
||||||
cp prepared-files/src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
|
||||||
mkdir src/wasm-lib/pkg
|
|
||||||
cp prepared-files/src/wasm-lib/pkg/wasm_lib* src/wasm-lib/pkg
|
|
||||||
|
|
||||||
- name: Sync node version and setup cache
|
- name: Sync node version and setup cache
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@ -108,10 +81,79 @@ jobs:
|
|||||||
|
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
|
|
||||||
- run: yarn tronb:vite
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src/wasm-lib'
|
||||||
|
|
||||||
|
- name: Run build:wasm
|
||||||
|
run: "yarn build:wasm${{ env.BUILD_RELEASE == 'true' && '-dev' || ''}}"
|
||||||
|
|
||||||
|
# TODO: sign the app (and updater bundle potentially)
|
||||||
|
- name: Add signing certificate
|
||||||
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
|
run: chmod +x add-osx-cert.sh && ./add-osx-cert.sh
|
||||||
|
|
||||||
|
- name: Build the app for arm64
|
||||||
|
run: "yarn electron-forge make"
|
||||||
|
|
||||||
|
- name: Build the app for x64
|
||||||
|
run: "yarn electron-forge make --arch x64"
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: "ls -R out/make"
|
||||||
|
|
||||||
|
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: "out/make/*/*/*/*"
|
||||||
|
|
||||||
|
|
||||||
|
build-test-app-windows:
|
||||||
|
needs: [prepare-json-files]
|
||||||
|
runs-on: windows-2022
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
|
||||||
|
- name: Copy updated .json files
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
run: |
|
||||||
|
ls -l artifact
|
||||||
|
cp artifact/package.json package.json
|
||||||
|
|
||||||
|
- name: Sync node version and setup cache
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||||
|
|
||||||
|
- run: yarn install
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src/wasm-lib'
|
||||||
|
|
||||||
|
- name: Run build:wasm manually
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MODE: ${{ env.BUILD_RELEASE == 'true' && '--release' || '--debug' }}
|
||||||
|
run: |
|
||||||
|
mkdir src/wasm-lib/pkg; cd src/wasm-lib
|
||||||
|
echo "building with ${{ env.MODE }}"
|
||||||
|
npx wasm-pack build --target web --out-dir pkg ${{ env.MODE }}
|
||||||
|
cd ../../
|
||||||
|
cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
||||||
|
|
||||||
- name: Prepare certificate and variables (Windows only)
|
- name: Prepare certificate and variables (Windows only)
|
||||||
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'windows-2022' }}
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
|
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
|
||||||
cat /d/Certificate_pkcs12.p12
|
cat /d/Certificate_pkcs12.p12
|
||||||
@ -126,7 +168,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Setup certicate with SSM KSP (Windows only)
|
- name: Setup certicate with SSM KSP (Windows only)
|
||||||
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'windows-2022' }}
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi
|
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi
|
||||||
msiexec /i smtools-windows-x64.msi /quiet /qn
|
msiexec /i smtools-windows-x64.msi /quiet /qn
|
||||||
@ -136,47 +178,83 @@ jobs:
|
|||||||
smksp_cert_sync.exe
|
smksp_cert_sync.exe
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app for x64
|
||||||
run: yarn electron-builder --config ${{ env.BUILD_RELEASE && '--publish always' || '' }}
|
run: "yarn electron-forge make --arch x64"
|
||||||
|
|
||||||
- name: List artifacts in out/
|
- name: Build the app for arm64
|
||||||
run: ls -R out
|
run: "yarn electron-forge make --arch arm64"
|
||||||
|
|
||||||
- name: Prepare the tauri update bundles (macOS)
|
- name: List artifacts
|
||||||
if: ${{ env.BUILD_RELEASE && matrix.os == 'macos-14' }}
|
run: "ls -R out/make"
|
||||||
|
|
||||||
|
- name: Sign using Signtool
|
||||||
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
|
env:
|
||||||
|
THUMBPRINT: "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D"
|
||||||
|
X64_FILE: "D:\\a\\modeling-app\\modeling-app\\out\\make\\squirrel.windows\\x64\\Zoo Modeling App-*Setup.exe"
|
||||||
|
ARM64_FILE: "D:\\a\\modeling-app\\modeling-app\\out\\make\\squirrel.windows\\arm64\\Zoo Modeling App-*Setup.exe"
|
||||||
run: |
|
run: |
|
||||||
for ARCH in arm64 x64; do
|
signtool.exe sign /sha1 ${{ env.THUMBPRINT }} /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 "${{ env.X64_FILE }}"
|
||||||
TAURI_DIR=out/tauri/$VERSION/macos
|
signtool.exe verify /v /pa "${{ env.X64_FILE }}"
|
||||||
TEMP_DIR=temp/$ARCH
|
signtool.exe sign /sha1 ${{ env.THUMBPRINT }} /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 "${{ env.ARM64_FILE }}"
|
||||||
mkdir -p $TAURI_DIR
|
signtool.exe verify /v /pa "${{ env.ARM64_FILE }}"
|
||||||
mkdir -p $TEMP_DIR
|
|
||||||
unzip out/*-$ARCH-mac.zip -d $TEMP_DIR
|
|
||||||
tar -czvf "$TAURI_DIR/Zoo Modeling App-$ARCH.app.tar.gz" -C $TEMP_DIR "Zoo Modeling App.app"
|
|
||||||
yarn tauri signer sign "$TAURI_DIR/Zoo Modeling App-$ARCH.app.tar.gz"
|
|
||||||
done
|
|
||||||
ls -R out
|
|
||||||
|
|
||||||
- name: Prepare the tauri update bundles (Windows)
|
|
||||||
if: ${{ env.BUILD_RELEASE && matrix.os == 'windows-2022' }}
|
|
||||||
run: |
|
|
||||||
$env:TAURI_DIR="out/tauri/${env:VERSION}/nsis"
|
|
||||||
mkdir -p ${env:TAURI_DIR}
|
|
||||||
$env:OUT_FILE="${env:TAURI_DIR}/Zoo Modeling App_${env:VERSION_NO_V}_x64-setup.nsis.zip"
|
|
||||||
7z a -mm=Copy "${env:OUT_FILE}" ./out/*-x64-win.exe
|
|
||||||
yarn tauri signer sign "${env:OUT_FILE}"
|
|
||||||
ls -R out
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: out-${{ matrix.os }}
|
path: "out/make/*/*/*"
|
||||||
path: |
|
|
||||||
out/Zoo*.*
|
# TODO: Run e2e tests
|
||||||
out/latest*.yml
|
|
||||||
out/tauri
|
|
||||||
|
build-test-app-ubuntu:
|
||||||
|
needs: [prepare-json-files]
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
|
||||||
|
- name: Copy updated .json files
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
run: |
|
||||||
|
ls -l artifact
|
||||||
|
cp artifact/package.json package.json
|
||||||
|
|
||||||
|
- name: Sync node version and setup cache
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||||
|
|
||||||
|
- run: yarn install
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src/wasm-lib'
|
||||||
|
|
||||||
|
- name: Run build:wasm
|
||||||
|
run: "yarn build:wasm${{ env.BUILD_RELEASE == 'true' && '-dev' || ''}}"
|
||||||
|
|
||||||
|
- name: Build the app for arm64
|
||||||
|
run: "yarn electron-forge make --arch arm64"
|
||||||
|
|
||||||
|
- name: Build the app for x64
|
||||||
|
run: "yarn electron-forge make --arch x64"
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: "ls -R out/make"
|
||||||
|
|
||||||
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
||||||
|
|
||||||
# TODO: add the updater tests back
|
# TODO: sign the app (and updater bundle potentially)
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: "out/make/*/*/*"
|
||||||
|
|
||||||
|
|
||||||
publish-apps-release:
|
publish-apps-release:
|
||||||
@ -184,169 +262,123 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
||||||
needs: [prepare-files, build-apps]
|
needs: [prepare-json-files, build-test-app-macos, build-test-app-windows, build-test-app-ubuntu]
|
||||||
env:
|
env:
|
||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-json-files.outputs.version }}
|
||||||
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
VERSION: ${{ github.event_name == 'release' && format('v{0}', needs.prepare-json-files.outputs.version) || needs.prepare-json-files.outputs.version }}
|
||||||
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
||||||
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
|
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Nightly build, commit {0}', github.sha) }}
|
||||||
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app' }}
|
BUCKET_DIR: ${{ github.event_name == 'release' && 'dl.kittycad.io/releases/modeling-app' || 'dl.kittycad.io/releases/modeling-app/nightly' }}
|
||||||
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
WEBSITE_DIR: ${{ github.event_name == 'release' && 'dl.zoo.dev/releases/modeling-app' || 'dl.zoo.dev/releases/modeling-app/nightly' }}
|
||||||
BUCKET_DIR_TAURI: 'dl.kittycad.io/releases/modeling-app/tauri-compat'
|
|
||||||
WEBSITE_DIR_TAURI: 'dl.zoo.dev/releases/modeling-app/tauri-compat'
|
|
||||||
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
|
||||||
name: out-windows-2022
|
|
||||||
path: out
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- name: Generate the update static endpoint
|
||||||
with:
|
|
||||||
name: out-macos-14
|
|
||||||
path: out
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: out-ubuntu-22.04
|
|
||||||
path: out
|
|
||||||
|
|
||||||
- name: Generate the download static endpoint
|
|
||||||
run: |
|
run: |
|
||||||
RELEASE_DIR=https://${WEBSITE_DIR}
|
ls -l artifact/*/*oo*
|
||||||
|
DARWIN_SIG=`cat artifact/macos/*.app.tar.gz.sig`
|
||||||
|
WINDOWS_X86_64_SIG=`cat artifact/msi/*x64*.msi.zip.sig`
|
||||||
|
WINDOWS_AARCH64_SIG=`cat artifact/msi/*arm64*.msi.zip.sig`
|
||||||
|
RELEASE_DIR=https://${WEBSITE_DIR}/${VERSION}
|
||||||
jq --null-input \
|
jq --null-input \
|
||||||
--arg version "${VERSION}" \
|
--arg version "${VERSION}" \
|
||||||
--arg pub_date "${PUB_DATE}" \
|
--arg pub_date "${PUB_DATE}" \
|
||||||
--arg notes "${NOTES}" \
|
--arg notes "${NOTES}" \
|
||||||
--arg mac_arm64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-arm64-mac.dmg" \
|
--arg darwin_sig "$DARWIN_SIG" \
|
||||||
--arg mac_x64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-x64-mac.dmg" \
|
--arg darwin_url "$RELEASE_DIR/macos/${{ env.URL_CODED_NAME }}.app.tar.gz" \
|
||||||
--arg windows_arm64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-arm64-win.msi" \
|
--arg windows_x86_64_sig "$WINDOWS_X86_64_SIG" \
|
||||||
--arg windows_x64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-x64-win.msi" \
|
--arg windows_x86_64_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_x64_en-US.msi.zip" \
|
||||||
--arg linux_arm64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-arm64-linux.AppImage" \
|
--arg windows_aarch64_sig "$WINDOWS_AARCH64_SIG" \
|
||||||
--arg linux_x64_url "$RELEASE_DIR/${{ env.URL_CODED_NAME }}-${VERSION_NO_V}-x86_64-linux.AppImage" \
|
--arg windows_aarch64_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_arm64_en-US.msi.zip" \
|
||||||
'{
|
|
||||||
"version": $version,
|
|
||||||
"pub_date": $pub_date,
|
|
||||||
"notes": $notes,
|
|
||||||
"platforms": {
|
|
||||||
"dmg-arm64": {
|
|
||||||
"url": $mac_arm64_url
|
|
||||||
},
|
|
||||||
"dmg-x64": {
|
|
||||||
"url": $mac_x64_url
|
|
||||||
},
|
|
||||||
"msi-arm64": {
|
|
||||||
"url": $windows_arm64_url
|
|
||||||
},
|
|
||||||
"msi-x64": {
|
|
||||||
"url": $windows_x64_url
|
|
||||||
},
|
|
||||||
"appimage-arm64": {
|
|
||||||
"url": $linux_arm64_url
|
|
||||||
},
|
|
||||||
"appimage-x64": {
|
|
||||||
"url": $linux_x64_url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' > last_download.json
|
|
||||||
cat last_download.json
|
|
||||||
|
|
||||||
- name: Generate the update static endpoint for tauri
|
|
||||||
run: |
|
|
||||||
TAURI_DIR=out/tauri/$VERSION
|
|
||||||
MAC_ARM64_SIG=`cat $TAURI_DIR/macos/*-arm64.app.tar.gz.sig`
|
|
||||||
MAC_X64_SIG=`cat $TAURI_DIR/macos/*-x64.app.tar.gz.sig`
|
|
||||||
WINDOWS_SIG=`cat $TAURI_DIR/nsis/*.nsis.zip.sig`
|
|
||||||
RELEASE_DIR=https://${WEBSITE_DIR_TAURI}/${VERSION}
|
|
||||||
jq --null-input \
|
|
||||||
--arg version "${VERSION}" \
|
|
||||||
--arg pub_date "${PUB_DATE}" \
|
|
||||||
--arg notes "${NOTES}" \
|
|
||||||
--arg mac_arm64_sig "$MAC_ARM64_SIG" \
|
|
||||||
--arg mac_arm64_url "$RELEASE_DIR/macos/${{ env.URL_CODED_NAME }}-arm64.app.tar.gz" \
|
|
||||||
--arg mac_x64_sig "$MAC_X64_SIG" \
|
|
||||||
--arg mac_x64_url "$RELEASE_DIR/macos/${{ env.URL_CODED_NAME }}-x64.app.tar.gz" \
|
|
||||||
--arg windows_sig "$WINDOWS_SIG" \
|
|
||||||
--arg windows_url "$RELEASE_DIR/nsis/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_x64-setup.nsis.zip" \
|
|
||||||
'{
|
'{
|
||||||
"version": $version,
|
"version": $version,
|
||||||
"pub_date": $pub_date,
|
"pub_date": $pub_date,
|
||||||
"notes": $notes,
|
"notes": $notes,
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"darwin-x86_64": {
|
"darwin-x86_64": {
|
||||||
"signature": $mac_x64_sig,
|
"signature": $darwin_sig,
|
||||||
"url": $mac_x64_url
|
"url": $darwin_url
|
||||||
},
|
},
|
||||||
"darwin-aarch64": {
|
"darwin-aarch64": {
|
||||||
"signature": $mac_arm64_sig,
|
"signature": $darwin_sig,
|
||||||
"url": $mac_arm64_url
|
"url": $darwin_url
|
||||||
},
|
},
|
||||||
"windows-x86_64": {
|
"windows-x86_64": {
|
||||||
"signature": $windows_sig,
|
"signature": $windows_x86_64_sig,
|
||||||
"url": $windows_url
|
"url": $windows_x86_64_url
|
||||||
|
},
|
||||||
|
"windows-aarch64": {
|
||||||
|
"signature": $windows_aarch64_sig,
|
||||||
|
"url": $windows_aarch64_url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' > last_update.json
|
}' > last_update.json
|
||||||
cat last_update.json
|
cat last_update.json
|
||||||
|
|
||||||
- name: List artifacts
|
- name: Generate the download static endpoint
|
||||||
run: "ls -R out"
|
run: |
|
||||||
|
RELEASE_DIR=https://${WEBSITE_DIR}/${VERSION}
|
||||||
|
jq --null-input \
|
||||||
|
--arg version "${VERSION}" \
|
||||||
|
--arg pub_date "${PUB_DATE}" \
|
||||||
|
--arg notes "${NOTES}" \
|
||||||
|
--arg darwin_url "$RELEASE_DIR/dmg/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_universal.dmg" \
|
||||||
|
--arg windows_x86_64_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_x64_en-US.msi" \
|
||||||
|
--arg windows_aarch64_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_arm64_en-US.msi" \
|
||||||
|
'{
|
||||||
|
"version": $version,
|
||||||
|
"pub_date": $pub_date,
|
||||||
|
"notes": $notes,
|
||||||
|
"platforms": {
|
||||||
|
"dmg-universal": {
|
||||||
|
"url": $darwin_url
|
||||||
|
},
|
||||||
|
"msi-x86_64": {
|
||||||
|
"url": $windows_x86_64_url
|
||||||
|
},
|
||||||
|
"msi-aarch64": {
|
||||||
|
"url": $windows_aarch64_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' > last_download.json
|
||||||
|
cat last_download.json
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.1.5'
|
uses: 'google-github-actions/auth@v2.1.3'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
- name: Set up Google Cloud SDK
|
- name: Set up Google Cloud SDK
|
||||||
uses: google-github-actions/setup-gcloud@v2.1.0
|
uses: google-github-actions/setup-gcloud@v2.1.0
|
||||||
with:
|
with:
|
||||||
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
project_id: kittycadapi
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
with:
|
with:
|
||||||
path: out
|
path: artifact
|
||||||
glob: 'Zoo*'
|
|
||||||
parent: false
|
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
|
||||||
with:
|
|
||||||
path: out
|
|
||||||
glob: 'latest*'
|
|
||||||
parent: false
|
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
|
||||||
with:
|
|
||||||
path: last_download.json
|
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
|
||||||
|
|
||||||
- name: Upload release files to public bucket for tauri
|
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
|
||||||
with:
|
|
||||||
path: "out/tauri/${{ env.VERSION }}"
|
|
||||||
glob: '*/Zoo*'
|
glob: '*/Zoo*'
|
||||||
parent: false
|
parent: false
|
||||||
destination: ${{ env.BUCKET_DIR_TAURI }}/${{ env.VERSION }}
|
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket for tauri
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
with:
|
with:
|
||||||
path: last_update.json
|
path: last_update.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
|
- name: Upload download endpoint to public bucket
|
||||||
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
|
with:
|
||||||
|
path: last_download.json
|
||||||
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
- name: Upload release files to Github
|
- name: Upload release files to Github
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: 'out/Zoo*'
|
files: 'artifact/*/Zoo*'
|
||||||
|
|
||||||
# TODO: Add GitHub publisher
|
|
||||||
|
|
||||||
announce_release:
|
announce_release:
|
||||||
needs: [publish-apps-release]
|
needs: [publish-apps-release]
|
||||||
|
2
.github/workflows/build-test-web.yml
vendored
@ -44,8 +44,6 @@ jobs:
|
|||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
- run: yarn xstate:typegen
|
- run: yarn xstate:typegen
|
||||||
- run: yarn tsc
|
- run: yarn tsc
|
||||||
- name: Lint
|
|
||||||
run: yarn eslint --max-warnings 0 src e2e
|
|
||||||
|
|
||||||
|
|
||||||
check-typos:
|
check-typos:
|
||||||
|
2
.github/workflows/cargo-check.yml
vendored
@ -37,4 +37,4 @@ jobs:
|
|||||||
# We specifically want to test the disable-println feature
|
# We specifically want to test the disable-println feature
|
||||||
# Since it is not enabled by default, we need to specify it
|
# Since it is not enabled by default, we need to specify it
|
||||||
# This is used in kcl-lsp
|
# This is used in kcl-lsp
|
||||||
cargo check --all --features disable-println --features pyo3 --features cli
|
cargo check --all --features disable-println --features pyo3
|
||||||
|
7
.github/workflows/cargo-test.yml
vendored
@ -7,7 +7,6 @@ on:
|
|||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
- 'src/wasm-lib/**.kcl'
|
|
||||||
- .github/workflows/cargo-test.yml
|
- .github/workflows/cargo-test.yml
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -16,7 +15,6 @@ on:
|
|||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
- 'src/wasm-lib/**.kcl'
|
|
||||||
- .github/workflows/cargo-test.yml
|
- .github/workflows/cargo-test.yml
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
@ -38,6 +36,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
|
- name: install dependencies
|
||||||
|
if: matrix.dir == 'src-tauri'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
- name: Install vector
|
- name: Install vector
|
||||||
run: |
|
run: |
|
||||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||||
|
31
.github/workflows/label-issues.yml
vendored
@ -1,31 +0,0 @@
|
|||||||
name: Label Issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
jobs:
|
|
||||||
label:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check if issue opener is ZooSpiritWolf
|
|
||||||
id: check_opener
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const issueOpener = context.payload.issue.user.login;
|
|
||||||
return issueOpener === 'ZooSpiritWolf';
|
|
||||||
|
|
||||||
- name: Add labels
|
|
||||||
if: steps.check_opener.outputs.result == 'true'
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
github.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.payload.issue.number,
|
|
||||||
labels: ['bug', 'regression', 'high-priority']
|
|
||||||
});
|
|
22
.github/workflows/playwright.yml
vendored
@ -139,7 +139,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@ -174,14 +174,14 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: steps.git-check.outputs.modified == 'true'
|
if: steps.git-check.outputs.modified == 'true'
|
||||||
with:
|
with:
|
||||||
name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run playwright/chrome flow (with retries)
|
- name: Run playwright/chrome flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -244,14 +244,14 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@ -262,7 +262,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-14]
|
os: [ubuntu-latest, windows-latest, macos-14]
|
||||||
timeout-minutes: 40
|
timeout-minutes: 30
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
@ -346,12 +346,12 @@ jobs:
|
|||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build electron
|
- name: build electron
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn tron:package
|
run: yarn electron:package
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.os }}-${{ github.sha }}
|
name: test-results-ubuntu-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run electron tests (with retries)
|
- name: Run electron tests (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -381,7 +381,7 @@ jobs:
|
|||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
if [[ "$IS_UBUNTU" == "true" ]]; then
|
if [[ "$IS_UBUNTU" == "true" ]]; then
|
||||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --last-failed --grep=@electron || true
|
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
|
||||||
else
|
else
|
||||||
yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
|
yarn playwright test --config=playwright.electron.config.ts --grep=@electron || true
|
||||||
fi
|
fi
|
||||||
@ -423,14 +423,14 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
with:
|
with:
|
||||||
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
name: test-results-electron-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-electron-${{ matrix.os }}-${{ github.sha }}
|
name: playwright-report-electron-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
6
.gitignore
vendored
@ -54,15 +54,19 @@ e2e/playwright/export-snapshots/*
|
|||||||
|
|
||||||
## generated files
|
## generated files
|
||||||
src/**/*.typegen.ts
|
src/**/*.typegen.ts
|
||||||
|
src-tauri/gen
|
||||||
|
|
||||||
src/wasm-lib/grackle/stdlib_cube_partial.json
|
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||||
Mac_App_Distribution.provisionprofile
|
Mac_App_Distribution.provisionprofile
|
||||||
|
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
src/wasm-lib/pkg
|
|
||||||
|
|
||||||
venv
|
venv
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
# electron
|
# electron
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
src-tauri/target
|
||||||
|
electron-test-projects-dir
|
||||||
|
electron-test-projects-dir-2
|
||||||
|
344
Info.plist
@ -1,344 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>KCL</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Owner</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.toml</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>TOML</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.gltf</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>glTF</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.glb</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>glb</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.step</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>STEP</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.fbx</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>FBX</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.sldprt</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Solidworks Part</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.geometry-definition-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>OBJ</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.polygon-file-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>PLY</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.standard-tesselated-geometry-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>STL</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.folder</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Folders</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>UTExportedTypeDeclarations</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.kcl</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://zoo.dev/docs/kcl</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.source-code</string>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.script</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>KCL (KittyCAD Language) document</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/vnd.zoo.kcl</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.gltf</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.json</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Graphics Library Transmission Format (glTF)</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>gltf</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/gltf+json</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.glb</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Graphics Library Transmission Format (glTF) binary</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>glb</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/gltf-binary</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.step</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>STEP-file, ISO 10303-21</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>step</string>
|
|
||||||
<string>stp</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/step</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.sldprt</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://docs.fileformat.com/cad/sldprt/</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Solidworks Part</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>sldprt</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/vnd.solidworks.sldprt</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.fbx</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://en.wikipedia.org/wiki/FBX</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Autodesk Filmbox (FBX) format</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>fbx</string>
|
|
||||||
<string>fbxb</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/vnd.autodesk.fbx</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.toml</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://toml.io/en/</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Tom's Obvious Minimal Language</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/toml</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
8
Makefile
@ -7,14 +7,6 @@ XSTATE_TYPEGENS := $(wildcard src/machines/*.typegen.ts)
|
|||||||
dev: node_modules public/wasm_lib_bg.wasm $(XSTATE_TYPEGENS)
|
dev: node_modules public/wasm_lib_bg.wasm $(XSTATE_TYPEGENS)
|
||||||
yarn start
|
yarn start
|
||||||
|
|
||||||
# I'm sorry this is so specific to my setup you may as well ignore this.
|
|
||||||
# This is so you don't have to deal with electron windows popping up constantly.
|
|
||||||
# It should work for you other Linux users.
|
|
||||||
lee-electron-test:
|
|
||||||
Xephyr -br -ac -noreset -screen 1200x500 :2 &
|
|
||||||
DISPLAY=:2 NODE_ENV=development PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn tron:test -g "when using the file tree"
|
|
||||||
killall Xephyr
|
|
||||||
|
|
||||||
$(XSTATE_TYPEGENS): $(TS_SRC)
|
$(XSTATE_TYPEGENS): $(TS_SRC)
|
||||||
yarn xstate typegen 'src/**/*.ts?(x)'
|
yarn xstate typegen 'src/**/*.ts?(x)'
|
||||||
|
|
||||||
|
33
README.md
@ -101,7 +101,7 @@ This will start the application and hot-reload on changed.
|
|||||||
|
|
||||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn electron:package`.
|
||||||
|
|
||||||
## Checking out commits / Bisecting
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
@ -110,6 +110,7 @@ Which commands from setup are one off vs need to be run every time?
|
|||||||
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
||||||
```bash
|
```bash
|
||||||
yarn install
|
yarn install
|
||||||
|
yarn wasm-prep
|
||||||
yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
|
yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
|
||||||
yarn start # or yarn build:local && yarn serve for slower but more production-like build
|
yarn start # or yarn build:local && yarn serve for slower but more production-like build
|
||||||
```
|
```
|
||||||
@ -188,22 +189,12 @@ For more information on fuzzing you can check out
|
|||||||
|
|
||||||
### Playwright tests
|
### Playwright tests
|
||||||
|
|
||||||
You will need a `./e2e/playwright/playwright-secrets.env` file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ touch ./e2e/playwright/playwright-secrets.env
|
|
||||||
$ cat ./e2e/playwright/playwright-secrets.env
|
|
||||||
token=<dev.zoo.dev/account/api-tokens>
|
|
||||||
snapshottoken=<your-snapshot-token>
|
|
||||||
```
|
|
||||||
|
|
||||||
For a portable way to run Playwright you'll need Docker.
|
For a portable way to run Playwright you'll need Docker.
|
||||||
|
|
||||||
#### Generic example
|
|
||||||
After that, open a terminal and run:
|
After that, open a terminal and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --network host --rm --init -it playwright/chrome:playwright-x.xx.x
|
docker run --network host --rm --init -it playwright/chrome:playwright-1.43.1
|
||||||
```
|
```
|
||||||
|
|
||||||
and in another terminal, run:
|
and in another terminal, run:
|
||||||
@ -212,27 +203,21 @@ and in another terminal, run:
|
|||||||
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" <test suite>
|
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" <test suite>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
An example of a `<test suite>` is: `e2e/playwright/flow-tests.spec.ts`
|
||||||
|
|
||||||
#### Specific example
|
YOU WILL NEED A PLAYWRIGHT-SECRETS.ENV FILE:
|
||||||
|
|
||||||
open a terminal and run:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --network host --rm --init -it playwright/chrome:playwright-1.46.0
|
# ./e2e/playwright/playwright-secrets.env
|
||||||
```
|
token=<your-token>
|
||||||
|
snapshottoken=<your-snapshot-token>
|
||||||
and in another terminal, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" e2e/playwright/command-bar-tests.spec.ts
|
|
||||||
```
|
```
|
||||||
|
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
|
||||||
|
|
||||||
run a specific test change the test from `test('...` to `test.only('...`
|
run a specific test change the test from `test('...` to `test.only('...`
|
||||||
(note if you commit this, the tests will instantly fail without running any of the tests)
|
(note if you commit this, the tests will instantly fail without running any of the tests)
|
||||||
|
|
||||||
|
|
||||||
**Gotcha**: running the docker container with a mismatched image against your `./node_modules/playwright` will cause a failure. Make sure the versions are matched and up to date.
|
|
||||||
|
|
||||||
run headed
|
run headed
|
||||||
|
|
||||||
```
|
```
|
||||||
|
24
add-osx-cert.sh
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# From https://dev.to/rwwagner90/signing-electron-apps-with-github-actions-4cof
|
||||||
|
|
||||||
|
KEY_CHAIN=build.keychain
|
||||||
|
CERTIFICATE_P12=certificate.p12
|
||||||
|
|
||||||
|
# Recreate the certificate from the secure environment variable
|
||||||
|
echo $APPLE_CERTIFICATE | base64 --decode > $CERTIFICATE_P12
|
||||||
|
|
||||||
|
#create a keychain
|
||||||
|
security create-keychain -p actions $KEY_CHAIN
|
||||||
|
|
||||||
|
# Make the keychain the default so identities are found
|
||||||
|
security default-keychain -s $KEY_CHAIN
|
||||||
|
|
||||||
|
# Unlock the keychain
|
||||||
|
security unlock-keychain -p actions $KEY_CHAIN
|
||||||
|
|
||||||
|
security import $CERTIFICATE_P12 -k $KEY_CHAIN -P $APPLE_CERTIFICATE_PASSWORD -T /usr/bin/codesign;
|
||||||
|
|
||||||
|
security set-key-partition-list -S apple-tool:,apple: -s -k actions $KEY_CHAIN
|
||||||
|
|
||||||
|
# remove certs
|
||||||
|
rm -fr *.p12
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
BIN
assets/icon.icns
BIN
assets/icon.ico
Before Width: | Height: | Size: 183 KiB |
@ -32,21 +32,17 @@ layout: manual
|
|||||||
* [`chamfer`](kcl/chamfer)
|
* [`chamfer`](kcl/chamfer)
|
||||||
* [`circle`](kcl/circle)
|
* [`circle`](kcl/circle)
|
||||||
* [`close`](kcl/close)
|
* [`close`](kcl/close)
|
||||||
* [`cm`](kcl/cm)
|
|
||||||
* [`cos`](kcl/cos)
|
* [`cos`](kcl/cos)
|
||||||
* [`e`](kcl/e)
|
* [`e`](kcl/e)
|
||||||
* [`extrude`](kcl/extrude)
|
* [`extrude`](kcl/extrude)
|
||||||
* [`fillet`](kcl/fillet)
|
* [`fillet`](kcl/fillet)
|
||||||
* [`floor`](kcl/floor)
|
* [`floor`](kcl/floor)
|
||||||
* [`ft`](kcl/ft)
|
|
||||||
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
* [`inch`](kcl/inch)
|
|
||||||
* [`int`](kcl/int)
|
* [`int`](kcl/int)
|
||||||
* [`lastSegX`](kcl/lastSegX)
|
* [`lastSegX`](kcl/lastSegX)
|
||||||
* [`lastSegY`](kcl/lastSegY)
|
* [`lastSegY`](kcl/lastSegY)
|
||||||
@ -56,15 +52,11 @@ layout: manual
|
|||||||
* [`line`](kcl/line)
|
* [`line`](kcl/line)
|
||||||
* [`lineTo`](kcl/lineTo)
|
* [`lineTo`](kcl/lineTo)
|
||||||
* [`ln`](kcl/ln)
|
* [`ln`](kcl/ln)
|
||||||
* [`loft`](kcl/loft)
|
|
||||||
* [`log`](kcl/log)
|
* [`log`](kcl/log)
|
||||||
* [`log10`](kcl/log10)
|
* [`log10`](kcl/log10)
|
||||||
* [`log2`](kcl/log2)
|
* [`log2`](kcl/log2)
|
||||||
* [`m`](kcl/m)
|
|
||||||
* [`max`](kcl/max)
|
* [`max`](kcl/max)
|
||||||
* [`min`](kcl/min)
|
* [`min`](kcl/min)
|
||||||
* [`mm`](kcl/mm)
|
|
||||||
* [`offsetPlane`](kcl/offsetPlane)
|
|
||||||
* [`patternCircular2d`](kcl/patternCircular2d)
|
* [`patternCircular2d`](kcl/patternCircular2d)
|
||||||
* [`patternCircular3d`](kcl/patternCircular3d)
|
* [`patternCircular3d`](kcl/patternCircular3d)
|
||||||
* [`patternLinear2d`](kcl/patternLinear2d)
|
* [`patternLinear2d`](kcl/patternLinear2d)
|
||||||
@ -90,7 +82,6 @@ layout: manual
|
|||||||
* [`tan`](kcl/tan)
|
* [`tan`](kcl/tan)
|
||||||
* [`tangentialArc`](kcl/tangentialArc)
|
* [`tangentialArc`](kcl/tangentialArc)
|
||||||
* [`tangentialArcTo`](kcl/tangentialArcTo)
|
* [`tangentialArcTo`](kcl/tangentialArcTo)
|
||||||
* [`tangentialArcToRelative`](kcl/tangentialArcToRelative)
|
|
||||||
* [`tau`](kcl/tau)
|
* [`tau`](kcl/tau)
|
||||||
* [`toDegrees`](kcl/toDegrees)
|
* [`toDegrees`](kcl/toDegrees)
|
||||||
* [`toRadians`](kcl/toRadians)
|
* [`toRadians`](kcl/toRadians)
|
||||||
@ -98,4 +89,3 @@ layout: manual
|
|||||||
* [`xLineTo`](kcl/xLineTo)
|
* [`xLineTo`](kcl/xLineTo)
|
||||||
* [`yLine`](kcl/yLine)
|
* [`yLine`](kcl/yLine)
|
||||||
* [`yLineTo`](kcl/yLineTo)
|
* [`yLineTo`](kcl/yLineTo)
|
||||||
* [`yd`](kcl/yd)
|
|
||||||
|
477
docs/kcl/loft.md
16964
docs/kcl/std.json
@ -1,66 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
|
||||||
|
|
||||||
import { setupElectron, tearDown } from './test-utils'
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('Electron app header tests', () => {
|
|
||||||
test(
|
|
||||||
'Open Command Palette button has correct shortcut',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
// No space before the shortcut since it checks textContent.
|
|
||||||
let text
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'darwin':
|
|
||||||
text = 'Commands⌘K'
|
|
||||||
break
|
|
||||||
case 'win32':
|
|
||||||
text = 'CommandsCtrl+K'
|
|
||||||
break
|
|
||||||
default: // 'linux' etc.
|
|
||||||
text = 'CommandsCtrl+K'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
|
||||||
await expect(commandsButton).toBeVisible()
|
|
||||||
await expect(commandsButton).toHaveText(text)
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'User settings has correct shortcut',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
// Open the user sidebar menu.
|
|
||||||
await page.getByTestId('user-sidebar-toggle').click()
|
|
||||||
|
|
||||||
// No space after "User settings" since it's textContent.
|
|
||||||
const text =
|
|
||||||
process.platform === 'darwin' ? 'User settings⌘,' : 'User settingsCtrl,'
|
|
||||||
const userSettingsButton = page.getByTestId('user-settings')
|
|
||||||
await expect(userSettingsButton).toBeVisible()
|
|
||||||
await expect(userSettingsButton).toHaveText(text)
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
@ -96,49 +96,33 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
const btnLine = page.getByTestId('line')
|
await page.getByTestId('line').click()
|
||||||
const btnLineAriaPressed = await btnLine.getAttribute('aria-pressed')
|
|
||||||
if (btnLineAriaPressed === 'true') {
|
|
||||||
await btnLine.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0)
|
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => u.getGreatestPixDiff(line1, TEST_COLORS.WHITE))
|
.poll(async () => u.getGreatestPixDiff(line1, TEST_COLORS.WHITE))
|
||||||
.toBeLessThan(3)
|
.toBeLessThan(3)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => u.getGreatestPixDiff(line1, [249, 249, 249]))
|
.poll(() => u.getGreatestPixDiff(line1, [249, 249, 249]))
|
||||||
.toBeLessThan(3)
|
.toBeLessThan(3)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// click between first two clicks to get center of the line
|
// click between first two clicks to get center of the line
|
||||||
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||||
await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)
|
|
||||||
).toBeLessThan(3)
|
|
||||||
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hold down shift
|
// hold down shift
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// click between the latest two clicks to get center of the line
|
// click between the latest two clicks to get center of the line
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// selected two lines therefore there should be two cursors
|
// selected two lines therefore there should be two cursors
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Length: open menu' }).click()
|
await page.getByRole('button', { name: 'Length: open menu' }).click()
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
getUtils,
|
|
||||||
setup,
|
|
||||||
setupElectron,
|
|
||||||
tearDown,
|
|
||||||
executorInputPath,
|
|
||||||
} from './test-utils'
|
|
||||||
import { join } from 'path'
|
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
import fsp from 'fs/promises'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -27,19 +19,9 @@ test.describe('Code pane and errors', () => {
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await page.addInitScript(() => {
|
await page.addInitScript((code) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem('persistCode', code)
|
||||||
'persistCode',
|
}, bracket)
|
||||||
`// Extruded Triangle
|
|
||||||
const sketch001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([10, 0], %)
|
|
||||||
|> line([-5, 10], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
const extrude001 = extrude(5, sketch001)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
@ -101,7 +83,7 @@ const extrude001 = extrude(5, sketch001)`
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
// Close the code pane
|
// Close the code pane
|
||||||
await codePaneButton.click()
|
await codePaneButton.click()
|
||||||
@ -124,7 +106,7 @@ const extrude001 = extrude(5, sketch001)`
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('When error is not in view you can click the badge to scroll to it', async ({
|
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||||
@ -235,79 +217,3 @@ const extrude001 = extrude(5, sketch001)`
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
|
||||||
'Opening multiple panes persists when switching projects',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
// Setup multiple projects.
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await Promise.all([
|
|
||||||
fsp.mkdir(routerTemplateDir, { recursive: true }),
|
|
||||||
fsp.mkdir(bracketDir, { recursive: true }),
|
|
||||||
])
|
|
||||||
await Promise.all([
|
|
||||||
fsp.copyFile(
|
|
||||||
executorInputPath('router-template-slate.kcl'),
|
|
||||||
join(routerTemplateDir, 'main.kcl')
|
|
||||||
),
|
|
||||||
fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load', async () => {
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
})
|
|
||||||
|
|
||||||
// If they're open by default, we're not actually testing anything.
|
|
||||||
await test.step('Pre-condition: panes are not already visible', async () => {
|
|
||||||
await expect(page.locator('#variables-pane')).not.toBeVisible()
|
|
||||||
await expect(page.locator('#logs-pane')).not.toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Open multiple panes', async () => {
|
|
||||||
await u.openKclCodePanel()
|
|
||||||
await u.openVariablesPane()
|
|
||||||
await u.openLogsPane()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
|
||||||
await page.getByTestId('app-logo').click()
|
|
||||||
|
|
||||||
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
|
||||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
|
||||||
await expect(page.getByText('New Project')).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Opening the router-template project should load', async () => {
|
|
||||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('router-template-slate').click()
|
|
||||||
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('All panes opened before should be visible', async () => {
|
|
||||||
await expect(page.locator('#code-pane')).toBeVisible()
|
|
||||||
await expect(page.locator('#variables-pane')).toBeVisible()
|
|
||||||
await expect(page.locator('#logs-pane')).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
@ -12,47 +12,50 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Command bar tests', () => {
|
test.describe('Command bar tests', () => {
|
||||||
test('Extrude from command bar selects extrude line after', async ({
|
// TODO fixme: enter is not working in the command bar
|
||||||
page,
|
test.fixme(
|
||||||
}) => {
|
'Extrude from command bar selects extrude line after',
|
||||||
await page.addInitScript(async () => {
|
async ({ page }) => {
|
||||||
localStorage.setItem(
|
await page.addInitScript(async () => {
|
||||||
'persistCode',
|
localStorage.setItem(
|
||||||
`const sketch001 = startSketchOn('XY')
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> xLine(-20, %)
|
|> xLine(-20, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click the line of code for xLine.
|
||||||
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const u = await getUtils(page)
|
// TODO fixme: enter is not working in the command bar
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
test.fixme('Fillet from command bar', async ({ page }) => {
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// Click the line of code for xLine.
|
|
||||||
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
|
||||||
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Fillet from command bar', async ({ page }) => {
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -124,7 +127,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await expect(cmdSearchBar).not.toBeVisible()
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('ControlOrMeta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
await expect(cmdSearchBar).toBeFocused()
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
@ -185,7 +188,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('ControlOrMeta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
@ -250,7 +253,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await page.keyboard.press('ControlOrMeta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
// Search for extrude command and choose it
|
// Search for extrude command and choose it
|
||||||
|
@ -9,7 +9,6 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
test.describe('Copilot ghost text', () => {
|
test.describe('Copilot ghost text', () => {
|
||||||
// eslint-disable-next-line jest/valid-title
|
|
||||||
test.skip(true, 'Needs to get covered again')
|
test.skip(true, 'Needs to get covered again')
|
||||||
|
|
||||||
test('completes code in empty file', async ({ page }) => {
|
test('completes code in empty file', async ({ page }) => {
|
||||||
@ -332,6 +331,7 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -348,10 +348,10 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Going elsewhere in the code should hide the ghost text.
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
@ -367,6 +367,8 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -379,17 +381,17 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
// Ctrl+z
|
// Ctrl+z
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
// Ctrl+shift+z
|
// Ctrl+shift+z
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
@ -408,14 +410,14 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Once for the enter.
|
// Once for the enter.
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
// Once for the text.
|
// Once for the text.
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
@ -1,196 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
|
||||||
import { join } from 'path'
|
|
||||||
import {
|
|
||||||
getUtils,
|
|
||||||
setupElectron,
|
|
||||||
tearDown,
|
|
||||||
executorInputPath,
|
|
||||||
} from './test-utils'
|
|
||||||
import fsp from 'fs/promises'
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test(
|
|
||||||
'export works on the first try',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
|
||||||
await Promise.all([
|
|
||||||
fsp.copyFile(
|
|
||||||
executorInputPath('router-template-slate.kcl'),
|
|
||||||
join(bracketDir, 'other.kcl')
|
|
||||||
),
|
|
||||||
fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
),
|
|
||||||
])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await test.step('on open of project', async () => {
|
|
||||||
await expect(page.getByText(`bracket`)).toBeVisible()
|
|
||||||
|
|
||||||
// open the project
|
|
||||||
await page.getByText(`bracket`).click()
|
|
||||||
|
|
||||||
// expect zero errors in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
||||||
|
|
||||||
// export the model
|
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
|
||||||
await expect(exportButton).toBeVisible()
|
|
||||||
|
|
||||||
// Wait for the model to finish loading
|
|
||||||
const modelStateIndicator = page.getByTestId(
|
|
||||||
'model-state-indicator-execution-done'
|
|
||||||
)
|
|
||||||
const modelStateIndicatorLoading = page.getByTestId(
|
|
||||||
'model-state-indicator-loading'
|
|
||||||
)
|
|
||||||
|
|
||||||
await expect(modelStateIndicatorLoading).toBeVisible()
|
|
||||||
await expect(modelStateIndicator).toBeVisible({ timeout: 60000 })
|
|
||||||
|
|
||||||
const gltfOption = page.getByText('glTF')
|
|
||||||
const submitButton = page.getByText('Confirm Export')
|
|
||||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
|
||||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
|
||||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
|
||||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
|
||||||
|
|
||||||
// Click the export button
|
|
||||||
await exportButton.click()
|
|
||||||
|
|
||||||
await expect(gltfOption).toBeVisible()
|
|
||||||
await expect(page.getByText('STL')).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Click the checkbox
|
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
|
||||||
// Look out for the toast message
|
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
|
||||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
// Expect it to succeed.
|
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
|
||||||
await expect(successToastMessage).toBeVisible()
|
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
await test.step('Check the export size', async () => {
|
|
||||||
await expect
|
|
||||||
.poll(
|
|
||||||
async () => {
|
|
||||||
try {
|
|
||||||
const outputGltf = await fsp.readFile('output.gltf')
|
|
||||||
return outputGltf.byteLength
|
|
||||||
} catch (e) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ timeout: 15_000 }
|
|
||||||
)
|
|
||||||
.toBe(477481)
|
|
||||||
|
|
||||||
// clean up output.gltf
|
|
||||||
await fsp.rm('output.gltf')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('on open of file in file pane', async () => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await u.openFilePanel()
|
|
||||||
|
|
||||||
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
|
||||||
|
|
||||||
// Click the file
|
|
||||||
await otherKclButton.click()
|
|
||||||
|
|
||||||
// Close the file pane
|
|
||||||
await u.closeFilePanel()
|
|
||||||
|
|
||||||
// wait for it to finish executing (todo: make this more robust)
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
// expect zero errors in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
||||||
|
|
||||||
// export the model
|
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
|
||||||
await expect(exportButton).toBeVisible()
|
|
||||||
|
|
||||||
const gltfOption = page.getByText('glTF')
|
|
||||||
const submitButton = page.getByText('Confirm Export')
|
|
||||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
|
||||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
|
||||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
|
||||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
|
||||||
|
|
||||||
// Click the export button
|
|
||||||
await exportButton.click()
|
|
||||||
|
|
||||||
await expect(gltfOption).toBeVisible()
|
|
||||||
await expect(page.getByText('STL')).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Click the checkbox
|
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
|
||||||
// Look out for the toast message
|
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
|
||||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
// Expect it to succeed.
|
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
|
||||||
await expect(successToastMessage).toBeVisible()
|
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
|
||||||
|
|
||||||
await test.step('Check the export size', async () => {
|
|
||||||
await expect
|
|
||||||
.poll(
|
|
||||||
async () => {
|
|
||||||
try {
|
|
||||||
const outputGltf = await fsp.readFile('output.gltf')
|
|
||||||
return outputGltf.byteLength
|
|
||||||
} catch (e) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ timeout: 15_000 }
|
|
||||||
)
|
|
||||||
.toBe(105022)
|
|
||||||
|
|
||||||
// clean up output.gltf
|
|
||||||
await fsp.rm('output.gltf')
|
|
||||||
})
|
|
||||||
await electronApp.close()
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
@ -16,6 +16,7 @@ test.describe('Editor tests', () => {
|
|||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -28,9 +29,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> line([-20, 0], %)
|
|> line([-20, 0], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
|
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -41,9 +42,9 @@ test.describe('Editor tests', () => {
|
|||||||
// |> close(%)`)
|
// |> close(%)`)
|
||||||
|
|
||||||
// uncomment the code
|
// uncomment the code
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down(CtrlKey)
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -84,63 +85,6 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
// check no error to begin with
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
|
||||||
|
|
||||||
await u.codeLocator.click()
|
|
||||||
await page.keyboard.type(`const sketch_001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> line([-20, 0], %)
|
|
||||||
|> close(%)`)
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-info')
|
|
||||||
await expect(
|
|
||||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
await page.locator('#code-pane button:first-child').click()
|
|
||||||
await page.locator('button:has-text("Format code")').click()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
|
||||||
.toHaveText(`const sketch_001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> line([-20, 0], %)
|
|
||||||
|> close(%)`)
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-info')
|
|
||||||
await expect(
|
|
||||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('fold gutters work', async ({ page }) => {
|
test('fold gutters work', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -204,7 +148,9 @@ test.describe('Editor tests', () => {
|
|||||||
// Delete all the code.
|
// Delete all the code.
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
// Select all
|
// Select all
|
||||||
await page.keyboard.press('ControlOrMeta+A')
|
await page.keyboard.press('Control+A')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.keyboard.press('Meta+A')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -298,67 +244,6 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.addInitScript(async () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`const sketch_001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> line([-20, 0], %)
|
|
||||||
|> close(%)`
|
|
||||||
)
|
|
||||||
localStorage.setItem('disableAxis', 'true')
|
|
||||||
})
|
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-info')
|
|
||||||
await expect(
|
|
||||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
// focus the editor
|
|
||||||
await u.codeLocator.click()
|
|
||||||
|
|
||||||
// Hit alt+shift+f to format the code
|
|
||||||
await page.keyboard.press('Alt+Shift+KeyF')
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
|
||||||
.toHaveText(`const sketch_001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> line([-20, 0], %)
|
|
||||||
|> close(%)`)
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-info')
|
|
||||||
await expect(
|
|
||||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
@ -469,7 +354,7 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
await page.getByText('$ error').click()
|
await page.getByText('$ error').click()
|
||||||
@ -517,7 +402,7 @@ test.describe('Editor tests', () => {
|
|||||||
const width = 0.500
|
const width = 0.500
|
||||||
const height = 0.500
|
const height = 0.500
|
||||||
const dia = 4
|
const dia = 4
|
||||||
|
|
||||||
fn squareHole = (l, w) => {
|
fn squareHole = (l, w) => {
|
||||||
const squareHoleSketch = startSketchOn('XY')
|
const squareHoleSketch = startSketchOn('XY')
|
||||||
|> startProfileAt([-width / 2, -length / 2], %)
|
|> startProfileAt([-width / 2, -length / 2], %)
|
||||||
@ -829,15 +714,17 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
|
||||||
|
// please fix together
|
||||||
|
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`const sketch001 = startSketchOn('XZ')
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([4.61, -10.01], %)
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`
|
|> extrude(5, %)`
|
||||||
)
|
)
|
||||||
@ -872,11 +759,11 @@ test.describe('Editor tests', () => {
|
|||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startPX = [665, 397]
|
const startPX = [665, 458]
|
||||||
|
|
||||||
const dragPX = 40
|
const dragPX = 40
|
||||||
|
|
||||||
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@ -914,7 +801,7 @@ test.describe('Editor tests', () => {
|
|||||||
// drag tangentialArcTo handle
|
// drag tangentialArcTo handle
|
||||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||||
targetPosition: {
|
targetPosition: {
|
||||||
x: tangentEnd.x + dragPX,
|
x: tangentEnd.x + dragPX,
|
||||||
y: tangentEnd.y + dragPX,
|
y: tangentEnd.y + dragPX,
|
||||||
@ -926,12 +813,12 @@ test.describe('Editor tests', () => {
|
|||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -12.68], %)
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|> line([15.39, -2.78], %)
|
|> line([15.4, -2.74], %)
|
||||||
|> tangentialArcTo([27.6, -3.05], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> line([2.65, -2.69], %)
|
||||||
|> extrude(5, %)
|
|> close(%)
|
||||||
`)
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
// Hit undo
|
// Hit undo
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -940,11 +827,11 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -12.68], %)
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|> line([15.39, -2.78], %)
|
|> line([15.4, -2.74], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -953,12 +840,11 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -12.68], %)
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)`)
|
||||||
`)
|
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -968,9 +854,9 @@ test.describe('Editor tests', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([4.61, -10.01], %)
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)`)
|
||||||
})
|
})
|
||||||
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 52 KiB |
@ -1,204 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
|
||||||
import * as fsp from 'fs/promises'
|
|
||||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
|
||||||
await setup(context, page)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('when using the file tree to', () => {
|
|
||||||
const fromFile = 'main.kcl'
|
|
||||||
const toFile = 'hello.kcl'
|
|
||||||
|
|
||||||
test(
|
|
||||||
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browser: _ }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
panesOpen,
|
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
renameFile,
|
|
||||||
editorTextMatches,
|
|
||||||
} = await getUtils(page, test)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
|
||||||
|
|
||||||
// File the main.kcl with contents
|
|
||||||
const kclCube = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCube)
|
|
||||||
|
|
||||||
await renameFile(fromFile, toFile)
|
|
||||||
await page.reload()
|
|
||||||
|
|
||||||
await test.step('Postcondition: editor has same content as before the rename', async () => {
|
|
||||||
await editorTextMatches(kclCube)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Postcondition: opening and closing settings works', async () => {
|
|
||||||
const settingsOpenButton = page.getByRole('link', {
|
|
||||||
name: 'settings Settings',
|
|
||||||
})
|
|
||||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
|
||||||
await settingsOpenButton.click()
|
|
||||||
await settingsCloseButton.click()
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
`create many new untitled files they increment their names`,
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browser: _ }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const { panesOpen, createAndSelectProject, createNewFile } =
|
|
||||||
await getUtils(page, test)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await panesOpen(['files'])
|
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
|
||||||
|
|
||||||
await createNewFile('')
|
|
||||||
await createNewFile('')
|
|
||||||
await createNewFile('')
|
|
||||||
await createNewFile('')
|
|
||||||
await createNewFile('')
|
|
||||||
|
|
||||||
await test.step('Postcondition: there are 5 new Untitled-*.kcl files', async () => {
|
|
||||||
await expect(
|
|
||||||
page
|
|
||||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
|
||||||
.filter({ hasText: /Untitled[-]?[0-5]?/ })
|
|
||||||
).toHaveCount(5)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'create a new file with the same name as an existing file cancels the operation',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browser: _ }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
panesOpen,
|
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
createNewFileAndSelect,
|
|
||||||
renameFile,
|
|
||||||
selectFile,
|
|
||||||
editorTextMatches,
|
|
||||||
} = await getUtils(page, test)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
|
||||||
// File the main.kcl with contents
|
|
||||||
const kclCube = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCube)
|
|
||||||
|
|
||||||
const kcl1 = 'main.kcl'
|
|
||||||
const kcl2 = '2.kcl'
|
|
||||||
|
|
||||||
await createNewFileAndSelect(kcl2)
|
|
||||||
const kclCylinder = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCylinder)
|
|
||||||
|
|
||||||
await renameFile(kcl2, kcl1)
|
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
|
|
||||||
await selectFile(kcl1)
|
|
||||||
await editorTextMatches(kclCube)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
|
|
||||||
await selectFile(kcl2)
|
|
||||||
await editorTextMatches(kclCylinder)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'deleting all files recreates a default main.kcl with no code',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browser: _ }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
panesOpen,
|
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
deleteFile,
|
|
||||||
editorTextMatches,
|
|
||||||
} = await getUtils(page, test)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
|
||||||
// File the main.kcl with contents
|
|
||||||
const kclCube = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCube)
|
|
||||||
|
|
||||||
const kcl1 = 'main.kcl'
|
|
||||||
|
|
||||||
await deleteFile(kcl1)
|
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl1} is recreated but has no content`, async () => {
|
|
||||||
await editorTextMatches('')
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
@ -1,97 +0,0 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
|
||||||
import { setupElectron, tearDown, executorInputPath } from './test-utils'
|
|
||||||
import { join } from 'path'
|
|
||||||
import fsp from 'fs/promises'
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test(
|
|
||||||
'When machine-api server not found butt is disabled and shows the reason',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
const notFoundText = 'Machine API server was not discovered'
|
|
||||||
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
|
|
||||||
|
|
||||||
// Find the make button
|
|
||||||
const makeButton = page.getByRole('button', { name: 'Make' })
|
|
||||||
// Make sure the button is visible but disabled
|
|
||||||
await expect(makeButton).toBeVisible()
|
|
||||||
await expect(makeButton).toBeDisabled()
|
|
||||||
|
|
||||||
// When you hover over the button, the tooltip should show
|
|
||||||
// that the machine-api server is not found
|
|
||||||
await makeButton.hover()
|
|
||||||
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'When machine-api server not found home screen & project status shows the reason',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
const notFoundText = 'Machine API server was not discovered'
|
|
||||||
|
|
||||||
await expect(page.getByText(notFoundText)).not.toBeVisible()
|
|
||||||
|
|
||||||
const networkMachineToggle = page.getByTestId('network-machine-toggle')
|
|
||||||
await networkMachineToggle.hover()
|
|
||||||
await expect(page.getByText(notFoundText)).toBeVisible()
|
|
||||||
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
|
|
||||||
|
|
||||||
await networkMachineToggle.hover()
|
|
||||||
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
@ -1,13 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { join } from 'path'
|
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import {
|
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||||
getUtils,
|
|
||||||
setup,
|
|
||||||
setupElectron,
|
|
||||||
tearDown,
|
|
||||||
executorInputPath,
|
|
||||||
} from './test-utils'
|
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import {
|
import {
|
||||||
@ -357,11 +350,10 @@ test(
|
|||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
|
||||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('router-template-slate.kcl'),
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
join(routerTemplateDir, 'main.kcl')
|
`${dir}/router-template-slate/main.kcl`
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import { test, expect, Page } from '@playwright/test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import { join } from 'path'
|
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import {
|
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||||
getUtils,
|
|
||||||
setup,
|
|
||||||
setupElectron,
|
|
||||||
tearDown,
|
|
||||||
executorInputPath,
|
|
||||||
} from './test-utils'
|
|
||||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
|
|
||||||
@ -201,7 +194,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: |').first()).toBeVisible()
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
// Okay execution finished, let's start editing text below the error.
|
// Okay execution finished, let's start editing text below the error.
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
@ -243,13 +236,9 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(
|
await page.addInitScript(async (code) => {
|
||||||
async ({ code }) => {
|
localStorage.setItem('persistCode', code)
|
||||||
localStorage.setItem('persistCode', code)
|
}, TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR)
|
||||||
;(window as any).playwrightSkipFilePicker = true
|
|
||||||
},
|
|
||||||
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
|
||||||
)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
@ -336,7 +325,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
// Expect it to succeed.
|
// Expect it to succeed.
|
||||||
await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
@ -348,7 +337,6 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
}) => {
|
}) => {
|
||||||
// This is being weird on ubuntu and windows.
|
// This is being weird on ubuntu and windows.
|
||||||
test.skip(
|
test.skip(
|
||||||
// eslint-disable-next-line jest/valid-title
|
|
||||||
process.platform === 'linux' || process.platform === 'win32',
|
process.platform === 'linux' || process.platform === 'win32',
|
||||||
'This test is being weird on ubuntu'
|
'This test is being weird on ubuntu'
|
||||||
)
|
)
|
||||||
@ -358,7 +346,6 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await page.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ code }) => {
|
async ({ code }) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
;(window as any).playwrightSkipFilePicker = true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: bracket,
|
code: bracket,
|
||||||
@ -394,22 +381,20 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await test.step('The second export is blocked', async () => {
|
await test.step('The second export is blocked', async () => {
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
await Promise.all([
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
expect(exportingToastMessage.first()).toBeVisible(),
|
await expect(alreadyExportingToastMessage).toBeVisible()
|
||||||
expect(alreadyExportingToastMessage).toBeVisible(),
|
|
||||||
])
|
await page.waitForTimeout(1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('The first export still succeeds', async () => {
|
await test.step('The first export still succeeds', async () => {
|
||||||
await Promise.all([
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 }),
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
expect(errorToastMessage).not.toBeVisible(),
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
expect(engineErrorToastMessage).not.toBeVisible(),
|
|
||||||
expect(successToastMessage).toBeVisible({ timeout: 15_000 }),
|
await expect(successToastMessage).toBeVisible()
|
||||||
expect(alreadyExportingToastMessage).not.toBeVisible({
|
|
||||||
timeout: 15_000,
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
}),
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -422,12 +407,10 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
// Expect it to succeed.
|
// Expect it to succeed.
|
||||||
await Promise.all([
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
expect(exportingToastMessage).not.toBeVisible(),
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
expect(errorToastMessage).not.toBeVisible(),
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
expect(engineErrorToastMessage).not.toBeVisible(),
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
expect(alreadyExportingToastMessage).not.toBeVisible(),
|
|
||||||
])
|
|
||||||
|
|
||||||
await expect(successToastMessage).toBeVisible()
|
await expect(successToastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -440,11 +423,10 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
join(bracketDir, 'main.kcl')
|
`${dir}/bracket/main.kcl`
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -490,7 +472,7 @@ async function clickExportButton(page: Page) {
|
|||||||
// Click the export button
|
// Click the export button
|
||||||
await exportButton.click()
|
await exportButton.click()
|
||||||
|
|
||||||
// Click the gltf.
|
// Click the stl.
|
||||||
const gltfOption = page.getByRole('option', { name: 'glTF' })
|
const gltfOption = page.getByRole('option', { name: 'glTF' })
|
||||||
await expect(gltfOption).toBeVisible()
|
await expect(gltfOption).toBeVisible()
|
||||||
|
|
||||||
|
@ -344,108 +344,111 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
// failing for the same reason as "Can undo a sketch modification with ctrl+z"
|
||||||
page,
|
// please fix together
|
||||||
}) => {
|
test.fixme(
|
||||||
const u = await getUtils(page)
|
'Can edit a sketch that has been extruded in the same pipe',
|
||||||
await page.addInitScript(async () => {
|
async ({ page }) => {
|
||||||
localStorage.setItem(
|
const u = await getUtils(page)
|
||||||
'persistCode',
|
await page.addInitScript(async () => {
|
||||||
`const sketch001 = startSketchOn('XZ')
|
localStorage.setItem(
|
||||||
|> startProfileAt([4.61, -10.01], %)
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`
|
|> extrude(5, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_look_at',
|
type: 'default_camera_look_at',
|
||||||
vantage: { x: 0, y: -1250, z: 580 },
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
center: { x: 0, y: 0, z: 0 },
|
center: { x: 0, y: 0, z: 0 },
|
||||||
up: { x: 0, y: 0, z: 1 },
|
up: { x: 0, y: 0, z: 1 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_get_settings',
|
type: 'default_camera_get_settings',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startPX = [665, 397]
|
const startPX = [665, 458]
|
||||||
|
|
||||||
const dragPX = 40
|
const dragPX = 40
|
||||||
|
|
||||||
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(400)
|
||||||
let prevContent = await page.locator('.cm-content').innerText()
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
|
|
||||||
// drag startProfieAt handle
|
// drag startProfieAt handle
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
// drag line handle
|
// drag line handle
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||||
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
// drag tangentialArcTo handle
|
// drag tangentialArcTo handle
|
||||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||||
targetPosition: {
|
targetPosition: {
|
||||||
x: tangentEnd.x + dragPX,
|
x: tangentEnd.x + dragPX,
|
||||||
y: tangentEnd.y + dragPX,
|
y: tangentEnd.y + dragPX,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
|
||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -12.68], %)
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|> line([15.39, -2.78], %)
|
|> line([15.4, -2.74], %)
|
||||||
|> tangentialArcTo([27.6, -3.05], %)
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|> close(%)
|
|> line([2.65, -2.69], %)
|
||||||
|> extrude(5, %)
|
|> close(%)
|
||||||
`)
|
|> extrude(5, %)`)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||||
page,
|
page,
|
||||||
@ -599,7 +602,7 @@ test.describe('Sketch tests', () => {
|
|||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
// exit the sketch, reset relative clicker
|
// exit the sketch, reset relative clicker
|
||||||
await click00r(undefined, undefined)
|
click00r(undefined, undefined)
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
@ -53,7 +53,6 @@ test(
|
|||||||
async ({ page, context }) => {
|
async ({ page, context }) => {
|
||||||
// skip on macos and windows.
|
// skip on macos and windows.
|
||||||
test.skip(
|
test.skip(
|
||||||
// eslint-disable-next-line jest/valid-title
|
|
||||||
process.platform === 'darwin' || process.platform === 'win32',
|
process.platform === 'darwin' || process.platform === 'win32',
|
||||||
'Skip on macos and windows'
|
'Skip on macos and windows'
|
||||||
)
|
)
|
||||||
@ -964,69 +963,3 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('theme persists', async ({ page, context }) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await context.addInitScript(async () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`const part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> line([-20, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(10, %)
|
|
||||||
`
|
|
||||||
)
|
|
||||||
}, KCL_DEFAULT_LENGTH)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
// await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click()
|
|
||||||
await expect(page.getByTestId('settings-link')).toBeVisible()
|
|
||||||
await page.getByTestId('settings-link').click()
|
|
||||||
|
|
||||||
// open user settingns
|
|
||||||
await page.getByRole('radio', { name: 'person User' }).click()
|
|
||||||
|
|
||||||
await page.getByTestId('app-theme').selectOption('light')
|
|
||||||
|
|
||||||
await page.getByTestId('settings-close-button').click()
|
|
||||||
|
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
|
||||||
|
|
||||||
// simulate network down
|
|
||||||
await u.emulateNetworkConditions({
|
|
||||||
offline: true,
|
|
||||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
|
||||||
latency: 0,
|
|
||||||
downloadThroughput: -1,
|
|
||||||
uploadThroughput: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Disconnect and reconnect to check the theme persists through a reload
|
|
||||||
|
|
||||||
// Expect the network to be down
|
|
||||||
await expect(networkToggle).toContainText('Offline')
|
|
||||||
|
|
||||||
// simulate network up
|
|
||||||
await u.emulateNetworkConditions({
|
|
||||||
offline: false,
|
|
||||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
|
||||||
latency: 0,
|
|
||||||
downloadThroughput: -1,
|
|
||||||
uploadThroughput: -1,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(networkToggle).toContainText('Connected')
|
|
||||||
|
|
||||||
await expect(page.getByText('building scene')).not.toBeVisible()
|
|
||||||
|
|
||||||
await expect(page, 'expect screenshot to have light theme').toHaveScreenshot({
|
|
||||||
maxDiffPixels: 100,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |