Merge remote-tracking branch 'origin/main' into paultag/import
This commit is contained in:
39
.github/workflows/build-apps.yml
vendored
39
.github/workflows/build-apps.yml
vendored
@ -33,26 +33,63 @@ jobs:
|
|||||||
|
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
|
|
||||||
|
- id: filter
|
||||||
|
name: Check for Rust changes
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
rust:
|
||||||
|
- 'rust/**'
|
||||||
|
|
||||||
|
- name: Download Wasm Cache
|
||||||
|
id: download-wasm
|
||||||
|
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
|
||||||
|
uses: dawidd6/action-download-artifact@v7
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
name: wasm-bundle
|
||||||
|
workflow: build-and-store-wasm.yml
|
||||||
|
branch: main
|
||||||
|
path: rust/kcl-wasm-lib/pkg
|
||||||
|
|
||||||
|
- name: Build WASM condition
|
||||||
|
id: wasm
|
||||||
|
run: |
|
||||||
|
set -euox pipefail
|
||||||
|
# Build wasm if this is a push to main or tag, there are Rust changes, or
|
||||||
|
# downloading from the wasm cache failed.
|
||||||
|
if [[ ${{github.event_name}} == 'push' || ${{steps.filter.outputs.rust}} == 'true' || ${{steps.download-wasm.outcome}} == 'failure' ]]; then
|
||||||
|
echo "should-build-wasm=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "should-build-wasm=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Use correct Rust toolchain
|
- name: Use correct Rust toolchain
|
||||||
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||||
|
|
||||||
- name: Install rust
|
- name: Install rust
|
||||||
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
|
|
||||||
# TODO: see if we can fetch from main instead if no diff at rust
|
|
||||||
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
||||||
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
|
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: rust
|
workspaces: rust
|
||||||
|
|
||||||
- name: Run build:wasm
|
- name: Run build:wasm
|
||||||
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
run: "yarn build:wasm"
|
run: "yarn build:wasm"
|
||||||
|
|
||||||
- name: Set nightly version, product name, release notes, and icons
|
- name: Set nightly version, product name, release notes, and icons
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
TEST_COLORS,
|
TEST_COLORS,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils } from './test-utils'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
|
@ -10,7 +10,11 @@ import fsp from 'fs/promises'
|
|||||||
test(
|
test(
|
||||||
'export works on the first try',
|
'export works on the first try',
|
||||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
{ tag: ['@electron', '@skipLocalEngine'] },
|
||||||
async ({ page, context, scene }, testInfo) => {
|
async ({ page, context, scene, tronApp }, testInfo) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
const bracketDir = path.join(dir, 'bracket')
|
||||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||||
@ -86,7 +90,7 @@ test(
|
|||||||
await expect(exportingToastMessage).not.toBeVisible()
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
const firstFileFullPath = path.resolve(
|
const firstFileFullPath = path.resolve(
|
||||||
getPlaywrightDownloadDir(page),
|
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||||
exportFileName
|
exportFileName
|
||||||
)
|
)
|
||||||
await test.step('Check the export size', async () => {
|
await test.step('Check the export size', async () => {
|
||||||
@ -165,7 +169,7 @@ test(
|
|||||||
]))
|
]))
|
||||||
|
|
||||||
const secondFileFullPath = path.resolve(
|
const secondFileFullPath = path.resolve(
|
||||||
getPlaywrightDownloadDir(page),
|
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||||
exportFileName
|
exportFileName
|
||||||
)
|
)
|
||||||
await test.step('Check the export size', async () => {
|
await test.step('Check the export size', async () => {
|
||||||
|
@ -158,11 +158,14 @@ test.describe('when using the file tree to', () => {
|
|||||||
await createNewFile('lee')
|
await createNewFile('lee')
|
||||||
|
|
||||||
await test.step('Postcondition: there are 5 new lee-*.kcl files', async () => {
|
await test.step('Postcondition: there are 5 new lee-*.kcl files', async () => {
|
||||||
await expect(
|
await expect
|
||||||
page
|
.poll(() =>
|
||||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
page
|
||||||
.filter({ hasText: /lee[-]?[0-5]?/ })
|
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||||
).toHaveCount(5)
|
.filter({ hasText: /lee[-]?[0-5]?/ })
|
||||||
|
.count()
|
||||||
|
)
|
||||||
|
.toEqual(5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -27,28 +27,19 @@ type CmdBarSerialised =
|
|||||||
|
|
||||||
export class CmdBarFixture {
|
export class CmdBarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
public cmdBarOpenBtn!: Locator
|
||||||
get cmdBarOpenBtn() {
|
public cmdBarElement!: Locator
|
||||||
return this.page.getByTestId('command-bar-open-button')
|
|
||||||
}
|
|
||||||
|
|
||||||
get cmdBarElement() {
|
|
||||||
return this.page.getByTestId('command-bar')
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
this.cmdBarOpenBtn = this.page.getByTestId('command-bar-open-button')
|
||||||
|
this.cmdBarElement = this.page.getByTestId('command-bar')
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentArgumentInput() {
|
get currentArgumentInput() {
|
||||||
return this.page.getByTestId('cmd-bar-arg-value')
|
return this.page.getByTestId('cmd-bar-arg-value')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put all selectors here because this method is re-run on fixture creation.
|
|
||||||
reConstruct = (page: Page) => {
|
|
||||||
this.page = page
|
|
||||||
}
|
|
||||||
|
|
||||||
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
||||||
if (!(await this.page.getByTestId('command-bar-wrapper').isVisible())) {
|
if (!(await this.page.getByTestId('command-bar-wrapper').isVisible())) {
|
||||||
return { stage: 'commandBarClosed' }
|
return { stage: 'commandBarClosed' }
|
||||||
|
@ -24,11 +24,6 @@ export class EditorFixture {
|
|||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.reConstruct(page)
|
|
||||||
}
|
|
||||||
reConstruct = (page: Page) => {
|
|
||||||
this.page = page
|
|
||||||
|
|
||||||
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
|
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
|
||||||
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
||||||
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
||||||
|
@ -1,13 +1,31 @@
|
|||||||
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
ElectronApplication,
|
ElectronApplication,
|
||||||
|
Fixtures as PlaywrightFixtures,
|
||||||
TestInfo,
|
TestInfo,
|
||||||
Page,
|
Page,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils, setup, setupElectron } from '../test-utils'
|
import {
|
||||||
|
_electron as electron,
|
||||||
|
PlaywrightTestArgs,
|
||||||
|
PlaywrightWorkerArgs,
|
||||||
|
} from '@playwright/test'
|
||||||
|
|
||||||
|
import * as TOML from '@iarna/toml'
|
||||||
|
import {
|
||||||
|
TEST_SETTINGS_KEY,
|
||||||
|
TEST_SETTINGS_CORRUPTED,
|
||||||
|
TEST_SETTINGS,
|
||||||
|
TEST_SETTINGS_DEFAULT_THEME,
|
||||||
|
} from '../storageStates'
|
||||||
|
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
|
import { getUtils, setup } from '../test-utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { join } from 'path'
|
import fs from 'node:fs'
|
||||||
|
import path from 'path'
|
||||||
import { CmdBarFixture } from './cmdBarFixture'
|
import { CmdBarFixture } from './cmdBarFixture'
|
||||||
import { EditorFixture } from './editorFixture'
|
import { EditorFixture } from './editorFixture'
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
import { ToolbarFixture } from './toolbarFixture'
|
||||||
@ -23,7 +41,7 @@ export class AuthenticatedApp {
|
|||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||||
public electronApp: undefined | ElectronApplication
|
public electronApp: undefined | ElectronApplication
|
||||||
public dir: string = ''
|
public projectDirName: string = ''
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this.context = context
|
this.context = context
|
||||||
@ -46,7 +64,7 @@ export class AuthenticatedApp {
|
|||||||
}
|
}
|
||||||
getInputFile = (fileName: string) => {
|
getInputFile = (fileName: string) => {
|
||||||
return fsp.readFile(
|
return fsp.readFile(
|
||||||
join('rust', 'kcl-lib', 'e2e', 'executor', 'inputs', fileName),
|
path.join('rust', 'kcl-lib', 'e2e', 'executor', 'inputs', fileName),
|
||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -59,101 +77,300 @@ export interface Fixtures {
|
|||||||
scene: SceneFixture
|
scene: SceneFixture
|
||||||
homePage: HomePageFixture
|
homePage: HomePageFixture
|
||||||
}
|
}
|
||||||
export class AuthenticatedTronApp {
|
|
||||||
public originalPage: Page
|
|
||||||
public page: Page
|
|
||||||
public browserContext: BrowserContext
|
|
||||||
public context: BrowserContext
|
|
||||||
public readonly testInfo: TestInfo
|
|
||||||
public electronApp: ElectronApplication | undefined
|
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
|
||||||
public dir: string = ''
|
|
||||||
|
|
||||||
constructor(
|
export class ElectronZoo {
|
||||||
browserContext: BrowserContext,
|
public available: boolean = true
|
||||||
originalPage: Page,
|
public electron!: ElectronApplication
|
||||||
testInfo: TestInfo
|
public firstUrl = ''
|
||||||
) {
|
public viewPortSize = { width: 1200, height: 500 }
|
||||||
this.page = originalPage
|
public projectDirName = ''
|
||||||
this.originalPage = originalPage
|
|
||||||
this.browserContext = browserContext
|
public page!: Page
|
||||||
// Will be overwritten in the initializer
|
public context!: BrowserContext
|
||||||
this.context = browserContext
|
|
||||||
this.testInfo = testInfo
|
constructor() {}
|
||||||
}
|
|
||||||
async initialise(
|
async makeAvailableAgain() {
|
||||||
arg: {
|
// Help remote end by signaling we're done with the connection.
|
||||||
fixtures: Partial<Fixtures>
|
await this.page.evaluate(async () => {
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
return new Promise((resolve) => {
|
||||||
cleanProjectDir?: boolean
|
if (!window.engineCommandManager.engineConnection?.state?.type) {
|
||||||
appSettings?: DeepPartial<Settings>
|
return resolve(undefined)
|
||||||
} = { fixtures: {} }
|
}
|
||||||
) {
|
|
||||||
const { electronApp, page, context, dir } = await setupElectron({
|
window.engineCommandManager.tearDown()
|
||||||
testInfo: this.testInfo,
|
// Keep polling (per js event tick) until state is Disconnected.
|
||||||
folderSetupFn: arg.folderSetupFn,
|
const checkDisconnected = () => {
|
||||||
cleanProjectDir: arg.cleanProjectDir,
|
// It's possible we never even created an engineConnection
|
||||||
appSettings: arg.appSettings,
|
// e.g. never left Projects view.
|
||||||
viewport: this.viewPortSize,
|
if (
|
||||||
|
window.engineCommandManager?.engineConnection?.state.type ===
|
||||||
|
'disconnected'
|
||||||
|
) {
|
||||||
|
return resolve(undefined)
|
||||||
|
}
|
||||||
|
setTimeout(checkDisconnected, 0)
|
||||||
|
}
|
||||||
|
checkDisconnected()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
this.page = page
|
|
||||||
|
|
||||||
// These assignments "fix" some brokenness in the Playwright Workbench when
|
await this.context.tracing.stopChunk({ path: 'trace.zip' })
|
||||||
// running against electron applications.
|
|
||||||
// The timeline is still broken but failure screenshots work again.
|
|
||||||
this.context = context
|
|
||||||
// TODO: try to get this to work again for screenshots, but it messed with test ends when enabled
|
|
||||||
// Object.assign(this.browserContext, this.context)
|
|
||||||
|
|
||||||
this.electronApp = electronApp
|
// Only after cleanup we're ready.
|
||||||
this.dir = dir
|
this.available = true
|
||||||
|
}
|
||||||
|
|
||||||
// Easier to access throughout utils
|
async createInstanceIfMissing(testInfo: TestInfo) {
|
||||||
this.page.dir = dir
|
// Create or otherwise clear the folder.
|
||||||
|
this.projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
|
||||||
// Setup localStorage, addCookies, reload
|
// We need to expose this in order for some tests that require folder
|
||||||
await setup(this.context, this.page, this.testInfo)
|
// creation and some code below.
|
||||||
|
const that = this
|
||||||
|
|
||||||
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
const options = {
|
||||||
const fixture = arg.fixtures[key]
|
args: ['.', '--no-sandbox'],
|
||||||
if (
|
env: {
|
||||||
!fixture ||
|
...process.env,
|
||||||
fixture instanceof AuthenticatedApp ||
|
TEST_SETTINGS_FILE_KEY: this.projectDirName,
|
||||||
fixture instanceof AuthenticatedTronApp
|
IS_PLAYWRIGHT: 'true',
|
||||||
)
|
},
|
||||||
continue
|
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||||
fixture.reConstruct(page)
|
? {
|
||||||
|
executablePath:
|
||||||
|
process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(process.env.PLAYWRIGHT_RECORD_VIDEO
|
||||||
|
? {
|
||||||
|
recordVideo: {
|
||||||
|
dir: testInfo.snapshotPath(),
|
||||||
|
size: this.viewPortSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do this once and then reuse window on subsequent calls.
|
||||||
|
if (!this.electron) {
|
||||||
|
this.electron = await electron.launch(options)
|
||||||
|
this.context = this.electron.context()
|
||||||
|
this.page = await this.electron.firstWindow()
|
||||||
|
await this.context.tracing.start({ screenshots: true, snapshots: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.context.tracing.startChunk()
|
||||||
|
|
||||||
|
await setup(this.context, this.page, testInfo)
|
||||||
|
|
||||||
|
await this.cleanProjectDir()
|
||||||
|
|
||||||
|
// Create a consistent way to resize the page across electron and web.
|
||||||
|
// (lee) I had to do everything in the book to make electron change its
|
||||||
|
// damn window size. I succeeded in making it consistently and reliably
|
||||||
|
// do it after a whole afternoon.
|
||||||
|
this.page.setBodyDimensions = async function (dims: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}) {
|
||||||
|
await this.setViewportSize(dims)
|
||||||
|
|
||||||
|
await that.electron?.evaluateHandle(async ({ app }, dims) => {
|
||||||
|
// @ts-ignore sorry jon but see comment in main.ts why this is ignored
|
||||||
|
await app.resizeWindow(dims.width, dims.height)
|
||||||
|
}, dims)
|
||||||
|
|
||||||
|
return this.evaluate(async (dims: { width: number; height: number }) => {
|
||||||
|
await window.electron.resizeWindow(dims.width, dims.height)
|
||||||
|
window.document.body.style.width = dims.width + 'px'
|
||||||
|
window.document.body.style.height = dims.height + 'px'
|
||||||
|
window.document.documentElement.style.width = dims.width + 'px'
|
||||||
|
window.document.documentElement.style.height = dims.height + 'px'
|
||||||
|
}, dims)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.page.setBodyDimensions(this.viewPortSize)
|
||||||
|
|
||||||
|
this.context.folderSetupFn = async function (fn) {
|
||||||
|
return fn(that.projectDirName)
|
||||||
|
.then(() => that.page.reload())
|
||||||
|
.then(() => ({
|
||||||
|
dir: that.projectDirName,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to patch this because addInitScript will bind too late in our
|
||||||
|
// electron tests, never running. We need to call reload() after each call
|
||||||
|
// to guarantee it runs.
|
||||||
|
const oldContextAddInitScript = this.context.addInitScript
|
||||||
|
this.context.addInitScript = async function (a, b) {
|
||||||
|
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||||
|
// This code works perfectly fine.
|
||||||
|
await oldContextAddInitScript.apply(this, [a, b])
|
||||||
|
await that.page.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// No idea why we mix and match page and context's addInitScript but we do
|
||||||
|
const oldPageAddInitScript = this.page.addInitScript
|
||||||
|
this.page.addInitScript = async function (a: any, b: any) {
|
||||||
|
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||||
|
// This code works perfectly fine.
|
||||||
|
await oldPageAddInitScript.apply(this, [a, b])
|
||||||
|
await that.page.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.firstUrl) {
|
||||||
|
await this.page.getByText('Your Projects').count()
|
||||||
|
this.firstUrl = this.page.url()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to the app controlling its own window context we need to inject new
|
||||||
|
// options and context here.
|
||||||
|
// NOTE TO LEE: Seems to destroy page context when calling an electron loadURL.
|
||||||
|
// await tronApp.electronApp.evaluate(({ app }) => {
|
||||||
|
// return app.reuseWindowForTest();
|
||||||
|
// });
|
||||||
|
|
||||||
|
await this.electron?.evaluate(({ app }, projectDirName) => {
|
||||||
|
// @ts-ignore can't declaration merge see main.ts
|
||||||
|
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
||||||
|
}, this.projectDirName)
|
||||||
|
|
||||||
|
// Always start at the root view
|
||||||
|
await this.page.goto(this.firstUrl)
|
||||||
|
|
||||||
|
// Force a hard reload, destroying the stream and other state
|
||||||
|
await this.page.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
close = async () => {
|
async cleanProjectDir(appSettings?: DeepPartial<Settings>) {
|
||||||
await this.electronApp?.close?.()
|
try {
|
||||||
|
if (fs.existsSync(this.projectDirName)) {
|
||||||
|
await fsp.rm(this.projectDirName, { recursive: true })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fsp.mkdir(this.projectDirName)
|
||||||
|
} catch (e) {
|
||||||
|
// Not a problem if it already exists.
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempSettingsFilePath = path.join(
|
||||||
|
this.projectDirName,
|
||||||
|
SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
let settingsOverridesToml = ''
|
||||||
|
|
||||||
|
if (appSettings) {
|
||||||
|
settingsOverridesToml = TOML.stringify({
|
||||||
|
// @ts-expect-error
|
||||||
|
settings: {
|
||||||
|
...TEST_SETTINGS,
|
||||||
|
...appSettings,
|
||||||
|
app: {
|
||||||
|
...TEST_SETTINGS.app,
|
||||||
|
project_directory: this.projectDirName,
|
||||||
|
...appSettings.app,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
settingsOverridesToml = TOML.stringify({
|
||||||
|
// @ts-expect-error
|
||||||
|
settings: {
|
||||||
|
...TEST_SETTINGS,
|
||||||
|
app: {
|
||||||
|
...TEST_SETTINGS.app,
|
||||||
|
project_directory: this.projectDirName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await fsp.writeFile(tempSettingsFilePath, settingsOverridesToml)
|
||||||
}
|
}
|
||||||
debugPause = () =>
|
|
||||||
new Promise(() => {
|
|
||||||
console.log('UN-RESOLVING PROMISE')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fixtures = {
|
// If yee encounter this, please try to type it.
|
||||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
type FnUse = any
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
|
const fixturesForElectron = {
|
||||||
|
page: async (
|
||||||
|
{ tronApp }: { tronApp: ElectronZoo },
|
||||||
|
use: FnUse,
|
||||||
|
testInfo: TestInfo
|
||||||
|
) => {
|
||||||
|
await tronApp.createInstanceIfMissing(testInfo)
|
||||||
|
await use(tronApp.page)
|
||||||
|
await tronApp?.makeAvailableAgain()
|
||||||
|
},
|
||||||
|
context: async (
|
||||||
|
{ tronApp }: { tronApp: ElectronZoo },
|
||||||
|
use: FnUse,
|
||||||
|
testInfo: TestInfo
|
||||||
|
) => {
|
||||||
|
await tronApp.createInstanceIfMissing(testInfo)
|
||||||
|
await use(tronApp.context)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixturesForWeb = {
|
||||||
|
page: async (
|
||||||
|
{ page, context }: { page: Page; context: BrowserContext },
|
||||||
|
use: FnUse,
|
||||||
|
testInfo: TestInfo
|
||||||
|
) => {
|
||||||
|
page.setBodyDimensions = page.setViewportSize
|
||||||
|
|
||||||
|
// We do the same thing in ElectronZoo. addInitScript simply doesn't fire
|
||||||
|
// at the correct time, so we reload the page and it fires appropriately.
|
||||||
|
const oldPageAddInitScript = page.addInitScript
|
||||||
|
page.addInitScript = async function (...args) {
|
||||||
|
// @ts-expect-error
|
||||||
|
await oldPageAddInitScript.apply(this, args)
|
||||||
|
await page.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldContextAddInitScript = context.addInitScript
|
||||||
|
context.addInitScript = async function (...args) {
|
||||||
|
// @ts-expect-error
|
||||||
|
await oldContextAddInitScript.apply(this, args)
|
||||||
|
await page.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
const webApp = new AuthenticatedApp(context, page, testInfo)
|
||||||
|
await webApp.initialise()
|
||||||
|
|
||||||
|
await use(page)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixturesBasedOnProcessEnvPlatform = {
|
||||||
|
cmdBar: async ({ page }: { page: Page }, use: FnUse) => {
|
||||||
await use(new CmdBarFixture(page))
|
await use(new CmdBarFixture(page))
|
||||||
},
|
},
|
||||||
editor: async ({ page }: { page: Page }, use: any) => {
|
editor: async ({ page }: { page: Page }, use: FnUse) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
await use(new EditorFixture(page))
|
await use(new EditorFixture(page))
|
||||||
},
|
},
|
||||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
toolbar: async ({ page }: { page: Page }, use: FnUse) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
await use(new ToolbarFixture(page))
|
await use(new ToolbarFixture(page))
|
||||||
},
|
},
|
||||||
scene: async ({ page }: { page: Page }, use: any) => {
|
scene: async ({ page }: { page: Page }, use: FnUse) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
await use(new SceneFixture(page))
|
await use(new SceneFixture(page))
|
||||||
},
|
},
|
||||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
homePage: async ({ page }: { page: Page }, use: FnUse) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
await use(new HomePageFixture(page))
|
await use(new HomePageFixture(page))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.PLATFORM === 'web') {
|
||||||
|
Object.assign(fixturesBasedOnProcessEnvPlatform, fixturesForWeb)
|
||||||
|
} else {
|
||||||
|
Object.assign(fixturesBasedOnProcessEnvPlatform, fixturesForElectron)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fixturesBasedOnProcessEnvPlatform }
|
||||||
|
@ -27,10 +27,6 @@ export class HomePageFixture {
|
|||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.reConstruct(page)
|
|
||||||
}
|
|
||||||
reConstruct = (page: Page) => {
|
|
||||||
this.page = page
|
|
||||||
|
|
||||||
this.projectSection = this.page.getByTestId('home-section')
|
this.projectSection = this.page.getByTestId('home-section')
|
||||||
|
|
||||||
@ -96,8 +92,12 @@ export class HomePageFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
projectsLoaded = async () => {
|
||||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||||
|
}
|
||||||
|
|
||||||
|
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
||||||
|
await this.projectsLoaded()
|
||||||
await this.projectButtonNew.click()
|
await this.projectButtonNew.click()
|
||||||
await this.projectTextName.click()
|
await this.projectTextName.click()
|
||||||
await this.projectTextName.fill(projectTitle)
|
await this.projectTextName.fill(projectTitle)
|
||||||
|
@ -53,7 +53,12 @@ export class SceneFixture {
|
|||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.reConstruct(page)
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
|
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
||||||
|
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||||
|
this.startEditSketchBtn = page
|
||||||
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
||||||
}
|
}
|
||||||
private _serialiseScene = async (): Promise<SceneSerialised> => {
|
private _serialiseScene = async (): Promise<SceneSerialised> => {
|
||||||
const camera = await this.getCameraInfo()
|
const camera = await this.getCameraInfo()
|
||||||
@ -72,17 +77,6 @@ export class SceneFixture {
|
|||||||
.toEqual(expected)
|
.toEqual(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
reConstruct = (page: Page) => {
|
|
||||||
this.page = page
|
|
||||||
|
|
||||||
this.streamWrapper = page.getByTestId('stream')
|
|
||||||
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
|
||||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
|
||||||
this.startEditSketchBtn = page
|
|
||||||
.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
|
||||||
}
|
|
||||||
|
|
||||||
makeMouseHelpers = (
|
makeMouseHelpers = (
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
@ -253,7 +247,7 @@ export class SceneFixture {
|
|||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.clearAndCloseDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await this.waitForExecutionDone()
|
await this.waitForExecutionDone()
|
||||||
await expect(this.startEditSketchBtn).not.toBeDisabled()
|
await expect(this.startEditSketchBtn).not.toBeDisabled()
|
||||||
|
@ -37,13 +37,12 @@ export class ToolbarFixture {
|
|||||||
featureTreeId = 'feature-tree' as const
|
featureTreeId = 'feature-tree' as const
|
||||||
/** The pane element for the Feature Tree */
|
/** The pane element for the Feature Tree */
|
||||||
featureTreePane!: Locator
|
featureTreePane!: Locator
|
||||||
|
gizmo!: Locator
|
||||||
|
gizmoDisabled!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.reConstruct(page)
|
|
||||||
}
|
|
||||||
reConstruct = (page: Page) => {
|
|
||||||
this.page = page
|
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
this.loftButton = page.getByTestId('loft')
|
||||||
this.sweepButton = page.getByTestId('sweep')
|
this.sweepButton = page.getByTestId('sweep')
|
||||||
@ -67,6 +66,13 @@ export class ToolbarFixture {
|
|||||||
this.filePane = page.locator('#files-pane')
|
this.filePane = page.locator('#files-pane')
|
||||||
this.featureTreePane = page.locator('#feature-tree-pane')
|
this.featureTreePane = page.locator('#feature-tree-pane')
|
||||||
this.fileCreateToast = page.getByText('Successfully created')
|
this.fileCreateToast = page.getByText('Successfully created')
|
||||||
|
|
||||||
|
// Note to test writers: having two locators like this is preferable to one
|
||||||
|
// which changes another el property because it means our test "signal" is
|
||||||
|
// completely decoupled from the elements themselves. It means the same
|
||||||
|
// element or two different elements can represent these states.
|
||||||
|
this.gizmo = page.getByTestId('gizmo')
|
||||||
|
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
|
||||||
}
|
}
|
||||||
|
|
||||||
get editSketchBtn() {
|
get editSketchBtn() {
|
||||||
@ -86,6 +92,18 @@ export class ToolbarFixture {
|
|||||||
startSketchPlaneSelection = async () =>
|
startSketchPlaneSelection = async () =>
|
||||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||||
|
|
||||||
|
waitUntilSketchingReady = async () => {
|
||||||
|
await expect(this.gizmoDisabled).toBeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
startSketchThenCallbackThenWaitUntilReady = async (
|
||||||
|
cb: () => Promise<void>
|
||||||
|
) => {
|
||||||
|
await this.startSketchBtn.click()
|
||||||
|
await cb()
|
||||||
|
await this.waitUntilSketchingReady()
|
||||||
|
}
|
||||||
|
|
||||||
exitSketch = async () => {
|
exitSketch = async () => {
|
||||||
await this.exitSketchBtn.click()
|
await this.exitSketchBtn.click()
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -21,58 +21,54 @@ import { expectPixelColor } from './fixtures/sceneFixture'
|
|||||||
// we must set it to empty for the tests where we want to see the onboarding immediately.
|
// we must set it to empty for the tests where we want to see the onboarding immediately.
|
||||||
|
|
||||||
test.describe('Onboarding tests', () => {
|
test.describe('Onboarding tests', () => {
|
||||||
test(
|
test('Onboarding code is shown in the editor', async ({
|
||||||
'Onboarding code is shown in the editor',
|
page,
|
||||||
{
|
homePage,
|
||||||
appSettings: {
|
tronApp,
|
||||||
app: {
|
}) => {
|
||||||
onboarding_status: '',
|
if (!tronApp) {
|
||||||
},
|
fail()
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ page, homePage }) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
|
||||||
await expect(
|
|
||||||
page.getByText('Welcome to Modeling App! This')
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
|
||||||
await expect(
|
|
||||||
page.getByText('Welcome to Modeling App! This')
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
// *and* that the code is shown in the editor
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
|
||||||
'// Shelf Bracket'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Make sure the model loaded
|
|
||||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
|
||||||
const modelColor: [number, number, number] = [45, 45, 45]
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
||||||
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(
|
|
||||||
8
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
await tronApp.cleanProjectDir({
|
||||||
|
app: {
|
||||||
|
onboarding_status: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// Test that the onboarding pane loaded
|
||||||
|
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||||
|
|
||||||
|
// Test that the onboarding pane loaded
|
||||||
|
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||||
|
|
||||||
|
// *and* that the code is shown in the editor
|
||||||
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
|
|
||||||
|
// Make sure the model loaded
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const modelColor: [number, number, number] = [45, 45, 45]
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(8)
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Desktop: fresh onboarding executes and loads',
|
'Desktop: fresh onboarding executes and loads',
|
||||||
{
|
{
|
||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
},
|
||||||
|
async ({ page, tronApp }) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: '',
|
onboarding_status: '',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
@ -107,223 +103,235 @@ test.describe('Onboarding tests', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test('Code resets after confirmation', async ({
|
||||||
'Code resets after confirmation',
|
context,
|
||||||
{
|
page,
|
||||||
cleanProjectDir: true,
|
homePage,
|
||||||
},
|
tronApp,
|
||||||
async ({ context, page, homePage }) => {
|
scene,
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
await tronApp.cleanProjectDir()
|
||||||
|
|
||||||
// Load the page up with some code so we see the confirmation warning
|
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||||
// when we go to replay onboarding
|
|
||||||
await context.addInitScript((code) => {
|
|
||||||
localStorage.setItem('persistCode', code)
|
|
||||||
}, initialCode)
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
// Load the page up with some code so we see the confirmation warning
|
||||||
await homePage.goToModelingScene()
|
// when we go to replay onboarding
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, initialCode)
|
||||||
|
|
||||||
// Replay the onboarding
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
await homePage.goToModelingScene()
|
||||||
const replayButton = page.getByRole('button', {
|
await scene.connectionEstablished()
|
||||||
name: 'Replay onboarding',
|
|
||||||
})
|
|
||||||
await expect(replayButton).toBeVisible()
|
|
||||||
await replayButton.click()
|
|
||||||
|
|
||||||
// Ensure we see the warning, and that the code has not yet updated
|
// Replay the onboarding
|
||||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
const replayButton = page.getByRole('button', {
|
||||||
|
name: 'Replay onboarding',
|
||||||
|
})
|
||||||
|
await expect(replayButton).toBeVisible()
|
||||||
|
await replayButton.click()
|
||||||
|
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
// Ensure we see the warning, and that the code has not yet updated
|
||||||
|
await expect(page.getByText('Would you like to create')).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||||
|
|
||||||
|
const nextButton = page.getByTestId('onboarding-next')
|
||||||
|
await nextButton.hover()
|
||||||
|
await nextButton.click()
|
||||||
|
|
||||||
|
// Ensure we see the introduction and that the code has been reset
|
||||||
|
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
|
|
||||||
|
// There used to be old code here that checked if we stored the reset
|
||||||
|
// code into localStorage but that isn't the case on desktop. It gets
|
||||||
|
// saved to the file system, which we have other tests for.
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Click through each onboarding step and back', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
tronApp,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
|
app: {
|
||||||
|
onboarding_status: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// Override beforeEach test setup
|
||||||
|
await context.addInitScript(
|
||||||
|
async ({ settingsKey, settings }) => {
|
||||||
|
// Give no initial code, so that the onboarding start is shown immediately
|
||||||
|
localStorage.setItem('persistCode', '')
|
||||||
|
localStorage.setItem(settingsKey, settings)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
|
settings: settingsToToml({
|
||||||
|
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// Test that the onboarding pane loaded
|
||||||
|
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||||
|
|
||||||
|
const nextButton = page.getByTestId('onboarding-next')
|
||||||
|
const prevButton = page.getByTestId('onboarding-prev')
|
||||||
|
|
||||||
|
while ((await nextButton.innerText()) !== 'Finish') {
|
||||||
await nextButton.hover()
|
await nextButton.hover()
|
||||||
await nextButton.click()
|
await nextButton.click()
|
||||||
|
|
||||||
// Ensure we see the introduction and that the code has been reset
|
|
||||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
|
||||||
'// Shelf Bracket'
|
|
||||||
)
|
|
||||||
|
|
||||||
// There used to be old code here that checked if we stored the reset
|
|
||||||
// code into localStorage but that isn't the case on desktop. It gets
|
|
||||||
// saved to the file system, which we have other tests for.
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
while ((await prevButton.innerText()) !== 'Dismiss') {
|
||||||
'Click through each onboarding step and back',
|
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboarding_status: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
|
||||||
await context.addInitScript(
|
|
||||||
async ({ settingsKey, settings }) => {
|
|
||||||
// Give no initial code, so that the onboarding start is shown immediately
|
|
||||||
localStorage.setItem('persistCode', '')
|
|
||||||
localStorage.setItem(settingsKey, settings)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
|
||||||
settings: settingsToToml({
|
|
||||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
|
||||||
await expect(
|
|
||||||
page.getByText('Welcome to Modeling App! This')
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
|
||||||
const prevButton = page.getByTestId('onboarding-prev')
|
|
||||||
|
|
||||||
while ((await nextButton.innerText()) !== 'Finish') {
|
|
||||||
await nextButton.hover()
|
|
||||||
await nextButton.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((await prevButton.innerText()) !== 'Dismiss') {
|
|
||||||
await prevButton.hover()
|
|
||||||
await prevButton.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss the onboarding
|
|
||||||
await prevButton.hover()
|
await prevButton.hover()
|
||||||
await prevButton.click()
|
await prevButton.click()
|
||||||
|
|
||||||
// Test that the onboarding pane is gone
|
|
||||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
|
||||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
// Dismiss the onboarding
|
||||||
'Onboarding redirects and code updating',
|
await prevButton.hover()
|
||||||
{
|
await prevButton.click()
|
||||||
appSettings: {
|
|
||||||
app: {
|
// Test that the onboarding pane is gone
|
||||||
onboarding_status: '/export',
|
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||||
},
|
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Onboarding redirects and code updating', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
tronApp,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
|
app: {
|
||||||
|
onboarding_status: '/export',
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
})
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
const originalCode = 'sigmaAllow = 15000'
|
|
||||||
|
|
||||||
// Override beforeEach test setup
|
const originalCode = 'sigmaAllow = 15000'
|
||||||
await context.addInitScript(
|
|
||||||
async ({ settingsKey, settings }) => {
|
|
||||||
// Give some initial code, so we can test that it's cleared
|
|
||||||
localStorage.setItem('persistCode', originalCode)
|
|
||||||
localStorage.setItem(settingsKey, settings)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
|
||||||
settings: settingsToToml({
|
|
||||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
// Override beforeEach test setup
|
||||||
await homePage.goToModelingScene()
|
await context.addInitScript(
|
||||||
|
async ({ settingsKey, settings }) => {
|
||||||
// Test that the redirect happened
|
// Give some initial code, so we can test that it's cleared
|
||||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
localStorage.setItem('persistCode', originalCode)
|
||||||
|
localStorage.setItem(settingsKey, settings)
|
||||||
// Test that you come back to this page when you refresh
|
|
||||||
await page.reload()
|
|
||||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
|
||||||
|
|
||||||
// Test that the code changes when you advance to the next step
|
|
||||||
await page.getByTestId('onboarding-next').hover()
|
|
||||||
await page.getByTestId('onboarding-next').click()
|
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
|
||||||
const title = page.locator('[data-testid="onboarding-content"]')
|
|
||||||
await expect(title).toBeAttached()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
|
||||||
|
|
||||||
// Test that the code is not empty when you click on the next step
|
|
||||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
|
||||||
await page.locator('[data-testid="onboarding-next"]').click()
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Onboarding code gets reset to demo on Interactive Numbers step',
|
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboarding_status: '/parametric-modeling',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
{
|
||||||
},
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
|
settings: settingsToToml({
|
||||||
|
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
async ({ page, homePage }) => {
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
const u = await getUtils(page)
|
await homePage.goToModelingScene()
|
||||||
const badCode = `// This is bad code we shouldn't see`
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
// Test that the redirect happened
|
||||||
await homePage.goToModelingScene()
|
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||||
|
|
||||||
await expect
|
// Test that you come back to this page when you refresh
|
||||||
.poll(() => page.url())
|
await page.reload()
|
||||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||||
|
|
||||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
// Test that the code changes when you advance to the next step
|
||||||
|
await page.getByTestId('onboarding-next').hover()
|
||||||
|
await page.getByTestId('onboarding-next').click()
|
||||||
|
|
||||||
// Check the code got reset on load
|
// Test that the onboarding pane loaded
|
||||||
await expect(page.locator('#code-pane')).toBeVisible()
|
const title = page.locator('[data-testid="onboarding-content"]')
|
||||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
await expect(title).toBeAttached()
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Mess with the code again
|
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
||||||
await u.codeLocator.selectText()
|
|
||||||
await u.codeLocator.fill(badCode)
|
|
||||||
await expect(u.codeLocator).toHaveText(badCode)
|
|
||||||
|
|
||||||
// Click to the next step
|
// Test that the code is not empty when you click on the next step
|
||||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||||
await page.locator('[data-testid="onboarding-next"]').click()
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||||
waitUntil: 'domcontentloaded',
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Check that the code has been reset
|
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
|
||||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
page,
|
||||||
|
homePage,
|
||||||
|
tronApp,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
}
|
}
|
||||||
)
|
await tronApp.cleanProjectDir({
|
||||||
|
app: {
|
||||||
|
onboarding_status: '/parametric-modeling',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const badCode = `// This is bad code we shouldn't see`
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
await expect
|
||||||
|
.poll(() => page.url())
|
||||||
|
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||||
|
|
||||||
|
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||||
|
|
||||||
|
// Check the code got reset on load
|
||||||
|
await expect(page.locator('#code-pane')).toBeVisible()
|
||||||
|
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mess with the code again
|
||||||
|
await u.codeLocator.selectText()
|
||||||
|
await u.codeLocator.fill(badCode)
|
||||||
|
await expect(u.codeLocator).toHaveText(badCode)
|
||||||
|
|
||||||
|
// Click to the next step
|
||||||
|
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||||
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
|
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||||
|
waitUntil: 'domcontentloaded',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check that the code has been reset
|
||||||
|
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||||
|
})
|
||||||
|
|
||||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
// (lee) The two avatar tests are weird because even on main, we don't have
|
||||||
// anything to do with the avatar inside the onboarding test. Due to the
|
// anything to do with the avatar inside the onboarding test. Due to the
|
||||||
// low impact of an avatar not showing I'm changing this to fixme.
|
// low impact of an avatar not showing I'm changing this to fixme.
|
||||||
test.fixme(
|
test.fixme(
|
||||||
'Avatar text updates depending on image load success',
|
'Avatar text updates depending on image load success',
|
||||||
{
|
async ({ context, page, homePage, tronApp }) => {
|
||||||
appSettings: {
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: '',
|
onboarding_status: '',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await context.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
@ -388,15 +396,16 @@ test.describe('Onboarding tests', () => {
|
|||||||
|
|
||||||
test.fixme(
|
test.fixme(
|
||||||
"Avatar text doesn't mention avatar when no avatar",
|
"Avatar text doesn't mention avatar when no avatar",
|
||||||
{
|
async ({ context, page, homePage, tronApp }) => {
|
||||||
appSettings: {
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: '',
|
onboarding_status: '',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await context.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
@ -444,15 +453,17 @@ test.describe('Onboarding tests', () => {
|
|||||||
|
|
||||||
test.fixme(
|
test.fixme(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
async ({ context, page, tronApp }) => {
|
||||||
appSettings: {
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'dismissed',
|
onboarding_status: 'dismissed',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page }) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import { EditorFixture } from './fixtures/editorFixture'
|
import { EditorFixture } from './fixtures/editorFixture'
|
||||||
import { SceneFixture } from './fixtures/sceneFixture'
|
import { SceneFixture } from './fixtures/sceneFixture'
|
||||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||||
|
@ -163,7 +163,7 @@ test(
|
|||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
})
|
})
|
||||||
.toBeLessThan(15)
|
.toBeLessThan(20)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
@ -464,7 +464,11 @@ test.describe('Can export from electron app', () => {
|
|||||||
test(
|
test(
|
||||||
`Can export using ${method}`,
|
`Can export using ${method}`,
|
||||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
{ tag: ['@electron', '@skipLocalEngine'] },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page, tronApp }, testInfo) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
const bracketDir = path.join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
@ -516,6 +520,7 @@ test.describe('Can export from electron app', () => {
|
|||||||
storage: 'embedded',
|
storage: 'embedded',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page,
|
page,
|
||||||
method
|
method
|
||||||
)
|
)
|
||||||
@ -523,7 +528,7 @@ test.describe('Can export from electron app', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filepath = path.resolve(
|
const filepath = path.resolve(
|
||||||
getPlaywrightDownloadDir(page),
|
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||||
'main.gltf'
|
'main.gltf'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -781,6 +786,7 @@ test(
|
|||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await expect(page.getByText('router-template-slate')).toBeVisible()
|
await expect(page.getByText('router-template-slate')).toBeVisible()
|
||||||
|
await expect(page.getByText('Loading your Projects...')).not.toBeVisible()
|
||||||
await expect(page.getByText('Your Projects')).toBeVisible()
|
await expect(page.getByText('Your Projects')).toBeVisible()
|
||||||
|
|
||||||
await page.keyboard.press('Delete')
|
await page.keyboard.press('Delete')
|
||||||
@ -858,7 +864,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
test(
|
test(
|
||||||
`Delete from project page`,
|
`Delete from project page`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page, scene, cmdBar }, testInfo) => {
|
||||||
const projectName = `my_project_to_delete`
|
const projectName = `my_project_to_delete`
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
@ -887,6 +893,8 @@ test.describe(`Project management commands`, () => {
|
|||||||
|
|
||||||
await projectHomeLink.click()
|
await projectHomeLink.click()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
await scene.connectionEstablished()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Run delete command via command palette`, async () => {
|
await test.step(`Run delete command via command palette`, async () => {
|
||||||
@ -909,7 +917,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
test(
|
test(
|
||||||
`Rename from home page`,
|
`Rename from home page`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page, homePage }, testInfo) => {
|
||||||
const projectName = `my_project_to_rename`
|
const projectName = `my_project_to_rename`
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
@ -936,6 +944,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
await homePage.projectsLoaded()
|
||||||
await expect(projectHomeLink).toBeVisible()
|
await expect(projectHomeLink).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1682,7 +1691,11 @@ test(
|
|||||||
test(
|
test(
|
||||||
'You can change the root projects directory and nothing is lost',
|
'You can change the root projects directory and nothing is lost',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page, electronApp }, testInfo) => {
|
async ({ context, page, tronApp, homePage }, testInfo) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }),
|
fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }),
|
||||||
@ -1712,6 +1725,8 @@ test(
|
|||||||
await fsp.rm(newProjectDirName, { recursive: true })
|
await fsp.rm(newProjectDirName, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await homePage.projectsLoaded()
|
||||||
|
|
||||||
await test.step('We can change the root project directory', async () => {
|
await test.step('We can change the root project directory', async () => {
|
||||||
// expect to see the project directory settings link
|
// expect to see the project directory settings link
|
||||||
await expect(
|
await expect(
|
||||||
@ -1725,7 +1740,7 @@ test(
|
|||||||
.locator('section#projectDirectory input')
|
.locator('section#projectDirectory input')
|
||||||
.inputValue()
|
.inputValue()
|
||||||
|
|
||||||
const handleFile = electronApp?.evaluate(
|
const handleFile = tronApp.electron.evaluate(
|
||||||
async ({ dialog }, filePaths) => {
|
async ({ dialog }, filePaths) => {
|
||||||
dialog.showOpenDialog = () =>
|
dialog.showOpenDialog = () =>
|
||||||
Promise.resolve({ canceled: false, filePaths })
|
Promise.resolve({ canceled: false, filePaths })
|
||||||
@ -1741,6 +1756,8 @@ test(
|
|||||||
|
|
||||||
await page.getByTestId('settings-close-button').click()
|
await page.getByTestId('settings-close-button').click()
|
||||||
|
|
||||||
|
await homePage.projectsLoaded()
|
||||||
|
|
||||||
await expect(page.getByText('No Projects found')).toBeVisible()
|
await expect(page.getByText('No Projects found')).toBeVisible()
|
||||||
await createProject({ name: 'project-000', page, returnHome: true })
|
await createProject({ name: 'project-000', page, returnHome: true })
|
||||||
await expect(
|
await expect(
|
||||||
@ -1755,7 +1772,7 @@ test(
|
|||||||
|
|
||||||
await page.getByTestId('project-directory-settings-link').click()
|
await page.getByTestId('project-directory-settings-link').click()
|
||||||
|
|
||||||
const handleFile = electronApp?.evaluate(
|
const handleFile = tronApp.electron.evaluate(
|
||||||
async ({ dialog }, filePaths) => {
|
async ({ dialog }, filePaths) => {
|
||||||
dialog.showOpenDialog = () =>
|
dialog.showOpenDialog = () =>
|
||||||
Promise.resolve({ canceled: false, filePaths })
|
Promise.resolve({ canceled: false, filePaths })
|
||||||
@ -1767,6 +1784,7 @@ test(
|
|||||||
await page.getByTestId('project-directory-button').click()
|
await page.getByTestId('project-directory-button').click()
|
||||||
await handleFile
|
await handleFile
|
||||||
|
|
||||||
|
await homePage.projectsLoaded()
|
||||||
await expect(page.locator('section#projectDirectory input')).toHaveValue(
|
await expect(page.locator('section#projectDirectory input')).toHaveValue(
|
||||||
originalProjectDirName
|
originalProjectDirName
|
||||||
)
|
)
|
||||||
@ -2000,8 +2018,8 @@ test(
|
|||||||
|
|
||||||
test(
|
test(
|
||||||
'Settings persist across restarts',
|
'Settings persist across restarts',
|
||||||
{ tag: '@electron', cleanProjectDir: true },
|
{ tag: '@electron' },
|
||||||
async ({ page }, testInfo) => {
|
async ({ page, scene, cmdBar }, testInfo) => {
|
||||||
await test.step('We can change a user setting like theme', async () => {
|
await test.step('We can change a user setting like theme', async () => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
@ -2014,6 +2032,10 @@ test(
|
|||||||
await expect(page.getByTestId('app-theme')).toHaveValue('dark')
|
await expect(page.getByTestId('app-theme')).toHaveValue('dark')
|
||||||
|
|
||||||
await page.getByTestId('app-theme').selectOption('light')
|
await page.getByTestId('app-theme').selectOption('light')
|
||||||
|
await expect(page.getByTestId('app-theme')).toHaveValue('light')
|
||||||
|
|
||||||
|
// Give time to system for writing to a persistent store
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Starting the app again and we can see the same theme', async () => {
|
await test.step('Starting the app again and we can see the same theme', async () => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import { getUtils, executorInputPath } from './test-utils'
|
import { getUtils, executorInputPath } from './test-utils'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import fs from 'node:fs/promises'
|
import fs from 'node:fs/promises'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||||
@ -2153,6 +2154,8 @@ extrude001 = extrude(profile003, length = 5)
|
|||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
await page.waitForTimeout(5000)
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -2165,7 +2168,7 @@ extrude001 = extrude(profile003, length = 5)
|
|||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
await editor.expectEditor.toContain(`sketch001 = startSketchOn('XZ')`)
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn('XZ')`)
|
||||||
await toolbar.exitSketchBtn.click()
|
await toolbar.exitSketch()
|
||||||
|
|
||||||
await editor.expectEditor.not.toContain(`sketch001 = startSketchOn('XZ')`)
|
await editor.expectEditor.not.toContain(`sketch001 = startSketchOn('XZ')`)
|
||||||
|
|
||||||
@ -2181,6 +2184,8 @@ extrude001 = extrude(profile003, length = 5)
|
|||||||
)`
|
)`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
await scene.expectPixelColor([255, 255, 255], { x: 633, y: 211 }, 15)
|
await scene.expectPixelColor([255, 255, 255], { x: 633, y: 211 }, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -31,8 +31,7 @@ test.beforeEach(async ({ page, context }) => {
|
|||||||
// Help engine-manager: tear shit down.
|
// Help engine-manager: tear shit down.
|
||||||
test.afterEach(async ({ page }) => {
|
test.afterEach(async ({ page }) => {
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
// @ts-expect-error
|
window.engineCommandManager.tearDown()
|
||||||
window.tearDown()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -45,7 +44,11 @@ test.setTimeout(60_000)
|
|||||||
test.skip(
|
test.skip(
|
||||||
'exports of each format should work',
|
'exports of each format should work',
|
||||||
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
||||||
async ({ page, context, scene, cmdBar }) => {
|
async ({ page, context, scene, cmdBar, tronApp }) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -134,6 +137,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
storage: 'ascii',
|
storage: 'ascii',
|
||||||
units: 'in',
|
units: 'in',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -146,6 +150,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
selection: { type: 'default_scene' },
|
selection: { type: 'default_scene' },
|
||||||
units: 'in',
|
units: 'in',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -158,6 +163,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
selection: { type: 'default_scene' },
|
selection: { type: 'default_scene' },
|
||||||
units: 'in',
|
units: 'in',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -170,6 +176,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
units: 'in',
|
units: 'in',
|
||||||
selection: { type: 'default_scene' },
|
selection: { type: 'default_scene' },
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -182,6 +189,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
units: 'in',
|
units: 'in',
|
||||||
selection: { type: 'default_scene' },
|
selection: { type: 'default_scene' },
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -193,6 +201,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
coords: sysType,
|
coords: sysType,
|
||||||
units: 'in',
|
units: 'in',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -203,6 +212,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
storage: 'embedded',
|
storage: 'embedded',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -213,6 +223,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
storage: 'binary',
|
storage: 'binary',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -223,6 +234,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
storage: 'standard',
|
storage: 'standard',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
|
tronApp.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -84,7 +84,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
'Engine disconnect & reconnect in sketch mode',
|
'Engine disconnect & reconnect in sketch mode',
|
||||||
{ tag: '@skipLocalEngine' },
|
{ tag: '@skipLocalEngine' },
|
||||||
async ({ page, homePage }) => {
|
async ({ page, homePage }) => {
|
||||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
// TODO: Don't skip Mac for these. After `window.engineCommandManager.tearDown` is working in Safari, these should work on webkit
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId('network-toggle')
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
@ -5,8 +5,9 @@ import {
|
|||||||
_electron as electron,
|
_electron as electron,
|
||||||
ElectronApplication,
|
ElectronApplication,
|
||||||
Locator,
|
Locator,
|
||||||
|
Page,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { test, Page } from './zoo-test'
|
import { test } from './zoo-test'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
import fsSync from 'fs'
|
||||||
@ -337,7 +338,7 @@ export const getMovementUtils = (opts: any) => {
|
|||||||
|
|
||||||
async function waitForAuthAndLsp(page: Page) {
|
async function waitForAuthAndLsp(page: Page) {
|
||||||
const waitForLspPromise = page.waitForEvent('console', {
|
const waitForLspPromise = page.waitForEvent('console', {
|
||||||
predicate: async (message) => {
|
predicate: async (message: any) => {
|
||||||
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
||||||
// but that doesn't seem to make it to the console for macos/safari :(
|
// but that doesn't seem to make it to the console for macos/safari :(
|
||||||
if (message.text().includes('start kcl lsp')) {
|
if (message.text().includes('start kcl lsp')) {
|
||||||
@ -420,7 +421,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
const overlay = page.locator(locator)
|
const overlay = page.locator(locator)
|
||||||
const bbox = await overlay
|
const bbox = await overlay
|
||||||
.boundingBox({ timeout: 5_000 })
|
.boundingBox({ timeout: 5_000 })
|
||||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 }))
|
.then((box: any) => ({ ...box, x: box?.x || 0, y: box?.y || 0 }))
|
||||||
const angle = Number(await overlay.getAttribute('data-overlay-angle'))
|
const angle = Number(await overlay.getAttribute('data-overlay-angle'))
|
||||||
const angleXOffset = Math.cos(((angle - 180) * Math.PI) / 180) * px
|
const angleXOffset = Math.cos(((angle - 180) * Math.PI) / 180) * px
|
||||||
const angleYOffset = Math.sin(((angle - 180) * Math.PI) / 180) * px
|
const angleYOffset = Math.sin(((angle - 180) * Math.PI) / 180) * px
|
||||||
@ -437,7 +438,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
page
|
page
|
||||||
.locator(locator)
|
.locator(locator)
|
||||||
.boundingBox({ timeout: 5_000 })
|
.boundingBox({ timeout: 5_000 })
|
||||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
.then((box: any) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
||||||
codeLocator: page.locator('.cm-content'),
|
codeLocator: page.locator('.cm-content'),
|
||||||
crushKclCodeIntoOneLineAndThenMaybeSome: async () => {
|
crushKclCodeIntoOneLineAndThenMaybeSome: async () => {
|
||||||
const code = await page.locator('.cm-content').innerText()
|
const code = await page.locator('.cm-content').innerText()
|
||||||
@ -504,7 +505,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
) => {
|
) => {
|
||||||
if (cdpSession === null) {
|
if (cdpSession === null) {
|
||||||
// Use a fail safe if we can't simulate disconnect (on Safari)
|
// Use a fail safe if we can't simulate disconnect (on Safari)
|
||||||
return page.evaluate('window.tearDown()')
|
return page.evaluate('window.engineCommandManager.tearDown()')
|
||||||
}
|
}
|
||||||
|
|
||||||
return cdpSession?.send(
|
return cdpSession?.send(
|
||||||
@ -631,7 +632,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
panesOpen: async (paneIds: PaneId[]) => {
|
panesOpen: async (paneIds: PaneId[]) => {
|
||||||
return test?.step(`Setting ${paneIds} panes to be open`, async () => {
|
return test?.step(`Setting ${paneIds} panes to be open`, async () => {
|
||||||
await page.addInitScript(
|
await page.addInitScript(
|
||||||
({ PERSIST_MODELING_CONTEXT, paneIds }) => {
|
({ PERSIST_MODELING_CONTEXT, paneIds }: any) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
JSON.stringify({ openPanes: paneIds })
|
JSON.stringify({ openPanes: paneIds })
|
||||||
@ -722,14 +723,14 @@ export const makeTemplate: (
|
|||||||
|
|
||||||
const PLAYWRIGHT_DOWNLOAD_DIR = 'downloads-during-playwright'
|
const PLAYWRIGHT_DOWNLOAD_DIR = 'downloads-during-playwright'
|
||||||
|
|
||||||
export const getPlaywrightDownloadDir = (page: Page) => {
|
export const getPlaywrightDownloadDir = (rootDir: string) => {
|
||||||
return path.resolve(page.dir, PLAYWRIGHT_DOWNLOAD_DIR)
|
return path.resolve(rootDir, PLAYWRIGHT_DOWNLOAD_DIR)
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
|
const moveDownloadedFileTo = async (rootDir: string, toLocation: string) => {
|
||||||
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
||||||
|
|
||||||
const downloadDir = getPlaywrightDownloadDir(page)
|
const downloadDir = getPlaywrightDownloadDir(rootDir)
|
||||||
|
|
||||||
// Expect there to be at least one file
|
// Expect there to be at least one file
|
||||||
await expect
|
await expect
|
||||||
@ -756,6 +757,7 @@ export interface Paths {
|
|||||||
|
|
||||||
export const doExport = async (
|
export const doExport = async (
|
||||||
output: Models['OutputFormat_type'],
|
output: Models['OutputFormat_type'],
|
||||||
|
rootDir: string,
|
||||||
page: Page,
|
page: Page,
|
||||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
@ -836,7 +838,7 @@ export const doExport = async (
|
|||||||
// (declared in src/lib/exportSave)
|
// (declared in src/lib/exportSave)
|
||||||
// To remain consistent with our old web tests, we want to move some downloads
|
// To remain consistent with our old web tests, we want to move some downloads
|
||||||
// (images) to another directory.
|
// (images) to another directory.
|
||||||
await moveDownloadedFileTo(page, downloadLocation)
|
await moveDownloadedFileTo(rootDir, downloadLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -859,12 +861,6 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
|||||||
downloadThroughput: -1,
|
downloadThroughput: -1,
|
||||||
uploadThroughput: -1,
|
uploadThroughput: -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
// It seems it's best to give the browser about 3s to close things
|
|
||||||
// It's not super reliable but we have no real other choice for now
|
|
||||||
await page.waitForTimeout(3000)
|
|
||||||
|
|
||||||
await testInfo.tronApp?.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// settingsOverrides may need to be augmented to take more generic items,
|
// settingsOverrides may need to be augmented to take more generic items,
|
||||||
@ -936,107 +932,11 @@ let electronApp: ElectronApplication | undefined = undefined
|
|||||||
let context: BrowserContext | undefined = undefined
|
let context: BrowserContext | undefined = undefined
|
||||||
let page: Page | undefined = undefined
|
let page: Page | undefined = undefined
|
||||||
|
|
||||||
export async function setupElectron({
|
|
||||||
testInfo,
|
|
||||||
cleanProjectDir = true,
|
|
||||||
appSettings,
|
|
||||||
viewport,
|
|
||||||
}: {
|
|
||||||
testInfo: TestInfo
|
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
|
||||||
cleanProjectDir?: boolean
|
|
||||||
appSettings?: DeepPartial<Settings>
|
|
||||||
viewport: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
}
|
|
||||||
}): Promise<{
|
|
||||||
electronApp: ElectronApplication
|
|
||||||
context: BrowserContext
|
|
||||||
page: Page
|
|
||||||
dir: string
|
|
||||||
}> {
|
|
||||||
// create or otherwise clear the folder
|
|
||||||
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
|
||||||
try {
|
|
||||||
if (fsSync.existsSync(projectDirName) && cleanProjectDir) {
|
|
||||||
await fsp.rm(projectDirName, { recursive: true })
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanProjectDir) {
|
|
||||||
await fsp.mkdir(projectDirName)
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
args: ['.', '--no-sandbox'],
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
TEST_SETTINGS_FILE_KEY: projectDirName,
|
|
||||||
IS_PLAYWRIGHT: 'true',
|
|
||||||
},
|
|
||||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
|
||||||
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
|
||||||
: {}),
|
|
||||||
...(process.env.PLAYWRIGHT_RECORD_VIDEO
|
|
||||||
? {
|
|
||||||
recordVideo: {
|
|
||||||
dir: testInfo.snapshotPath(),
|
|
||||||
size: viewport,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do this once and then reuse window on subsequent calls.
|
|
||||||
if (!electronApp) {
|
|
||||||
electronApp = await electron.launch(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context || !page) {
|
|
||||||
context = electronApp.context()
|
|
||||||
page = await electronApp.firstWindow()
|
|
||||||
context.on('console', console.log)
|
|
||||||
page.on('console', console.log)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanProjectDir) {
|
|
||||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
|
||||||
const settingsOverrides = settingsToToml(
|
|
||||||
appSettings
|
|
||||||
? {
|
|
||||||
settings: {
|
|
||||||
...TEST_SETTINGS,
|
|
||||||
...appSettings,
|
|
||||||
app: {
|
|
||||||
...TEST_SETTINGS.app,
|
|
||||||
project_directory: projectDirName,
|
|
||||||
...appSettings.app,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
settings: {
|
|
||||||
...TEST_SETTINGS,
|
|
||||||
app: {
|
|
||||||
...TEST_SETTINGS.app,
|
|
||||||
project_directory: projectDirName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { electronApp, page, context, dir: projectDirName }
|
|
||||||
}
|
|
||||||
|
|
||||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||||
// enabled for chrome for now
|
// enabled for chrome for now
|
||||||
if (page.context().browser()?.browserType().name() === 'chromium') {
|
if (page.context().browser()?.browserType().name() === 'chromium') {
|
||||||
page.on('pageerror', (exception) => {
|
// No idea wtf exception is
|
||||||
|
page.on('pageerror', (exception: any) => {
|
||||||
if (isErrorWhitelisted(exception)) {
|
if (isErrorWhitelisted(exception)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
|
|
||||||
import { deg, getUtils, wiggleMove } from './test-utils'
|
import { deg, getUtils, wiggleMove } from './test-utils'
|
||||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
import { LineInputsType } from 'lang/std/sketchcombos'
|
||||||
|
@ -20,35 +20,40 @@ import { DeepPartial } from 'lib/types'
|
|||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
|
|
||||||
test.describe('Testing settings', () => {
|
test.describe('Testing settings', () => {
|
||||||
test(
|
test('Stored settings are validated and fall back to defaults', async ({
|
||||||
'Stored settings are validated and fall back to defaults',
|
page,
|
||||||
|
homePage,
|
||||||
|
tronApp,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
// with corrupted settings
|
// with corrupted settings
|
||||||
{
|
await tronApp.cleanProjectDir(
|
||||||
appSettings: TEST_SETTINGS_CORRUPTED as DeepPartial<Settings>,
|
TEST_SETTINGS_CORRUPTED as DeepPartial<Settings>
|
||||||
},
|
)
|
||||||
async ({ page, homePage }) => {
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
// Check the settings were reset
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
const storedSettings = tomlToSettings(
|
|
||||||
await page.evaluate(
|
// Check the settings were reset
|
||||||
({ settingsKey }) => localStorage.getItem(settingsKey) || '',
|
const storedSettings = tomlToSettings(
|
||||||
{ settingsKey: TEST_SETTINGS_KEY }
|
await page.evaluate(
|
||||||
)
|
({ settingsKey }) => localStorage.getItem(settingsKey) || '',
|
||||||
|
{ settingsKey: TEST_SETTINGS_KEY }
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
expect(storedSettings.settings?.app?.theme).toBe('dark')
|
expect(storedSettings.settings?.app?.theme).toBe('dark')
|
||||||
|
|
||||||
// Check that the invalid settings were changed to good defaults
|
// Check that the invalid settings were changed to good defaults
|
||||||
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
|
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
|
||||||
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo')
|
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo')
|
||||||
expect(storedSettings.settings?.app?.project_directory).toBe('')
|
expect(storedSettings.settings?.app?.project_directory).toBe('')
|
||||||
expect(storedSettings.settings?.project?.default_project_name).toBe(
|
expect(storedSettings.settings?.project?.default_project_name).toBe(
|
||||||
'project-$nnn'
|
'project-$nnn'
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// The behavior is actually broken. Parent always takes precedence
|
// The behavior is actually broken. Parent always takes precedence
|
||||||
test.fixme(
|
test.fixme(
|
||||||
@ -357,8 +362,6 @@ test.describe('Testing settings', () => {
|
|||||||
`Load desktop app with no settings file`,
|
`Load desktop app with no settings file`,
|
||||||
{
|
{
|
||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
// This is what makes no settings file get created
|
|
||||||
cleanProjectDir: false,
|
|
||||||
},
|
},
|
||||||
async ({ page }, testInfo) => {
|
async ({ page }, testInfo) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
@ -379,13 +382,17 @@ test.describe('Testing settings', () => {
|
|||||||
`Load desktop app with a settings file, but no project directory setting`,
|
`Load desktop app with a settings file, but no project directory setting`,
|
||||||
{
|
{
|
||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
},
|
||||||
|
async ({ context, page, tronApp }, testInfo) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
theme_color: '259',
|
theme_color: '259',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
},
|
|
||||||
async ({ context, page }, testInfo) => {
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Selectors and constants
|
// Selectors and constants
|
||||||
@ -405,15 +412,20 @@ test.describe('Testing settings', () => {
|
|||||||
'user settings reload on external change, on project and modeling view',
|
'user settings reload on external change, on project and modeling view',
|
||||||
{
|
{
|
||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
},
|
||||||
|
async ({ context, page, tronApp }, testInfo) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
app: {
|
app: {
|
||||||
// Doesn't matter what you set it to. It will
|
// Doesn't matter what you set it to. It will
|
||||||
// default to 264.5
|
// default to 264.5
|
||||||
theme_color: '0',
|
theme_color: '0',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
},
|
|
||||||
async ({ context, page }, testInfo) => {
|
|
||||||
const { dir: projectDirName } = await context.folderSetupFn(
|
const { dir: projectDirName } = await context.folderSetupFn(
|
||||||
async () => {}
|
async () => {}
|
||||||
)
|
)
|
||||||
@ -783,128 +795,136 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test(`Changing system theme preferences (via media query) should update UI and stream`, async ({
|
||||||
`Changing system theme preferences (via media query) should update UI and stream`,
|
page,
|
||||||
{
|
homePage,
|
||||||
// Override the settings so that the theme is set to `system`
|
tronApp,
|
||||||
appSettings: TEST_SETTINGS_DEFAULT_THEME,
|
}) => {
|
||||||
},
|
if (!tronApp) {
|
||||||
async ({ page, homePage }) => {
|
fail()
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
// Selectors and constants
|
|
||||||
const darkBackgroundCss = 'oklch(0.3012 0 264.5)'
|
|
||||||
const lightBackgroundCss = 'oklch(0.9911 0 264.5)'
|
|
||||||
const darkBackgroundColor: [number, number, number] = [27, 27, 27]
|
|
||||||
const lightBackgroundColor: [number, number, number] = [245, 245, 245]
|
|
||||||
const streamBackgroundPixelIsColor = async (
|
|
||||||
color: [number, number, number]
|
|
||||||
) => {
|
|
||||||
return u.getGreatestPixDiff({ x: 1000, y: 200 }, color)
|
|
||||||
}
|
|
||||||
const toolbar = page.locator('menu').filter({ hasText: 'Start Sketch' })
|
|
||||||
|
|
||||||
await test.step(`Test setup`, async () => {
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
await expect(toolbar).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Check the background color is light before`, async () => {
|
|
||||||
await expect(toolbar).toHaveCSS('background-color', lightBackgroundCss)
|
|
||||||
await expect
|
|
||||||
.poll(() => streamBackgroundPixelIsColor(lightBackgroundColor))
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Change media query preference to dark, emulating dusk with system theme`, async () => {
|
|
||||||
await page.emulateMedia({ colorScheme: 'dark' })
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Check the background color is dark after`, async () => {
|
|
||||||
await expect(toolbar).toHaveCSS('background-color', darkBackgroundCss)
|
|
||||||
await expect
|
|
||||||
.poll(() => streamBackgroundPixelIsColor(darkBackgroundColor))
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
await tronApp.cleanProjectDir({
|
||||||
`Turning off "Show debug panel" with debug panel open leaves no phantom panel`,
|
// Override the settings so that the theme is set to `system`
|
||||||
{
|
...TEST_SETTINGS_DEFAULT_THEME,
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Selectors and constants
|
||||||
|
const darkBackgroundCss = 'oklch(0.3012 0 264.5)'
|
||||||
|
const lightBackgroundCss = 'oklch(0.9911 0 264.5)'
|
||||||
|
const darkBackgroundColor: [number, number, number] = [27, 27, 27]
|
||||||
|
const lightBackgroundColor: [number, number, number] = [245, 245, 245]
|
||||||
|
const streamBackgroundPixelIsColor = async (
|
||||||
|
color: [number, number, number]
|
||||||
|
) => {
|
||||||
|
return u.getGreatestPixDiff({ x: 1000, y: 200 }, color)
|
||||||
|
}
|
||||||
|
const toolbar = page.locator('menu').filter({ hasText: 'Start Sketch' })
|
||||||
|
|
||||||
|
await test.step(`Test setup`, async () => {
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await expect(toolbar).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Check the background color is light before`, async () => {
|
||||||
|
await expect(toolbar).toHaveCSS('background-color', lightBackgroundCss)
|
||||||
|
await expect
|
||||||
|
.poll(() => streamBackgroundPixelIsColor(lightBackgroundColor))
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Change media query preference to dark, emulating dusk with system theme`, async () => {
|
||||||
|
await page.emulateMedia({ colorScheme: 'dark' })
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Check the background color is dark after`, async () => {
|
||||||
|
await expect(toolbar).toHaveCSS('background-color', darkBackgroundCss)
|
||||||
|
await expect
|
||||||
|
.poll(() => streamBackgroundPixelIsColor(darkBackgroundColor))
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`Turning off "Show debug panel" with debug panel open leaves no phantom panel`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
tronApp,
|
||||||
|
}) => {
|
||||||
|
if (!tronApp) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
await tronApp.cleanProjectDir({
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
// with debug panel open
|
// with debug panel open
|
||||||
// but "show debug panel" set to false
|
// but "show debug panel" set to false
|
||||||
appSettings: {
|
...TEST_SETTINGS,
|
||||||
...TEST_SETTINGS,
|
app: { ...TEST_SETTINGS.app, show_debug_panel: false },
|
||||||
app: { ...TEST_SETTINGS.app, show_debug_panel: false },
|
modeling: { ...TEST_SETTINGS.modeling },
|
||||||
modeling: { ...TEST_SETTINGS.modeling },
|
})
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
await context.addInitScript(async () => {
|
const u = await getUtils(page)
|
||||||
localStorage.setItem(
|
|
||||||
'persistModelingContext',
|
await context.addInitScript(async () => {
|
||||||
'{"openPanes":["debug"]}'
|
localStorage.setItem('persistModelingContext', '{"openPanes":["debug"]}')
|
||||||
|
})
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const resizeHandle = page.locator('.sidebar-resize-handles > div.block')
|
||||||
|
const debugPaneButton = page.getByTestId('debug-pane-button')
|
||||||
|
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const debugPaneOption = page.getByRole('option', {
|
||||||
|
name: 'Settings · app · show debug panel',
|
||||||
|
})
|
||||||
|
|
||||||
|
async function setShowDebugPanelTo(value: 'On' | 'Off') {
|
||||||
|
await commandsButton.click()
|
||||||
|
await debugPaneOption.click()
|
||||||
|
await page.getByRole('option', { name: value }).click()
|
||||||
|
await expect(
|
||||||
|
page.getByText(
|
||||||
|
`Set show debug panel to "${value === 'On'}" for this project`
|
||||||
)
|
)
|
||||||
})
|
).toBeVisible()
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Constants and locators
|
|
||||||
const resizeHandle = page.locator('.sidebar-resize-handles > div.block')
|
|
||||||
const debugPaneButton = page.getByTestId('debug-pane-button')
|
|
||||||
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
|
||||||
const debugPaneOption = page.getByRole('option', {
|
|
||||||
name: 'Settings · app · show debug panel',
|
|
||||||
})
|
|
||||||
|
|
||||||
async function setShowDebugPanelTo(value: 'On' | 'Off') {
|
|
||||||
await commandsButton.click()
|
|
||||||
await debugPaneOption.click()
|
|
||||||
await page.getByRole('option', { name: value }).click()
|
|
||||||
await expect(
|
|
||||||
page.getByText(
|
|
||||||
`Set show debug panel to "${value === 'On'}" for this project`
|
|
||||||
)
|
|
||||||
).toBeVisible()
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Initial load with corrupted settings`, async () => {
|
|
||||||
// Check that the debug panel is not visible
|
|
||||||
await expect(debugPaneButton).not.toBeVisible()
|
|
||||||
// Check the pane resize handle wrapper is not visible
|
|
||||||
await expect(resizeHandle).not.toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Open code pane to verify we see the resize handles`, async () => {
|
|
||||||
await u.openKclCodePanel()
|
|
||||||
await expect(resizeHandle).toBeVisible()
|
|
||||||
await u.closeKclCodePanel()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Turn on debug panel, open it`, async () => {
|
|
||||||
await setShowDebugPanelTo('On')
|
|
||||||
await expect(debugPaneButton).toBeVisible()
|
|
||||||
// We want the logic to clear the phantom panel, so we shouldn't see
|
|
||||||
// the real panel (and therefore the resize handle) yet
|
|
||||||
await expect(resizeHandle).not.toBeVisible()
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await expect(resizeHandle).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Turn off debug panel setting with it open`, async () => {
|
|
||||||
await setShowDebugPanelTo('Off')
|
|
||||||
await expect(debugPaneButton).not.toBeVisible()
|
|
||||||
await expect(resizeHandle).not.toBeVisible()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
await test.step(`Initial load with corrupted settings`, async () => {
|
||||||
|
// Check that the debug panel is not visible
|
||||||
|
await expect(debugPaneButton).not.toBeVisible()
|
||||||
|
// Check the pane resize handle wrapper is not visible
|
||||||
|
await expect(resizeHandle).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Open code pane to verify we see the resize handles`, async () => {
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await expect(resizeHandle).toBeVisible()
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Turn on debug panel, open it`, async () => {
|
||||||
|
await setShowDebugPanelTo('On')
|
||||||
|
await expect(debugPaneButton).toBeVisible()
|
||||||
|
// We want the logic to clear the phantom panel, so we shouldn't see
|
||||||
|
// the real panel (and therefore the resize handle) yet
|
||||||
|
await expect(resizeHandle).not.toBeVisible()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await expect(resizeHandle).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Turn off debug panel setting with it open`, async () => {
|
||||||
|
await setShowDebugPanelTo('Off')
|
||||||
|
await expect(debugPaneButton).not.toBeVisible()
|
||||||
|
await expect(resizeHandle).not.toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test(`Change inline units setting`, async ({
|
test(`Change inline units setting`, async ({
|
||||||
page,
|
page,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { Page } from '@playwright/test'
|
||||||
|
import { test, expect } from './zoo-test'
|
||||||
import { getUtils, createProject } from './test-utils'
|
import { getUtils, createProject } from './test-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
@ -35,7 +35,7 @@ test.fixme('Units menu', async ({ page, homePage }) => {
|
|||||||
test(
|
test(
|
||||||
'Successful export shows a success toast',
|
'Successful export shows a success toast',
|
||||||
{ tag: '@skipLocalEngine' },
|
{ tag: '@skipLocalEngine' },
|
||||||
async ({ page, homePage }) => {
|
async ({ page, homePage, tronApp }) => {
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -92,12 +92,17 @@ part001 = startSketchOn('-XZ')
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await u.clearAndCloseDebugPanel()
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
if (!tronApp?.projectDirName) {
|
||||||
|
fail()
|
||||||
|
}
|
||||||
|
|
||||||
await doExport(
|
await doExport(
|
||||||
{
|
{
|
||||||
type: 'gltf',
|
type: 'gltf',
|
||||||
storage: 'embedded',
|
storage: 'embedded',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
|
tronApp?.projectDirName,
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -465,7 +470,7 @@ test('Delete key does not navigate back', async ({ page, homePage }) => {
|
|||||||
await expect.poll(() => page.url()).not.toContain('/settings')
|
await expect.poll(() => page.url()).not.toContain('/settings')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Sketch on face', async ({ page, homePage, scene, cmdBar }) => {
|
test('Sketch on face', async ({ page, homePage, scene, cmdBar, toolbar }) => {
|
||||||
test.setTimeout(90_000)
|
test.setTimeout(90_000)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -491,25 +496,22 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
await scene.connectionEstablished()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).not.toBeDisabled()
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
||||||
await page.waitForTimeout(300)
|
|
||||||
|
|
||||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await toolbar.startSketchThenCallbackThenWaitUntilReady(async () => {
|
||||||
await u.doAndWaitForCmd(
|
await u.openAndClearDebugPanel()
|
||||||
() => page.mouse.click(625, 165),
|
await u.doAndWaitForCmd(
|
||||||
'default_camera_get_settings',
|
() => page.mouse.click(625, 165),
|
||||||
true
|
'default_camera_get_settings',
|
||||||
)
|
true
|
||||||
await page.waitForTimeout(150)
|
)
|
||||||
await u.closeDebugPanel()
|
await page.waitForTimeout(150)
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
const firstClickPosition = [612, 238]
|
const firstClickPosition = [612, 238]
|
||||||
const secondClickPosition = [661, 242]
|
const secondClickPosition = [661, 242]
|
||||||
|
@ -1,21 +1,11 @@
|
|||||||
import {
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
test as playwrightTestFn,
|
|
||||||
TestInfo as TestInfoPlaywright,
|
import { test as playwrightTestFn, ElectronApplication } from '@playwright/test'
|
||||||
BrowserContext as BrowserContextPlaywright,
|
|
||||||
Page as PagePlaywright,
|
|
||||||
TestDetails as TestDetailsPlaywright,
|
|
||||||
PlaywrightTestArgs,
|
|
||||||
PlaywrightTestOptions,
|
|
||||||
PlaywrightWorkerArgs,
|
|
||||||
PlaywrightWorkerOptions,
|
|
||||||
ElectronApplication,
|
|
||||||
} from '@playwright/test'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fixtures,
|
fixturesBasedOnProcessEnvPlatform,
|
||||||
Fixtures,
|
Fixtures,
|
||||||
AuthenticatedTronApp,
|
ElectronZoo,
|
||||||
AuthenticatedApp,
|
|
||||||
} from './fixtures/fixtureSetup'
|
} from './fixtures/fixtureSetup'
|
||||||
|
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
@ -23,9 +13,6 @@ import { DeepPartial } from 'lib/types'
|
|||||||
export { expect } from '@playwright/test'
|
export { expect } from '@playwright/test'
|
||||||
|
|
||||||
declare module '@playwright/test' {
|
declare module '@playwright/test' {
|
||||||
interface TestInfo {
|
|
||||||
tronApp?: AuthenticatedTronApp
|
|
||||||
}
|
|
||||||
interface BrowserContext {
|
interface BrowserContext {
|
||||||
folderSetupFn: (
|
folderSetupFn: (
|
||||||
cb: (dir: string) => Promise<void>
|
cb: (dir: string) => Promise<void>
|
||||||
@ -41,288 +28,29 @@ declare module '@playwright/test' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TestInfo = TestInfoPlaywright
|
// Each worker spawns a new thread, which will spawn its own ElectronZoo.
|
||||||
export type BrowserContext = BrowserContextPlaywright
|
// So in some sense there is an implicit pool.
|
||||||
export type Page = PagePlaywright
|
// For example, the variable just beneath this text is reused many times
|
||||||
export type TestDetails = TestDetailsPlaywright & {
|
// *for one worker*.
|
||||||
cleanProjectDir?: boolean
|
const electronZooInstance = new ElectronZoo()
|
||||||
appSettings?: DeepPartial<Settings>
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
||||||
// switch between web and electron if needed.
|
// switch between web and electron if needed.
|
||||||
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
|
const playwrightTestFnWithFixtures_ = playwrightTestFn.extend<{
|
||||||
|
tronApp?: ElectronZoo
|
||||||
// In JavaScript you cannot replace a function's body only (despite functions
|
}>({
|
||||||
// are themselves objects, which you'd expect a body property or something...)
|
tronApp: async ({}, use, testInfo) => {
|
||||||
// So we must redefine the function and then re-attach properties.
|
if (process.env.PLATFORM === 'web') {
|
||||||
type PWFunction = (
|
await use(undefined)
|
||||||
args: PlaywrightTestArgs &
|
return
|
||||||
Fixtures &
|
|
||||||
PlaywrightWorkerArgs &
|
|
||||||
PlaywrightTestOptions &
|
|
||||||
PlaywrightWorkerOptions & {
|
|
||||||
electronApp?: ElectronApplication
|
|
||||||
},
|
|
||||||
testInfo: TestInfo
|
|
||||||
) => void | Promise<void>
|
|
||||||
|
|
||||||
let firstUrl = ''
|
|
||||||
|
|
||||||
export const test = (
|
|
||||||
desc: string,
|
|
||||||
objOrFn: PWFunction | TestDetails,
|
|
||||||
fnMaybe?: PWFunction
|
|
||||||
) => {
|
|
||||||
const hasTestConf = typeof objOrFn === 'object'
|
|
||||||
const fn = hasTestConf ? fnMaybe : objOrFn
|
|
||||||
|
|
||||||
return pwTestFnWithFixtures(
|
|
||||||
desc,
|
|
||||||
hasTestConf ? objOrFn : {},
|
|
||||||
async (
|
|
||||||
{
|
|
||||||
page,
|
|
||||||
context,
|
|
||||||
cmdBar,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
scene,
|
|
||||||
homePage,
|
|
||||||
request,
|
|
||||||
playwright,
|
|
||||||
browser,
|
|
||||||
acceptDownloads,
|
|
||||||
bypassCSP,
|
|
||||||
colorScheme,
|
|
||||||
clientCertificates,
|
|
||||||
deviceScaleFactor,
|
|
||||||
extraHTTPHeaders,
|
|
||||||
geolocation,
|
|
||||||
hasTouch,
|
|
||||||
httpCredentials,
|
|
||||||
ignoreHTTPSErrors,
|
|
||||||
isMobile,
|
|
||||||
javaScriptEnabled,
|
|
||||||
locale,
|
|
||||||
offline,
|
|
||||||
permissions,
|
|
||||||
proxy,
|
|
||||||
storageState,
|
|
||||||
timezoneId,
|
|
||||||
userAgent,
|
|
||||||
viewport,
|
|
||||||
baseURL,
|
|
||||||
contextOptions,
|
|
||||||
actionTimeout,
|
|
||||||
navigationTimeout,
|
|
||||||
serviceWorkers,
|
|
||||||
testIdAttribute,
|
|
||||||
browserName,
|
|
||||||
defaultBrowserType,
|
|
||||||
headless,
|
|
||||||
channel,
|
|
||||||
launchOptions,
|
|
||||||
connectOptions,
|
|
||||||
screenshot,
|
|
||||||
trace,
|
|
||||||
video,
|
|
||||||
},
|
|
||||||
testInfo
|
|
||||||
) => {
|
|
||||||
// To switch to web, use PLATFORM=web environment variable.
|
|
||||||
// Only use this for debugging, since the playwright tracer is busted
|
|
||||||
// for electron.
|
|
||||||
|
|
||||||
let tronApp
|
|
||||||
|
|
||||||
if (process.env.PLATFORM === 'web') {
|
|
||||||
tronApp = new AuthenticatedApp(context, page, testInfo)
|
|
||||||
} else {
|
|
||||||
tronApp = new AuthenticatedTronApp(context, page, testInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixtures: Fixtures = { cmdBar, editor, toolbar, scene, homePage }
|
|
||||||
if (tronApp instanceof AuthenticatedTronApp) {
|
|
||||||
const options = {
|
|
||||||
fixtures,
|
|
||||||
}
|
|
||||||
if (hasTestConf) {
|
|
||||||
Object.assign(options, {
|
|
||||||
appSettings: objOrFn?.appSettings,
|
|
||||||
cleanProjectDir: objOrFn?.cleanProjectDir,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
await tronApp.initialise(options)
|
|
||||||
} else {
|
|
||||||
await tronApp.initialise('')
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to patch this because addInitScript will bind too late in our
|
|
||||||
// electron tests, never running. We need to call reload() after each call
|
|
||||||
// to guarantee it runs.
|
|
||||||
const oldContextAddInitScript = tronApp.context.addInitScript
|
|
||||||
tronApp.context.addInitScript = async function (a, b) {
|
|
||||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
|
||||||
// This code works perfectly fine.
|
|
||||||
await oldContextAddInitScript.apply(this, [a, b])
|
|
||||||
await tronApp.page.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
// No idea why we mix and match page and context's addInitScript but we do
|
|
||||||
const oldPageAddInitScript = tronApp.page.addInitScript
|
|
||||||
tronApp.page.addInitScript = async function (a: any, b: any) {
|
|
||||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
|
||||||
// This code works perfectly fine.
|
|
||||||
await oldPageAddInitScript.apply(this, [a, b])
|
|
||||||
await tronApp.page.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a consistent way to resize the page across electron and web.
|
|
||||||
// (lee) I had to do everything in the book to make electron change its
|
|
||||||
// damn window size. I succeeded in making it consistently and reliably
|
|
||||||
// do it after a whole afternoon.
|
|
||||||
tronApp.page.setBodyDimensions = async function (dims: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
}) {
|
|
||||||
await tronApp.page.setViewportSize(dims)
|
|
||||||
|
|
||||||
if (!(tronApp instanceof AuthenticatedTronApp)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await tronApp.electronApp?.evaluateHandle(async ({ app }, dims) => {
|
|
||||||
// @ts-ignore sorry jon but see comment in main.ts why this is ignored
|
|
||||||
await app.resizeWindow(dims.width, dims.height)
|
|
||||||
}, dims)
|
|
||||||
|
|
||||||
return tronApp.page.evaluate(
|
|
||||||
async (dims: { width: number; height: number }) => {
|
|
||||||
await window.electron.resizeWindow(dims.width, dims.height)
|
|
||||||
window.document.body.style.width = dims.width + 'px'
|
|
||||||
window.document.body.style.height = dims.height + 'px'
|
|
||||||
window.document.documentElement.style.width = dims.width + 'px'
|
|
||||||
window.document.documentElement.style.height = dims.height + 'px'
|
|
||||||
},
|
|
||||||
dims
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await tronApp.page.setBodyDimensions(tronApp.viewPortSize)
|
|
||||||
|
|
||||||
// We need to expose this in order for some tests that require folder
|
|
||||||
// creation. Before they used to do this by their own electronSetup({...})
|
|
||||||
// calls.
|
|
||||||
if (tronApp instanceof AuthenticatedTronApp) {
|
|
||||||
tronApp.context.folderSetupFn = async function (fn) {
|
|
||||||
return fn(tronApp.dir)
|
|
||||||
.then(() => tronApp.page.reload())
|
|
||||||
.then(() => ({
|
|
||||||
dir: tronApp.dir,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!firstUrl) {
|
|
||||||
await tronApp.page.getByText('Your Projects').count()
|
|
||||||
firstUrl = tronApp.page.url()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Due to the app controlling its own window context we need to inject new
|
|
||||||
// options and context here.
|
|
||||||
// NOTE TO LEE: Seems to destroy page context when calling an electron loadURL.
|
|
||||||
// await tronApp.electronApp.evaluate(({ app }) => {
|
|
||||||
// return app.reuseWindowForTest();
|
|
||||||
// });
|
|
||||||
|
|
||||||
await tronApp.electronApp?.evaluate(({ app }, projectDirName) => {
|
|
||||||
// @ts-ignore can't declaration merge see main.ts
|
|
||||||
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
|
||||||
}, tronApp.dir)
|
|
||||||
|
|
||||||
// Always start at the root view
|
|
||||||
await tronApp.page.goto(firstUrl)
|
|
||||||
|
|
||||||
// Force a hard reload, destroying the stream and other state
|
|
||||||
await tronApp.page.reload()
|
|
||||||
|
|
||||||
// tsc aint smart enough to know this'll never be undefined
|
|
||||||
// but I dont blame it, the logic to know is complex
|
|
||||||
if (fn) {
|
|
||||||
await fn(
|
|
||||||
{
|
|
||||||
context: tronApp.context,
|
|
||||||
page: tronApp.page,
|
|
||||||
electronApp:
|
|
||||||
tronApp instanceof AuthenticatedTronApp
|
|
||||||
? tronApp.electronApp
|
|
||||||
: undefined,
|
|
||||||
...fixtures,
|
|
||||||
request,
|
|
||||||
playwright,
|
|
||||||
browser,
|
|
||||||
acceptDownloads,
|
|
||||||
bypassCSP,
|
|
||||||
colorScheme,
|
|
||||||
clientCertificates,
|
|
||||||
deviceScaleFactor,
|
|
||||||
extraHTTPHeaders,
|
|
||||||
geolocation,
|
|
||||||
hasTouch,
|
|
||||||
httpCredentials,
|
|
||||||
ignoreHTTPSErrors,
|
|
||||||
isMobile,
|
|
||||||
javaScriptEnabled,
|
|
||||||
locale,
|
|
||||||
offline,
|
|
||||||
permissions,
|
|
||||||
proxy,
|
|
||||||
storageState,
|
|
||||||
timezoneId,
|
|
||||||
userAgent,
|
|
||||||
viewport,
|
|
||||||
baseURL,
|
|
||||||
contextOptions,
|
|
||||||
actionTimeout,
|
|
||||||
navigationTimeout,
|
|
||||||
serviceWorkers,
|
|
||||||
testIdAttribute,
|
|
||||||
browserName,
|
|
||||||
defaultBrowserType,
|
|
||||||
headless,
|
|
||||||
channel,
|
|
||||||
launchOptions,
|
|
||||||
connectOptions,
|
|
||||||
screenshot,
|
|
||||||
trace,
|
|
||||||
video,
|
|
||||||
},
|
|
||||||
testInfo
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
testInfo.tronApp =
|
|
||||||
tronApp instanceof AuthenticatedTronApp ? tronApp : undefined
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ZooTest = typeof test
|
await use(electronZooInstance)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
test.describe = pwTestFnWithFixtures.describe
|
const test = playwrightTestFnWithFixtures_.extend<Fixtures>(
|
||||||
test.beforeEach = pwTestFnWithFixtures.beforeEach
|
fixturesBasedOnProcessEnvPlatform
|
||||||
test.afterEach = pwTestFnWithFixtures.afterEach
|
)
|
||||||
test.step = pwTestFnWithFixtures.step
|
|
||||||
test.skip = pwTestFnWithFixtures.skip
|
export { test }
|
||||||
test.setTimeout = pwTestFnWithFixtures.setTimeout
|
|
||||||
test.fixme = pwTestFnWithFixtures.fixme as unknown as ZooTest
|
|
||||||
test.only = pwTestFnWithFixtures.only
|
|
||||||
test.fail = pwTestFnWithFixtures.fail
|
|
||||||
test.slow = pwTestFnWithFixtures.slow
|
|
||||||
test.beforeAll = pwTestFnWithFixtures.beforeAll
|
|
||||||
test.afterAll = pwTestFnWithFixtures.afterAll
|
|
||||||
test.use = pwTestFnWithFixtures.use
|
|
||||||
test.expect = pwTestFnWithFixtures.expect
|
|
||||||
test.extend = pwTestFnWithFixtures.extend
|
|
||||||
test.info = pwTestFnWithFixtures.info
|
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
"files:flip-to-nightly:windows": "./scripts/flip-files-to-nightly.ps1",
|
"files:flip-to-nightly:windows": "./scripts/flip-files-to-nightly.ps1",
|
||||||
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
||||||
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
||||||
"postinstall": "./node_modules/.bin/electron-rebuild",
|
"postinstall": "yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install && ./node_modules/.bin/electron-rebuild",
|
||||||
"make:dev": "make dev",
|
"make:dev": "make dev",
|
||||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||||
"generate:samples-manifest": "cd public/kcl-samples && node generate-manifest.js",
|
"generate:samples-manifest": "cd public/kcl-samples && node generate-manifest.js",
|
||||||
|
31
rust/Cargo.lock
generated
31
rust/Cargo.lock
generated
@ -1783,7 +1783,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1794,7 +1794,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1813,7 +1813,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1822,7 +1822,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1843,7 +1843,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1863,7 +1863,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1931,7 +1931,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.48"
|
version = "0.3.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
@ -1946,7 +1946,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
@ -1959,7 +1959,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1973,7 +1973,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bson",
|
"bson",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
@ -3097,15 +3097,14 @@ checksum = "e6cd655523701785087f69900df39892fb7b9b0721aa67682f571c38c32ac58a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.8"
|
version = "0.17.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"libc",
|
"libc",
|
||||||
"spin",
|
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@ -3560,12 +3559,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.9.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.48"
|
version = "0.1.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -72,7 +72,7 @@ impl Build {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_client(sh: &Shell, version: &str, release_tag: &str, target: &Target) -> anyhow::Result<()> {
|
fn build_client(sh: &Shell, version: &str, release_tag: &str, target: &Target) -> anyhow::Result<()> {
|
||||||
let bundle_path = Path::new("server");
|
let bundle_path = Path::new("kcl-language-server/server");
|
||||||
sh.create_dir(bundle_path)?;
|
sh.create_dir(bundle_path)?;
|
||||||
sh.copy_file(&target.server_path, bundle_path)?;
|
sh.copy_file(&target.server_path, bundle_path)?;
|
||||||
if let Some(symbols_path) = &target.symbols_path {
|
if let Some(symbols_path) = &target.symbols_path {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -97,7 +97,7 @@ async function getServer(
|
|||||||
'You need to manually clone the kcl-lsp repository and ' +
|
'You need to manually clone the kcl-lsp repository and ' +
|
||||||
'run `cargo install` to build the language server from sources. ' +
|
'run `cargo install` to build the language server from sources. ' +
|
||||||
'If you feel that your platform should be supported, please create an issue ' +
|
'If you feel that your platform should be supported, please create an issue ' +
|
||||||
'about that [here](https://github.com/kittycad/kcl-lsp/issues) and we ' +
|
'about that [here](https://github.com/kittycad/modeling-app/issues) and we ' +
|
||||||
'will consider it.'
|
'will consider it.'
|
||||||
)
|
)
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.48"
|
version = "0.2.49"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
const part001 = startSketchOn('XY')
|
startProfileAt([0, 0], startSketchOn("XY"))
|
||||||
|> startProfileAt([0,0], %)
|
|> xLine(length = 10, tag = $line000)
|
||||||
|> line(end = [0, 10], tag = $thing)
|
|> yLine(length = 10, tag = $line001)
|
||||||
|> line(end = [10, 0])
|
|> xLine(endAbsolute = profileStartX(%), tag = $line002)
|
||||||
|> line(end = [0, -10], tag = $thing2)
|
|> close(tag = $line003)
|
||||||
|> close()
|
|> extrude(length = 10)
|
||||||
|> extrude(length = 10)
|
|> fillet(
|
||||||
|> fillet(radius = 0.5, tags = [thing, thing])
|
radius = 1,
|
||||||
|
tags = [
|
||||||
|
line003,
|
||||||
|
getNextAdjacentEdge(line000),
|
||||||
|
getPreviousAdjacentEdge(line001)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@ -27,11 +27,14 @@ async fn kcl_test_fillet_duplicate_tags() {
|
|||||||
let code = kcl_input!("fillet_duplicate_tags");
|
let code = kcl_input!("fillet_duplicate_tags");
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm, None).await;
|
let result = execute_and_snapshot(code, UnitLength::Mm, None).await;
|
||||||
assert!(result.is_err());
|
let err = result.expect_err("Code should have failed due to the duplicate edges being filletted");
|
||||||
|
|
||||||
|
let err = err.as_kcl_error().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.err().unwrap().to_string(),
|
err.message(),
|
||||||
r#"type: KclErrorDetails { source_ranges: [SourceRange([229, 272, 0])], message: "Duplicate tags are not allowed." }"#,
|
"The same edge ID is being referenced multiple times, which is not allowed. Please select a different edge"
|
||||||
);
|
);
|
||||||
|
assert_eq!(err.source_ranges().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -857,7 +860,7 @@ part = rectShape([0, 0], 20, 20)
|
|||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err.error.message(),
|
err.error.message(),
|
||||||
"This function expected this argument to be of type SketchOrSurface but it's actually of type string (text)"
|
"This function expected the input argument to be of type SketchOrSurface but it's actually of type string (text)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2103,7 +2106,7 @@ async fn kcl_test_better_type_names() {
|
|||||||
},
|
},
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
};
|
};
|
||||||
assert_eq!(err, "This function expected this argument to be of type SolidSet but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
assert_eq!(err, "This function expected the input argument to be of type SolidSet but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
@ -48,6 +48,15 @@ impl ExecErrorWithState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExecError {
|
||||||
|
pub fn as_kcl_error(&self) -> Option<&crate::KclError> {
|
||||||
|
let ExecError::Kcl(k) = &self else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(&k.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ExecError> for ExecErrorWithState {
|
impl From<ExecError> for ExecErrorWithState {
|
||||||
fn from(error: ExecError) -> Self {
|
fn from(error: ExecError) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -596,12 +596,11 @@ impl ExecutorContext {
|
|||||||
self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
|
self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
|
||||||
.await?
|
.await?
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
// The module didn't have a return value. Currently,
|
exec_state.warn(CompilationError::err(
|
||||||
// the only way to have a return value is with the final
|
metadata.source_range,
|
||||||
// statement being an expression statement.
|
"Imported module has no return value. The last statement of the module must be an expression, usually the Solid.",
|
||||||
//
|
));
|
||||||
// TODO: Make a warning when we support them in the
|
|
||||||
// execution phase.
|
|
||||||
let mut new_meta = vec![metadata.to_owned()];
|
let mut new_meta = vec![metadata.to_owned()];
|
||||||
new_meta.extend(meta);
|
new_meta.extend(meta);
|
||||||
KclValue::KclNone {
|
KclValue::KclNone {
|
||||||
@ -1187,7 +1186,7 @@ impl Node<CallExpressionKw> {
|
|||||||
},
|
},
|
||||||
self.into(),
|
self.into(),
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
exec_state.mod_local.pipe_value.clone().map(|v| Arg::new(v, callsite)),
|
||||||
);
|
);
|
||||||
match ctx.stdlib.get_either(fn_name) {
|
match ctx.stdlib.get_either(fn_name) {
|
||||||
FunctionKind::Core(func) => {
|
FunctionKind::Core(func) => {
|
||||||
@ -1349,7 +1348,7 @@ impl Node<CallExpression> {
|
|||||||
fn_args,
|
fn_args,
|
||||||
self.into(),
|
self.into(),
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
exec_state.mod_local.pipe_value.clone().map(|v| Arg::new(v, callsite)),
|
||||||
);
|
);
|
||||||
let mut return_value = {
|
let mut return_value = {
|
||||||
// Don't early-return in this block.
|
// Don't early-return in this block.
|
||||||
@ -2000,7 +1999,11 @@ impl FunctionSource {
|
|||||||
args,
|
args,
|
||||||
source_range,
|
source_range,
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
exec_state
|
||||||
|
.mod_local
|
||||||
|
.pipe_value
|
||||||
|
.clone()
|
||||||
|
.map(|v| Arg::new(v, source_range)),
|
||||||
);
|
);
|
||||||
|
|
||||||
func(exec_state, args).await.map(Some)
|
func(exec_state, args).await.map(Some)
|
||||||
|
@ -659,7 +659,11 @@ impl KclValue {
|
|||||||
args,
|
args,
|
||||||
source_range,
|
source_range,
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
exec_state
|
||||||
|
.mod_local
|
||||||
|
.pipe_value
|
||||||
|
.clone()
|
||||||
|
.map(|v| Arg::new(v, source_range)),
|
||||||
);
|
);
|
||||||
let result = func(exec_state, args).await.map(Some);
|
let result = func(exec_state, args).await.map(Some);
|
||||||
exec_state.mut_stack().pop_env();
|
exec_state.mut_stack().pop_env();
|
||||||
|
@ -844,11 +844,23 @@ fn object_property(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
|||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let expr = expression
|
let expr = match expression
|
||||||
.context(expected(
|
.context(expected(
|
||||||
"the value which you're setting the property to, e.g. in 'height: 4', the value is 4",
|
"the value which you're setting the property to, e.g. in 'height: 4', the value is 4",
|
||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)
|
||||||
|
{
|
||||||
|
Ok(expr) => expr,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(ErrMode::Cut(
|
||||||
|
CompilationError::fatal(
|
||||||
|
SourceRange::from(sep),
|
||||||
|
"This property has a label, but no value. Put some value after the equals sign",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = Node {
|
let result = Node {
|
||||||
start: key.start,
|
start: key.start,
|
||||||
@ -2810,7 +2822,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
|
|||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ArgPlace {
|
enum ArgPlace {
|
||||||
NonCode(Node<NonCodeNode>),
|
NonCode(Node<NonCodeNode>),
|
||||||
LabeledArg(LabeledArg),
|
LabeledArg(LabeledArg),
|
||||||
UnlabeledArg(Expr),
|
UnlabeledArg(Expr),
|
||||||
@ -2827,22 +2839,34 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
|
|||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
let (args, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = args.into_iter().enumerate().try_fold(
|
let (args, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = args.into_iter().enumerate().try_fold(
|
||||||
(Vec::new(), BTreeMap::new()),
|
(Vec::new(), BTreeMap::new()),
|
||||||
|(mut args, mut non_code_nodes), (i, e)| {
|
|(mut args, mut non_code_nodes), (index, e)| {
|
||||||
match e {
|
match e {
|
||||||
ArgPlace::NonCode(x) => {
|
ArgPlace::NonCode(x) => {
|
||||||
non_code_nodes.insert(i, vec![x]);
|
non_code_nodes.insert(index, vec![x]);
|
||||||
}
|
}
|
||||||
ArgPlace::LabeledArg(x) => {
|
ArgPlace::LabeledArg(x) => {
|
||||||
args.push(x);
|
args.push(x);
|
||||||
}
|
}
|
||||||
ArgPlace::UnlabeledArg(arg) => {
|
ArgPlace::UnlabeledArg(arg) => {
|
||||||
return Err(ErrMode::Cut(
|
let followed_by_equals = peek((opt(whitespace), equals)).parse_next(i).is_ok();
|
||||||
CompilationError::fatal(
|
let err = if followed_by_equals {
|
||||||
SourceRange::from(arg),
|
ErrMode::Cut(
|
||||||
"This argument needs a label, but it doesn't have one",
|
CompilationError::fatal(
|
||||||
|
SourceRange::from(arg),
|
||||||
|
"This argument has a label, but no value. Put some value after the equals sign",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
)
|
)
|
||||||
.into(),
|
} else {
|
||||||
));
|
ErrMode::Cut(
|
||||||
|
CompilationError::fatal(
|
||||||
|
SourceRange::from(arg),
|
||||||
|
"This argument needs a label, but it doesn't have one",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((args, non_code_nodes))
|
Ok((args, non_code_nodes))
|
||||||
@ -4678,6 +4702,42 @@ baz = 2
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sensible_error_when_missing_rhs_of_kw_arg() {
|
||||||
|
for (i, program) in ["f(x, y=)"].into_iter().enumerate() {
|
||||||
|
let tokens = crate::parsing::token::lex(program, ModuleId::default()).unwrap();
|
||||||
|
let err = fn_call_kw.parse(tokens.as_slice()).unwrap_err();
|
||||||
|
let cause = err.inner().cause.as_ref().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
cause.message, "This argument has a label, but no value. Put some value after the equals sign",
|
||||||
|
"failed test {i}: {program}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cause.source_range.start(),
|
||||||
|
program.find("y").unwrap(),
|
||||||
|
"failed test {i}: {program}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sensible_error_when_missing_rhs_of_obj_property() {
|
||||||
|
for (i, program) in ["{x = 1, y =}"].into_iter().enumerate() {
|
||||||
|
let tokens = crate::parsing::token::lex(program, ModuleId::default()).unwrap();
|
||||||
|
let err = object.parse(tokens.as_slice()).unwrap_err();
|
||||||
|
let cause = err.inner().cause.as_ref().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
cause.message, "This property has a label, but no value. Put some value after the equals sign",
|
||||||
|
"failed test {i}: {program}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cause.source_range.start(),
|
||||||
|
program.rfind('=').unwrap(),
|
||||||
|
"failed test {i}: {program}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -159,6 +159,49 @@ impl Args {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a labelled keyword arg, check it's an array, and return all items in the array
|
||||||
|
/// plus their source range.
|
||||||
|
pub(crate) fn kw_arg_array_and_source<'a, T>(&'a self, label: &str) -> Result<Vec<(T, SourceRange)>, KclError>
|
||||||
|
where
|
||||||
|
T: FromKclValue<'a>,
|
||||||
|
{
|
||||||
|
let Some(arg) = self.kw_args.labeled.get(label) else {
|
||||||
|
let err = KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
message: format!("This function requires a keyword argument '{label}'"),
|
||||||
|
});
|
||||||
|
return Err(err);
|
||||||
|
};
|
||||||
|
let Some(array) = arg.value.as_array() else {
|
||||||
|
let err = KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges: vec![arg.source_range],
|
||||||
|
message: format!(
|
||||||
|
"Expected an array of {} but found {}",
|
||||||
|
type_name::<T>(),
|
||||||
|
arg.value.human_friendly_type()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return Err(err);
|
||||||
|
};
|
||||||
|
array
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
let source = SourceRange::from(item);
|
||||||
|
let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
|
||||||
|
KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges: arg.source_ranges(),
|
||||||
|
message: format!(
|
||||||
|
"Expected a {} but found {}",
|
||||||
|
type_name::<T>(),
|
||||||
|
arg.value.human_friendly_type()
|
||||||
|
),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
Ok((val, source))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the unlabeled keyword argument. If not set, returns None.
|
/// Get the unlabeled keyword argument. If not set, returns None.
|
||||||
pub(crate) fn unlabeled_kw_arg_unconverted(&self) -> Option<&Arg> {
|
pub(crate) fn unlabeled_kw_arg_unconverted(&self) -> Option<&Arg> {
|
||||||
self.kw_args
|
self.kw_args
|
||||||
@ -184,7 +227,7 @@ impl Args {
|
|||||||
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
||||||
let expected_type_name = tynm::type_name::<T>();
|
let expected_type_name = tynm::type_name::<T>();
|
||||||
let actual_type_name = arg.value.human_friendly_type();
|
let actual_type_name = arg.value.human_friendly_type();
|
||||||
let msg_base = format!("This function expected this argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
let msg_base = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
||||||
let suggestion = match (expected_type_name.as_str(), actual_type_name) {
|
let suggestion = match (expected_type_name.as_str(), actual_type_name) {
|
||||||
("SolidSet", "Sketch") => Some(
|
("SolidSet", "Sketch") => Some(
|
||||||
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
||||||
|
@ -5,7 +5,6 @@ use kcl_derive_docs::stdlib;
|
|||||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::CutType, ModelingCmd};
|
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::CutType, ModelingCmd};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
|
||||||
use super::utils::unique_count;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid},
|
execution::{ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid},
|
||||||
@ -19,9 +18,11 @@ pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
|||||||
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
let solid = args.get_unlabeled_kw_arg("solid")?;
|
||||||
let length = args.get_kw_arg("length")?;
|
let length = args.get_kw_arg("length")?;
|
||||||
let tags = args.get_kw_arg("tags")?;
|
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||||
let tag = args.get_kw_arg_opt("tag")?;
|
let tag = args.get_kw_arg_opt("tag")?;
|
||||||
|
|
||||||
|
super::fillet::validate_unique(&tags)?;
|
||||||
|
let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
|
||||||
let value = inner_chamfer(solid, length, tags, tag, exec_state, args).await?;
|
let value = inner_chamfer(solid, length, tags, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Solid { value })
|
Ok(KclValue::Solid { value })
|
||||||
}
|
}
|
||||||
@ -109,15 +110,6 @@ async fn inner_chamfer(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<Solid>, KclError> {
|
) -> Result<Box<Solid>, KclError> {
|
||||||
// Check if tags contains any duplicate values.
|
|
||||||
let unique_tags = unique_count(tags.clone());
|
|
||||||
if unique_tags != tags.len() {
|
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
|
||||||
message: "Duplicate tags are not allowed.".to_string(),
|
|
||||||
source_ranges: vec![args.source_range],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you try and tag multiple edges with a tagged chamfer, we want to return an
|
// If you try and tag multiple edges with a tagged chamfer, we want to return an
|
||||||
// error to the user that they can only tag one edge at a time.
|
// error to the user that they can only tag one edge at a time.
|
||||||
if tag.is_some() && tags.len() > 1 {
|
if tag.is_some() && tags.len() > 1 {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Standard library fillets.
|
//! Standard library fillets.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use kcl_derive_docs::stdlib;
|
use kcl_derive_docs::stdlib;
|
||||||
use kcmc::{
|
use kcmc::{
|
||||||
each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, shared::CutType,
|
each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, shared::CutType,
|
||||||
@ -11,13 +12,13 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::utils::unique_count;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
execution::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
std::Args,
|
std::Args,
|
||||||
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A tag or a uuid of an edge.
|
/// A tag or a uuid of an edge.
|
||||||
@ -40,13 +41,39 @@ impl EdgeReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]) -> Result<(), KclError> {
|
||||||
|
// Check if tags contains any duplicate values.
|
||||||
|
let mut tag_counts: IndexMap<&T, Vec<SourceRange>> = Default::default();
|
||||||
|
for tag in tags {
|
||||||
|
tag_counts.entry(&tag.0).or_insert(Vec::new()).push(tag.1);
|
||||||
|
}
|
||||||
|
let mut duplicate_tags_source = Vec::new();
|
||||||
|
for (_tag, count) in tag_counts {
|
||||||
|
if count.len() > 1 {
|
||||||
|
duplicate_tags_source.extend(count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !duplicate_tags_source.is_empty() {
|
||||||
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
|
message: "The same edge ID is being referenced multiple times, which is not allowed. Please select a different edge".to_string(),
|
||||||
|
source_ranges: duplicate_tags_source,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create fillets on tagged paths.
|
/// Create fillets on tagged paths.
|
||||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
|
// Get all args:
|
||||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
let solid = args.get_unlabeled_kw_arg("solid")?;
|
||||||
let radius = args.get_kw_arg("radius")?;
|
let radius = args.get_kw_arg("radius")?;
|
||||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||||
let tags = args.get_kw_arg("tags")?;
|
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||||
let tag = args.get_kw_arg_opt("tag")?;
|
let tag = args.get_kw_arg_opt("tag")?;
|
||||||
|
|
||||||
|
// Run the function.
|
||||||
|
validate_unique(&tags)?;
|
||||||
|
let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
|
||||||
let value = inner_fillet(solid, radius, tags, tolerance, tag, exec_state, args).await?;
|
let value = inner_fillet(solid, radius, tags, tolerance, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Solid { value })
|
Ok(KclValue::Solid { value })
|
||||||
}
|
}
|
||||||
@ -129,15 +156,6 @@ async fn inner_fillet(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<Solid>, KclError> {
|
) -> Result<Box<Solid>, KclError> {
|
||||||
// Check if tags contains any duplicate values.
|
|
||||||
let unique_tags = unique_count(tags.clone());
|
|
||||||
if unique_tags != tags.len() {
|
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
|
||||||
message: "Duplicate tags are not allowed.".to_string(),
|
|
||||||
source_ranges: vec![args.source_range],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut solid = solid.clone();
|
let mut solid = solid.clone();
|
||||||
for edge_tag in tags {
|
for edge_tag in tags {
|
||||||
let edge_id = edge_tag.get_engine_id(exec_state, &args)?;
|
let edge_id = edge_tag.get_engine_id(exec_state, &args)?;
|
||||||
@ -432,3 +450,22 @@ pub(crate) fn default_tolerance(units: &UnitLength) -> f64 {
|
|||||||
UnitLength::M => 0.001,
|
UnitLength::M => 0.001,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_unique() {
|
||||||
|
let dup_a = SourceRange::from([1, 3, 0]);
|
||||||
|
let dup_b = SourceRange::from([10, 30, 0]);
|
||||||
|
// Two entries are duplicates (abc) with different source ranges.
|
||||||
|
let tags = vec![("abc", dup_a), ("abc", dup_b), ("def", SourceRange::from([2, 4, 0]))];
|
||||||
|
let actual = validate_unique(&tags);
|
||||||
|
// Both the duplicates should show up as errors, with both of the
|
||||||
|
// source ranges they correspond to.
|
||||||
|
// But the unique source range 'def' should not.
|
||||||
|
let expected = vec![dup_a, dup_b];
|
||||||
|
assert_eq!(actual.err().unwrap().source_ranges(), expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{collections::HashSet, f64::consts::PI};
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
use kittycad_modeling_cmds::shared::Angle;
|
use kittycad_modeling_cmds::shared::Angle;
|
||||||
|
|
||||||
@ -8,16 +8,6 @@ use crate::{
|
|||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Count the number of unique items in a `Vec` in O(n) time.
|
|
||||||
pub(crate) fn unique_count<T: Eq + std::hash::Hash>(vec: Vec<T>) -> usize {
|
|
||||||
// Add to a set.
|
|
||||||
let mut set = HashSet::with_capacity(vec.len());
|
|
||||||
for item in vec {
|
|
||||||
set.insert(item);
|
|
||||||
}
|
|
||||||
set.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the distance between two points.
|
/// Get the distance between two points.
|
||||||
pub fn distance(a: Point2d, b: Point2d) -> f64 {
|
pub fn distance(a: Point2d, b: Point2d) -> f64 {
|
||||||
((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt()
|
((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt()
|
||||||
@ -686,11 +676,6 @@ mod get_tangential_arc_to_info_tests {
|
|||||||
(num * 1000.0).round() / 1000.0
|
(num * 1000.0).round() / 1000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unique_count() {
|
|
||||||
assert_eq!(unique_count(vec![1, 2, 2, 3, 2]), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_case() {
|
fn test_basic_case() {
|
||||||
let result = get_tangential_arc_to_info(TangentialArcInfoInput {
|
let result = get_tangential_arc_to_info(TangentialArcInfoInput {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed angled_line.kcl
|
description: Operations executed angled_line.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed angled_line.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
270,
|
||||||
0,
|
289,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed artifact_graph_example_code1.kcl
|
description: Operations executed artifact_graph_example_code1.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -125,8 +125,8 @@ description: Operations executed artifact_graph_example_code1.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
298,
|
||||||
0,
|
332,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed basic_fillet_cube_close_opposite.kcl
|
description: Operations executed basic_fillet_cube_close_opposite.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed basic_fillet_cube_close_opposite.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
197,
|
||||||
0,
|
217,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -129,8 +129,8 @@ description: Operations executed basic_fillet_cube_close_opposite.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
223,
|
||||||
0,
|
283,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed basic_fillet_cube_end.kcl
|
description: Operations executed basic_fillet_cube_end.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed basic_fillet_cube_end.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
185,
|
||||||
0,
|
205,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -129,8 +129,8 @@ description: Operations executed basic_fillet_cube_end.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
211,
|
||||||
0,
|
269,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed basic_fillet_cube_next_adjacent.kcl
|
description: Operations executed basic_fillet_cube_next_adjacent.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed basic_fillet_cube_next_adjacent.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
212,
|
||||||
0,
|
232,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -124,8 +124,8 @@ description: Operations executed basic_fillet_cube_next_adjacent.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
238,
|
||||||
0,
|
294,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed basic_fillet_cube_previous_adjacent.kcl
|
description: Operations executed basic_fillet_cube_previous_adjacent.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed basic_fillet_cube_previous_adjacent.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
212,
|
||||||
0,
|
232,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -124,8 +124,8 @@ description: Operations executed basic_fillet_cube_previous_adjacent.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
238,
|
||||||
0,
|
298,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed basic_fillet_cube_start.kcl
|
description: Operations executed basic_fillet_cube_start.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed basic_fillet_cube_start.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
185,
|
||||||
0,
|
205,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -130,8 +130,8 @@ description: Operations executed basic_fillet_cube_start.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
211,
|
||||||
0,
|
253,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed big_number_angle_to_match_length_x.kcl
|
description: Operations executed big_number_angle_to_match_length_x.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed big_number_angle_to_match_length_x.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
183,
|
||||||
0,
|
203,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed big_number_angle_to_match_length_y.kcl
|
description: Operations executed big_number_angle_to_match_length_y.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed big_number_angle_to_match_length_y.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
183,
|
||||||
0,
|
203,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed circle_three_point.kcl
|
description: Operations executed circle_three_point.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed circle_three_point.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
104,
|
||||||
0,
|
124,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed circular_pattern3d_a_pattern.kcl
|
description: Operations executed circular_pattern3d_a_pattern.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed circular_pattern3d_a_pattern.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
159,
|
||||||
0,
|
178,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -139,8 +139,8 @@ description: Operations executed cube.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
374,
|
||||||
0,
|
402,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,8 @@ description: Operations executed cube_with_error.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
366,
|
||||||
0,
|
390,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1057,
|
||||||
0,
|
1085,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -159,8 +159,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1091,
|
||||||
0,
|
1297,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -280,8 +280,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1497,
|
||||||
0,
|
1521,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -404,8 +404,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1497,
|
||||||
0,
|
1521,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -528,8 +528,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1497,
|
||||||
0,
|
1521,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -652,8 +652,8 @@ description: Operations executed fillet-and-shell.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1497,
|
||||||
0,
|
1521,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed function_sketch.kcl
|
description: Operations executed function_sketch.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -80,8 +80,8 @@ description: Operations executed function_sketch.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
183,
|
||||||
0,
|
202,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed function_sketch_with_position.kcl
|
description: Operations executed function_sketch_with_position.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -80,8 +80,8 @@ description: Operations executed function_sketch_with_position.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
181,
|
||||||
0,
|
200,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed helix_ccw.kcl
|
description: Operations executed helix_ccw.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed helix_ccw.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
77,
|
||||||
0,
|
97,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -125,8 +125,8 @@ description: Operations executed i_shape.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2510,
|
||||||
0,
|
2531,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed import_whole.kcl
|
description: Operations executed import_whole.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,9 +64,9 @@ description: Operations executed import_whole.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
103,
|
||||||
0,
|
123,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -124,8 +124,8 @@ description: Operations executed import_whole.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
83,
|
||||||
0,
|
123,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -93,9 +93,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1379,
|
||||||
0,
|
1417,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -173,9 +173,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1455,
|
||||||
0,
|
1494,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -428,9 +428,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1379,
|
||||||
0,
|
1417,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -508,9 +508,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1455,
|
||||||
0,
|
1494,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -763,9 +763,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1379,
|
||||||
0,
|
1417,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -843,9 +843,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1455,
|
||||||
0,
|
1494,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1104,9 +1104,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1949,
|
||||||
0,
|
1973,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1190,9 +1190,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2015,
|
||||||
0,
|
2039,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1339,9 +1339,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2523,
|
||||||
0,
|
2547,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1485,9 +1485,9 @@ description: Operations executed 3d-boaty.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3047,
|
||||||
0,
|
3071,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -118,8 +118,8 @@ description: Operations executed 80-20-rail.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6006,
|
||||||
0,
|
6034,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -238,8 +238,8 @@ description: Operations executed 80-20-rail.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6042,
|
||||||
0,
|
6746,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -358,8 +358,8 @@ description: Operations executed 80-20-rail.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6754,
|
||||||
0,
|
7457,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed a-parametric-bearing-pillow-block.kcl
|
description: Operations executed a-parametric-bearing-pillow-block.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -254,8 +254,8 @@ description: Operations executed a-parametric-bearing-pillow-block.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1902,
|
||||||
0,
|
1936,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -693,8 +693,8 @@ description: Operations executed a-parametric-bearing-pillow-block.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3383,
|
||||||
0,
|
3408,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed ball-bearing.kcl
|
description: Operations executed ball-bearing.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -519,8 +519,8 @@ description: Operations executed ball-bearing.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1561,
|
||||||
0,
|
1721,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -779,8 +779,8 @@ description: Operations executed ball-bearing.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2214,
|
||||||
0,
|
2374,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1027,8 +1027,8 @@ description: Operations executed ball-bearing.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2718,
|
||||||
0,
|
2878,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed bracket.kcl
|
description: Operations executed bracket.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -274,8 +274,8 @@ description: Operations executed bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1903,
|
||||||
0,
|
2052,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -724,8 +724,8 @@ description: Operations executed bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3306,
|
||||||
0,
|
3455,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1510,9 +1510,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
529,
|
||||||
0,
|
562,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1622,9 +1622,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
859,
|
||||||
0,
|
892,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1710,9 +1710,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1214,
|
||||||
0,
|
1248,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1798,9 +1798,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1572,
|
||||||
0,
|
1606,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2362,9 +2362,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4067,
|
||||||
0,
|
4247,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2801,9 +2801,9 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4067,
|
||||||
0,
|
4247,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3298,8 +3298,8 @@ description: Operations executed car-wheel-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
373,
|
||||||
0,
|
524,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed enclosure.kcl
|
description: Operations executed enclosure.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -130,8 +130,8 @@ description: Operations executed enclosure.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
740,
|
||||||
0,
|
1021,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -190,8 +190,8 @@ description: Operations executed enclosure.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1100,
|
||||||
0,
|
1170,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1699,8 +1699,8 @@ description: Operations executed enclosure.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3601,
|
||||||
0,
|
3882,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1997,8 +1997,8 @@ description: Operations executed enclosure.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5327,
|
||||||
0,
|
5608,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -338,8 +338,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1547,
|
||||||
0,
|
1570,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -682,8 +682,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1547,
|
||||||
0,
|
1570,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1026,8 +1026,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1547,
|
||||||
0,
|
1570,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1370,8 +1370,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1547,
|
||||||
0,
|
1570,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1744,8 +1744,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3933,
|
||||||
0,
|
3962,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1808,8 +1808,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3968,
|
||||||
0,
|
4101,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1872,8 +1872,8 @@ description: Operations executed exhaust-manifold.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4107,
|
||||||
0,
|
4240,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed flange-with-patterns.kcl
|
description: Operations executed flange-with-patterns.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -174,8 +174,8 @@ description: Operations executed flange-with-patterns.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1413,
|
||||||
0,
|
1444,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -461,8 +461,8 @@ description: Operations executed flange-with-patterns.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1928,
|
||||||
0,
|
1963,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -566,8 +566,8 @@ description: Operations executed flange-with-patterns.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2230,
|
||||||
0,
|
2264,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed flange-xy.kcl
|
description: Operations executed flange-xy.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -254,8 +254,8 @@ description: Operations executed flange-xy.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1555,
|
||||||
0,
|
1586,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -552,8 +552,8 @@ description: Operations executed flange-xy.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2077,
|
||||||
0,
|
2112,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -657,8 +657,8 @@ description: Operations executed flange-xy.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2379,
|
||||||
0,
|
2413,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -253,8 +253,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1831,
|
||||||
0,
|
1865,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -325,8 +325,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1871,
|
||||||
0,
|
2129,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -612,8 +612,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2875,
|
||||||
0,
|
2900,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -670,8 +670,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2906,
|
||||||
0,
|
3050,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -779,8 +779,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3056,
|
||||||
0,
|
3184,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1066,8 +1066,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3719,
|
||||||
0,
|
3744,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1124,8 +1124,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3750,
|
||||||
0,
|
3894,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1233,8 +1233,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3900,
|
||||||
0,
|
4028,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1476,8 +1476,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4458,
|
||||||
0,
|
4486,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1719,8 +1719,8 @@ description: Operations executed focusrite-scarlett-mounting-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4706,
|
||||||
0,
|
4734,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -323,8 +323,8 @@ description: Operations executed french-press.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2157,
|
||||||
0,
|
2179,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -500,8 +500,8 @@ description: Operations executed french-press.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2185,
|
||||||
0,
|
2340,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1293,8 +1293,8 @@ description: Operations executed french-press.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5053,
|
||||||
0,
|
5092,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed gear-rack.kcl
|
description: Operations executed gear-rack.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed gear-rack.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
731,
|
||||||
0,
|
754,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -147,8 +147,8 @@ description: Operations executed gear-rack.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1279,
|
||||||
0,
|
1302,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -265,8 +265,8 @@ description: Operations executed gear-rack.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1409,
|
||||||
0,
|
1508,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -332,8 +332,8 @@ description: Operations executed gear-rack.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1820,
|
||||||
0,
|
1843,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -399,8 +399,8 @@ description: Operations executed gear-rack.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2157,
|
||||||
0,
|
2180,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5897,8 +5897,8 @@ description: Operations executed gear.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1405,
|
||||||
0,
|
1433,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -9802,8 +9802,8 @@ description: Operations executed gear.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2128,
|
||||||
0,
|
2156,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -9979,8 +9979,8 @@ description: Operations executed gear.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2162,
|
||||||
0,
|
2322,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -10097,8 +10097,8 @@ description: Operations executed gear.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3058,
|
||||||
0,
|
3087,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -919,8 +919,8 @@ description: Operations executed gridfinity-baseplate-magnets.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2243,
|
||||||
0,
|
2360,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1183,8 +1183,8 @@ description: Operations executed gridfinity-baseplate-magnets.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2584,
|
||||||
0,
|
2701,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2008,8 +2008,8 @@ description: Operations executed gridfinity-baseplate-magnets.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6613,
|
||||||
0,
|
6730,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2243,8 +2243,8 @@ description: Operations executed gridfinity-baseplate-magnets.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6933,
|
||||||
0,
|
7050,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -919,8 +919,8 @@ description: Operations executed gridfinity-baseplate.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2118,
|
||||||
0,
|
2235,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1183,8 +1183,8 @@ description: Operations executed gridfinity-baseplate.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2459,
|
||||||
0,
|
2576,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -722,8 +722,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2875,
|
||||||
0,
|
2899,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -794,8 +794,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2905,
|
||||||
0,
|
3134,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -885,8 +885,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3580,
|
||||||
0,
|
3607,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1137,8 +1137,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3813,
|
||||||
0,
|
3943,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1389,8 +1389,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4174,
|
||||||
0,
|
4304,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1612,8 +1612,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4529,
|
||||||
0,
|
4659,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1715,8 +1715,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5002,
|
||||||
0,
|
5046,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1787,8 +1787,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5052,
|
||||||
0,
|
5284,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1847,8 +1847,8 @@ description: Operations executed gridfinity-bins-stacking-lip.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5290,
|
||||||
0,
|
5332,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -722,8 +722,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2618,
|
||||||
0,
|
2642,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -794,8 +794,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2648,
|
||||||
0,
|
2877,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -885,8 +885,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3323,
|
||||||
0,
|
3350,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1137,8 +1137,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3556,
|
||||||
0,
|
3686,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1389,8 +1389,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3917,
|
||||||
0,
|
4047,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1612,8 +1612,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4272,
|
||||||
0,
|
4402,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1715,8 +1715,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4771,
|
||||||
0,
|
4815,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1787,8 +1787,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4821,
|
||||||
0,
|
5053,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1847,8 +1847,8 @@ description: Operations executed gridfinity-bins.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5059,
|
||||||
0,
|
5101,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed hex-nut.kcl
|
description: Operations executed hex-nut.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -118,8 +118,8 @@ description: Operations executed hex-nut.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1038,
|
||||||
0,
|
1059,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@ description: Operations executed i-beam.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
652,
|
||||||
0,
|
680,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed kitt.kcl
|
description: Operations executed kitt.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
900,
|
||||||
0,
|
930,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -160,8 +160,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -243,8 +243,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2204,
|
||||||
0,
|
2235,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -339,8 +339,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -438,8 +438,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -537,8 +537,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -636,8 +636,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -719,8 +719,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3485,
|
||||||
0,
|
3514,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -815,8 +815,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -914,8 +914,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1013,8 +1013,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1112,8 +1112,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1211,8 +1211,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1310,8 +1310,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1409,8 +1409,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1508,8 +1508,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1607,8 +1607,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1706,8 +1706,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1805,8 +1805,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1904,8 +1904,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2003,8 +2003,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2185,8 +2185,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2370,8 +2370,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2489,8 +2489,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2588,8 +2588,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2687,8 +2687,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2786,8 +2786,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2905,8 +2905,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3004,8 +3004,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3103,8 +3103,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3202,8 +3202,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3305,8 +3305,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3405,8 +3405,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3505,8 +3505,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3605,8 +3605,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3705,8 +3705,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3805,8 +3805,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -3905,8 +3905,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -4005,8 +4005,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -4105,8 +4105,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -4205,8 +4205,8 @@ description: Operations executed kitt.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
449,
|
||||||
0,
|
472,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed lego.kcl
|
description: Operations executed lego.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed lego.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1780,
|
||||||
0,
|
1804,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -138,8 +138,8 @@ description: Operations executed lego.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2221,
|
||||||
0,
|
2252,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -262,8 +262,8 @@ description: Operations executed lego.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2687,
|
||||||
0,
|
2715,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -528,8 +528,8 @@ description: Operations executed lego.kcl
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3197,
|
||||||
0,
|
3226,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed mounting-plate.kcl
|
description: Operations executed mounting-plate.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -273,8 +273,8 @@ description: Operations executed mounting-plate.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1825,
|
||||||
0,
|
1857,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -345,8 +345,8 @@ description: Operations executed mounting-plate.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1863,
|
||||||
0,
|
2127,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed multi-axis-robot.kcl
|
description: Operations executed multi-axis-robot.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -212,9 +212,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
769,
|
||||||
0,
|
1045,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -352,9 +352,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1280,
|
||||||
0,
|
1362,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -923,9 +923,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
323,
|
||||||
0,
|
406,
|
||||||
0
|
4
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1340,9 +1340,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1143,
|
||||||
0,
|
1226,
|
||||||
0
|
4
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1685,9 +1685,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1996,
|
||||||
0,
|
2079,
|
||||||
0
|
4
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2341,9 +2341,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1033,
|
||||||
0,
|
1116,
|
||||||
0
|
5
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2519,9 +2519,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1415,
|
||||||
0,
|
1498,
|
||||||
0
|
5
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3400,9 +3400,9 @@ description: Operations executed multi-axis-robot.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1299,
|
||||||
0,
|
1382,
|
||||||
0
|
6
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -247,8 +247,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5504,
|
||||||
0,
|
5535,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -410,8 +410,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
5838,
|
||||||
0,
|
5872,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -713,8 +713,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3177,
|
||||||
0,
|
3198,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -893,8 +893,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6049,
|
||||||
0,
|
6204,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1431,8 +1431,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6407,
|
||||||
0,
|
6562,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1857,8 +1857,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
6781,
|
||||||
0,
|
6936,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2160,8 +2160,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3177,
|
||||||
0,
|
3198,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2340,8 +2340,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
7426,
|
||||||
0,
|
7581,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2643,8 +2643,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4236,
|
||||||
0,
|
4258,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -2823,8 +2823,8 @@ description: Operations executed pipe-flange-assembly.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
7778,
|
||||||
0,
|
7933,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -209,8 +209,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1706,
|
||||||
0,
|
1743,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -452,8 +452,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2178,
|
||||||
0,
|
2209,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -532,8 +532,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2907,
|
||||||
0,
|
2938,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -775,8 +775,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3566,
|
||||||
0,
|
3597,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -856,8 +856,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3816,
|
||||||
0,
|
3847,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -930,8 +930,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4043,
|
||||||
0,
|
4093,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1173,8 +1173,8 @@ description: Operations executed poopy-shoe.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
4597,
|
||||||
0,
|
4629,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed sheet-metal-bracket.kcl
|
description: Operations executed sheet-metal-bracket.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1299,
|
||||||
0,
|
1325,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -124,8 +124,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1331,
|
||||||
0,
|
1410,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -184,8 +184,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1416,
|
||||||
0,
|
1502,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -244,8 +244,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1508,
|
||||||
0,
|
1594,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -304,8 +304,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1600,
|
||||||
0,
|
1679,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -364,8 +364,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1685,
|
||||||
0,
|
1771,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -424,8 +424,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1777,
|
||||||
0,
|
1856,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -484,8 +484,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1862,
|
||||||
0,
|
1942,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -544,8 +544,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1948,
|
||||||
0,
|
2035,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -687,8 +687,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2446,
|
||||||
0,
|
2473,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -751,8 +751,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2479,
|
||||||
0,
|
2608,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -894,8 +894,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
2976,
|
||||||
0,
|
3003,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -958,8 +958,8 @@ description: Operations executed sheet-metal-bracket.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
3009,
|
||||||
0,
|
3139,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -136,9 +136,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
564,
|
||||||
0,
|
794,
|
||||||
0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2035,9 +2035,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
745,
|
||||||
0,
|
890,
|
||||||
0
|
8
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2227,9 +2227,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
745,
|
||||||
0,
|
890,
|
||||||
0
|
8
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2419,9 +2419,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
745,
|
||||||
0,
|
890,
|
||||||
0
|
8
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2611,9 +2611,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
745,
|
||||||
0,
|
890,
|
||||||
0
|
8
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2923,9 +2923,9 @@ description: Operations executed walkie-talkie.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
891,
|
||||||
0,
|
1096,
|
||||||
0
|
6
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed kittycad_svg.kcl
|
description: Operations executed kittycad_svg.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed kittycad_svg.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
18347,
|
||||||
0,
|
18366,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed linear_pattern3d_a_pattern.kcl
|
description: Operations executed linear_pattern3d_a_pattern.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed linear_pattern3d_a_pattern.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
159,
|
||||||
0,
|
178,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed mike_stress_test.kcl
|
description: Operations executed mike_stress_test.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed mike_stress_test.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
77102,
|
||||||
0,
|
77121,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed neg_xz_plane.kcl
|
description: Operations executed neg_xz_plane.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed neg_xz_plane.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
151,
|
||||||
0,
|
174,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed parametric.kcl
|
description: Operations executed parametric.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed parametric.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
465,
|
||||||
0,
|
488,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ description: Operations executed parametric_with_tan_arc.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
622,
|
||||||
0,
|
645,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Operations executed pentagon_fillet_sugar.kcl
|
description: Operations executed pentagon_fillet_sugar.kcl
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
@ -64,8 +64,8 @@ description: Operations executed pentagon_fillet_sugar.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
379,
|
||||||
0,
|
411,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -164,8 +164,8 @@ description: Operations executed pentagon_fillet_sugar.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
612,
|
||||||
0,
|
640,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -229,8 +229,8 @@ description: Operations executed pentagon_fillet_sugar.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
646,
|
||||||
0,
|
773,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -329,8 +329,8 @@ description: Operations executed pentagon_fillet_sugar.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
812,
|
||||||
0,
|
840,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -394,8 +394,8 @@ description: Operations executed pentagon_fillet_sugar.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
846,
|
||||||
0,
|
973,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,8 @@ description: Operations executed pipe_as_arg.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
367,
|
||||||
0,
|
391,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -209,8 +209,8 @@ description: Operations executed poop_chute.kcl
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": [
|
"sourceRange": [
|
||||||
0,
|
1719,
|
||||||
0,
|
1757,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user