From b68a2f2d864d76895ca9a34577eaf1570ef6dc75 Mon Sep 17 00:00:00 2001 From: 49lf Date: Thu, 5 Dec 2024 16:37:56 -0500 Subject: [PATCH] Correct all tsc errors --- e2e/playwright/app-header-tests.spec.ts | 4 +- ...on-all-planes-and-their-back-sides.spec.ts | 2 +- e2e/playwright/code-pane-and-errors.spec.ts | 10 +- e2e/playwright/command-bar-tests.spec.ts | 4 - e2e/playwright/copilot-ghost-test.spec.ts | 1 + e2e/playwright/desktop-export.spec.ts | 7 +- e2e/playwright/editor-tests.spec.ts | 4 - e2e/playwright/fixtures/editorFixture.ts | 6 +- e2e/playwright/fixtures/fixtureSetup.ts | 18 +- e2e/playwright/fixtures/homePageFixture.ts | 2 +- e2e/playwright/onboarding-tests.spec.ts | 10 - e2e/playwright/point-click.spec.ts | 19 +- e2e/playwright/projects.spec.ts | 4 +- e2e/playwright/sketch-tests.spec.ts | 13 +- ...test-network-and-connection-issues.spec.ts | 1 - e2e/playwright/test-utils.ts | 22 +- e2e/playwright/testing-constraints.spec.ts | 4 +- e2e/playwright/testing-gizmo.spec.ts | 4 +- .../testing-perspective-toggle.spec.ts | 2 - .../testing-samples-loading.spec.ts | 112 +- .../testing-segment-overlays.spec.ts | 72 +- e2e/playwright/testing-settings.spec.ts | 1078 ++++++++-------- e2e/playwright/text-to-cad-tests.spec.ts | 1085 +++++++++-------- e2e/playwright/various.spec.ts | 11 +- e2e/playwright/zoo-test.ts | 169 ++- playwright.electron.config.ts | 2 +- src/lang/codeManager.ts | 6 +- src/main.ts | 5 +- src/preload.ts | 10 +- 29 files changed, 1433 insertions(+), 1254 deletions(-) diff --git a/e2e/playwright/app-header-tests.spec.ts b/e2e/playwright/app-header-tests.spec.ts index 1037a07c2..c663c83a8 100644 --- a/e2e/playwright/app-header-tests.spec.ts +++ b/e2e/playwright/app-header-tests.spec.ts @@ -4,7 +4,7 @@ test.describe('Electron app header tests', () => { test( 'Open Command Palette button has correct shortcut', { tag: '@electron' }, - async ({ page, browserName }, testInfo) => { + async ({ page }, testInfo) => { await page.setBodyDimensions({ width: 1200, height: 500 }) // No space before the shortcut since it checks textContent. @@ -29,7 +29,7 @@ test.describe('Electron app header tests', () => { test( 'User settings has correct shortcut', { tag: '@electron' }, - async ({ page, browserName }, testInfo) => { + async ({ page }, testInfo) => { await page.setBodyDimensions({ width: 1200, height: 500 }) // Open the user sidebar menu. diff --git a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts index 5fefbb6e4..aa9333013 100644 --- a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts +++ b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts @@ -1,5 +1,5 @@ import { test, expect, Page } from './zoo-test' -import { HomePageFixture } from './fixtures/HomePageFixture' +import { HomePageFixture } from './fixtures/homePageFixture' import { getUtils } from './test-utils' import { EngineCommand } from 'lang/std/artifactGraph' import { uuidv4 } from 'lib/utils' diff --git a/e2e/playwright/code-pane-and-errors.spec.ts b/e2e/playwright/code-pane-and-errors.spec.ts index eb63c9d8d..2d807d1c9 100644 --- a/e2e/playwright/code-pane-and-errors.spec.ts +++ b/e2e/playwright/code-pane-and-errors.spec.ts @@ -125,8 +125,6 @@ test.describe('Code pane and errors', () => { homePage, context, }) => { - const u = await getUtils(page) - // Load the app with the working starter code await context.addInitScript((code) => { localStorage.setItem('persistCode', code) @@ -168,8 +166,6 @@ test.describe('Code pane and errors', () => { page, homePage, }) => { - const u = await getUtils(page) - // Load the app with the working starter code await context.addInitScript((code) => { localStorage.setItem('persistCode', code) @@ -236,9 +232,9 @@ test.describe('Code pane and errors', () => { test( 'Opening multiple panes persists when switching projects', { tag: '@electron' }, - async ({ context, browserName, page }, testInfo) => { + async ({ context, page }, testInfo) => { // Setup multiple projects. - context.folderSetupFn(async (dir) => { + await context.folderSetupFn(async (dir) => { const routerTemplateDir = join(dir, 'router-template-slate') const bracketDir = join(dir, 'bracket') await Promise.all([ @@ -307,7 +303,7 @@ test( test( 'external change of file contents are reflected in editor', { tag: '@electron' }, - async ({ context, browserName, page }, testInfo) => { + async ({ context, page }, testInfo) => { const PROJECT_DIR_NAME = 'lee-was-here' const { dir: projectsDir } = await context.folderSetupFn(async (dir) => { const aProjectDir = join(dir, PROJECT_DIR_NAME) diff --git a/e2e/playwright/command-bar-tests.spec.ts b/e2e/playwright/command-bar-tests.spec.ts index d2ca92178..2fef1d746 100644 --- a/e2e/playwright/command-bar-tests.spec.ts +++ b/e2e/playwright/command-bar-tests.spec.ts @@ -2,7 +2,6 @@ import { test, expect } from './zoo-test' import { getUtils } from './test-utils' import { KCL_DEFAULT_LENGTH } from 'lib/constants' -import { normalizeLineEndings } from 'lib/codeEditor' test.describe('Command bar tests', () => { test('Extrude from command bar selects extrude line after', async ({ @@ -89,7 +88,6 @@ test.describe('Command bar tests', () => { page, homePage, }) => { - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -171,7 +169,6 @@ test.describe('Command bar tests', () => { page, homePage, }) => { - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -300,7 +297,6 @@ test.describe('Command bar tests', () => { page, homePage, }) => { - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() diff --git a/e2e/playwright/copilot-ghost-test.spec.ts b/e2e/playwright/copilot-ghost-test.spec.ts index ae84339f0..ad96f1110 100644 --- a/e2e/playwright/copilot-ghost-test.spec.ts +++ b/e2e/playwright/copilot-ghost-test.spec.ts @@ -45,6 +45,7 @@ test.describe('Copilot ghost text', () => { test.skip('copilot disabled in sketch mode no select plane', async ({ page, + homePage, }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio diff --git a/e2e/playwright/desktop-export.spec.ts b/e2e/playwright/desktop-export.spec.ts index e47394689..0d6eebfef 100644 --- a/e2e/playwright/desktop-export.spec.ts +++ b/e2e/playwright/desktop-export.spec.ts @@ -2,7 +2,6 @@ import { test, expect } from './zoo-test' import path from 'path' import { getUtils, - setupElectron, executorInputPath, getPlaywrightDownloadDir, } from './test-utils' @@ -12,7 +11,7 @@ test( 'export works on the first try', { tag: '@electron' }, async ({ page, context }, testInfo) => { - context.folderSetupFn(async (dir) => { + await context.folderSetupFn(async (dir) => { const bracketDir = path.join(dir, 'bracket') await Promise.all([fsp.mkdir(bracketDir, { recursive: true })]) await Promise.all([ @@ -25,8 +24,8 @@ test( path.join(bracketDir, 'main.kcl') ), ]) - }), - await page.setBodyDimensions({ width: 1200, height: 500 }) + }) + await page.setBodyDimensions({ width: 1200, height: 500 }) page.on('console', console.log) diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index 1490e4f12..42a2f93a8 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -190,8 +190,6 @@ test.describe('Editor tests', () => { }) test('fold gutters work', async ({ page, homePage }) => { - const u = await getUtils(page) - const fullCode = `sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) @@ -642,8 +640,6 @@ test.describe('Editor tests', () => { page, homePage, }) => { - const u = await getUtils(page) - await context.addInitScript(async () => { localStorage.setItem( 'persistCode', diff --git a/e2e/playwright/fixtures/editorFixture.ts b/e2e/playwright/fixtures/editorFixture.ts index 88c5454f2..d8dd83b39 100644 --- a/e2e/playwright/fixtures/editorFixture.ts +++ b/e2e/playwright/fixtures/editorFixture.ts @@ -56,11 +56,11 @@ export class EditorFixture { if (!shouldNormalise) { const expectStart = expect.poll(() => this.codeContent.textContent()) if (not) { - const result = await expectStart.not.toContain(code, { timeout }) + const result = await expectStart.not.toContain(code) await resetPane() return result } - const result = await expectStart.toContain(code, { timeout }) + const result = await expectStart.toContain(code) await resetPane() return result } @@ -150,9 +150,11 @@ export class EditorFixture { scrollToText(text: string) { return this.page.evaluate((scrollToText: string) => { // editorManager is available on the window object. + // @ts-ignore let index = editorManager._editorView.docView.view.state.doc .toString() .indexOf(scrollToText) + // @ts-ignore editorManager._editorView.dispatch({ selection: { anchor: index, diff --git a/e2e/playwright/fixtures/fixtureSetup.ts b/e2e/playwright/fixtures/fixtureSetup.ts index 0d4b63f6f..ac3896d6d 100644 --- a/e2e/playwright/fixtures/fixtureSetup.ts +++ b/e2e/playwright/fixtures/fixtureSetup.ts @@ -1,11 +1,11 @@ import type { BrowserContext, ElectronApplication, - Page, TestInfo, + Page, } from '@playwright/test' -import { test as base } from '@playwright/test' -import { getUtils, setup, setupElectron, tearDown } from '../test-utils' + +import { getUtils, setup, setupElectron } from '../test-utils' import fsp from 'fs/promises' import { join } from 'path' import { CmdBarFixture } from './cmdBarFixture' @@ -50,8 +50,6 @@ export class AuthenticatedApp { } export interface Fixtures { - app: AuthenticatedApp - tronApp: AuthenticatedTronApp cmdBar: CmdBarFixture editor: EditorFixture toolbar: ToolbarFixture @@ -122,19 +120,19 @@ export class AuthenticatedTronApp { } export const fixtures = { - cmdBar: async ({ page }, use) => { + cmdBar: async ({ page }: { page: Page }, use: any) => { await use(new CmdBarFixture(page)) }, - editor: async ({ page }, use) => { + editor: async ({ page }: { page: Page }, use: any) => { await use(new EditorFixture(page)) }, - toolbar: async ({ page }, use) => { + toolbar: async ({ page }: { page: Page }, use: any) => { await use(new ToolbarFixture(page)) }, - scene: async ({ page }, use) => { + scene: async ({ page }: { page: Page }, use: any) => { await use(new SceneFixture(page)) }, - homePage: async ({ page }, use, testInfo) => { + homePage: async ({ page }: { page: Page }, use: any) => { await use(new HomePageFixture(page)) }, } diff --git a/e2e/playwright/fixtures/homePageFixture.ts b/e2e/playwright/fixtures/homePageFixture.ts index aa5eb23de..0c38bf426 100644 --- a/e2e/playwright/fixtures/homePageFixture.ts +++ b/e2e/playwright/fixtures/homePageFixture.ts @@ -118,7 +118,7 @@ export class HomePageFixture { await projectCard.click() } - goToModelingScene = async (name?: string = 'testDefault') => { + goToModelingScene = async (name: string = 'testDefault') => { // On web this is a no-op. There is no project view. if (process.env.PLATFORM === 'web') return diff --git a/e2e/playwright/onboarding-tests.spec.ts b/e2e/playwright/onboarding-tests.spec.ts index 954dd079d..caed94743 100644 --- a/e2e/playwright/onboarding-tests.spec.ts +++ b/e2e/playwright/onboarding-tests.spec.ts @@ -8,7 +8,6 @@ import { TEST_SETTINGS_KEY, TEST_SETTINGS_ONBOARDING_START, TEST_SETTINGS_ONBOARDING_EXPORT, - TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING, TEST_SETTINGS_ONBOARDING_USER_MENU, } from './storageStates' import * as TOML from '@iarna/toml' @@ -29,8 +28,6 @@ test.describe('Onboarding tests', () => { cleanProjectDir: true, }, async ({ context, page, homePage }) => { - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -117,7 +114,6 @@ test.describe('Onboarding tests', () => { localStorage.setItem('persistCode', code) }, initialCode) - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -159,8 +155,6 @@ test.describe('Onboarding tests', () => { }, }, async ({ context, page, homePage }) => { - const u = await getUtils(page) - // Override beforeEach test setup await context.addInitScript( async ({ settingsKey, settings }) => { @@ -212,8 +206,6 @@ test.describe('Onboarding tests', () => { cleanProjectDir: true, }, async ({ context, page, homePage }) => { - const u = await getUtils(page) - const originalCode = 'sigmaAllow = 15000' // Override beforeEach test setup @@ -332,7 +324,6 @@ test.describe('Onboarding tests', () => { } ) - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -407,7 +398,6 @@ test.describe('Onboarding tests', () => { } ) - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index d41bfcaff..ed3216d3c 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -622,18 +622,12 @@ openSketch = startSketchOn('XY') localStorage.setItem('persistCode', code) }, initialCode) - await homePage.goToModelingScene() await u.waitForPageLoad() await page.waitForTimeout(1000) - const pointOnSketchArea = { - x: viewPortSize.width * 0.7, - y: viewPortSize.height * 0.7, - } - const pointInsideCircle = { - x: viewPortSize.width * 0.55, + x: viewPortSize.width * 0.58, y: viewPortSize.height * 0.5, } const pointOnPathAfterSketching = { @@ -678,8 +672,14 @@ openSketch = startSketchOn('XY') // Drag the sketch line out of the axis view which blocks the click await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: viewPortSize.width * 0.7, y: viewPortSize.height* 0.5 }, - targetPosition: { x: viewPortSize.width * 0.7, y: viewPortSize.height* 0.4 }, + sourcePosition: { + x: viewPortSize.width * 0.7, + y: viewPortSize.height * 0.5, + }, + targetPosition: { + x: viewPortSize.width * 0.7, + y: viewPortSize.height * 0.4, + }, }) await page.waitForTimeout(500) @@ -711,7 +711,6 @@ test(`Offset plane point-and-click`, async ({ toolbar, cmdBar, }) => { - // One dumb hardcoded screen pixel value const testPoint = { x: 700, y: 150 } const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y) diff --git a/e2e/playwright/projects.spec.ts b/e2e/playwright/projects.spec.ts index 0130383a3..46006d5fb 100644 --- a/e2e/playwright/projects.spec.ts +++ b/e2e/playwright/projects.spec.ts @@ -1574,7 +1574,7 @@ test( .locator('section#projectDirectory input') .inputValue() - const handleFile = electronApp.evaluate( + const handleFile = electronApp?.evaluate( async ({ dialog }, filePaths) => { dialog.showOpenDialog = () => Promise.resolve({ canceled: false, filePaths }) @@ -1604,7 +1604,7 @@ test( await page.getByTestId('project-directory-settings-link').click() - const handleFile = electronApp.evaluate( + const handleFile = electronApp?.evaluate( async ({ dialog }, filePaths) => { dialog.showOpenDialog = () => Promise.resolve({ canceled: false, filePaths }) diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts index cf3a22a38..1d439782c 100644 --- a/e2e/playwright/sketch-tests.spec.ts +++ b/e2e/playwright/sketch-tests.spec.ts @@ -101,7 +101,6 @@ test.describe('Sketch tests', () => { }) test('Can delete most of a sketch and the line tool will still work', async ({ page, - center, homePage, }) => { const u = await getUtils(page) @@ -142,7 +141,7 @@ test.describe('Sketch tests', () => { await page.waitForTimeout(100) await expect(async () => { - await page.mouse.move(700, 200, { step: 25 }) + await page.mouse.move(700, 200, { steps: 25 }) await page.mouse.click(700, 200) await expect @@ -164,7 +163,6 @@ test.describe('Sketch tests', () => { localStorage.setItem('persistCode', ``) }) - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -428,8 +426,11 @@ test.describe('Sketch tests', () => { }) // expect the code to have changed - await editor.expectEditor.toContain(`sketch001 = startSketchOn('XZ') - |> circle({ center = [7.26, -2.37], radius = 11.44 }, %)`, { shouldNormalise: true }) + await editor.expectEditor.toContain( + `sketch001 = startSketchOn('XZ') + |> circle({ center = [7.26, -2.37], radius = 11.44 }, %)`, + { shouldNormalise: true } + ) }) test('Can edit a sketch that has been extruded in the same pipe', async ({ page, @@ -1289,7 +1290,7 @@ test.describe(`Sketching with offset planes`, () => { ) }) - homePage.goToModelingScene() + await homePage.goToModelingScene() const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200) diff --git a/e2e/playwright/test-network-and-connection-issues.spec.ts b/e2e/playwright/test-network-and-connection-issues.spec.ts index b13681f82..c1ba42eb5 100644 --- a/e2e/playwright/test-network-and-connection-issues.spec.ts +++ b/e2e/playwright/test-network-and-connection-issues.spec.ts @@ -80,7 +80,6 @@ test.describe('Test network and connection issues', () => { test('Engine disconnect & reconnect in sketch mode', async ({ page, - browserName, homePage, }) => { // TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit diff --git a/e2e/playwright/test-utils.ts b/e2e/playwright/test-utils.ts index fd18c2396..45c874256 100644 --- a/e2e/playwright/test-utils.ts +++ b/e2e/playwright/test-utils.ts @@ -1,13 +1,11 @@ import { expect, - Page, - Download, BrowserContext, TestInfo, _electron as electron, Locator, - test, } from '@playwright/test' +import { test, Page } from './zoo-test' import { EngineCommand } from 'lang/std/artifactGraph' import fsp from 'fs/promises' import fsSync from 'fs' @@ -16,7 +14,7 @@ import pixelMatch from 'pixelmatch' import { PNG } from 'pngjs' import { Protocol } from 'playwright-core/types/protocol' import type { Models } from '@kittycad/lib' -import { APP_NAME, COOKIE_NAME } from 'lib/constants' +import { COOKIE_NAME } from 'lib/constants' import { secrets } from './secrets' import { TEST_SETTINGS_KEY, @@ -163,8 +161,10 @@ async function openKclCodePanel(page: Page) { // Code Mirror lazy loads text! Wowza! Let's force-load the text for tests. await page.evaluate(() => { // editorManager is available on the window object. + //@ts-ignore this is in an entirely different context that tsc can't see. editorManager._editorView.dispatch({ selection: { + //@ts-ignore this is in an entirely different context that tsc can't see. anchor: editorManager._editorView.docView.length, }, scrollIntoView: true, @@ -515,10 +515,12 @@ export async function getUtils(page: Page, test_?: typeof test) { async editorTextMatches(code: string) { const editor = page.locator(editorSelector) - return expect.poll(async () => { - const text = await editor.textContent() - return toNormalizedCode(text) - }).toContain(toNormalizedCode(code)) + return expect + .poll(async () => { + const text = await editor.textContent() + return toNormalizedCode(text ?? '') + }) + .toContain(toNormalizedCode(code)) }, pasteCodeInEditor: async (code: string) => { @@ -716,7 +718,7 @@ const moveDownloadedFileTo = async (page: Page, toLocation: string) => { const downloadDir = getPlaywrightDownloadDir(page) // Expect there to be at least one file - expect + await expect .poll(async () => { const files = await fsp.readdir(downloadDir) return files.length @@ -848,7 +850,7 @@ export async function tearDown(page: Page, testInfo: TestInfo) { // It's not super reliable but we have no real other choice for now await page.waitForTimeout(3000) - await testInfo.tronApp.close() + await testInfo.tronApp?.close() } // settingsOverrides may need to be augmented to take more generic items, diff --git a/e2e/playwright/testing-constraints.spec.ts b/e2e/playwright/testing-constraints.spec.ts index 81aba7891..1786427cd 100644 --- a/e2e/playwright/testing-constraints.spec.ts +++ b/e2e/playwright/testing-constraints.spec.ts @@ -1069,10 +1069,10 @@ part002 = startSketchOn('XZ') ) const linebb = await u.getBoundingBox('[data-overlay-index="1"]') - await page.mouse.move(linebb.x, linebb.y, { step: 25 }) + await page.mouse.move(linebb.x, linebb.y, { steps: 25 }) await page.mouse.click(linebb.x, linebb.y) - expect + await expect .poll(async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)) .toBeLessThan(3) diff --git a/e2e/playwright/testing-gizmo.spec.ts b/e2e/playwright/testing-gizmo.spec.ts index f502b3a1b..67f0e0a0e 100644 --- a/e2e/playwright/testing-gizmo.spec.ts +++ b/e2e/playwright/testing-gizmo.spec.ts @@ -1,9 +1,7 @@ import { test, expect } from './zoo-test' -import { getUtils, executorInputPath } from './test-utils' +import { getUtils } from './test-utils' import { uuidv4 } from 'lib/utils' import { TEST_CODE_GIZMO } from './storageStates' -import path from 'node:path' -import fsp from 'fs/promises' test.describe('Testing Gizmo', () => { const cases = [ diff --git a/e2e/playwright/testing-perspective-toggle.spec.ts b/e2e/playwright/testing-perspective-toggle.spec.ts index 5c859b8da..55977e08a 100644 --- a/e2e/playwright/testing-perspective-toggle.spec.ts +++ b/e2e/playwright/testing-perspective-toggle.spec.ts @@ -1,7 +1,5 @@ import { test, expect } from './zoo-test' import { getUtils } from './test-utils' -import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates' -import * as TOML from '@iarna/toml' test.describe('Test toggling perspective', () => { test('via command palette and toggle', async ({ page, homePage }) => { diff --git a/e2e/playwright/testing-samples-loading.spec.ts b/e2e/playwright/testing-samples-loading.spec.ts index b1e4ef7b7..96da7d5ba 100644 --- a/e2e/playwright/testing-samples-loading.spec.ts +++ b/e2e/playwright/testing-samples-loading.spec.ts @@ -11,61 +11,67 @@ test.describe('Testing in-app sample loading', () => { * Note this test implicitly depends on the KCL sample "car-wheel.kcl", * its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl */ - test('Web: should overwrite current code, cannot create new file', async ({ editor, context, page, homePage }) => { + test('Web: should overwrite current code, cannot create new file', async ({ + editor, + context, + page, + homePage, + }) => { const u = await getUtils(page) - - await test.step(`Test setup`, async () => { - await context.addInitScript((code) => { - window.localStorage.setItem('persistCode', code) - }, bracket) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - }) - - // Locators and constants - const newSample = { - file: 'car-wheel' + FILE_EXT, - title: 'Car Wheel', - } - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - const samplesCommandOption = page.getByRole('option', { - name: 'Open Sample', - }) - const commandSampleOption = page.getByRole('option', { - name: newSample.title, - exact: true, - }) - const commandMethodArgButton = page.getByRole('button', { - name: 'Method', - }) - const commandMethodOption = (name: 'Overwrite' | 'Create new file') => - page.getByRole('option', { - name, + + await test.step(`Test setup`, async () => { + await context.addInitScript((code) => { + window.localStorage.setItem('persistCode', code) + }, bracket) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + }) + + // Locators and constants + const newSample = { + file: 'car-wheel' + FILE_EXT, + title: 'Car Wheel', + } + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + const samplesCommandOption = page.getByRole('option', { + name: 'Open Sample', + }) + const commandSampleOption = page.getByRole('option', { + name: newSample.title, + exact: true, + }) + const commandMethodArgButton = page.getByRole('button', { + name: 'Method', + }) + const commandMethodOption = (name: 'Overwrite' | 'Create new file') => + page.getByRole('option', { + name, + }) + const warningText = page.getByText('Overwrite current file and units?') + const confirmButton = page.getByRole('button', { name: 'Submit command' }) + const unitsToast = (unit: UnitLength_type) => + page.getByText(`Set default unit to "${unit}" for this project`) + + await test.step(`Precondition: check the initial code`, async () => { + await u.openKclCodePanel() + await editor.scrollToText(bracket.split('\n')[0]) + await editor.expectEditor.toContain(bracket.split('\n')[0]) + }) + + await test.step(`Load a KCL sample with the command palette`, async () => { + await commandBarButton.click() + await samplesCommandOption.click() + await commandSampleOption.click() + await commandMethodArgButton.click() + await expect(commandMethodOption('Create new file')).not.toBeVisible() + await commandMethodOption('Overwrite').click() + await expect(warningText).toBeVisible() + await confirmButton.click() + + await editor.expectEditor.toContain('// ' + newSample.title) + await expect(unitsToast('in')).toBeVisible() }) - const warningText = page.getByText('Overwrite current file and units?') - const confirmButton = page.getByRole('button', { name: 'Submit command' }) - const unitsToast = (unit: UnitLength_type) => - page.getByText(`Set default unit to "${unit}" for this project`) - - await test.step(`Precondition: check the initial code`, async () => { - await u.openKclCodePanel() - await editor.scrollToText(bracket.split('\n')[0]) - await editor.expectEditor.toContain(bracket.split('\n')[0]) }) - - await test.step(`Load a KCL sample with the command palette`, async () => { - await commandBarButton.click() - await samplesCommandOption.click() - await commandSampleOption.click() - await commandMethodArgButton.click() - await expect(commandMethodOption('Create new file')).not.toBeVisible() - await commandMethodOption('Overwrite').click() - await expect(warningText).toBeVisible() - await confirmButton.click() - - await editor.expectEditor.toContain('// ' + newSample.title) - await expect(unitsToast('in')).toBeVisible() - }) }) /** * Note this test implicitly depends on the KCL samples: @@ -75,7 +81,7 @@ test.describe('Testing in-app sample loading', () => { test( 'Desktop: should create new file by default, optionally overwrite', { tag: '@electron' }, - async ({ editor, context, page, browserName: _ }, testInfo) => { + async ({ editor, context, page }, testInfo) => { const { dir } = await context.folderSetupFn(async (dir) => { const bracketDir = join(dir, 'bracket') await fsp.mkdir(bracketDir, { recursive: true }) diff --git a/e2e/playwright/testing-segment-overlays.spec.ts b/e2e/playwright/testing-segment-overlays.spec.ts index 14d1677a7..782b3a6fd 100644 --- a/e2e/playwright/testing-segment-overlays.spec.ts +++ b/e2e/playwright/testing-segment-overlays.spec.ts @@ -3,6 +3,7 @@ import { test, expect, Page } from './zoo-test' import { deg, getUtils, wiggleMove } from './test-utils' import { LineInputsType } from 'lang/std/sketchcombos' import { uuidv4 } from 'lib/utils' +import { EditorFixture } from './fixtures/editorFixture' test.describe('Testing segment overlays', () => { test.describe('Hover over a segment should show its overlay, hovering over the input overlays should show its popover, clicking the input overlay should constrain/unconstrain it:\nfor the following segments', () => { @@ -52,7 +53,9 @@ test.describe('Testing segment overlays', () => { await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator) await page.mouse.move(x, y) - await editor.expectEditor.toContain(expectBeforeUnconstrained, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectBeforeUnconstrained, { + shouldNormalise: true, + }) const constrainedLocator = page.locator( `[data-constraint-type="${constraintType}"][data-is-constrained="true"]` ) @@ -62,7 +65,9 @@ test.describe('Testing segment overlays', () => { await page.getByTestId('constraint-symbol-popover').count() ).toBeGreaterThan(0) await constrainedLocator.click() - await editor.expectEditor.toContain(expectAfterUnconstrained, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectAfterUnconstrained, { + shouldNormalise: true, + }) await page.mouse.move(0, 0) await page.waitForTimeout(1000) @@ -91,6 +96,9 @@ test.describe('Testing segment overlays', () => { .click() await expect(page.locator('.cm-content')).toContainText(expectFinal) await editor.expectEditor.toContain(expectFinal, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectFinal, { + shouldNormalise: true, + }) } /** @@ -138,7 +146,9 @@ test.describe('Testing segment overlays', () => { await page.mouse.move(x, y) await expect(page.getByText('Added variable')).not.toBeVisible() - await editor.expectEditor.toContain(expectBeforeUnconstrained, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectBeforeUnconstrained, { + shouldNormalise: true, + }) const unconstrainedLocator = page.locator( `[data-constraint-type="${constraintType}"][data-is-constrained="false"]` ) @@ -156,7 +166,9 @@ test.describe('Testing segment overlays', () => { name: 'arrow right Continue', }) .click() - await editor.expectEditor.toContain(expectAfterUnconstrained, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectAfterUnconstrained, { + shouldNormalise: true, + }) await expect(page.getByText('Added variable')).not.toBeVisible() await page.mouse.move(0, 0) @@ -176,7 +188,9 @@ test.describe('Testing segment overlays', () => { await page.getByTestId('constraint-symbol-popover').count() ).toBeGreaterThan(0) await constrainedLocator.click() - await editor.expectEditor.toContain(expectFinal, { shouldNormalise: true }) + await editor.expectEditor.toContain(expectFinal, { + shouldNormalise: true, + }) } test.setTimeout(120000) test('for segments [line, angledLine, lineTo, xLineTo]', async ({ @@ -344,7 +358,11 @@ test.describe('Testing segment overlays', () => { locator: '[data-overlay-toolbar-index="3"]', }) }) - test('for segments [yLineTo, xLine]', async ({ page, editor, homePage }) => { + test('for segments [yLineTo, xLine]', async ({ + page, + editor, + homePage, + }) => { await page.addInitScript(async () => { localStorage.setItem( 'persistCode', @@ -709,7 +727,11 @@ test.describe('Testing segment overlays', () => { locator: '[data-overlay-toolbar-index="11"]', }) }) - test('for segment [tangentialArcTo]', async ({ page, editor, homePage }) => { + test('for segment [tangentialArcTo]', async ({ + page, + editor, + homePage, + }) => { await page.addInitScript(async () => { localStorage.setItem( 'persistCode', @@ -888,12 +910,16 @@ test.describe('Testing segment overlays', () => { await wiggleMove(page, x, y, 20, 30, ang, 10, 5, locator) await page.mouse.move(x, y) - await editor.expectEditor.toContain(codeToBeDeleted, { shouldNormalise: true }) + await editor.expectEditor.toContain(codeToBeDeleted, { + shouldNormalise: true, + }) await page.locator(`[data-stdlib-fn-name="${stdLibFnName}"]`).click() await page.getByText('Delete Segment').click() - await editor.expectEditor.not.toContain(codeToBeDeleted, { shouldNormalise: true }) + await editor.expectEditor.not.toContain(codeToBeDeleted, { + shouldNormalise: true, + }) } test('all segment types', async ({ page, editor, homePage }) => { await page.addInitScript(async () => { @@ -1075,12 +1101,16 @@ test.describe('Testing segment overlays', () => { await page.mouse.move(hoverPos.x, hoverPos.y) const codeToBeDeleted = 'lineTo([33, 11.5 + 0], %)' - await editor.expectEditor.toContain(codeToBeDeleted, { shouldNormalise: true }) + await editor.expectEditor.toContain(codeToBeDeleted, { + shouldNormalise: true, + }) await page.getByTestId('overlay-menu').click() await page.getByText('Delete Segment').click() - await editor.expectEditor.not.toContain(codeToBeDeleted, { shouldNormalise: true }) + await editor.expectEditor.not.toContain(codeToBeDeleted, { + shouldNormalise: true, + }) segmentToDelete = await getOverlayByIndex(1) ang = await u.getAngle(`[data-overlay-index="${1}"]`) @@ -1175,7 +1205,9 @@ test.describe('Testing segment overlays', () => { await page.mouse.move(hoverPos.x + x, hoverPos.y + y) await page.mouse.move(hoverPos.x, hoverPos.y, { steps: 5 }) - await editor.expectEditor.toContain(lineOfInterest, { shouldNormalise: true }) + await editor.expectEditor.toContain(lineOfInterest, { + shouldNormalise: true, + }) await page.getByTestId('overlay-menu').click() await page.waitForTimeout(100) @@ -1186,7 +1218,9 @@ test.describe('Testing segment overlays', () => { await page.mouse.move(hoverPos.x + x, hoverPos.y + y) await page.mouse.move(hoverPos.x, hoverPos.y, { steps: 5 }) - await editor.expectEditor.toContain(lineOfInterest, { shouldNormalise: true }) + await editor.expectEditor.toContain(lineOfInterest, { + shouldNormalise: true, + }) await page.getByTestId('overlay-menu').click() await page.waitForTimeout(100) @@ -1202,12 +1236,18 @@ test.describe('Testing segment overlays', () => { ) ).toBeTruthy() // eslint-disable-next-line jest/no-conditional-expect - await editor.expectEditor.toContain(lineOfInterest, { shouldNormalise: true }) + await editor.expectEditor.toContain(lineOfInterest, { + shouldNormalise: true, + }) } else { // eslint-disable-next-line jest/no-conditional-expect - await editor.expectEditor.not.toContain(lineOfInterest, { shouldNormalise: true }) + await editor.expectEditor.not.toContain(lineOfInterest, { + shouldNormalise: true, + }) // eslint-disable-next-line jest/no-conditional-expect - await editor.expectEditor.not.toContain('seg01', { shouldNormalise: true }) + await editor.expectEditor.not.toContain('seg01', { + shouldNormalise: true, + }) } }) } diff --git a/e2e/playwright/testing-settings.spec.ts b/e2e/playwright/testing-settings.spec.ts index 3c5783693..75d06feac 100644 --- a/e2e/playwright/testing-settings.spec.ts +++ b/e2e/playwright/testing-settings.spec.ts @@ -1,11 +1,7 @@ import { test, expect } from './zoo-test' import * as fsp from 'fs/promises' import { join } from 'path' -import { - getUtils, - executorInputPath, - createProject, -} from './test-utils' +import { getUtils, executorInputPath, createProject } from './test-utils' import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes' import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants' import { @@ -17,242 +13,260 @@ import { import * as TOML from '@iarna/toml' test.describe('Testing settings', () => { - test('Stored settings are validated and fall back to defaults', - // Override beforeEach test setup - // with corrupted settings - { - appSettings: TEST_SETTINGS_CORRUPTED - }, - async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1200, height: 500 }) - - // Check the settings were reset - const storedSettings = TOML.parse( - await page.evaluate( - ({ settingsKey }) => localStorage.getItem(settingsKey) || '', - { settingsKey: TEST_SETTINGS_KEY } - ) - ) as { settings: SaveSettingsPayload } - - expect(storedSettings.settings?.app?.theme).toBe('dark') - - // Check that the invalid settings were changed to good defaults - expect(storedSettings.settings?.modeling?.defaultUnit).toBe("in") - expect(storedSettings.settings?.modeling?.mouseControls).toBe("KittyCAD") - expect(storedSettings.settings?.app?.projectDirectory).toBe("") - expect(storedSettings.settings?.projects?.defaultProjectName).toBe( - "project-$nnn" - ) }) + test( + 'Stored settings are validated and fall back to defaults', + // Override beforeEach test setup + // with corrupted settings + { + appSettings: TEST_SETTINGS_CORRUPTED, + }, + async ({ page, homePage }) => { + await page.setBodyDimensions({ width: 1200, height: 500 }) + + // Check the settings were reset + const storedSettings = TOML.parse( + await page.evaluate( + ({ settingsKey }) => localStorage.getItem(settingsKey) || '', + { settingsKey: TEST_SETTINGS_KEY } + ) + ) as { settings: SaveSettingsPayload } + + expect(storedSettings.settings?.app?.theme).toBe('dark') + + // Check that the invalid settings were changed to good defaults + expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in') + expect(storedSettings.settings?.modeling?.mouseControls).toBe('KittyCAD') + expect(storedSettings.settings?.app?.projectDirectory).toBe('') + expect(storedSettings.settings?.projects?.defaultProjectName).toBe( + 'project-$nnn' + ) + } + ) // The behavior is actually broken. Parent always takes precedence - test.fixme('Project settings can be set and override user settings', async ({ page, homePage }) => { const u = await getUtils(page) - await test.step(`Setup`, async () => { - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - await page - .getByRole('button', { name: 'Start Sketch' }) - .waitFor({ state: 'visible' }) - }) - - // Selectors and constants - const paneButtonLocator = page.getByTestId('debug-pane-button') - const headingLocator = page.getByRole('heading', { - name: 'Settings', - exact: true, - }) - const inputLocator = page.locator('input[name="modeling-showDebugPanel"]') - - await test.step('Open settings dialog and set "Show debug panel" to on', async () => { - await page.keyboard.press('ControlOrMeta+,') - await expect(headingLocator).toBeVisible() - - /** Test to close https://github.com/KittyCAD/modeling-app/issues/2713 */ - await test.step(`Confirm that this dialog has a solid background`, async () => { - await expect - .poll(() => u.getGreatestPixDiff({ x: 600, y: 250 }, [28, 28, 28]), { - timeout: 1000, - message: - 'Checking for solid background, should not see default plane colors', + test.fixme( + 'Project settings can be set and override user settings', + async ({ page, homePage }) => { + const u = await getUtils(page) + await test.step(`Setup`, async () => { + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + await page + .getByRole('button', { name: 'Start Sketch' }) + .waitFor({ state: 'visible' }) + }) + + // Selectors and constants + const paneButtonLocator = page.getByTestId('debug-pane-button') + const headingLocator = page.getByRole('heading', { + name: 'Settings', + exact: true, + }) + const inputLocator = page.locator('input[name="modeling-showDebugPanel"]') + + await test.step('Open settings dialog and set "Show debug panel" to on', async () => { + await page.keyboard.press('ControlOrMeta+,') + await expect(headingLocator).toBeVisible() + + /** Test to close https://github.com/KittyCAD/modeling-app/issues/2713 */ + await test.step(`Confirm that this dialog has a solid background`, async () => { + await expect + .poll( + () => u.getGreatestPixDiff({ x: 600, y: 250 }, [28, 28, 28]), + { + timeout: 1000, + message: + 'Checking for solid background, should not see default plane colors', + } + ) + .toBeLessThan(15) }) - .toBeLessThan(15) - }) - - await page.locator('#showDebugPanel').getByText('OffOn').click() - }) - - // Close it and open again with keyboard shortcut, while KCL editor is focused - // Put the cursor in the editor - await test.step('Open settings with keyboard shortcut', async () => { - await page.getByTestId('settings-close-button').click() - await page.locator('.cm-content').click() - await page.keyboard.press('ControlOrMeta+,') - await expect(headingLocator).toBeVisible() - }) - - // Verify the toast appeared - await expect( - page.getByText(`Set show debug panel to "false" for this project`) - ).toBeVisible() - await expect( - page.getByText(`Set show debug panel to "false" for this project`) - ).not.toBeVisible() - // Check that the debug panel button is gone - await expect(paneButtonLocator).not.toBeVisible() - - // Check that the user setting was not changed - await page.getByRole('radio', { name: 'User' }).click() - await expect(inputLocator).toBeChecked() - - // Roll back to default of "off" - await await page - .getByText('show debug panelRoll back show debug panelRoll back to match') - .hover() - await page - .getByRole('button', { - name: 'Roll back show debug panel', - }) - .click() - await expect(inputLocator).not.toBeChecked() - - // Check that the project setting did not change - await page.getByRole('radio', { name: 'Project' }).click() - await expect( - page.locator('input[name="modeling-showDebugPanel"]') - ).not.toBeChecked() }) + await page.locator('#showDebugPanel').getByText('OffOn').click() + }) - test('Keybindings display the correct hotkey for Command Palette', async ({ page, homePage }) => { + // Close it and open again with keyboard shortcut, while KCL editor is focused + // Put the cursor in the editor + await test.step('Open settings with keyboard shortcut', async () => { + await page.getByTestId('settings-close-button').click() + await page.locator('.cm-content').click() + await page.keyboard.press('ControlOrMeta+,') + await expect(headingLocator).toBeVisible() + }) + + // Verify the toast appeared + await expect( + page.getByText(`Set show debug panel to "false" for this project`) + ).toBeVisible() + await expect( + page.getByText(`Set show debug panel to "false" for this project`) + ).not.toBeVisible() + + // Check that the debug panel button is gone + await expect(paneButtonLocator).not.toBeVisible() + + // Check that the user setting was not changed + await page.getByRole('radio', { name: 'User' }).click() + await expect(inputLocator).toBeChecked() + + // Roll back to default of "off" + await await page + .getByText( + 'show debug panelRoll back show debug panelRoll back to match' + ) + .hover() + await page + .getByRole('button', { + name: 'Roll back show debug panel', + }) + .click() + await expect(inputLocator).not.toBeChecked() + + // Check that the project setting did not change + await page.getByRole('radio', { name: 'Project' }).click() + await expect( + page.locator('input[name="modeling-showDebugPanel"]') + ).not.toBeChecked() + } + ) + + test('Keybindings display the correct hotkey for Command Palette', async ({ + page, + homePage, + }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - await u.waitForPageLoad() - - await test.step('Open keybindings settings', async () => { - // Open the settings modal with the keyboard shortcut - await page.keyboard.press('ControlOrMeta+,') - - // Go to Keybindings tab. - const keybindingsTab = page.getByRole('radio', { name: 'Keybindings' }) - await keybindingsTab.click() - }) - - // Go to the hotkey for Command Palette. - const commandPalette = page.getByText('Toggle Command Palette') - await commandPalette.scrollIntoViewIfNeeded() - - // The heading is above it and should be in view now. - const commandPaletteHeading = page.getByRole('heading', { - name: 'Command Palette', - }) - // The hotkey is in a kbd element next to the heading. - const hotkey = commandPaletteHeading.locator('+ div kbd') - const text = process.platform === 'darwin' ? 'Command+K' : 'Control+K' - await expect(hotkey).toHaveText(text) }) - - test('Project and user settings can be reset', async ({ page, homePage }) => { const u = await getUtils(page) - await test.step(`Setup`, async () => { await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() await u.waitForPageLoad() - await page.waitForTimeout(1000) - }) - - // Selectors and constants - const projectSettingsTab = page.getByRole('radio', { name: 'Project' }) - const userSettingsTab = page.getByRole('radio', { name: 'User' }) - const resetButton = (level: SettingsLevel) => - page.getByRole('button', { - name: `Reset ${level}-level settings`, + + await test.step('Open keybindings settings', async () => { + // Open the settings modal with the keyboard shortcut + await page.keyboard.press('ControlOrMeta+,') + + // Go to Keybindings tab. + const keybindingsTab = page.getByRole('radio', { name: 'Keybindings' }) + await keybindingsTab.click() }) - const themeColorSetting = page.locator('#themeColor').getByRole('slider') - const settingValues = { - default: '259', - user: '120', - project: '50', - } - const resetToast = (level: SettingsLevel) => - page.getByText(`${level}-level settings were reset`) - - await test.step(`Open the settings modal`, async () => { - await page.getByRole('link', { name: 'Settings' }).last().click() - await expect( - page.getByRole('heading', { name: 'Settings', exact: true }) - ).toBeVisible() - }) - - await test.step('Set up theme color', async () => { - // Verify we're looking at the project-level settings, - // and it's set to default value - await expect(projectSettingsTab).toBeChecked() - await expect(themeColorSetting).toHaveValue(settingValues.default) - - // Set project-level value to 50 - await themeColorSetting.fill(settingValues.project) - - // Set user-level value to 120 - await userSettingsTab.click() - await themeColorSetting.fill(settingValues.user) - await projectSettingsTab.click() - }) - - await test.step('Reset project settings', async () => { - // Click the reset settings button. - await resetButton('project').click() - - await expect(resetToast('project')).toBeVisible() - await expect(resetToast('project')).not.toBeVisible() - - // Verify it is now set to the inherited user value - await expect(themeColorSetting).toHaveValue(settingValues.user) - - await test.step(`Check that the user settings did not change`, async () => { - await userSettingsTab.click() - await expect(themeColorSetting).toHaveValue(settingValues.user) + + // Go to the hotkey for Command Palette. + const commandPalette = page.getByText('Toggle Command Palette') + await commandPalette.scrollIntoViewIfNeeded() + + // The heading is above it and should be in view now. + const commandPaletteHeading = page.getByRole('heading', { + name: 'Command Palette', }) - - await test.step(`Set project-level again to test the user-level reset`, async () => { - await projectSettingsTab.click() + // The hotkey is in a kbd element next to the heading. + const hotkey = commandPaletteHeading.locator('+ div kbd') + const text = process.platform === 'darwin' ? 'Command+K' : 'Control+K' + await expect(hotkey).toHaveText(text) + }) + + test('Project and user settings can be reset', async ({ page, homePage }) => { + const u = await getUtils(page) + await test.step(`Setup`, async () => { + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + await u.waitForPageLoad() + await page.waitForTimeout(1000) + }) + + // Selectors and constants + const projectSettingsTab = page.getByRole('radio', { name: 'Project' }) + const userSettingsTab = page.getByRole('radio', { name: 'User' }) + const resetButton = (level: SettingsLevel) => + page.getByRole('button', { + name: `Reset ${level}-level settings`, + }) + const themeColorSetting = page.locator('#themeColor').getByRole('slider') + const settingValues = { + default: '259', + user: '120', + project: '50', + } + const resetToast = (level: SettingsLevel) => + page.getByText(`${level}-level settings were reset`) + + await test.step(`Open the settings modal`, async () => { + await page.getByRole('link', { name: 'Settings' }).last().click() + await expect( + page.getByRole('heading', { name: 'Settings', exact: true }) + ).toBeVisible() + }) + + await test.step('Set up theme color', async () => { + // Verify we're looking at the project-level settings, + // and it's set to default value + await expect(projectSettingsTab).toBeChecked() + await expect(themeColorSetting).toHaveValue(settingValues.default) + + // Set project-level value to 50 await themeColorSetting.fill(settingValues.project) + + // Set user-level value to 120 await userSettingsTab.click() + await themeColorSetting.fill(settingValues.user) + await projectSettingsTab.click() + }) + + await test.step('Reset project settings', async () => { + // Click the reset settings button. + await resetButton('project').click() + + await expect(resetToast('project')).toBeVisible() + await expect(resetToast('project')).not.toBeVisible() + + // Verify it is now set to the inherited user value + await expect(themeColorSetting).toHaveValue(settingValues.user) + + await test.step(`Check that the user settings did not change`, async () => { + await userSettingsTab.click() + await expect(themeColorSetting).toHaveValue(settingValues.user) + }) + + await test.step(`Set project-level again to test the user-level reset`, async () => { + await projectSettingsTab.click() + await themeColorSetting.fill(settingValues.project) + await userSettingsTab.click() + }) + }) + + await test.step('Reset user settings', async () => { + // Click the reset settings button. + await resetButton('user').click() + + await expect(resetToast('user')).toBeVisible() + await expect(resetToast('user')).not.toBeVisible() + + // Verify it is now set to the default value + await expect(themeColorSetting).toHaveValue(settingValues.default) + + await test.step(`Check that the project settings did not change`, async () => { + await projectSettingsTab.click() + await expect(themeColorSetting).toHaveValue(settingValues.project) + }) }) }) - - await test.step('Reset user settings', async () => { - // Click the reset settings button. - await resetButton('user').click() - - await expect(resetToast('user')).toBeVisible() - await expect(resetToast('user')).not.toBeVisible() - - // Verify it is now set to the default value - await expect(themeColorSetting).toHaveValue(settingValues.default) - - await test.step(`Check that the project settings did not change`, async () => { - await projectSettingsTab.click() - await expect(themeColorSetting).toHaveValue(settingValues.project) - }) - }) }) test.fixme( `Project settings override user settings on desktop`, { tag: ['@electron', '@skipWin'] }, - async ({ context, page, browser: _ }, testInfo) => { + async ({ context, page }, testInfo) => { test.skip( process.platform === 'win32', 'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557' ) const projectName = 'bracket' - const { - dir: projectDirName, - } = await context.folderSetupFn(async (dir) => { - const bracketDir = join(dir, projectName) - await fsp.mkdir(bracketDir, { recursive: true }) - await fsp.copyFile( - executorInputPath('focusrite_scarlett_mounting_braket.kcl'), - join(bracketDir, 'main.kcl') - ) - }) + const { dir: projectDirName } = await context.folderSetupFn( + async (dir) => { + const bracketDir = join(dir, projectName) + await fsp.mkdir(bracketDir, { recursive: true }) + await fsp.copyFile( + executorInputPath('focusrite_scarlett_mounting_braket.kcl'), + join(bracketDir, 'main.kcl') + ) + } + ) await page.setBodyDimensions({ width: 1200, height: 500 }) @@ -324,7 +338,6 @@ test.describe('Testing settings', () => { await logoLink.click() await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor) }) - } ) @@ -335,7 +348,7 @@ test.describe('Testing settings', () => { // This is what makes no settings file get created cleanProjectDir: false, }, - async ({ page, browser: _ }, testInfo) => { + async ({ page }, testInfo) => { await page.setBodyDimensions({ width: 1200, height: 500 }) // Selectors and constants @@ -347,7 +360,6 @@ test.describe('Testing settings', () => { // If the app loads without exploding we're in the clear await expect(errorHeading).not.toBeVisible() await expect(projectDirLink).toBeVisible() - } ) @@ -361,7 +373,7 @@ test.describe('Testing settings', () => { }, }, }, - async ({ context, page, browser: _ }, testInfo) => { + async ({ context, page }, testInfo) => { await page.setBodyDimensions({ width: 1200, height: 500 }) // Selectors and constants @@ -387,10 +399,12 @@ test.describe('Testing settings', () => { // default to 264.5 themeColor: '0', }, - } + }, }, - async ({ context, page, browserName }, testInfo) => { - const { dir: projectDirName } = await context.folderSetupFn(async () => {}) + async ({ context, page }, testInfo) => { + const { dir: projectDirName } = await context.folderSetupFn( + async () => {} + ) await page.setBodyDimensions({ width: 1200, height: 500 }) @@ -432,8 +446,10 @@ test.describe('Testing settings', () => { test( 'project settings reload on external change', { tag: '@electron' }, - async ({ context, page, browserName: _ }, testInfo) => { - const { dir: projectDirName } = await context.folderSetupFn(async () => {}) + async ({ context, page }, testInfo) => { + const { dir: projectDirName } = await context.folderSetupFn( + async () => {} + ) await page.setBodyDimensions({ width: 1200, height: 500 }) @@ -466,14 +482,13 @@ test.describe('Testing settings', () => { await changeColorFs('99') await expect(logoLink).toHaveCSS('--primary-hue', '99') }) - } ) test( `Closing settings modal should go back to the original file being viewed`, { tag: '@electron' }, - async ({ context, page, browser: _ }, testInfo) => { + async ({ context, page }, testInfo) => { await context.folderSetupFn(async (dir) => { const bracketDir = join(dir, 'project-000') await fsp.mkdir(bracketDir, { recursive: true }) @@ -534,159 +549,159 @@ test.describe('Testing settings', () => { } ) - test('Changing modeling default unit', async ({ page, homePage }) => { const u = await getUtils(page) - await test.step(`Test setup`, async () => { - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - const toastMessage = page.getByText( - `Successfully created "testDefault"` + test('Changing modeling default unit', async ({ page, homePage }) => { + await test.step(`Test setup`, async () => { + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + const toastMessage = page.getByText(`Successfully created "testDefault"`) + await expect(toastMessage).not.toBeVisible() + await page + .getByRole('button', { name: 'Start Sketch' }) + .waitFor({ state: 'visible' }) + }) + + // Selectors and constants + const userSettingsTab = page.getByRole('radio', { name: 'User' }) + const projectSettingsTab = page.getByRole('radio', { name: 'Project' }) + const defaultUnitSection = page.getByText( + 'default unitRoll back default unitRoll back to match' ) - await expect(toastMessage).not.toBeVisible() - await page - .getByRole('button', { name: 'Start Sketch' }) - .waitFor({ state: 'visible' }) - }) - - // Selectors and constants - const userSettingsTab = page.getByRole('radio', { name: 'User' }) - const projectSettingsTab = page.getByRole('radio', { name: 'Project' }) - const defaultUnitSection = page.getByText( - 'default unitRoll back default unitRoll back to match' - ) - const defaultUnitRollbackButton = page.getByRole('button', { - name: 'Roll back default unit', - }) - - await test.step(`Open the settings modal`, async () => { - await page.getByRole('link', { name: 'Settings' }).last().click() - await expect( - page.getByRole('heading', { name: 'Settings', exact: true }) - ).toBeVisible() - }) - - await test.step(`Reset unit setting`, async () => { + const defaultUnitRollbackButton = page.getByRole('button', { + name: 'Roll back default unit', + }) + + await test.step(`Open the settings modal`, async () => { + await page.getByRole('link', { name: 'Settings' }).last().click() + await expect( + page.getByRole('heading', { name: 'Settings', exact: true }) + ).toBeVisible() + }) + + await test.step(`Reset unit setting`, async () => { + await userSettingsTab.click() + await defaultUnitSection.hover() + await defaultUnitRollbackButton.click() + await projectSettingsTab.hover() + await projectSettingsTab.click() + await page.waitForTimeout(1000) + }) + + await test.step('Change modeling default unit within project tab', async () => { + const changeUnitOfMeasureInProjectTab = async (unitOfMeasure: string) => { + await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => { + await page + .getByTestId('modeling-defaultUnit') + .selectOption(`${unitOfMeasure}`) + const toastMessage = page.getByText( + `Set default unit to "${unitOfMeasure}" for this project` + ) + + // Assert visibility and disapperance + await expect(toastMessage).toBeVisible() + await expect(toastMessage).not.toBeVisible() + }) + } + await changeUnitOfMeasureInProjectTab('in') + await changeUnitOfMeasureInProjectTab('ft') + await changeUnitOfMeasureInProjectTab('yd') + await changeUnitOfMeasureInProjectTab('mm') + await changeUnitOfMeasureInProjectTab('cm') + await changeUnitOfMeasureInProjectTab('m') + }) + + // Go to the user tab + await userSettingsTab.hover() await userSettingsTab.click() - await defaultUnitSection.hover() - await defaultUnitRollbackButton.click() - await projectSettingsTab.hover() - await projectSettingsTab.click() await page.waitForTimeout(1000) - }) - - await test.step('Change modeling default unit within project tab', async () => { - const changeUnitOfMeasureInProjectTab = async (unitOfMeasure: string) => { - await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => { - await page - .getByTestId('modeling-defaultUnit') - .selectOption(`${unitOfMeasure}`) + + await test.step('Change modeling default unit within user tab', async () => { + const changeUnitOfMeasureInUserTab = async (unitOfMeasure: string) => { + await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => { + await page + .getByTestId('modeling-defaultUnit') + .selectOption(`${unitOfMeasure}`) + const toastMessage = page.getByText( + `Set default unit to "${unitOfMeasure}" as a user default` + ) + await expect(toastMessage).toBeVisible() + }) + } + await changeUnitOfMeasureInUserTab('in') + await changeUnitOfMeasureInUserTab('ft') + await changeUnitOfMeasureInUserTab('yd') + await changeUnitOfMeasureInUserTab('mm') + await changeUnitOfMeasureInUserTab('cm') + await changeUnitOfMeasureInUserTab('m') + }) + + // Close settings + const settingsCloseButton = page.getByTestId('settings-close-button') + await settingsCloseButton.click() + + await test.step('Change modeling default unit within command bar', async () => { + const commands = page.getByRole('button', { name: 'Commands' }) + const changeUnitOfMeasureInCommandBar = async (unitOfMeasure: string) => { + // Open command bar + await commands.click() + const settingsModelingDefaultUnitCommand = page.getByText( + 'Settings · modeling · default unit' + ) + await settingsModelingDefaultUnitCommand.click() + + const commandOption = page.getByRole('option', { + name: unitOfMeasure, + exact: true, + }) + await commandOption.click() + const toastMessage = page.getByText( `Set default unit to "${unitOfMeasure}" for this project` ) - - // Assert visibility and disapperance await expect(toastMessage).toBeVisible() - await expect(toastMessage).not.toBeVisible() - }) - } - await changeUnitOfMeasureInProjectTab('in') - await changeUnitOfMeasureInProjectTab('ft') - await changeUnitOfMeasureInProjectTab('yd') - await changeUnitOfMeasureInProjectTab('mm') - await changeUnitOfMeasureInProjectTab('cm') - await changeUnitOfMeasureInProjectTab('m') - }) - - // Go to the user tab - await userSettingsTab.hover() - await userSettingsTab.click() - await page.waitForTimeout(1000) + } + await changeUnitOfMeasureInCommandBar('in') + await changeUnitOfMeasureInCommandBar('ft') + await changeUnitOfMeasureInCommandBar('yd') + await changeUnitOfMeasureInCommandBar('mm') + await changeUnitOfMeasureInCommandBar('cm') + await changeUnitOfMeasureInCommandBar('m') + }) - await test.step('Change modeling default unit within user tab', async () => { - const changeUnitOfMeasureInUserTab = async (unitOfMeasure: string) => { - await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => { - await page - .getByTestId('modeling-defaultUnit') - .selectOption(`${unitOfMeasure}`) + await test.step('Change modeling default unit within gizmo', async () => { + const changeUnitOfMeasureInGizmo = async ( + unitOfMeasure: string, + copy: string + ) => { + const gizmo = page.getByRole('button', { + name: 'Current units are: ', + }) + await gizmo.click() + const button = page.getByRole('button', { + name: copy, + exact: true, + }) + await button.click() const toastMessage = page.getByText( - `Set default unit to "${unitOfMeasure}" as a user default` + `Set default unit to "${unitOfMeasure}" for this project` ) await expect(toastMessage).toBeVisible() - }) - } - await changeUnitOfMeasureInUserTab('in') - await changeUnitOfMeasureInUserTab('ft') - await changeUnitOfMeasureInUserTab('yd') - await changeUnitOfMeasureInUserTab('mm') - await changeUnitOfMeasureInUserTab('cm') - await changeUnitOfMeasureInUserTab('m') - }) - - // Close settings - const settingsCloseButton = page.getByTestId('settings-close-button') - await settingsCloseButton.click() - - await test.step('Change modeling default unit within command bar', async () => { - const commands = page.getByRole('button', { name: 'Commands' }) - const changeUnitOfMeasureInCommandBar = async (unitOfMeasure: string) => { - // Open command bar - await commands.click() - const settingsModelingDefaultUnitCommand = page.getByText( - 'Settings · modeling · default unit' - ) - await settingsModelingDefaultUnitCommand.click() - - const commandOption = page.getByRole('option', { - name: unitOfMeasure, - exact: true, - }) - await commandOption.click() - - const toastMessage = page.getByText( - `Set default unit to "${unitOfMeasure}" for this project` - ) - await expect(toastMessage).toBeVisible() - } - await changeUnitOfMeasureInCommandBar('in') - await changeUnitOfMeasureInCommandBar('ft') - await changeUnitOfMeasureInCommandBar('yd') - await changeUnitOfMeasureInCommandBar('mm') - await changeUnitOfMeasureInCommandBar('cm') - await changeUnitOfMeasureInCommandBar('m') - }) - - await test.step('Change modeling default unit within gizmo', async () => { - const changeUnitOfMeasureInGizmo = async ( - unitOfMeasure: string, - copy: string - ) => { - const gizmo = page.getByRole('button', { - name: 'Current units are: ', - }) - await gizmo.click() - const button = page.getByRole('button', { - name: copy, - exact: true, - }) - await button.click() - const toastMessage = page.getByText( - `Set default unit to "${unitOfMeasure}" for this project` - ) - await expect(toastMessage).toBeVisible() - } - - await changeUnitOfMeasureInGizmo('in', 'Inches') - await changeUnitOfMeasureInGizmo('ft', 'Feet') - await changeUnitOfMeasureInGizmo('yd', 'Yards') - await changeUnitOfMeasureInGizmo('mm', 'Millimeters') - await changeUnitOfMeasureInGizmo('cm', 'Centimeters') - await changeUnitOfMeasureInGizmo('m', 'Meters') - }) }) + } - test('Changing theme in sketch mode', async ({ context, page, homePage }) => { const u = await getUtils(page) - await context.addInitScript(() => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + await changeUnitOfMeasureInGizmo('in', 'Inches') + await changeUnitOfMeasureInGizmo('ft', 'Feet') + await changeUnitOfMeasureInGizmo('yd', 'Yards') + await changeUnitOfMeasureInGizmo('mm', 'Millimeters') + await changeUnitOfMeasureInGizmo('cm', 'Centimeters') + await changeUnitOfMeasureInGizmo('m', 'Meters') + }) + }) + + test('Changing theme in sketch mode', async ({ context, page, homePage }) => { + const u = await getUtils(page) + await context.addInitScript(() => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([0, 0], %) |> line([5, 0], %) |> line([0, 5], %) @@ -695,172 +710,179 @@ test.describe('Testing settings', () => { |> close(%) extrude001 = extrude(5, sketch001) ` - ) - }) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - await u.waitForPageLoad() - await page.waitForTimeout(1000) - - // Selectors and constants - const editSketchButton = page.getByRole('button', { name: 'Edit Sketch' }) - const lineToolButton = page.getByTestId('line') - const segmentOverlays = page.getByTestId('segment-overlay') - const sketchOriginLocation = { x: 600, y: 250 } - const darkThemeSegmentColor: [number, number, number] = [215, 215, 215] - const lightThemeSegmentColor: [number, number, number] = [90, 90, 90] - - await test.step(`Get into sketch mode`, async () => { - await page.mouse.click(700, 200) - await expect(editSketchButton).toBeVisible() - await editSketchButton.click() - - // We use the line tool as a proxy for sketch mode - await expect(lineToolButton).toBeVisible() - await expect(segmentOverlays).toHaveCount(4) - // but we allow more time to pass for animating to the sketch - await page.waitForTimeout(1000) - }) - - await test.step(`Check the sketch line color before`, async () => { - await expect - .poll(() => - u.getGreatestPixDiff(sketchOriginLocation, darkThemeSegmentColor) ) - .toBeLessThan(15) - }) - - await test.step(`Change theme to light using command palette`, async () => { - await page.keyboard.press('ControlOrMeta+K') - await page.getByRole('option', { name: 'theme' }).click() - await page.getByRole('option', { name: 'light' }).click() - await expect(page.getByText('theme to "light"')).toBeVisible() - - // Make sure we haven't left sketch mode - await expect(lineToolButton).toBeVisible() - }) - - await test.step(`Check the sketch line color after`, async () => { - await expect - .poll(() => - u.getGreatestPixDiff(sketchOriginLocation, lightThemeSegmentColor) - ) - .toBeLessThan(15) - }) }) - - test(`Changing system theme preferences (via media query) should update UI and stream`, { - // Override the settings so that the theme is set to `system` - appSettings: TEST_SETTINGS_DEFAULT_THEME, - }, async ({ page, homePage }) => { - - 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`, { - // Override beforeEach test setup - // with debug panel open - // but "show debug panel" set to false - appSettings: { - ...TEST_SETTINGS, - modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false }, - } - }, async ({ context, page, homePage }) => { + // Selectors and constants + const editSketchButton = page.getByRole('button', { name: 'Edit Sketch' }) + const lineToolButton = page.getByTestId('line') + const segmentOverlays = page.getByTestId('segment-overlay') + const sketchOriginLocation = { x: 600, y: 250 } + const darkThemeSegmentColor: [number, number, number] = [215, 215, 215] + const lightThemeSegmentColor: [number, number, number] = [90, 90, 90] - const u = await getUtils(page) - - await context.addInitScript( - async ({ }) => { - localStorage.setItem( - 'persistModelingContext', - '{"openPanes":["debug"]}' - ) + await test.step(`Get into sketch mode`, async () => { + await page.mouse.click(700, 200) + await expect(editSketchButton).toBeVisible() + await editSketchButton.click() + + // We use the line tool as a proxy for sketch mode + await expect(lineToolButton).toBeVisible() + await expect(segmentOverlays).toHaveCount(4) + // but we allow more time to pass for animating to the sketch + await page.waitForTimeout(1000) + }) + + await test.step(`Check the sketch line color before`, async () => { + await expect + .poll(() => + u.getGreatestPixDiff(sketchOriginLocation, darkThemeSegmentColor) + ) + .toBeLessThan(15) + }) + + await test.step(`Change theme to light using command palette`, async () => { + await page.keyboard.press('ControlOrMeta+K') + await page.getByRole('option', { name: 'theme' }).click() + await page.getByRole('option', { name: 'light' }).click() + await expect(page.getByText('theme to "light"')).toBeVisible() + + // Make sure we haven't left sketch mode + await expect(lineToolButton).toBeVisible() + }) + + await test.step(`Check the sketch line color after`, async () => { + await expect + .poll(() => + u.getGreatestPixDiff(sketchOriginLocation, lightThemeSegmentColor) + ) + .toBeLessThan(15) + }) + }) + + test( + `Changing system theme preferences (via media query) should update UI and stream`, + { + // Override the settings so that the theme is set to `system` + appSettings: TEST_SETTINGS_DEFAULT_THEME, + }, + async ({ page, homePage }) => { + 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`, + { + // Override beforeEach test setup + // with debug panel open + // but "show debug panel" set to false + appSettings: { + ...TEST_SETTINGS, + modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false }, + }, + }, + async ({ context, page, homePage }) => { + const u = await getUtils(page) + + await context.addInitScript(async () => { + 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 · modeling · 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 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 · modeling · 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() - }) }) }) diff --git a/e2e/playwright/text-to-cad-tests.spec.ts b/e2e/playwright/text-to-cad-tests.spec.ts index 7bf796771..0846c7baa 100644 --- a/e2e/playwright/text-to-cad-tests.spec.ts +++ b/e2e/playwright/text-to-cad-tests.spec.ts @@ -1,549 +1,584 @@ import { test, expect, Page } from './zoo-test' -import { getUtils, createProject, } from './test-utils' +import { getUtils, createProject } from './test-utils' import { join } from 'path' import fs from 'fs' test.describe('Text-to-CAD tests', () => { test('basic lego happy case', async ({ page, homePage }) => { + const u = await getUtils(page) + + await test.step('Set up', async () => { + await page.setBodyDimensions({ width: 1000, height: 500 }) + await homePage.goToModelingScene() + await u.waitForPageLoad() + }) + + await sendPromptFromCommandBar(page, 'a 2x4 lego') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + + // Hit accept. + const copyToClipboardButton = page.getByRole('button', { + name: 'Accept', + }) + await expect(copyToClipboardButton).toBeVisible() + + await copyToClipboardButton.click() + + // Click in the code editor. + await page.locator('.cm-content').click() + + // Expect the code to be pasted. + await expect(page.locator('.cm-content')).toContainText(`const`) + + // make sure a model renders. + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + }) + + test('success model, then ignore success toast, user can create new prompt from command bar', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) - const u = await getUtils(page) - - await test.step('Set up', async () => { await page.setBodyDimensions({ width: 1000, height: 500 }) + await homePage.goToModelingScene() - await u.waitForPageLoad() - }) - - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) - + await u.waitForPageLoad() - // Hit accept. - const copyToClipboardButton = page.getByRole('button', { - name: 'Accept', - }) - await expect(copyToClipboardButton).toBeVisible() - - await copyToClipboardButton.click() - - // Click in the code editor. - await page.locator('.cm-content').click() - - // Expect the code to be pasted. - await expect(page.locator('.cm-content')).toContainText(`const`) - - // make sure a model renders. - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - + await sendPromptFromCommandBar(page, 'a 2x6 lego') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + + // Can send a new prompt from the command bar. + await sendPromptFromCommandBar(page, 'a 2x4 lego') + + // Find the toast. + // Look out for the toast message + await expect(submittingToastMessage).toBeVisible() + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + // Expect 2 success toasts. + await expect(successToastMessage).toHaveCount(2, { + timeout: 15000, + }) + await expect(page.getByText('a 2x4 lego')).toBeVisible() + await expect(page.getByText('a 2x6 lego')).toBeVisible() }) - test('success model, then ignore success toast, user can create new prompt from command bar', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - await sendPromptFromCommandBar(page, 'a 2x6 lego') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) - - // Can send a new prompt from the command bar. - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - // Find the toast. - // Look out for the toast message - await expect(submittingToastMessage).toBeVisible() - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - // Expect 2 success toasts. - await expect(successToastMessage).toHaveCount(2, { - timeout: 15000, - }) - await expect(page.getByText('a 2x4 lego')).toBeVisible() - await expect(page.getByText('a 2x6 lego')).toBeVisible() }) + test('you can reject text-to-cad output and it does nothing', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) - test('you can reject text-to-cad output and it does nothing', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) - - // Hit copy to clipboard. - const rejectButton = page.getByRole('button', { name: 'Reject' }) - await expect(rejectButton).toBeVisible() - - await rejectButton.click() - - // The toast should disappear. - await expect(successToastMessage).not.toBeVisible() - - // Expect no code. - await expect(page.locator('.cm-content')).toContainText(``) }) + await page.setBodyDimensions({ width: 1000, height: 500 }) - test('sending a bad prompt fails, can dismiss', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - await expect(commandBarButton).toBeVisible() - // Click the command bar button - await commandBarButton.click() - - // Wait for the command bar to appear - const cmdSearchBar = page.getByPlaceholder('Search commands') - await expect(cmdSearchBar).toBeVisible() - - const textToCadCommand = page.getByText('Text-to-CAD') - await expect(textToCadCommand.first()).toBeVisible() - // Click the Text-to-CAD command - await textToCadCommand.first().click() - - // Enter the prompt. - const prompt = page.getByText('Prompt') - await expect(prompt.first()).toBeVisible() - - // Type the prompt. - const randomPrompt = `aslkdfja;` + Date.now() + `FFFFEIWJF` - await page.keyboard.type(randomPrompt) - await page.waitForTimeout(1000) - await page.keyboard.press('Enter') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible() - - const failureToastMessage = page.getByText( - `The prompt must clearly describe a CAD model` - ) - await expect(failureToastMessage).toBeVisible() - - await page.waitForTimeout(1000) - - // Make sure the toast did not say it was successful. - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).not.toBeVisible() - await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() - - // Find the toast dismiss button. - const dismissButton = page.getByRole('button', { name: 'Dismiss' }) - await expect(dismissButton).toBeVisible() - await dismissButton.click() - - // The toast should disappear. - await expect(failureToastMessage).not.toBeVisible() }) + await homePage.goToModelingScene() + await u.waitForPageLoad() - test('sending a bad prompt fails, can start over from toast', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - await expect(commandBarButton).toBeVisible() - // Click the command bar button - await commandBarButton.click() - - // Wait for the command bar to appear - const cmdSearchBar = page.getByPlaceholder('Search commands') - await expect(cmdSearchBar).toBeVisible() - - const textToCadCommand = page.getByText('Text-to-CAD') - await expect(textToCadCommand.first()).toBeVisible() - // Click the Text-to-CAD command - await textToCadCommand.first().click() - - // Enter the prompt. - const prompt = page.getByText('Prompt') - await expect(prompt.first()).toBeVisible() - - const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss' - - // Type the prompt. - await page.keyboard.type(badPrompt) - await page.waitForTimeout(1000) - await page.keyboard.press('Enter') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible() - - const failureToastMessage = page.getByText( - `The prompt must clearly describe a CAD model` - ) - await expect(failureToastMessage).toBeVisible() - - await page.waitForTimeout(1000) - - // Make sure the toast did not say it was successful. - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).not.toBeVisible() - await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() - - // Click the edit prompt button to try again. - const editPromptButton = page.getByRole('button', { name: 'Edit prompt' }) - await expect(editPromptButton).toBeVisible() - await editPromptButton.click() - - // The toast should disappear. - await expect(failureToastMessage).not.toBeVisible() - - // Make sure the old prompt is still there and can be edited. - await expect(page.locator('textarea')).toContainText(badPrompt) - - // Select all and start a new prompt. - await page.keyboard.down('ControlOrMeta') - await page.keyboard.press('KeyA') - await page.keyboard.up('ControlOrMeta') - await page.keyboard.type('a 2x4 lego') - - // Submit the new prompt. - await page.keyboard.press('Enter') - - // Make sure the new prompt works. - // Find the toast. - // Look out for the toast message - await expect(submittingToastMessage).toBeVisible() - - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) }) + await sendPromptFromCommandBar(page, 'a 2x4 lego') - test('sending a bad prompt fails, can ignore toast, can start over from command bar', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - await expect(commandBarButton).toBeVisible() - // Click the command bar button - await commandBarButton.click() - - // Wait for the command bar to appear - const cmdSearchBar = page.getByPlaceholder('Search commands') - await expect(cmdSearchBar).toBeVisible() - - const textToCadCommand = page.getByText('Text-to-CAD') - await expect(textToCadCommand.first()).toBeVisible() - // Click the Text-to-CAD command - await textToCadCommand.first().click() - - // Enter the prompt. - const prompt = page.getByText('Prompt') - await expect(prompt.first()).toBeVisible() - - const badPrompt = 'akjsndladflajbhflauweyf15;' - - // Type the prompt. - await page.keyboard.type(badPrompt) - await page.waitForTimeout(1000) - await page.keyboard.press('Enter') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible() - - const failureToastMessage = page.getByText( - `The prompt must clearly describe a CAD model` - ) - await expect(failureToastMessage).toBeVisible() - - await page.waitForTimeout(1000) - - // Make sure the toast did not say it was successful. - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).not.toBeVisible() - await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() - - // They should be able to try again from the command bar. - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - // Find the toast. - // Look out for the toast message - await expect(submittingToastMessage).toBeVisible() - - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) - - // old failure toast should stick around. - await expect(failureToastMessage).toBeVisible() - await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() }) + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() - test('ensure you can shift+enter in the prompt box', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - const promptWithNewline = `a 2x4\nlego` - - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - await expect(commandBarButton).toBeVisible() - // Click the command bar button - await commandBarButton.click() - - // Wait for the command bar to appear - const cmdSearchBar = page.getByPlaceholder('Search commands') - await expect(cmdSearchBar).toBeVisible() - - const textToCadCommand = page.getByText('Text-to-CAD') - await expect(textToCadCommand.first()).toBeVisible() - // Click the Text-to-CAD command - await textToCadCommand.first().click() - - // Enter the prompt. - const prompt = page.getByText('Prompt') - await expect(prompt.first()).toBeVisible() - - // Type the prompt. - await page.keyboard.type('a 2x4') - await page.waitForTimeout(1000) - await page.keyboard.down('Shift') - await page.keyboard.press('Enter') - await page.keyboard.up('Shift') - await page.keyboard.type('lego') - await page.waitForTimeout(1000) - await page.keyboard.press('Enter') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - await expect(successToastMessage).toBeVisible({ timeout: 15000 }) - - await expect(page.getByText(promptWithNewline)).toBeVisible() }) + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) - test('can do many at once and get many prompts back, and interact with many', { tag: ['@skipWin'] }, async ({ page, homePage }) => { // Let this test run longer since we've seen it timeout. - test.setTimeout(180_000) - // skip on windows - test.skip( - process.platform === 'win32', - 'This test is flaky, skipping for now' - ) - - const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - await sendPromptFromCommandBar(page, 'a 2x8 lego') - - await sendPromptFromCommandBar(page, 'a 2x10 lego') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage.first()).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage.first()).toBeVisible({ - timeout: 10_000, - }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - // We should have three success toasts. - await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 }) - - await expect(page.getByText(`a 2x4 lego`)).toBeVisible() - await expect(page.getByText(`a 2x8 lego`)).toBeVisible() - await expect(page.getByText(`a 2x10 lego`)).toBeVisible() - - // Ensure if you reject one, the others stay. - const rejectButton = page.getByRole('button', { name: 'Reject' }) - await expect(rejectButton.first()).toBeVisible() - // Click the reject button on the first toast. - await rejectButton.first().click() - - // The first toast should disappear, but not the others. - await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible() - await expect(page.getByText(`a 2x8 lego`)).toBeVisible() - await expect(page.getByText(`a 2x4 lego`)).toBeVisible() - - // Ensure you can copy the code for one of the models remaining. - const copyToClipboardButton = page.getByRole('button', { - name: 'Accept', - }) - await expect(copyToClipboardButton.first()).toBeVisible() - // Click the button. - await copyToClipboardButton.first().click() - - // Expect the code to be pasted. - await expect(page.locator('.cm-content')).toContainText(`2x8`) - - // Ensure the final toast remains. - await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible() - await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible() - await expect(page.getByText(`a 2x4 lego`)).toBeVisible() - - // Ensure you can copy the code for the final model. - await expect(copyToClipboardButton).toBeVisible() - // Click the button. - await copyToClipboardButton.click() - - // Expect the code to be pasted. - await expect(page.locator('.cm-content')).toContainText(`2x4`) - + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + + // Hit copy to clipboard. + const rejectButton = page.getByRole('button', { name: 'Reject' }) + await expect(rejectButton).toBeVisible() + + await rejectButton.click() + + // The toast should disappear. + await expect(successToastMessage).not.toBeVisible() + + // Expect no code. + await expect(page.locator('.cm-content')).toContainText(``) }) - test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({ page, homePage }) => { const u = await getUtils(page) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - await u.waitForPageLoad() - - await sendPromptFromCommandBar(page, 'a 2x4 lego') - - await sendPromptFromCommandBar(page, 'alkjsdnlajshdbfjlhsbdf a;askjdnf') - - // Find the toast. - // Look out for the toast message - const submittingToastMessage = page.getByText( - `Submitting to Text-to-CAD API...` - ) - await expect(submittingToastMessage.first()).toBeVisible() - - const generatingToastMessage = page.getByText( - `Generating parametric model...` - ) - await expect(generatingToastMessage.first()).toBeVisible({ timeout: 10000 }) - - const successToastMessage = page.getByText(`Text-to-CAD successful`) - // We should have three success toasts. - await expect(successToastMessage).toHaveCount(1, { timeout: 15000 }) - - await expect(page.getByText('Copied')).not.toBeVisible() - - const failureToastMessage = page.getByText( - `The prompt must clearly describe a CAD model` - ) - await expect(failureToastMessage).toBeVisible() - - // Make sure the toast did not say it was successful. - await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() - - await expect(page.getByText(`a 2x4 lego`)).toBeVisible() - - // Ensure if you dismiss the error the others stay. - const dismissButton = page.getByRole('button', { name: 'Dismiss' }) - await expect(dismissButton).toBeVisible() - // Click the dismiss button on the first toast. - await dismissButton.first().click() - - // Make sure the failure toast disappears. - await expect(failureToastMessage).not.toBeVisible() - await expect(page.getByText(`Text-to-CAD failed`)).not.toBeVisible() - - // The first toast should disappear, but not the others. - await expect(page.getByText(`a 2x4 lego`)).toBeVisible() - - // Ensure you can copy the code for one of the models remaining. - const copyToClipboardButton = page.getByRole('button', { - name: 'Accept', + test('sending a bad prompt fails, can dismiss', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + await expect(commandBarButton).toBeVisible() + // Click the command bar button + await commandBarButton.click() + + // Wait for the command bar to appear + const cmdSearchBar = page.getByPlaceholder('Search commands') + await expect(cmdSearchBar).toBeVisible() + + const textToCadCommand = page.getByText('Text-to-CAD') + await expect(textToCadCommand.first()).toBeVisible() + // Click the Text-to-CAD command + await textToCadCommand.first().click() + + // Enter the prompt. + const prompt = page.getByText('Prompt') + await expect(prompt.first()).toBeVisible() + + // Type the prompt. + const randomPrompt = `aslkdfja;` + Date.now() + `FFFFEIWJF` + await page.keyboard.type(randomPrompt) + await page.waitForTimeout(1000) + await page.keyboard.press('Enter') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible() + + const failureToastMessage = page.getByText( + `The prompt must clearly describe a CAD model` + ) + await expect(failureToastMessage).toBeVisible() + + await page.waitForTimeout(1000) + + // Make sure the toast did not say it was successful. + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).not.toBeVisible() + await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() + + // Find the toast dismiss button. + const dismissButton = page.getByRole('button', { name: 'Dismiss' }) + await expect(dismissButton).toBeVisible() + await dismissButton.click() + + // The toast should disappear. + await expect(failureToastMessage).not.toBeVisible() }) - await expect(copyToClipboardButton.first()).toBeVisible() - // Click the button. - await copyToClipboardButton.first().click() - - // Expect the code to be pasted. - await expect(page.locator('.cm-content')).toContainText(`2x4`) + + test('sending a bad prompt fails, can start over from toast', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + await expect(commandBarButton).toBeVisible() + // Click the command bar button + await commandBarButton.click() + + // Wait for the command bar to appear + const cmdSearchBar = page.getByPlaceholder('Search commands') + await expect(cmdSearchBar).toBeVisible() + + const textToCadCommand = page.getByText('Text-to-CAD') + await expect(textToCadCommand.first()).toBeVisible() + // Click the Text-to-CAD command + await textToCadCommand.first().click() + + // Enter the prompt. + const prompt = page.getByText('Prompt') + await expect(prompt.first()).toBeVisible() + + const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss' + + // Type the prompt. + await page.keyboard.type(badPrompt) + await page.waitForTimeout(1000) + await page.keyboard.press('Enter') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible() + + const failureToastMessage = page.getByText( + `The prompt must clearly describe a CAD model` + ) + await expect(failureToastMessage).toBeVisible() + + await page.waitForTimeout(1000) + + // Make sure the toast did not say it was successful. + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).not.toBeVisible() + await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() + + // Click the edit prompt button to try again. + const editPromptButton = page.getByRole('button', { name: 'Edit prompt' }) + await expect(editPromptButton).toBeVisible() + await editPromptButton.click() + + // The toast should disappear. + await expect(failureToastMessage).not.toBeVisible() + + // Make sure the old prompt is still there and can be edited. + await expect(page.locator('textarea')).toContainText(badPrompt) + + // Select all and start a new prompt. + await page.keyboard.down('ControlOrMeta') + await page.keyboard.press('KeyA') + await page.keyboard.up('ControlOrMeta') + await page.keyboard.type('a 2x4 lego') + + // Submit the new prompt. + await page.keyboard.press('Enter') + + // Make sure the new prompt works. + // Find the toast. + // Look out for the toast message + await expect(submittingToastMessage).toBeVisible() + + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + }) + + test('sending a bad prompt fails, can ignore toast, can start over from command bar', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + await expect(commandBarButton).toBeVisible() + // Click the command bar button + await commandBarButton.click() + + // Wait for the command bar to appear + const cmdSearchBar = page.getByPlaceholder('Search commands') + await expect(cmdSearchBar).toBeVisible() + + const textToCadCommand = page.getByText('Text-to-CAD') + await expect(textToCadCommand.first()).toBeVisible() + // Click the Text-to-CAD command + await textToCadCommand.first().click() + + // Enter the prompt. + const prompt = page.getByText('Prompt') + await expect(prompt.first()).toBeVisible() + + const badPrompt = 'akjsndladflajbhflauweyf15;' + + // Type the prompt. + await page.keyboard.type(badPrompt) + await page.waitForTimeout(1000) + await page.keyboard.press('Enter') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible() + + const failureToastMessage = page.getByText( + `The prompt must clearly describe a CAD model` + ) + await expect(failureToastMessage).toBeVisible() + + await page.waitForTimeout(1000) + + // Make sure the toast did not say it was successful. + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).not.toBeVisible() + await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() + + // They should be able to try again from the command bar. + await sendPromptFromCommandBar(page, 'a 2x4 lego') + + // Find the toast. + // Look out for the toast message + await expect(submittingToastMessage).toBeVisible() + + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + + // old failure toast should stick around. + await expect(failureToastMessage).toBeVisible() + await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() + }) + + test('ensure you can shift+enter in the prompt box', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + const promptWithNewline = `a 2x4\nlego` + + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + await expect(commandBarButton).toBeVisible() + // Click the command bar button + await commandBarButton.click() + + // Wait for the command bar to appear + const cmdSearchBar = page.getByPlaceholder('Search commands') + await expect(cmdSearchBar).toBeVisible() + + const textToCadCommand = page.getByText('Text-to-CAD') + await expect(textToCadCommand.first()).toBeVisible() + // Click the Text-to-CAD command + await textToCadCommand.first().click() + + // Enter the prompt. + const prompt = page.getByText('Prompt') + await expect(prompt.first()).toBeVisible() + + // Type the prompt. + await page.keyboard.type('a 2x4') + await page.waitForTimeout(1000) + await page.keyboard.down('Shift') + await page.keyboard.press('Enter') + await page.keyboard.up('Shift') + await page.keyboard.type('lego') + await page.waitForTimeout(1000) + await page.keyboard.press('Enter') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage).toBeVisible({ timeout: 10000 }) + + const successToastMessage = page.getByText(`Text-to-CAD successful`) + await expect(successToastMessage).toBeVisible({ timeout: 15000 }) + + await expect(page.getByText(promptWithNewline)).toBeVisible() + }) + + test( + 'can do many at once and get many prompts back, and interact with many', + { tag: ['@skipWin'] }, + async ({ page, homePage }) => { + // Let this test run longer since we've seen it timeout. + test.setTimeout(180_000) + // skip on windows + test.skip( + process.platform === 'win32', + 'This test is flaky, skipping for now' + ) + + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + await sendPromptFromCommandBar(page, 'a 2x4 lego') + + await sendPromptFromCommandBar(page, 'a 2x8 lego') + + await sendPromptFromCommandBar(page, 'a 2x10 lego') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage.first()).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage.first()).toBeVisible({ + timeout: 10_000, + }) + + const successToastMessage = page.getByText(`Text-to-CAD successful`) + // We should have three success toasts. + await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 }) + + await expect(page.getByText(`a 2x4 lego`)).toBeVisible() + await expect(page.getByText(`a 2x8 lego`)).toBeVisible() + await expect(page.getByText(`a 2x10 lego`)).toBeVisible() + + // Ensure if you reject one, the others stay. + const rejectButton = page.getByRole('button', { name: 'Reject' }) + await expect(rejectButton.first()).toBeVisible() + // Click the reject button on the first toast. + await rejectButton.first().click() + + // The first toast should disappear, but not the others. + await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible() + await expect(page.getByText(`a 2x8 lego`)).toBeVisible() + await expect(page.getByText(`a 2x4 lego`)).toBeVisible() + + // Ensure you can copy the code for one of the models remaining. + const copyToClipboardButton = page.getByRole('button', { + name: 'Accept', + }) + await expect(copyToClipboardButton.first()).toBeVisible() + // Click the button. + await copyToClipboardButton.first().click() + + // Expect the code to be pasted. + await expect(page.locator('.cm-content')).toContainText(`2x8`) + + // Ensure the final toast remains. + await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible() + await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible() + await expect(page.getByText(`a 2x4 lego`)).toBeVisible() + + // Ensure you can copy the code for the final model. + await expect(copyToClipboardButton).toBeVisible() + // Click the button. + await copyToClipboardButton.click() + + // Expect the code to be pasted. + await expect(page.locator('.cm-content')).toContainText(`2x4`) + } + ) + + test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + await u.waitForPageLoad() + + await sendPromptFromCommandBar(page, 'a 2x4 lego') + + await sendPromptFromCommandBar(page, 'alkjsdnlajshdbfjlhsbdf a;askjdnf') + + // Find the toast. + // Look out for the toast message + const submittingToastMessage = page.getByText( + `Submitting to Text-to-CAD API...` + ) + await expect(submittingToastMessage.first()).toBeVisible() + + const generatingToastMessage = page.getByText( + `Generating parametric model...` + ) + await expect(generatingToastMessage.first()).toBeVisible({ timeout: 10000 }) + + const successToastMessage = page.getByText(`Text-to-CAD successful`) + // We should have three success toasts. + await expect(successToastMessage).toHaveCount(1, { timeout: 15000 }) + + await expect(page.getByText('Copied')).not.toBeVisible() + + const failureToastMessage = page.getByText( + `The prompt must clearly describe a CAD model` + ) + await expect(failureToastMessage).toBeVisible() + + // Make sure the toast did not say it was successful. + await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible() + + await expect(page.getByText(`a 2x4 lego`)).toBeVisible() + + // Ensure if you dismiss the error the others stay. + const dismissButton = page.getByRole('button', { name: 'Dismiss' }) + await expect(dismissButton).toBeVisible() + // Click the dismiss button on the first toast. + await dismissButton.first().click() + + // Make sure the failure toast disappears. + await expect(failureToastMessage).not.toBeVisible() + await expect(page.getByText(`Text-to-CAD failed`)).not.toBeVisible() + + // The first toast should disappear, but not the others. + await expect(page.getByText(`a 2x4 lego`)).toBeVisible() + + // Ensure you can copy the code for one of the models remaining. + const copyToClipboardButton = page.getByRole('button', { + name: 'Accept', + }) + await expect(copyToClipboardButton.first()).toBeVisible() + // Click the button. + await copyToClipboardButton.first().click() + + // Expect the code to be pasted. + await expect(page.locator('.cm-content')).toContainText(`2x4`) }) }) diff --git a/e2e/playwright/various.spec.ts b/e2e/playwright/various.spec.ts index b1ac1d8a7..77d972dbf 100644 --- a/e2e/playwright/various.spec.ts +++ b/e2e/playwright/various.spec.ts @@ -3,7 +3,6 @@ import { test, expect } from './zoo-test' import { doExport, getUtils, makeTemplate } from './test-utils' test.fixme('Units menu', async ({ page, homePage }) => { - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -102,15 +101,8 @@ part001 = startSketchOn('-XZ') 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 - test.skip( - browserName !== 'firefox', - "This bug is really Firefox-only, which we don't run in CI." - ) - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() await page @@ -154,7 +146,6 @@ test('Keyboard shortcuts can be viewed through the help menu', async ({ page, homePage, }) => { - const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() @@ -248,7 +239,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn test.fixme( 'Basic default modeling and sketch hotkeys work', - async ({ page }) => { + async ({ page, homePage }) => { const u = await getUtils(page) // This test can run long if it takes a little too long to load diff --git a/e2e/playwright/zoo-test.ts b/e2e/playwright/zoo-test.ts index a14c3b765..0bbdbdb26 100644 --- a/e2e/playwright/zoo-test.ts +++ b/e2e/playwright/zoo-test.ts @@ -1,11 +1,52 @@ -import { test as playwrightTestFn } from '@playwright/test' +import { + test as playwrightTestFn, + TestInfo as TestInfoPlaywright, + BrowserContext as BrowserContextPlaywright, + Page as PagePlaywright, + TestDetails as TestDetailsPlaywright, + PlaywrightTestArgs, + PlaywrightTestOptions, + PlaywrightWorkerArgs, + PlaywrightWorkerOptions, + ElectronApplication, +} from '@playwright/test' + import { fixtures, Fixtures, AuthenticatedTronApp, AuthenticatedApp, } from './fixtures/fixtureSetup' -export { expect, Page, BrowserContext, TestInfo } from '@playwright/test' + +import { SaveSettingsPayload } from 'lib/settings/settingsTypes' +export { expect } from '@playwright/test' + +declare module '@playwright/test' { + interface TestInfo { + tronApp?: AuthenticatedTronApp + } + interface BrowserContext { + folderSetupFn: ( + cb: (dir: string) => Promise + ) => Promise<{ dir: string }> + } + interface Page { + dir: string + TEST_SETTINGS_FILE_KEY?: string + setBodyDimensions: (dims: { + width: number + height: number + }) => Promise + } +} + +export type TestInfo = TestInfoPlaywright +export type BrowserContext = BrowserContextPlaywright +export type Page = PagePlaywright +export type TestDetails = TestDetailsPlaywright & { + cleanProjectDir?: boolean + appSettings?: Partial +} // Our custom decorated Zoo test object. Makes it easier to add fixtures, and // switch between web and electron if needed. @@ -14,7 +55,29 @@ const pwTestFnWithFixtures = playwrightTestFn.extend(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) { +type PWFunction = ( + args: PlaywrightTestArgs & + Fixtures & + PlaywrightWorkerArgs & + PlaywrightTestOptions & + PlaywrightWorkerOptions & { + electronApp?: ElectronApplication + }, + testInfo: TestInfo +) => void | Promise + +// The below error is due to the extreme type spaghetti going on. playwright/ +// types/test.d.ts does not export 2 functions (below is one of them) but tsc +// is trying to use a interface name it can't see. +// e2e/playwright/zoo-test.ts:64:14 - error TS4023: Exported variable 'test' has +// or is using name 'TestFunction' from external module +// "/home/lee/Code/Zoo/modeling-app/dirty2/node_modules/playwright/types/test" +// but cannot be named. +export const test = ( + desc: string, + objOrFn: PWFunction | TestDetails, + fnMaybe?: PWFunction +) => { const hasTestConf = typeof objOrFn === 'object' const fn = hasTestConf ? fnMaybe : objOrFn @@ -22,41 +85,53 @@ export function test(desc, objOrFn, fnMaybe) { desc, hasTestConf ? objOrFn : {}, async ( - { page, context, cmdBar, editor, toolbar, scene, homePage }, + { page, context, cmdBar, editor, toolbar, scene, homePage, ...rest }, 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; + let tronApp - if (process.env.PLATFORM === 'web') { + 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 } - const options = { - fixtures, - appSettings: objOrFn?.appSettings, - cleanProjectDir: objOrFn?.cleanProjectDir, + 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('') } - await tronApp.initialise(options) // 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, b) { + 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() } @@ -75,45 +150,73 @@ export function test(desc, objOrFn, fnMaybe) { return } - await tronApp.electronApp.evaluateHandle(async ({ app }, dims) => { + 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) => { - 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) + 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 + ) } // 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 })) + if (tronApp instanceof AuthenticatedTronApp) { + tronApp.context.folderSetupFn = function (fn) { + return fn(tronApp.dir).then(() => ({ + dir: tronApp.dir, + })) + } } - await fn( - { - context: tronApp.context, - page: tronApp.page, - electronApp: tronApp.electronApp, - ...fixtures, - }, - testInfo - ) + // 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, + ...rest, + }, + testInfo + ) + } - testInfo.tronApp = tronApp + testInfo.tronApp = + tronApp instanceof AuthenticatedTronApp ? tronApp : undefined } ) } +type ZooTest = typeof test + test.describe = pwTestFnWithFixtures.describe test.beforeEach = pwTestFnWithFixtures.beforeEach test.afterEach = pwTestFnWithFixtures.afterEach test.step = pwTestFnWithFixtures.step test.skip = pwTestFnWithFixtures.skip test.setTimeout = pwTestFnWithFixtures.setTimeout -test.fixme = pwTestFnWithFixtures.fixme +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 diff --git a/playwright.electron.config.ts b/playwright.electron.config.ts index ae36b4c55..2dae6d8bb 100644 --- a/playwright.electron.config.ts +++ b/playwright.electron.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ /* Do not retry */ retries: 0, /* Different amount of parallelism on CI and local. */ - workers: 4, + workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: [ ['dot'], diff --git a/src/lang/codeManager.ts b/src/lang/codeManager.ts index 623f6a30a..257df317a 100644 --- a/src/lang/codeManager.ts +++ b/src/lang/codeManager.ts @@ -45,7 +45,7 @@ export default class CodeManager { } else if (storedCode === null) { this.code = bracket } else { - this.code = storedCode + this.code = storedCode || '' } } @@ -58,7 +58,7 @@ export default class CodeManager { } localStoragePersistCode(): string { - return safeLSGetItem(PERSIST_CODE_KEY) + return safeLSGetItem(PERSIST_CODE_KEY) || '' } registerCallBacks({ setCode }: { setCode: (arg: string) => void }) { @@ -169,7 +169,7 @@ export default class CodeManager { } function safeLSGetItem(key: string) { - if (typeof window === 'undefined') return null + if (typeof window === 'undefined') return return localStorage?.getItem(key) } diff --git a/src/main.ts b/src/main.ts index 00e7ebdba..0984f0c99 100644 --- a/src/main.ts +++ b/src/main.ts @@ -134,12 +134,15 @@ app.on('ready', (event, data) => { // There is just not enough code to warrant it and further abstracts everything // which is already quite abstracted +// @ts-ignore +// electron/electron.d.ts has done type = App, making declaration merging not +// possible :( app.resizeWindow = async (width: number, height: number) => { return mainWindow?.setSize(width, height) } ipcMain.handle('app.resizeWindow', (event, data) => { - return mainWindow?.setSize(...data) + return mainWindow?.setSize(data[0], data[1]) }) ipcMain.handle('app.getPath', (event, data) => { diff --git a/src/preload.ts b/src/preload.ts index af655c027..793fc9448 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -20,12 +20,16 @@ const loginWithDeviceFlow = (): Promise => ipcRenderer.invoke('loginWithDeviceFlow') const onUpdateDownloaded = ( callback: (value: { version: string; releaseNotes: string }) => void -) => ipcRenderer.on('update-downloaded', (_event, value) => callback(value)) +) => + ipcRenderer.on('update-downloaded', (_event: any, value) => callback(value)) const onUpdateDownloadStart = ( callback: (value: { version: string }) => void -) => ipcRenderer.on('update-download-start', (_event, value) => callback(value)) +) => + ipcRenderer.on('update-download-start', (_event: any, value) => + callback(value) + ) const onUpdateError = (callback: (value: Error) => void) => - ipcRenderer.on('update-error', (_event, value) => callback(value)) + ipcRenderer.on('update-error', (_event: any, value) => callback(value)) const appRestart = () => ipcRenderer.invoke('app.restart') const isMac = os.platform() === 'darwin'