Files
modeling-app/e2e/playwright/testing-settings.spec.ts
49fl 02055a8b31 Move all tests over to electron (#4484)
* Move all tests over to electron

* Pass the correct param to playwright-electron.sh

* Add shebang to script and add macos-14-large as a target

* Get sketch-tests.spec.ts passing in electron

* Try out 4 workers

* Got testing-segment-overlays passing

* Pass testing-selections.spec.ts

* Go back to fix up sketch-tests test

* Pass various.spec.ts, by far the hardest one

* Pass can-sketch-on-all-planes... with ease

* Pass command bar tests

* fmt

* Completely fix code mirror text navigating for tests

* Pass debug pane tests

* Pass desktop export tests

* Pass editor tests

* Pass file tree tests

* Pass onboarding tests

* Corrected a fixme in file-tree.spec!

* Painfully fix hardcoded coordinates in point-click.spec

* Pass machine.spec tests

* Pass projects, fought hard with filechooser

* Pass regresion-tests.spec tests

* Pass network and connection tests

* Pass camera-movement.spec tests

* Extreme time eaten by gizmo test fixes. All passing now.

* Merge main (tests changed x_x) and pass all constraints.spec tests (pain)

* Pass another painful spec suite: testing-settings

* Pass perspective-toggle, interesting note

* Pass samples loading tests

* Pass app header tests

* Pass text-to-cad tests

* Pass segment-overlays (minor ache) and ability to switch to web if needed :)

* Fix a ton of syntax changes and deflake 2 more tests (pain)

* Correct all tsc errors

* Remove to-electron script

* Add an f-ton of shit because playwright doesnt want S P R E A D

* Try CI again

* Stop snapshots of exports (already test in e2e)

* Fix flake in double click editor

* Hopefully help CI flake

* Fixmes, fixmes everywhere

* One more fixme to settings

* Skip another code pane flake

* Port jess's projects.spec tests

* fixup

* Reuse electron window; difficult task

* Rebased and refixed

* Remove duplicate cases

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* Reduce the workers to something CI can handle

* Lower it further, we need to think about the others

* Update package.json

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>

* Update package.json

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>

* Fix the last tests and tsc errors

* Timeout to 120 and windows-2022-16core

* Fix windows runner detection, enable concurrency temporarily

* Hopefully this time fix windows runner detection

* Comment out Vector, add back removed camera test code

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* Fix camera tests again

* Massively deflake a whole class of tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* Try new CI and fix small onboarding test

* Derp

* No github tuning

* Try mac

* Add back all the OS

* Lord, hallow be thy name

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores)

* One last try with window-16-cores

* Trigger CI

* Try AWS Windows runner

* Passing on windows locally with a few skips

* Skip more win tests, add back all three oses

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-16-cores)

* Add two more fixmes

* 2 more fixmes

* skip segment overlays on win32

* Another fixme

* Trigger CI

* Trigger CI

* Quick clean up

* Move all tests over to electron

* Pass the correct param to playwright-electron.sh

* Add shebang to script and add macos-14-large as a target

* Get sketch-tests.spec.ts passing in electron

* Try out 4 workers

* Got testing-segment-overlays passing

* Pass testing-selections.spec.ts

* Go back to fix up sketch-tests test

* Pass various.spec.ts, by far the hardest one

* Pass can-sketch-on-all-planes... with ease

* Pass command bar tests

* fmt

* Completely fix code mirror text navigating for tests

* Pass debug pane tests

* Pass desktop export tests

* Pass editor tests

* Pass file tree tests

* Pass onboarding tests

* Corrected a fixme in file-tree.spec!

* Painfully fix hardcoded coordinates in point-click.spec

* Pass machine.spec tests

* Pass projects, fought hard with filechooser

* Pass regresion-tests.spec tests

* Pass network and connection tests

* Pass camera-movement.spec tests

* Extreme time eaten by gizmo test fixes. All passing now.

* Merge main (tests changed x_x) and pass all constraints.spec tests (pain)

* Pass another painful spec suite: testing-settings

* Pass perspective-toggle, interesting note

* Pass samples loading tests

* Pass app header tests

* Pass text-to-cad tests

* Pass segment-overlays (minor ache) and ability to switch to web if needed :)

* Fix a ton of syntax changes and deflake 2 more tests (pain)

* Correct all tsc errors

* Remove to-electron script

* Add an f-ton of shit because playwright doesnt want S P R E A D

* Try CI again

* Stop snapshots of exports (already test in e2e)

* Fix flake in double click editor

* Hopefully help CI flake

* Fixmes, fixmes everywhere

* One more fixme to settings

* Skip another code pane flake

* Port jess's projects.spec tests

* fixup

* Reuse electron window; difficult task

* Rebased and refixed

* Remove duplicate cases

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* Reduce the workers to something CI can handle

* Lower it further, we need to think about the others

* Update package.json

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>

* Update package.json

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>

* Fix the last tests and tsc errors

* Timeout to 120 and windows-2022-16core

* Fix windows runner detection, enable concurrency temporarily

* Hopefully this time fix windows runner detection

* Comment out Vector, add back removed camera test code

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* Fix camera tests again

* Massively deflake a whole class of tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: macos-14-large)

* Try new CI and fix small onboarding test

* Derp

* No github tuning

* Try mac

* Add back all the OS

* Lord, hallow be thy name

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores)

* Try AWS Windows runner

* Passing on windows locally with a few skips

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* fmt, tsc, lint

* Enable two fixmes again

* Fix lint, codespell, fmt

* Fix lint

* Don't run e2e on draft, add back concurrency, clean up

* One last windows skip

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2024-12-18 17:58:03 -05:00

900 lines
32 KiB
TypeScript

import { test, expect } from './zoo-test'
import * as fsp from 'fs/promises'
import { join } from 'path'
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 {
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS,
TEST_SETTINGS_DEFAULT_THEME,
} from './storageStates'
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 }) => {
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('Zoo')
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',
}
)
.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()
}
)
test('Keybindings display the correct hotkey for Command Palette', async ({
page,
homePage,
}) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
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.fixme(
'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)
})
})
}
)
test.fixme(
`Project settings override user settings on desktop`,
{ tag: ['@electron', '@skipWin'] },
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')
)
}
)
await page.setBodyDimensions({ width: 1200, height: 500 })
// Selectors and constants
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
const tempUserSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
const userThemeColor = '120'
const projectThemeColor = '50'
const settingsOpenButton = page.getByRole('link', {
name: 'settings Settings',
})
const themeColorSetting = page.locator('#themeColor').getByRole('slider')
const projectSettingsTab = page.getByRole('radio', { name: 'Project' })
const userSettingsTab = page.getByRole('radio', { name: 'User' })
const settingsCloseButton = page.getByTestId('settings-close-button')
const projectLink = page.getByText('bracket')
const logoLink = page.getByTestId('app-logo')
await test.step('Set user theme color on home', async () => {
await expect(settingsOpenButton).toBeVisible()
await settingsOpenButton.click()
// The user tab should be selected by default on home
await expect(userSettingsTab).toBeChecked()
await themeColorSetting.fill(userThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
await settingsCloseButton.click()
await expect
.poll(async () => fsp.readFile(tempUserSettingsFilePath, 'utf-8'), {
message: 'Setting should now be written to the file',
timeout: 5_000,
})
.toContain(`themeColor = "${userThemeColor}"`)
})
await test.step('Set project theme color', async () => {
// Open the project
await projectLink.click()
await settingsOpenButton.click()
// The project tab should be selected by default within a project
await expect(projectSettingsTab).toBeChecked()
await themeColorSetting.fill(projectThemeColor)
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
await settingsCloseButton.click()
// Make sure that the project settings file has been written to before continuing
await expect
.poll(
async () => fsp.readFile(tempProjectSettingsFilePath, 'utf-8'),
{
message: 'Setting should now be written to the file',
timeout: 5_000,
}
)
.toContain(`themeColor = "${projectThemeColor}"`)
})
await test.step('Refresh the application and see project setting applied', async () => {
// Make sure we're done navigating before we reload
await expect(settingsCloseButton).not.toBeVisible()
await page.reload({ waitUntil: 'domcontentloaded' })
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
})
await test.step(`Navigate back to the home view and see user setting applied`, async () => {
await logoLink.click()
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
})
}
)
test(
`Load desktop app with no settings file`,
{
tag: '@electron',
// This is what makes no settings file get created
cleanProjectDir: false,
},
async ({ page }, testInfo) => {
await page.setBodyDimensions({ width: 1200, height: 500 })
// Selectors and constants
const errorHeading = page.getByRole('heading', {
name: 'An unexpected error occurred',
})
const projectDirLink = page.getByText('Loaded from')
// If the app loads without exploding we're in the clear
await expect(errorHeading).not.toBeVisible()
await expect(projectDirLink).toBeVisible()
}
)
test(
`Load desktop app with a settings file, but no project directory setting`,
{
tag: '@electron',
appSettings: {
app: {
themeColor: '259',
},
},
},
async ({ context, page }, testInfo) => {
await page.setBodyDimensions({ width: 1200, height: 500 })
// Selectors and constants
const errorHeading = page.getByRole('heading', {
name: 'An unexpected error occurred',
})
const projectDirLink = page.getByText('Loaded from')
// If the app loads without exploding we're in the clear
await expect(errorHeading).not.toBeVisible()
await expect(projectDirLink).toBeVisible()
}
)
// It was much easier to test the logo color than the background stream color.
test.fixme(
'user settings reload on external change, on project and modeling view',
{
tag: '@electron',
appSettings: {
app: {
// Doesn't matter what you set it to. It will
// default to 264.5
themeColor: '0',
},
},
},
async ({ context, page }, testInfo) => {
const { dir: projectDirName } = await context.folderSetupFn(
async () => {}
)
await page.setBodyDimensions({ width: 1200, height: 500 })
const logoLink = page.getByTestId('app-logo')
const projectDirLink = page.getByText('Loaded from')
await test.step('Wait for project view', async () => {
await expect(projectDirLink).toBeVisible()
await expect(logoLink).toHaveCSS('--primary-hue', '264.5')
})
const changeColor = async (color: string) => {
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
let tomlStr = await fsp.readFile(tempSettingsFilePath, 'utf-8')
tomlStr = tomlStr.replace(/(themeColor = ")[0-9]+(")/, `$1${color}$2`)
await fsp.writeFile(tempSettingsFilePath, tomlStr)
}
await test.step('Check color of logo changed', async () => {
await changeColor('99')
await expect(logoLink).toHaveCSS('--primary-hue', '99')
})
await test.step('Check color of logo changed when in modeling view', async () => {
await createProject({ name: 'project-000', page })
await changeColor('58')
await expect(logoLink).toHaveCSS('--primary-hue', '58')
})
await test.step('Check going back to projects view still changes the color', async () => {
await logoLink.click()
await expect(projectDirLink).toBeVisible()
await changeColor('21')
await expect(logoLink).toHaveCSS('--primary-hue', '21')
})
}
)
test.fixme(
'project settings reload on external change',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
const { dir: projectDirName } = await context.folderSetupFn(
async () => {}
)
await page.setBodyDimensions({ width: 1200, height: 500 })
const logoLink = page.getByTestId('app-logo')
const projectDirLink = page.getByText('Loaded from')
await test.step('Wait for project view', async () => {
await expect(projectDirLink).toBeVisible()
})
await createProject({ name: 'project-000', page })
const changeColorFs = async (color: string) => {
const tempSettingsFilePath = join(
projectDirName,
'project-000',
PROJECT_SETTINGS_FILE_NAME
)
await fsp.writeFile(
tempSettingsFilePath,
`[settings.app]\nthemeColor = "${color}"`
)
}
await test.step('Check the color is first starting as we expect', async () => {
await expect(logoLink).toHaveCSS('--primary-hue', '264.5')
})
await test.step('Check color of logo changed', async () => {
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 }, testInfo) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'project-000')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cube.kcl'),
join(bracketDir, 'main.kcl')
)
await fsp.copyFile(
executorInputPath('cylinder.kcl'),
join(bracketDir, '2.kcl')
)
})
const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
const kclCylinder = await fsp.readFile(
executorInputPath('cylinder.kcl'),
'utf8'
)
const {
openKclCodePanel,
openFilePanel,
waitForPageLoad,
selectFile,
editorTextMatches,
} = await getUtils(page, test)
await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await test.step('Precondition: Open to second project file', async () => {
await expect(page.getByTestId('home-section')).toBeVisible()
await page.getByText('project-000').click()
await waitForPageLoad()
await openKclCodePanel()
await openFilePanel()
await editorTextMatches(kclCube)
await selectFile('2.kcl')
await editorTextMatches(kclCylinder)
})
const settingsOpenButton = page.getByRole('link', {
name: 'settings Settings',
})
const settingsCloseButton = page.getByTestId('settings-close-button')
await test.step('Open and close settings', async () => {
await settingsOpenButton.click()
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await settingsCloseButton.click()
})
await test.step('Postcondition: Same file content is in editor as before settings opened', async () => {
await editorTextMatches(kclCylinder)
})
}
)
test('Changing modeling default unit', async ({ page, homePage }) => {
await test.step(`Test setup`, async () => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
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'
)
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 page.waitForTimeout(1000)
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`
)
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 }) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const u = await getUtils(page)
await context.addInitScript(() => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([5, 0], %)
|> line([0, 5], %)
|> line([-5, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> 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 }) => {
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()
})
}
)
})