selections e2e test (#1136)
* selections e2e test * .only * adde test * make invalid kcl test pass on macos runner * further attempt to solve invalid code test on macos * add comment
This commit is contained in:
@ -49,7 +49,7 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
await u.waitForDefaultPlanesVisibilityChange()
|
||||||
@ -134,7 +134,7 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
@ -148,6 +148,11 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|||||||
*/
|
*/
|
||||||
await page.click('.cm-content')
|
await page.click('.cm-content')
|
||||||
await page.keyboard.type('# error')
|
await page.keyboard.type('# error')
|
||||||
|
|
||||||
|
// press arrows to clear autocomplete
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type('const topAng = 30')
|
await page.keyboard.type('const topAng = 30')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
@ -186,7 +191,7 @@ test('executes on load', async ({ page, context }) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// expand variables section
|
// expand variables section
|
||||||
@ -211,7 +216,7 @@ test('re-executes', async ({ page, context }) => {
|
|||||||
localStorage.setItem('persistCode', `const myVar = 5`)
|
localStorage.setItem('persistCode', `const myVar = 5`)
|
||||||
})
|
})
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.getByText('Variables').click()
|
await page.getByText('Variables').click()
|
||||||
@ -237,7 +242,7 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
await u.waitForDefaultPlanesVisibilityChange()
|
||||||
@ -354,7 +359,7 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
await u.waitForDefaultPlanesVisibilityChange()
|
||||||
|
|
||||||
@ -410,18 +415,18 @@ test('Onboarding redirects and code updating', async ({ page, context }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Test that the redirect happened
|
// Test that the redirect happened
|
||||||
await expect(page.url()).toBe(
|
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||||
`http://localhost:3000/file/new/onboarding/export`
|
`/file/new/onboarding/export`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test that you come back to this page when you refresh
|
// Test that you come back to this page when you refresh
|
||||||
await page.reload()
|
await page.reload()
|
||||||
await expect(page.url()).toBe(
|
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||||
`http://localhost:3000/file/new/onboarding/export`
|
`/file/new/onboarding/export`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
// Test that the onboarding pane loaded
|
||||||
@ -436,3 +441,106 @@ test('Onboarding redirects and code updating', async ({ page, context }) => {
|
|||||||
await page.locator('[data-testid="onboarding-next"]').click()
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||||
|
// tests mapping works on fresh sketch and edited sketch
|
||||||
|
// tests using hovers which is the same as selections, because if
|
||||||
|
// source ranges are wrong, hovers won't work
|
||||||
|
const u = getUtils(page)
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.waitForDefaultPlanesVisibilityChange()
|
||||||
|
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await u.waitForDefaultPlanesVisibilityChange()
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
|
||||||
|
await u.waitForCmdReceive('set_tool')
|
||||||
|
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||||
|
'set_tool'
|
||||||
|
)
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
|
||||||
|
'mouse_click',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
|
const startAt = '[9.94, -13.41]'
|
||||||
|
const tenish = '10.03'
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${tenish}, 0], %)`)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${tenish}, 0], %)
|
||||||
|
|> line([0, ${tenish}], %)`)
|
||||||
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${tenish}, 0], %)
|
||||||
|
|> line([0, ${tenish}], %)
|
||||||
|
|> line([-19.97, 0], %)`)
|
||||||
|
|
||||||
|
// deselect line tool
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||||
|
'set_tool'
|
||||||
|
)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
const hoverSequency = async () => {
|
||||||
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
|
|
||||||
|
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
|
|
||||||
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||||
|
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
|
||||||
|
// and will be an easy fix if it breaks because we change the colour
|
||||||
|
await expect(page.locator('.bg-yellow-200')).toBeVisible()
|
||||||
|
|
||||||
|
// check mousing off, than mousing onto another line
|
||||||
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
|
||||||
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
|
||||||
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||||
|
}
|
||||||
|
await hoverSequency()
|
||||||
|
|
||||||
|
// hovering in fresh sketch worked, lets try exiting and re-entering
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
|
||||||
|
'edit_mode_exit'
|
||||||
|
)
|
||||||
|
|
||||||
|
// select a line
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20),
|
||||||
|
'select_with_point'
|
||||||
|
)
|
||||||
|
|
||||||
|
// enter sketch again
|
||||||
|
await u.doAndWaitForCmd(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
'edit_mode_enter',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
// hover again and check it works
|
||||||
|
await hoverSequency()
|
||||||
|
})
|
||||||
|
@ -32,7 +32,7 @@ test.setTimeout(60000)
|
|||||||
test('change camera, show planes', async ({ page, context }) => {
|
test('change camera, show planes', async ({ page, context }) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('localhost:3000')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ import { expect, Page } from '@playwright/test'
|
|||||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
||||||
|
|
||||||
async function waitForPageLoad(page: Page) {
|
async function waitForPageLoad(page: Page) {
|
||||||
// wait for 'Loading KittyCAD Modeling App...' spinner
|
|
||||||
await page.getByTestId('initial-load').waitFor()
|
|
||||||
// wait for 'Loading stream...' spinner
|
// wait for 'Loading stream...' spinner
|
||||||
await page.getByTestId('loading-stream').waitFor()
|
await page.getByTestId('loading-stream').waitFor()
|
||||||
// wait for all spinners to be gone
|
// wait for all spinners to be gone
|
||||||
|
@ -24,7 +24,7 @@ export default defineConfig({
|
|||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
use: {
|
use: {
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
// baseURL: 'http://127.0.0.1:3000',
|
baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
|
@ -32,7 +32,7 @@ export const EngineCommands = () => {
|
|||||||
const stringer = JSON.stringify(command)
|
const stringer = JSON.stringify(command)
|
||||||
if (containsFilter && !stringer.includes(containsFilter)) return null
|
if (containsFilter && !stringer.includes(containsFilter)) return null
|
||||||
return (
|
return (
|
||||||
<pre className="text-xs">
|
<pre className="text-xs" key={index}>
|
||||||
<code
|
<code
|
||||||
key={index}
|
key={index}
|
||||||
data-message-type={command.type}
|
data-message-type={command.type}
|
||||||
|
@ -27,4 +27,7 @@ export const lineHighlightField = StateField.define({
|
|||||||
provide: (f) => EditorView.decorations.from(f),
|
provide: (f) => EditorView.decorations.from(f),
|
||||||
})
|
})
|
||||||
|
|
||||||
const matchDeco = Decoration.mark({ class: 'bg-yellow-200' })
|
const matchDeco = Decoration.mark({
|
||||||
|
class: 'bg-yellow-200',
|
||||||
|
attributes: { 'data-testid': 'hover-highlight' },
|
||||||
|
})
|
||||||
|
@ -51,7 +51,7 @@ type ClientMetrics = Models['ClientMetrics_type']
|
|||||||
// EngineConnection encapsulates the connection(s) to the Engine
|
// EngineConnection encapsulates the connection(s) to the Engine
|
||||||
// for the EngineCommandManager; namely, the underlying WebSocket
|
// for the EngineCommandManager; namely, the underlying WebSocket
|
||||||
// and WebRTC connections.
|
// and WebRTC connections.
|
||||||
export class EngineConnection {
|
class EngineConnection {
|
||||||
websocket?: WebSocket
|
websocket?: WebSocket
|
||||||
pc?: RTCPeerConnection
|
pc?: RTCPeerConnection
|
||||||
unreliableDataChannel?: RTCDataChannel
|
unreliableDataChannel?: RTCDataChannel
|
||||||
@ -610,10 +610,9 @@ export type CommandLog =
|
|||||||
data: null
|
data: null
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandLogTypes = Extract<CommandLog, { type: string }>['type']
|
|
||||||
|
|
||||||
export class EngineCommandManager {
|
export class EngineCommandManager {
|
||||||
artifactMap: ArtifactMap = {}
|
artifactMap: ArtifactMap = {}
|
||||||
|
lastArtifactMap: ArtifactMap = {}
|
||||||
outSequence = 1
|
outSequence = 1
|
||||||
inSequence = 1
|
inSequence = 1
|
||||||
engineConnection?: EngineConnection
|
engineConnection?: EngineConnection
|
||||||
@ -810,17 +809,17 @@ export class EngineCommandManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const modelingResponse = message.data.modeling_response
|
const modelingResponse = message.data.modeling_response
|
||||||
|
const command = this.artifactMap[id]
|
||||||
this.addCommandLog({
|
this.addCommandLog({
|
||||||
type: 'receive-reliable',
|
type: 'receive-reliable',
|
||||||
data: message,
|
data: message,
|
||||||
id,
|
id,
|
||||||
cmd_type: this.artifactMap[id]?.commandType,
|
cmd_type: command?.commandType || this.lastArtifactMap[id]?.commandType,
|
||||||
})
|
})
|
||||||
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
|
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
|
||||||
(callback) => callback(modelingResponse)
|
(callback) => callback(modelingResponse)
|
||||||
)
|
)
|
||||||
|
|
||||||
const command = this.artifactMap[id]
|
|
||||||
if (command && command.type === 'pending') {
|
if (command && command.type === 'pending') {
|
||||||
const resolve = command.resolve
|
const resolve = command.resolve
|
||||||
this.artifactMap[id] = {
|
this.artifactMap[id] = {
|
||||||
@ -884,6 +883,7 @@ export class EngineCommandManager {
|
|||||||
this.engineConnection?.tearDown()
|
this.engineConnection?.tearDown()
|
||||||
}
|
}
|
||||||
startNewSession() {
|
startNewSession() {
|
||||||
|
this.lastArtifactMap = this.artifactMap
|
||||||
this.artifactMap = {}
|
this.artifactMap = {}
|
||||||
}
|
}
|
||||||
subscribeTo<T extends ModelTypes>({
|
subscribeTo<T extends ModelTypes>({
|
||||||
|
@ -26,8 +26,7 @@
|
|||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src",
|
"src"
|
||||||
"e2e"
|
|
||||||
],
|
],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user