* Install Vector on Ubuntu to log failed test to Axiom * Allow flaky tests to run on main for Axiom metrics * Enable problematic tests on a dedicated branch
1043 lines
36 KiB
TypeScript
1043 lines
36 KiB
TypeScript
import { test, expect } from './zoo-test'
|
|
import * as fsp from 'fs/promises'
|
|
import { join } from 'path'
|
|
import {
|
|
getUtils,
|
|
executorInputPath,
|
|
createProject,
|
|
tomlToSettings,
|
|
} from './test-utils'
|
|
import { 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 { DeepPartial } from 'lib/types'
|
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
|
|
|
test.describe('Testing settings', () => {
|
|
test('Stored settings are validated and fall back to defaults', async ({
|
|
page,
|
|
homePage,
|
|
tronApp,
|
|
}) => {
|
|
if (!tronApp) {
|
|
fail()
|
|
}
|
|
// Override beforeEach test setup
|
|
// with corrupted settings
|
|
await tronApp.cleanProjectDir(
|
|
TEST_SETTINGS_CORRUPTED as DeepPartial<Settings>
|
|
)
|
|
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
|
|
// Check the settings were reset
|
|
const storedSettings = tomlToSettings(
|
|
await page.evaluate(
|
|
({ settingsKey }) => localStorage.getItem(settingsKey) || '',
|
|
{ settingsKey: TEST_SETTINGS_KEY }
|
|
)
|
|
)
|
|
|
|
expect(storedSettings.settings?.app?.theme).toBe('dark')
|
|
|
|
// Check that the invalid settings were changed to good defaults
|
|
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
|
|
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo')
|
|
expect(storedSettings.settings?.app?.project_directory).toBe('')
|
|
expect(storedSettings.settings?.project?.default_project_name).toBe(
|
|
'project-$nnn'
|
|
)
|
|
})
|
|
|
|
// The behavior is actually broken. Parent always takes precedence
|
|
test('Project settings can be set and override user settings', async ({
|
|
page,
|
|
homePage,
|
|
}) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
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="app-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="app-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('Project and user settings can be reset', async ({ page, homePage }) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
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(
|
|
`Project settings override user settings on desktop`,
|
|
{ tag: ['@electron', '@skipWin'] },
|
|
async ({ context, page }, testInfo) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
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',
|
|
},
|
|
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',
|
|
},
|
|
async ({ context, page, tronApp }, testInfo) => {
|
|
if (!tronApp) {
|
|
fail()
|
|
}
|
|
await tronApp.cleanProjectDir({
|
|
app: {
|
|
theme_color: '259',
|
|
},
|
|
})
|
|
|
|
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(
|
|
'user settings reload on external change, on project and modeling view',
|
|
{
|
|
tag: '@electron',
|
|
},
|
|
async ({ context, page, tronApp }, testInfo) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
if (!tronApp) {
|
|
fail()
|
|
}
|
|
|
|
await tronApp.cleanProjectDir({
|
|
app: {
|
|
// Doesn't matter what you set it to. It will
|
|
// default to 264.5
|
|
theme_color: '0',
|
|
},
|
|
})
|
|
|
|
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(
|
|
'project settings reload on external change',
|
|
{ tag: '@electron' },
|
|
async ({ context, page }, testInfo) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
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 expect(toastMessage).not.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,
|
|
toolbar,
|
|
scene,
|
|
cmdBar,
|
|
}) => {
|
|
// 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(end = [5, 0])
|
|
|> line(end = [0, 5])
|
|
|> line(end = [-5, 0])
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|> close()
|
|
extrude001 = extrude(sketch001, length = 5)
|
|
`
|
|
)
|
|
})
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await scene.settled(cmdBar)
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Selectors and constants
|
|
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 toolbar.editSketch()
|
|
|
|
// 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`, async ({
|
|
page,
|
|
homePage,
|
|
tronApp,
|
|
}) => {
|
|
if (!tronApp) {
|
|
fail()
|
|
}
|
|
|
|
await tronApp.cleanProjectDir({
|
|
// Override the settings so that the theme is set to `system`
|
|
...TEST_SETTINGS_DEFAULT_THEME,
|
|
})
|
|
|
|
const u = await getUtils(page)
|
|
|
|
// Selectors and constants
|
|
const darkBackgroundCss = 'oklch(0.3012 0 264.5)'
|
|
const lightBackgroundCss = 'oklch(0.9911 0 264.5)'
|
|
const darkBackgroundColor: [number, number, number] = [27, 27, 27]
|
|
const lightBackgroundColor: [number, number, number] = [245, 245, 245]
|
|
const streamBackgroundPixelIsColor = async (
|
|
color: [number, number, number]
|
|
) => {
|
|
return u.getGreatestPixDiff({ x: 1000, y: 200 }, color)
|
|
}
|
|
const toolbar = page.locator('menu').filter({ hasText: 'Start Sketch' })
|
|
|
|
await test.step(`Test setup`, async () => {
|
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
await homePage.goToModelingScene()
|
|
await u.waitForPageLoad()
|
|
await page.waitForTimeout(1000)
|
|
await expect(toolbar).toBeVisible()
|
|
})
|
|
|
|
await test.step(`Check the background color is light before`, async () => {
|
|
await expect(toolbar).toHaveCSS('background-color', lightBackgroundCss)
|
|
await expect
|
|
.poll(() => streamBackgroundPixelIsColor(lightBackgroundColor))
|
|
.toBeLessThan(15)
|
|
})
|
|
|
|
await test.step(`Change media query preference to dark, emulating dusk with system theme`, async () => {
|
|
await page.emulateMedia({ colorScheme: 'dark' })
|
|
})
|
|
|
|
await test.step(`Check the background color is dark after`, async () => {
|
|
await expect(toolbar).toHaveCSS('background-color', darkBackgroundCss)
|
|
await expect
|
|
.poll(() => streamBackgroundPixelIsColor(darkBackgroundColor))
|
|
.toBeLessThan(15)
|
|
})
|
|
})
|
|
|
|
test(`Turning off "Show debug panel" with debug panel open leaves no phantom panel`, async ({
|
|
context,
|
|
page,
|
|
homePage,
|
|
tronApp,
|
|
}) => {
|
|
if (!tronApp) {
|
|
fail()
|
|
}
|
|
|
|
await tronApp.cleanProjectDir({
|
|
// Override beforeEach test setup
|
|
// with debug panel open
|
|
// but "show debug panel" set to false
|
|
...TEST_SETTINGS,
|
|
app: { ...TEST_SETTINGS.app, show_debug_panel: false },
|
|
modeling: { ...TEST_SETTINGS.modeling },
|
|
})
|
|
|
|
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 · app · show debug panel',
|
|
})
|
|
|
|
async function setShowDebugPanelTo(value: 'On' | 'Off') {
|
|
await commandsButton.click()
|
|
await debugPaneOption.click()
|
|
await page.getByRole('option', { name: value }).click()
|
|
await expect(
|
|
page.getByText(
|
|
`Set show debug panel to "${value === 'On'}" for this project`
|
|
)
|
|
).toBeVisible()
|
|
}
|
|
|
|
await test.step(`Initial load with corrupted settings`, async () => {
|
|
// Check that the debug panel is not visible
|
|
await expect(debugPaneButton).not.toBeVisible()
|
|
// Check the pane resize handle wrapper is not visible
|
|
await expect(resizeHandle).not.toBeVisible()
|
|
})
|
|
|
|
await test.step(`Open code pane to verify we see the resize handles`, async () => {
|
|
await u.openKclCodePanel()
|
|
await expect(resizeHandle).toBeVisible()
|
|
await u.closeKclCodePanel()
|
|
})
|
|
|
|
await test.step(`Turn on debug panel, open it`, async () => {
|
|
await setShowDebugPanelTo('On')
|
|
await expect(debugPaneButton).toBeVisible()
|
|
// We want the logic to clear the phantom panel, so we shouldn't see
|
|
// the real panel (and therefore the resize handle) yet
|
|
await expect(resizeHandle).not.toBeVisible()
|
|
await u.openDebugPanel()
|
|
await expect(resizeHandle).toBeVisible()
|
|
})
|
|
|
|
await test.step(`Turn off debug panel setting with it open`, async () => {
|
|
await setShowDebugPanelTo('Off')
|
|
await expect(debugPaneButton).not.toBeVisible()
|
|
await expect(resizeHandle).not.toBeVisible()
|
|
})
|
|
})
|
|
|
|
test(`Change inline units setting`, async ({
|
|
page,
|
|
homePage,
|
|
context,
|
|
editor,
|
|
}) => {
|
|
const initialInlineUnits = 'yd'
|
|
const editedInlineUnits = { short: 'mm', long: 'Millimeters' }
|
|
const inlineSettingsString = (s: string) =>
|
|
`@settings(defaultLengthUnit = ${s})`
|
|
const unitsIndicator = page.getByRole('button', {
|
|
name: 'Current units are:',
|
|
})
|
|
const unitsChangeButton = (name: string) =>
|
|
page.getByRole('button', { name, exact: true })
|
|
|
|
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 test.step(`Initial units from settings`, async () => {
|
|
await homePage.openProject('project-000')
|
|
await expect(unitsIndicator).toHaveText('Current units are: in')
|
|
})
|
|
|
|
await test.step(`Manually write inline settings`, async () => {
|
|
await editor.openPane()
|
|
await editor.replaceCode(
|
|
`fn cube`,
|
|
`${inlineSettingsString(initialInlineUnits)}
|
|
fn cube`
|
|
)
|
|
await expect(unitsIndicator).toContainText(initialInlineUnits)
|
|
})
|
|
|
|
await test.step(`Change units setting via lower-right control`, async () => {
|
|
await unitsIndicator.click()
|
|
await unitsChangeButton(editedInlineUnits.long).click()
|
|
await expect(
|
|
page.getByText(`Updated per-file units to ${editedInlineUnits.short}`)
|
|
).toBeVisible()
|
|
})
|
|
})
|
|
|
|
/**
|
|
* This test assumes that the default value of the "highlight edges" setting is "on".
|
|
*/
|
|
test(`Toggle stream settings multiple times`, async ({
|
|
page,
|
|
scene,
|
|
homePage,
|
|
context,
|
|
toolbar,
|
|
cmdBar,
|
|
}, testInfo) => {
|
|
test.fixme(process.env.GITHUB_HEAD_REF !== 'all-e2e')
|
|
await context.folderSetupFn(async (dir) => {
|
|
const projectDir = join(dir, 'project-000')
|
|
await fsp.mkdir(projectDir, { recursive: true })
|
|
await fsp.copyFile(
|
|
executorInputPath('cube.kcl'),
|
|
join(projectDir, 'main.kcl')
|
|
)
|
|
})
|
|
|
|
await test.step(`First snapshot`, async () => {
|
|
await homePage.openProject('project-000')
|
|
await toolbar.closePane('code')
|
|
await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 20_000 })
|
|
await scene.clickNoWhere()
|
|
})
|
|
|
|
const toast = (value: boolean) =>
|
|
page.getByText(
|
|
`Set highlight edges to "${String(value)}" as a user default`
|
|
)
|
|
|
|
await test.step(`Toggle highlightEdges off`, async () => {
|
|
await cmdBar.openCmdBar()
|
|
await cmdBar.chooseCommand('Settings · modeling · highlight edges')
|
|
await cmdBar.selectOption({ name: 'off' }).click()
|
|
const falseToast = toast(false)
|
|
await expect(falseToast).toBeVisible()
|
|
await falseToast.waitFor({ state: 'detached' })
|
|
})
|
|
|
|
await expect(scene.streamWrapper).not.toHaveScreenshot(
|
|
'toggle-settings-initial.png',
|
|
{
|
|
maxDiffPixels: 15,
|
|
mask: [page.getByTestId('model-state-indicator')],
|
|
}
|
|
)
|
|
|
|
await test.step(`Toggle highlightEdges on`, async () => {
|
|
await cmdBar.openCmdBar()
|
|
await cmdBar.chooseCommand('Settings · modeling · highlight edges')
|
|
await cmdBar.selectOption({ name: 'on' }).click()
|
|
const trueToast = toast(true)
|
|
await expect(trueToast).toBeVisible()
|
|
await trueToast.waitFor({ state: 'detached' })
|
|
})
|
|
|
|
await expect(scene.streamWrapper).toHaveScreenshot(
|
|
'toggle-settings-initial.png',
|
|
{
|
|
maxDiffPixels: 15,
|
|
mask: [page.getByTestId('model-state-indicator')],
|
|
}
|
|
)
|
|
})
|
|
})
|