Pass various.spec.ts, by far the hardest one
This commit is contained in:
@ -1,29 +1,20 @@
|
|||||||
import { test, expect, Page } from '@playwright/test'
|
import { test, expect, Page } from './zoo-test'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
TEST_COLORS,
|
TEST_COLORS,
|
||||||
setup,
|
|
||||||
tearDown,
|
|
||||||
commonPoints,
|
commonPoints,
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
|
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
|
||||||
await setup(context, page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.setTimeout(120000)
|
test.setTimeout(120000)
|
||||||
|
|
||||||
async function doBasicSketch(page: Page, openPanes: string[]) {
|
async function doBasicSketch(page: Page, homePage: HomePageFixture, openPanes: string[]) {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
// If we have the code pane open, we should see the code.
|
// If we have the code pane open, we should see the code.
|
||||||
@ -148,20 +139,16 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Basic sketch', () => {
|
test.describe('Basic sketch', () => {
|
||||||
test('code pane open at start', { tag: ['@skipWin'] }, async ({ page }) => {
|
test('code pane open at start', { tag: ['@skipWin'] }, async ({ page, homePage }) => { // Skip on windows it is being weird.
|
||||||
// Skip on windows it is being weird.
|
|
||||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
await doBasicSketch(page, ['code'])
|
await doBasicSketch(page, homePage, ['code']) })
|
||||||
})
|
|
||||||
|
|
||||||
test('code pane closed at start', async ({ page }) => {
|
test('code pane closed at start', async ({ page, homePage }) => { // Load the app with the code panes
|
||||||
// Load the app with the code panes
|
|
||||||
await page.addInitScript(async (persistModelingContext) => {
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
persistModelingContext,
|
persistModelingContext,
|
||||||
JSON.stringify({ openPanes: [] })
|
JSON.stringify({ openPanes: [] })
|
||||||
)
|
)
|
||||||
}, PERSIST_MODELING_CONTEXT)
|
}, PERSIST_MODELING_CONTEXT)
|
||||||
await doBasicSketch(page, [])
|
await doBasicSketch(page, homePage, []) })
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from './zoo-test'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
setup,
|
|
||||||
setupElectron,
|
|
||||||
tearDown,
|
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
@ -12,37 +9,26 @@ import { bracket } from 'lib/exampleKcl'
|
|||||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
|
||||||
await setup(context, page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('Code pane and errors', () => {
|
test.describe('Code pane and errors', () => {
|
||||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
test('Typing KCL errors induces a badge on the code pane button', async ({ page, homePage }) => { const u = await getUtils(page)
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await page.addInitScript(() => {
|
await page.addInitScript(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`// Extruded Triangle
|
`// Extruded Triangle
|
||||||
sketch001 = startSketchOn('XZ')
|
sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([10, 0], %)
|
|> line([10, 0], %)
|
||||||
|> line([-5, 10], %)
|
|> line([-5, 10], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(5, sketch001)`
|
extrude001 = extrude(5, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -59,13 +45,9 @@ extrude001 = extrude(5, sketch001)`
|
|||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
// Ensure that a badge appears on the button
|
// Ensure that a badge appears on the button
|
||||||
await expect(codePaneButtonHolder).toContainText('notification')
|
await expect(codePaneButtonHolder).toContainText('notification') })
|
||||||
})
|
|
||||||
|
|
||||||
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
test('Opening and closing the code pane will consistently show error diagnostics', async ({ page, homePage }) => {
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
await page.goto('http://localhost:3000')
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -74,8 +56,8 @@ extrude001 = extrude(5, sketch001)`
|
|||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, bracket)
|
}, bracket)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 900 })
|
await page.setBodyDimensions({ width: 1200, height: 900 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -126,21 +108,17 @@ extrude001 = extrude(5, sketch001)`
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
|
await expect(page.locator('.cm-tooltip').first()).toBeVisible() })
|
||||||
})
|
|
||||||
|
|
||||||
test('When error is not in view you can click the badge to scroll to it', async ({
|
test('When error is not in view you can click the badge to scroll to it', async ({ page, homePage, context }) => { const u = await getUtils(page)
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await page.addInitScript((code) => {
|
await context.addInitScript((code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
@ -164,24 +142,20 @@ extrude001 = extrude(5, sketch001)`
|
|||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByText(
|
.getByText(
|
||||||
'sketch profile must lie entirely on one side of the revolution axis'
|
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
).toBeVisible()
|
).toBeVisible() })
|
||||||
})
|
|
||||||
|
|
||||||
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ context, page, homePage }) => { const u = await getUtils(page)
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await page.addInitScript((code) => {
|
await context.addInitScript((code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
@ -234,18 +208,15 @@ extrude001 = extrude(5, sketch001)`
|
|||||||
'sketch profile must lie entirely on one side of the revolution axis'
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
).toBeVisible()
|
).toBeVisible() })
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Opening multiple panes persists when switching projects',
|
'Opening multiple panes persists when switching projects',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ context, browserName, page }, testInfo) => {
|
||||||
// Setup multiple projects.
|
// Setup multiple projects.
|
||||||
const { electronApp, page } = await setupElectron({
|
context.folderSetupFn(async (dir) => {
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -262,11 +233,10 @@ test(
|
|||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load', async () => {
|
await test.step('Opening the bracket project should load', async () => {
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
@ -309,30 +279,21 @@ test(
|
|||||||
await expect(page.locator('#variables-pane')).toBeVisible()
|
await expect(page.locator('#variables-pane')).toBeVisible()
|
||||||
await expect(page.locator('#logs-pane')).toBeVisible()
|
await expect(page.locator('#logs-pane')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'external change of file contents are reflected in editor',
|
'external change of file contents are reflected in editor',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ context, browserName, page }, testInfo) => {
|
||||||
const PROJECT_DIR_NAME = 'lee-was-here'
|
const PROJECT_DIR_NAME = 'lee-was-here'
|
||||||
const {
|
const { dir: projectsDir, } = await context.folderSetupFn(async (dir) => {
|
||||||
electronApp,
|
|
||||||
page,
|
|
||||||
dir: projectsDir,
|
|
||||||
} = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
||||||
await fsp.mkdir(aProjectDir, { recursive: true })
|
await fsp.mkdir(aProjectDir, { recursive: true })
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await test.step('Open the project', async () => {
|
await test.step('Open the project', async () => {
|
||||||
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
||||||
@ -351,7 +312,5 @@ test(
|
|||||||
)
|
)
|
||||||
await u.editorTextMatches(content)
|
await u.editorTextMatches(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -65,6 +65,7 @@ export class AuthenticatedTronApp {
|
|||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public electronApp?: ElectronApplication
|
public electronApp?: ElectronApplication
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||||
|
public dir: string = ""
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this._page = page
|
this._page = page
|
||||||
@ -80,7 +81,7 @@ export class AuthenticatedTronApp {
|
|||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context } = await setupElectron({
|
const { electronApp, page, context, dir } = await setupElectron({
|
||||||
testInfo: this.testInfo,
|
testInfo: this.testInfo,
|
||||||
folderSetupFn: arg.folderSetupFn,
|
folderSetupFn: arg.folderSetupFn,
|
||||||
cleanProjectDir: arg.cleanProjectDir,
|
cleanProjectDir: arg.cleanProjectDir,
|
||||||
@ -89,6 +90,7 @@ export class AuthenticatedTronApp {
|
|||||||
this.page = page
|
this.page = page
|
||||||
this.context = context
|
this.context = context
|
||||||
this.electronApp = electronApp
|
this.electronApp = electronApp
|
||||||
|
this.dir = dir
|
||||||
|
|
||||||
// Setup localStorage, addCookies, reload
|
// Setup localStorage, addCookies, reload
|
||||||
await setup(this.context, this.page, this.testInfo)
|
await setup(this.context, this.page, this.testInfo)
|
||||||
|
@ -109,242 +109,21 @@ keychain = startSketchOn("XY")
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(thickness, %)
|
|> extrude(thickness, %)
|
||||||
|
|
||||||
// generated from /home/paultag/Downloads/zma-logomark.svg
|
keychain1 = startSketchOn("XY")
|
||||||
fn svg = (surface, origin, depth) => {
|
|> startProfileAt([0, 0], %)
|
||||||
let a0 = surface |> startProfileAt([origin[0] + 45.430427, origin[1] + -14.627736], %)
|
|> lineTo([width, 0], %)
|
||||||
|> bezierCurve({
|
|> lineTo([width, height], %)
|
||||||
control1: [ 0, 0.764157 ],
|
|> lineTo([0, height], %)
|
||||||
control2: [ 0, 1.528314 ],
|
|
||||||
to: [ 0, 2.292469 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -3.03202, 0 ],
|
|
||||||
control2: [ -6.064039, 0 ],
|
|
||||||
to: [ -9.09606, 0 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0, -1.077657 ],
|
|
||||||
control2: [ 0, -2.155312 ],
|
|
||||||
to: [ 0, -3.232969 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.741805, 0 ],
|
|
||||||
control2: [ 5.483613, 0 ],
|
|
||||||
to: [ 8.225417, 0 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.740682, -2.961815 ],
|
|
||||||
control2: [ -5.490342, -5.925794 ],
|
|
||||||
to: [ -8.225417, -8.886255 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0, -0.723995 ],
|
|
||||||
control2: [ 0, -1.447988 ],
|
|
||||||
to: [ 0, -2.171981 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.712124, 0.05061 ],
|
|
||||||
control2: [ 1.511636, -0.09877 ],
|
|
||||||
to: [ 2.172096, 0.07005 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.68573, 0.740811 ],
|
|
||||||
control2: [ 1.371459, 1.481622 ],
|
|
||||||
to: [ 2.057187, 2.222436 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0, -0.76416 ],
|
|
||||||
control2: [ 0, -1.52832 ],
|
|
||||||
to: [ 0, -2.29248 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 3.032013, 0 ],
|
|
||||||
control2: [ 6.064026, 0 ],
|
|
||||||
to: [ 9.096038, 0 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0, 1.077657 ],
|
|
||||||
control2: [ 0, 2.155314 ],
|
|
||||||
to: [ 0, 3.232973 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.741312, 0 ],
|
|
||||||
control2: [ -5.482623, 0 ],
|
|
||||||
to: [ -8.223936, 0 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.741313, 2.961108 ],
|
|
||||||
control2: [ 5.482624, 5.922216 ],
|
|
||||||
to: [ 8.223936, 8.883325 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0, 0.724968 ],
|
|
||||||
control2: [ 0, 1.449938 ],
|
|
||||||
to: [ 0, 2.174907 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.712656, -0.05145 ],
|
|
||||||
control2: [ -1.512554, 0.09643 ],
|
|
||||||
to: [ -2.173592, -0.07298 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.685222, -0.739834 ],
|
|
||||||
control2: [ -1.370445, -1.479669 ],
|
|
||||||
to: [ -2.055669, -2.219505 ]
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(depth, %)
|
|> extrude(thickness, %)
|
||||||
|
|
||||||
let a1 = surface |> startProfileAt([origin[0] + 57.920488, origin[1] + -15.244943], %)
|
keychain2 = startSketchOn("XY")
|
||||||
|> bezierCurve({
|
|> startProfileAt([0, 0], %)
|
||||||
control1: [ -2.78904, 0.106635 ],
|
|> lineTo([width, 0], %)
|
||||||
control2: [ -5.052548, -2.969529 ],
|
|> lineTo([width, height], %)
|
||||||
to: [ -4.055141, -5.598369 ]
|
|> lineTo([0, height], %)
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.841523, -0.918736 ],
|
|
||||||
control2: [ 0.439412, -1.541892 ],
|
|
||||||
to: [ -0.368488, -2.214378 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.418245, -0.448461 ],
|
|
||||||
control2: [ -0.836489, -0.896922 ],
|
|
||||||
to: [ -1.254732, -1.345384 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.76806, 2.995359 ],
|
|
||||||
control2: [ -2.32667, 8.18409 ],
|
|
||||||
to: [ 0.897655, 10.678932 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.562822, 2.186098 ],
|
|
||||||
control2: [ 6.605111, 2.28043 ],
|
|
||||||
to: [ 9.271202, 0.226476 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.743744, -0.797465 ],
|
|
||||||
control2: [ -1.487487, -1.594932 ],
|
|
||||||
to: [ -2.231232, -2.392397 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.672938, 0.421422 ],
|
|
||||||
control2: [ -1.465362, 0.646946 ],
|
|
||||||
to: [ -2.259264, 0.64512 ]
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(depth, %)
|
|> extrude(thickness, %)
|
||||||
|
|
||||||
let a2 = surface |> startProfileAt([origin[0] + 62.19406300000001, origin[1] + -19.500698999999997], %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.302938, 1.281141 ],
|
|
||||||
control2: [ -1.53575, 2.434288 ],
|
|
||||||
to: [ -0.10908, 3.279477 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.504637, 0.54145 ],
|
|
||||||
control2: [ 1.009273, 1.082899 ],
|
|
||||||
to: [ 1.513909, 1.624348 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.767778, -2.995425 ],
|
|
||||||
control2: [ 2.327135, -8.184384 ],
|
|
||||||
to: [ -0.897661, -10.679047 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.562947, -2.186022 ],
|
|
||||||
control2: [ -6.604089, -2.279606 ],
|
|
||||||
to: [ -9.271196, -0.227813 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.744231, 0.797952 ],
|
|
||||||
control2: [ 1.488461, 1.595904 ],
|
|
||||||
to: [ 2.232692, 2.393856 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.302377, -1.564629 ],
|
|
||||||
control2: [ 5.793126, -0.15358 ],
|
|
||||||
to: [ 6.396577, 2.547372 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.08981, 0.346302 ],
|
|
||||||
control2: [ 0.134865, 0.704078 ],
|
|
||||||
to: [ 0.13476, 1.061807 ]
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(depth, %)
|
|
||||||
|
|
||||||
let a3 = surface |> startProfileAt([origin[0] + 74.124866, origin[1] + -15.244943], %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.78904, 0.106635 ],
|
|
||||||
control2: [ -5.052549, -2.969529 ],
|
|
||||||
to: [ -4.055142, -5.598369 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.841527, -0.918738 ],
|
|
||||||
control2: [ 0.43941, -1.541892 ],
|
|
||||||
to: [ -0.368497, -2.214367 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.418254, -0.448466 ],
|
|
||||||
control2: [ -0.836507, -0.896931 ],
|
|
||||||
to: [ -1.254761, -1.345395 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.768019, 2.995371 ],
|
|
||||||
control2: [ -2.326624, 8.184088 ],
|
|
||||||
to: [ 0.897678, 10.678932 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.56289, 2.186191 ],
|
|
||||||
control2: [ 6.60516, 2.280307 ],
|
|
||||||
to: [ 9.271371, 0.226476 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.743808, -0.797465 ],
|
|
||||||
control2: [ -1.487616, -1.594932 ],
|
|
||||||
to: [ -2.231424, -2.392397 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -0.672916, 0.421433 ],
|
|
||||||
control2: [ -1.465344, 0.646926 ],
|
|
||||||
to: [ -2.259225, 0.64512 ]
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(depth, %)
|
|
||||||
|
|
||||||
let a4 = surface |> startProfileAt([origin[0] + 77.57333899999998, origin[1] + -16.989262999999998], %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.743298, 0.797463 ],
|
|
||||||
control2: [ 1.486592, 1.594926 ],
|
|
||||||
to: [ 2.229888, 2.392389 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.767827, -2.995393 ],
|
|
||||||
control2: [ 2.327103, -8.184396 ],
|
|
||||||
to: [ -0.897672, -10.679047 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ -2.562939, -2.186037 ],
|
|
||||||
control2: [ -6.604077, -2.279589 ],
|
|
||||||
to: [ -9.271185, -0.227813 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.744243, 0.797952 ],
|
|
||||||
control2: [ 1.488486, 1.595904 ],
|
|
||||||
to: [ 2.232729, 2.393856 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 2.302394, -1.564623 ],
|
|
||||||
control2: [ 5.793201, -0.153598 ],
|
|
||||||
to: [ 6.396692, 2.547372 ]
|
|
||||||
}, %)
|
|
||||||
|> bezierCurve({
|
|
||||||
control1: [ 0.32074, 1.215468 ],
|
|
||||||
control2: [ 0.06159, 2.564765 ],
|
|
||||||
to: [ -0.690452, 3.573243 ]
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(depth, %)
|
|
||||||
|
|
||||||
box = startSketchOn('XY')
|
box = startSketchOn('XY')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
@ -354,7 +133,7 @@ box = startSketchOn('XY')
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|
|
||||||
sketch001 = startSketchOn(box, revolveAxis)
|
sketch001 = startSketchOn(box, revolveAxis)
|
||||||
|> startProfileAt([5, 10], %)
|
|> startProfileAt([5, 10], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([2, 0], %)
|
|> line([2, 0], %)
|
||||||
@ -364,18 +143,12 @@ box = startSketchOn('XY')
|
|||||||
axis: revolveAxis,
|
axis: revolveAxis,
|
||||||
angle: 90
|
angle: 90
|
||||||
}, %)
|
}, %)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.0, 0.0], %)
|
||||||
|
|> xLine(0.0, %)
|
||||||
|
|> close(%)
|
||||||
|
|
||||||
|
`
|
||||||
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
|
|
||||||
|
|
||||||
startSketchOn(keychain, 'end')
|
|
||||||
|> circle({ center: [
|
|
||||||
width / 2,
|
|
||||||
height - (keychainHoleSize + 1.5)
|
|
||||||
], radius: keychainHoleSize }, %)
|
|
||||||
|> extrude(-thickness, %)`
|
|
||||||
|
|
||||||
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
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'
|
||||||
import { join } from 'path'
|
import path from 'path'
|
||||||
import pixelMatch from 'pixelmatch'
|
import pixelMatch from 'pixelmatch'
|
||||||
import { PNG } from 'pngjs'
|
import { PNG } from 'pngjs'
|
||||||
import { Protocol } from 'playwright-core/types/protocol'
|
import { Protocol } from 'playwright-core/types/protocol'
|
||||||
@ -682,6 +682,29 @@ export const makeTemplate: (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
|
||||||
|
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
||||||
|
|
||||||
|
const downloadDir = path.resolve(
|
||||||
|
page.TEST_SETTINGS_FILE_KEY,
|
||||||
|
"downloads-during-playwright"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expect there to be at least one file
|
||||||
|
expect.poll(async () => {
|
||||||
|
const files = await fsp.readdir(downloadDir)
|
||||||
|
return files.length
|
||||||
|
}).toBe(1)
|
||||||
|
|
||||||
|
// Go through the downloads dir and move files to new location
|
||||||
|
const files = await fsp.readdir(downloadDir)
|
||||||
|
|
||||||
|
// Assumption: only ever one file here.
|
||||||
|
for (let file of files) {
|
||||||
|
await fsp.rename(path.resolve(downloadDir, file), toLocation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface Paths {
|
export interface Paths {
|
||||||
modelPath: string
|
modelPath: string
|
||||||
imagePath: string
|
imagePath: string
|
||||||
@ -694,7 +717,8 @@ export const doExport = async (
|
|||||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
if (exportFrom === 'dropdown') {
|
if (exportFrom === 'dropdown') {
|
||||||
await page.getByRole('button', { name: APP_NAME }).click()
|
await page.getByTestId('project-sidebar-toggle').click()
|
||||||
|
|
||||||
const exportMenuButton = page.getByRole('button', {
|
const exportMenuButton = page.getByRole('button', {
|
||||||
name: 'Export current part',
|
name: 'Export current part',
|
||||||
})
|
})
|
||||||
@ -735,25 +759,12 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||||
|
|
||||||
const getPromiseAndResolve = () => {
|
|
||||||
let resolve: any = () => {}
|
|
||||||
const promise = new Promise<Download>((r) => {
|
|
||||||
resolve = r
|
|
||||||
})
|
|
||||||
return [promise, resolve]
|
|
||||||
}
|
|
||||||
|
|
||||||
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
|
||||||
let downloadCnt = 0
|
|
||||||
|
|
||||||
if (exportFrom === 'dropdown')
|
|
||||||
page.on('download', async (download) => {
|
|
||||||
if (downloadCnt === 0) {
|
|
||||||
downloadResolve1(download)
|
|
||||||
}
|
|
||||||
downloadCnt++
|
|
||||||
})
|
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||||
|
|
||||||
|
// This usually happens immediately after. If we're too slow we don't
|
||||||
|
// catch it.
|
||||||
|
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||||
|
|
||||||
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
||||||
return {
|
return {
|
||||||
modelPath: '',
|
modelPath: '',
|
||||||
@ -763,15 +774,12 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle download
|
// Handle download
|
||||||
const download = await downloadPromise1
|
|
||||||
const downloadLocationer = (extra = '', isImage = false) =>
|
const downloadLocationer = (extra = '', isImage = false) =>
|
||||||
`./e2e/playwright/export-snapshots/${output.type}-${
|
`./e2e/playwright/export-snapshots/${output.type}-${
|
||||||
'storage' in output ? output.storage : ''
|
'storage' in output ? output.storage : ''
|
||||||
}${extra}.${isImage ? 'png' : output.type}`
|
}${extra}.${isImage ? 'png' : output.type}`
|
||||||
const downloadLocation = downloadLocationer()
|
const downloadLocation = downloadLocationer()
|
||||||
|
|
||||||
await download.saveAs(downloadLocation)
|
|
||||||
|
|
||||||
if (output.type === 'step') {
|
if (output.type === 'step') {
|
||||||
// stable timestamps for step files
|
// stable timestamps for step files
|
||||||
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
||||||
@ -780,6 +788,12 @@ export const doExport = async (
|
|||||||
'1970-01-01T00:00:00.0+00:00'
|
'1970-01-01T00:00:00.0+00:00'
|
||||||
)
|
)
|
||||||
await fsp.writeFile(downloadLocation, newFileContents)
|
await fsp.writeFile(downloadLocation, newFileContents)
|
||||||
|
} else {
|
||||||
|
// By default all files are downloaded to the same place in playwright
|
||||||
|
// (declared in src/lib/exportSave)
|
||||||
|
// To remain consistent with our old web tests, we want to move some downloads
|
||||||
|
// (images) to another directory.
|
||||||
|
await moveDownloadedFileTo(page, downloadLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -818,12 +832,13 @@ export async function setup(
|
|||||||
testInfo?: TestInfo
|
testInfo?: TestInfo
|
||||||
) {
|
) {
|
||||||
await context.addInitScript(
|
await context.addInitScript(
|
||||||
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY, PLAYWRIGHT_TEST_DIR }) => {
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||||
localStorage.setItem('persistCode', ``)
|
localStorage.setItem('persistCode', ``)
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
||||||
|
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
@ -840,6 +855,7 @@ export async function setup(
|
|||||||
} as Partial<SaveSettingsPayload>,
|
} as Partial<SaveSettingsPayload>,
|
||||||
}),
|
}),
|
||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
|
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -901,11 +917,13 @@ export async function setupElectron({
|
|||||||
const context = electronApp.context()
|
const context = electronApp.context()
|
||||||
const page = await electronApp.firstWindow()
|
const page = await electronApp.firstWindow()
|
||||||
|
|
||||||
|
page.TEST_SETTINGS_FILE_KEY = projectDirName
|
||||||
|
|
||||||
context.on('console', console.log)
|
context.on('console', console.log)
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
if (cleanProjectDir) {
|
if (cleanProjectDir) {
|
||||||
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const settingsOverrides = TOML.stringify(
|
const settingsOverrides = TOML.stringify(
|
||||||
appSettings
|
appSettings
|
||||||
? {
|
? {
|
||||||
@ -1022,7 +1040,7 @@ export async function createProject({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function executorInputPath(fileName: string): string {
|
export function executorInputPath(fileName: string): string {
|
||||||
return join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
return path.join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doAndWaitForImageDiff(
|
export async function doAndWaitForImageDiff(
|
||||||
|
@ -1,51 +1,41 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from './zoo-test'
|
||||||
|
|
||||||
import { doExport, getUtils, makeTemplate, setup, tearDown } from './test-utils'
|
import { doExport, getUtils, makeTemplate } from './test-utils'
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
test.fixme('Units menu', async ({ page, homePage }) => {
|
||||||
await setup(context, page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test.afterEach(async ({ page }, testInfo) => {
|
|
||||||
await tearDown(page, testInfo)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Units menu', async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
const unitsMenuButton = page.getByRole('button', {
|
const unitsMenuButton = page.getByRole('button', {
|
||||||
name: 'Current Units',
|
name: 'Current Units',
|
||||||
exact: false,
|
exact: false,
|
||||||
})
|
|
||||||
await expect(unitsMenuButton).toBeVisible()
|
|
||||||
await expect(unitsMenuButton).toContainText('in')
|
|
||||||
|
|
||||||
await unitsMenuButton.click()
|
|
||||||
const millimetersButton = page.getByRole('button', { name: 'Millimeters' })
|
|
||||||
|
|
||||||
await expect(millimetersButton).toBeVisible()
|
|
||||||
await millimetersButton.click()
|
|
||||||
|
|
||||||
// Look out for the toast message
|
|
||||||
const toastMessage = page.getByText(
|
|
||||||
`Set default unit to "mm" for this project`
|
|
||||||
)
|
|
||||||
await expect(toastMessage).toBeVisible()
|
|
||||||
|
|
||||||
// Verify that the popover has closed
|
|
||||||
await expect(millimetersButton).not.toBeAttached()
|
|
||||||
|
|
||||||
// Verify that the button label has updated
|
|
||||||
await expect(unitsMenuButton).toContainText('mm')
|
|
||||||
})
|
})
|
||||||
|
await expect(unitsMenuButton).toBeVisible()
|
||||||
|
await expect(unitsMenuButton).toContainText('in')
|
||||||
|
|
||||||
test('Successful export shows a success toast', async ({ page }) => {
|
await unitsMenuButton.click()
|
||||||
// FYI this test doesn't work with only engine running locally
|
const millimetersButton = page.getByRole('button', { name: 'Millimeters' })
|
||||||
// And you will need to have the KittyCAD CLI installed
|
|
||||||
const u = await getUtils(page)
|
await expect(millimetersButton).toBeVisible()
|
||||||
await page.addInitScript(async () => {
|
await millimetersButton.click()
|
||||||
|
|
||||||
|
// Look out for the toast message
|
||||||
|
const toastMessage = page.getByText(
|
||||||
|
`Set default unit to "mm" for this project`
|
||||||
|
)
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
|
||||||
|
// Verify that the popover has closed
|
||||||
|
await expect(millimetersButton).not.toBeAttached()
|
||||||
|
|
||||||
|
// Verify that the button label has updated
|
||||||
|
await expect(unitsMenuButton).toContainText('mm') })
|
||||||
|
|
||||||
|
test('Successful export shows a success toast', async ({ page, homePage }) => { // FYI this test doesn't work with only engine running locally
|
||||||
|
// And you will need to have the KittyCAD CLI installed
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
;(window as any).playwrightSkipFilePicker = true
|
;(window as any).playwrightSkipFilePicker = true
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -57,205 +47,189 @@ totalHeightHalf = 2
|
|||||||
armThick = 0.5
|
armThick = 0.5
|
||||||
totalLen = 9.5
|
totalLen = 9.5
|
||||||
part001 = startSketchOn('-XZ')
|
part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> yLine(baseHeight, %)
|
|> yLine(baseHeight, %)
|
||||||
|> xLine(baseLen, %)
|
|> xLine(baseLen, %)
|
||||||
|> angledLineToY({
|
|> angledLineToY({
|
||||||
angle: topAng,
|
angle: topAng,
|
||||||
to: totalHeightHalf,
|
to: totalHeightHalf,
|
||||||
}, %, $seg04)
|
}, %, $seg04)
|
||||||
|> xLineTo(totalLen, %, $seg03)
|
|> xLineTo(totalLen, %, $seg03)
|
||||||
|> yLine(-armThick, %, $seg01)
|
|> yLine(-armThick, %, $seg01)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle: HALF_TURN,
|
angle: HALF_TURN,
|
||||||
offset: -armThick,
|
offset: -armThick,
|
||||||
intersectTag: seg04
|
intersectTag: seg04
|
||||||
}, %)
|
}, %)
|
||||||
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
||||||
|> angledLineToY({
|
|> angledLineToY({
|
||||||
angle: -bottomAng,
|
angle: -bottomAng,
|
||||||
to: -totalHeightHalf - armThick,
|
to: -totalHeightHalf - armThick,
|
||||||
}, %, $seg02)
|
}, %, $seg02)
|
||||||
|> xLineTo(segEndX(seg03) + 0, %)
|
|> xLineTo(segEndX(seg03) + 0, %)
|
||||||
|> yLine(-segLen(seg01), %)
|
|> yLine(-segLen(seg01), %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle: HALF_TURN,
|
angle: HALF_TURN,
|
||||||
offset: -armThick,
|
offset: -armThick,
|
||||||
intersectTag: seg02
|
intersectTag: seg02
|
||||||
}, %)
|
}, %)
|
||||||
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
||||||
|> xLineTo(ZERO, %)
|
|> xLineTo(ZERO, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(4, %)`
|
|> extrude(4, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.waitForCmdReceive('extrude')
|
await u.waitForCmdReceive('extrude')
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await u.clearAndCloseDebugPanel()
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
await doExport(
|
await doExport(
|
||||||
{
|
{
|
||||||
type: 'gltf',
|
type: 'gltf',
|
||||||
storage: 'embedded',
|
storage: 'embedded',
|
||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is the main thing we're testing,
|
|
||||||
// We test the export functionality across all
|
|
||||||
// file types in snapshot-tests.spec.ts
|
|
||||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Paste should not work unless an input is focused', async ({
|
test('Paste should not work unless an input is focused', async ({ page, browserName, homePage }) => { // To run this test locally, uncomment Firefox in playwright.config.ts
|
||||||
page,
|
test.skip(
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
// To run this test locally, uncomment Firefox in playwright.config.ts
|
|
||||||
test.skip(
|
|
||||||
browserName !== 'firefox',
|
browserName !== 'firefox',
|
||||||
"This bug is really Firefox-only, which we don't run in CI."
|
"This bug is really Firefox-only, which we don't run in CI."
|
||||||
)
|
)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
await page
|
await page
|
||||||
.getByRole('button', { name: 'Start Sketch' })
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
.waitFor({ state: 'visible' })
|
.waitFor({ state: 'visible' })
|
||||||
|
|
||||||
const codeEditorText = page.locator('.cm-content')
|
const codeEditorText = page.locator('.cm-content')
|
||||||
const pasteContent = `// was this pasted?`
|
const pasteContent = `// was this pasted?`
|
||||||
const typeContent = `// this should be typed`
|
const typeContent = `// this should be typed`
|
||||||
|
|
||||||
// Load text into the clipboard
|
// Load text into the clipboard
|
||||||
await page.evaluate((t) => navigator.clipboard.writeText(t), pasteContent)
|
await page.evaluate((t) => navigator.clipboard.writeText(t), pasteContent)
|
||||||
|
|
||||||
// Focus the text editor
|
// Focus the text editor
|
||||||
await codeEditorText.focus()
|
await codeEditorText.focus()
|
||||||
|
|
||||||
// Show that we can type into it
|
// Show that we can type into it
|
||||||
await page.keyboard.type(typeContent)
|
await page.keyboard.type(typeContent)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Paste without the code pane focused
|
// Paste without the code pane focused
|
||||||
await codeEditorText.blur()
|
await codeEditorText.blur()
|
||||||
await page.keyboard.press('ControlOrMeta+KeyV')
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
|
|
||||||
// Show that the paste didn't work but typing did
|
// Show that the paste didn't work but typing did
|
||||||
await expect(codeEditorText).not.toContainText(pasteContent)
|
await expect(codeEditorText).not.toContainText(pasteContent)
|
||||||
await expect(codeEditorText).toContainText(typeContent)
|
await expect(codeEditorText).toContainText(typeContent)
|
||||||
|
|
||||||
// Paste with the code editor focused
|
// Paste with the code editor focused
|
||||||
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
||||||
await codeEditorText.focus()
|
await codeEditorText.focus()
|
||||||
await page.keyboard.press('ControlOrMeta+KeyV')
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
await expect(
|
await expect(
|
||||||
await page.evaluate(
|
await page.evaluate(
|
||||||
() => document.querySelector('.cm-content')?.textContent
|
() => document.querySelector('.cm-content')?.textContent
|
||||||
)
|
)
|
||||||
).toContain(pasteContent)
|
).toContain(pasteContent) })
|
||||||
})
|
|
||||||
|
|
||||||
test('Keyboard shortcuts can be viewed through the help menu', async ({
|
test('Keyboard shortcuts can be viewed through the help menu', async ({ page, homePage }) => { const u = await getUtils(page)
|
||||||
page,
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
}) => {
|
await homePage.goToModelingScene()
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||||
await page
|
await page
|
||||||
.getByRole('button', { name: 'Start Sketch' })
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
.waitFor({ state: 'visible' })
|
.waitFor({ state: 'visible' })
|
||||||
|
|
||||||
// Open the help menu
|
// Open the help menu
|
||||||
await page.getByRole('button', { name: 'Help and resources' }).click()
|
await page.getByRole('button', { name: 'Help and resources' }).click()
|
||||||
|
|
||||||
// Open the keyboard shortcuts
|
// Open the keyboard shortcuts
|
||||||
await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click()
|
await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click()
|
||||||
|
|
||||||
// Verify the URL and that you can see a list of shortcuts
|
// Verify the URL and that you can see a list of shortcuts
|
||||||
await expect(page.url()).toContain('?tab=keybindings')
|
await expect.poll(() => page.url()).toContain('?tab=keybindings')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('heading', { name: 'Enter Sketch Mode' })
|
page.getByRole('heading', { name: 'Enter Sketch Mode' })
|
||||||
).toBeAttached()
|
).toBeAttached()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('First escape in tool pops you out of tool, second exits sketch mode', async ({
|
test('First escape in tool pops you out of tool, second exits sketch mode', async ({ page, homePage }) => { // Wait for the app to be ready for use
|
||||||
page,
|
const u = await getUtils(page)
|
||||||
}) => {
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
// Wait for the app to be ready for use
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
const lineButton = page.getByRole('button', {
|
const lineButton = page.getByRole('button', {
|
||||||
name: 'line Line',
|
name: 'line Line',
|
||||||
exact: true,
|
exact: true,
|
||||||
})
|
})
|
||||||
const arcButton = page.getByRole('button', {
|
const arcButton = page.getByRole('button', {
|
||||||
name: 'arc Tangential Arc',
|
name: 'arc Tangential Arc',
|
||||||
exact: true,
|
exact: true,
|
||||||
})
|
|
||||||
|
|
||||||
// Test these hotkeys perform actions when
|
|
||||||
// focus is on the canvas
|
|
||||||
await page.mouse.move(600, 250)
|
|
||||||
await page.mouse.click(600, 250)
|
|
||||||
|
|
||||||
// Start a sketch
|
|
||||||
await page.keyboard.press('s')
|
|
||||||
await page.mouse.move(800, 300)
|
|
||||||
await page.mouse.click(800, 300)
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
await expect(lineButton).toBeVisible()
|
|
||||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
|
||||||
|
|
||||||
// Draw a line
|
|
||||||
await page.mouse.move(700, 200, { steps: 5 })
|
|
||||||
await page.mouse.click(700, 200)
|
|
||||||
await page.mouse.move(800, 250, { steps: 5 })
|
|
||||||
await page.mouse.click(800, 250)
|
|
||||||
// Unequip line tool
|
|
||||||
await page.keyboard.press('Escape')
|
|
||||||
// Make sure we didn't pop out of sketch mode.
|
|
||||||
await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible()
|
|
||||||
await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
|
|
||||||
// Equip arc tool
|
|
||||||
await page.keyboard.press('a')
|
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
|
||||||
await page.mouse.move(1000, 100, { steps: 5 })
|
|
||||||
await page.mouse.click(1000, 100)
|
|
||||||
await page.keyboard.press('Escape')
|
|
||||||
await page.keyboard.press('l')
|
|
||||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
|
||||||
|
|
||||||
// Do not close the sketch.
|
|
||||||
// On close it will exit sketch mode.
|
|
||||||
|
|
||||||
// Unequip line tool
|
|
||||||
await page.keyboard.press('Escape')
|
|
||||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'false')
|
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
|
||||||
// Make sure we didn't pop out of sketch mode.
|
|
||||||
await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible()
|
|
||||||
// Exit sketch
|
|
||||||
await page.keyboard.press('Escape')
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Exit Sketch' })
|
|
||||||
).not.toBeVisible()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test these hotkeys perform actions when
|
||||||
|
// focus is on the canvas
|
||||||
|
await page.mouse.move(600, 250)
|
||||||
|
await page.mouse.click(600, 250)
|
||||||
|
|
||||||
|
// Start a sketch
|
||||||
|
await page.keyboard.press('s')
|
||||||
|
await page.mouse.move(800, 300)
|
||||||
|
await page.mouse.click(800, 300)
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await expect(lineButton).toBeVisible()
|
||||||
|
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// Draw a line
|
||||||
|
await page.mouse.move(700, 200, { steps: 5 })
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.mouse.move(800, 250, { steps: 5 })
|
||||||
|
await page.mouse.click(800, 250)
|
||||||
|
// Unequip line tool
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
// Make sure we didn't pop out of sketch mode.
|
||||||
|
await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible()
|
||||||
|
await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
|
||||||
|
// Equip arc tool
|
||||||
|
await page.keyboard.press('a')
|
||||||
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await page.mouse.move(1000, 100, { steps: 5 })
|
||||||
|
await page.mouse.click(1000, 100)
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await page.keyboard.press('l')
|
||||||
|
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// Do not close the sketch.
|
||||||
|
// On close it will exit sketch mode.
|
||||||
|
|
||||||
|
// Unequip line tool
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await expect(lineButton).toHaveAttribute('aria-pressed', 'false')
|
||||||
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
||||||
|
// Make sure we didn't pop out of sketch mode.
|
||||||
|
await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible()
|
||||||
|
// Exit sketch
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
|
).not.toBeVisible() })
|
||||||
|
|
||||||
test.fixme(
|
test.fixme(
|
||||||
'Basic default modeling and sketch hotkeys work',
|
'Basic default modeling and sketch hotkeys work',
|
||||||
async ({ page }) => {
|
async ({ page }) => {
|
||||||
@ -285,8 +259,8 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -437,171 +411,171 @@ test.fixme(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test('Delete key does not navigate back', async ({ page }) => {
|
test('Delete key does not navigate back', async ({ page, homePage }) => {
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
await page.goto('/')
|
|
||||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
|
||||||
|
|
||||||
const settingsButton = page.getByRole('link', {
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
await homePage.goToModelingScene();
|
||||||
|
|
||||||
|
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
|
const settingsButton = page.getByRole('link', {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
exact: false,
|
exact: false,
|
||||||
})
|
|
||||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
|
||||||
|
|
||||||
await settingsButton.click()
|
|
||||||
await expect(page.url()).toContain('/settings')
|
|
||||||
|
|
||||||
// Make sure that delete doesn't go back from settings
|
|
||||||
await page.keyboard.press('Delete')
|
|
||||||
await expect(page.url()).toContain('/settings')
|
|
||||||
|
|
||||||
// Now close the settings and try delete again,
|
|
||||||
// make sure it doesn't go back to settings
|
|
||||||
await settingsCloseButton.click()
|
|
||||||
await page.keyboard.press('Delete')
|
|
||||||
await expect(page.url()).not.toContain('/settings')
|
|
||||||
})
|
})
|
||||||
|
const settingsCloseButton = page.getByTestId('settings-close-button')
|
||||||
|
|
||||||
test('Sketch on face', async ({ page }) => {
|
await settingsButton.click()
|
||||||
test.setTimeout(90_000)
|
await expect.poll(() => page.url()).toContain('/settings')
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.addInitScript(async () => {
|
// Make sure that delete doesn't go back from settings
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await expect.poll(() => page.url()).toContain('/settings')
|
||||||
|
|
||||||
|
// Now close the settings and try delete again,
|
||||||
|
// make sure it doesn't go back to settings
|
||||||
|
await settingsCloseButton.click()
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await expect.poll(() => page.url()).not.toContain('/settings') })
|
||||||
|
|
||||||
|
test('Sketch on face', async ({ page, homePage }) => { test.setTimeout(90_000)
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([3.29, 7.86], %)
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
|> line([2.48, 2.44], %)
|
|> line([2.48, 2.44], %)
|
||||||
|> line([2.66, 1.17], %)
|
|> line([2.66, 1.17], %)
|
||||||
|> line([3.75, 0.46], %)
|
|> line([3.75, 0.46], %)
|
||||||
|> line([4.99, -0.46], %)
|
|> line([4.99, -0.46], %)
|
||||||
|> line([3.3, -2.12], %)
|
|> line([3.3, -2.12], %)
|
||||||
|> line([2.16, -3.33], %)
|
|> line([2.16, -3.33], %)
|
||||||
|> line([0.85, -3.08], %)
|
|> line([0.85, -3.08], %)
|
||||||
|> line([-0.18, -3.36], %)
|
|> line([-0.18, -3.36], %)
|
||||||
|> line([-3.86, -2.73], %)
|
|> line([-3.86, -2.73], %)
|
||||||
|> line([-17.67, 0.85], %)
|
|> line([-17.67, 0.85], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(5 + 7, sketch001)`
|
extrude001 = extrude(5 + 7, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.waitForTimeout(300)
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.doAndWaitForCmd(
|
await u.doAndWaitForCmd(
|
||||||
() => page.mouse.click(625, 165),
|
() => page.mouse.click(625, 165),
|
||||||
'default_camera_get_settings',
|
'default_camera_get_settings',
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(150)
|
await page.waitForTimeout(150)
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
const firstClickPosition = [612, 238]
|
const firstClickPosition = [612, 238]
|
||||||
const secondClickPosition = [661, 242]
|
const secondClickPosition = [661, 242]
|
||||||
const thirdClickPosition = [609, 267]
|
const thirdClickPosition = [609, 267]
|
||||||
|
|
||||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode).toContain(
|
await expect.poll(u.normalisedEditorCode).toContain(
|
||||||
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
||||||
|> startProfileAt([-12.94, 6.6], %)
|
|> startProfileAt([-12.94, 6.6], %)
|
||||||
|> line([2.45, -0.2], %)
|
|> line([2.45, -0.2], %)
|
||||||
|> line([-2.6, -1.25], %)
|
|> line([-2.6, -1.25], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)`)
|
|> close(%)
|
||||||
)
|
`)
|
||||||
|
)
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
await u.updateCamPosition([1049, 239, 686])
|
await u.updateCamPosition([1049, 239, 686])
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.getByText('startProfileAt([-12').click()
|
await page.getByText('startProfileAt([-12').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(400)
|
||||||
await page.waitForTimeout(150)
|
await page.waitForTimeout(150)
|
||||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
await page.setBodyDimensions({ width: 1200, height: 1200 })
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.updateCamPosition([452, -152, 1166])
|
await u.updateCamPosition([452, -152, 1166])
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
const pointToDragFirst = [787, 565]
|
const pointToDragFirst = [787, 565]
|
||||||
await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1])
|
await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1])
|
||||||
await page.mouse.down()
|
await page.mouse.down()
|
||||||
await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], {
|
await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], {
|
||||||
steps: 5,
|
steps: 5,
|
||||||
})
|
|
||||||
await page.mouse.up()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
|
||||||
|
|
||||||
const result = makeTemplate`sketch002 = startSketchOn(extrude001, seg01)
|
|
||||||
|> startProfileAt([-12.83, 6.7], %)
|
|
||||||
|> line([${[2.28, 2.35]}, -${0.07}], %)
|
|
||||||
|> line([-3.05, -1.47], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)`
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(result.regExp)
|
|
||||||
|
|
||||||
// exit sketch
|
|
||||||
await u.openAndClearDebugPanel()
|
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
|
|
||||||
await page.getByText('startProfileAt([-12').click()
|
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('command-bar')).toBeVisible()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
|
||||||
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
|
|
||||||
|
|
||||||
const result2 = result.genNext`
|
|
||||||
const sketch002 = extrude(${[5, 5]} + 7, sketch002)`
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(result2.regExp)
|
|
||||||
})
|
})
|
||||||
|
await page.mouse.up()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
const result = makeTemplate`sketch002 = startSketchOn(extrude001, seg01)
|
||||||
|
|> startProfileAt([-12.83, 6.7], %)
|
||||||
|
|> line([${[2.28, 2.35]}, -${0.07}], %)
|
||||||
|
|> line([-3.05, -1.47], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(result.regExp)
|
||||||
|
|
||||||
|
// exit sketch
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([-12').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
|
||||||
|
|
||||||
|
const result2 = result.genNext`
|
||||||
|
const sketch002 = extrude(${[5, 5]} + 7, sketch002)`
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(result2.regExp) })
|
||||||
|
@ -10,6 +10,10 @@ export { expect, Page, BrowserContext, TestInfo } from '@playwright/test'
|
|||||||
// switch between web and electron if needed.
|
// switch between web and electron if needed.
|
||||||
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
|
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
|
||||||
|
|
||||||
|
|
||||||
|
// In JavaScript you cannot replace a function's body only (despite functions
|
||||||
|
// are themselves objects, which you'd expect a body property or something...)
|
||||||
|
// So we must redefine the function and then re-attach properties.
|
||||||
export function test(desc, objOrFn, fnMaybe) {
|
export function test(desc, objOrFn, fnMaybe) {
|
||||||
const hasTestConf = typeof objOrFn === 'object'
|
const hasTestConf = typeof objOrFn === 'object'
|
||||||
const fn = hasTestConf ? fnMaybe : objOrFn
|
const fn = hasTestConf ? fnMaybe : objOrFn
|
||||||
@ -54,6 +58,13 @@ export function test(desc, objOrFn, fnMaybe) {
|
|||||||
}, dims)
|
}, dims)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
tronApp.context.folderSetupFn = function(fn) {
|
||||||
|
return fn(tronApp.dir).then(() => ({ dir: tronApp.dir }))
|
||||||
|
}
|
||||||
|
|
||||||
await fn(
|
await fn(
|
||||||
{
|
{
|
||||||
context: tronApp.context,
|
context: tronApp.context,
|
||||||
@ -74,3 +85,4 @@ test.afterEach = pwTestFnWithFixtures.afterEach
|
|||||||
test.step = pwTestFnWithFixtures.step
|
test.step = pwTestFnWithFixtures.step
|
||||||
test.skip = pwTestFnWithFixtures.skip
|
test.skip = pwTestFnWithFixtures.skip
|
||||||
test.setTimeout = pwTestFnWithFixtures.setTimeout
|
test.setTimeout = pwTestFnWithFixtures.setTimeout
|
||||||
|
test.fixme = pwTestFnWithFixtures.fixme
|
||||||
|
@ -17,9 +17,14 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (window.electron.process.env.IS_PLAYWRIGHT) {
|
if (window.electron.process.env.IS_PLAYWRIGHT) {
|
||||||
// skip file picker, save to default location
|
// Skip file picker, save to the test dir downloads directory
|
||||||
|
const downloadDir = window.electron.join(
|
||||||
|
window.electron.process.env.TEST_SETTINGS_FILE_KEY,
|
||||||
|
"downloads-during-playwright",
|
||||||
|
)
|
||||||
|
await window.electron.mkdir(downloadDir, { recursive: true })
|
||||||
await window.electron.writeFile(
|
await window.electron.writeFile(
|
||||||
file.name,
|
window.electron.join(downloadDir, file.name),
|
||||||
new Uint8Array(file.contents)
|
new Uint8Array(file.contents)
|
||||||
)
|
)
|
||||||
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS, { id: toastId })
|
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS, { id: toastId })
|
||||||
|
Reference in New Issue
Block a user