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)
 | 
			
		||||
  await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
  const PUR = 400 / 37.5 //pixeltoUnitRatio
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
  await u.openDebugPanel()
 | 
			
		||||
  await u.waitForDefaultPlanesVisibilityChange()
 | 
			
		||||
@ -134,7 +134,7 @@ test('Basic sketch', async ({ page }) => {
 | 
			
		||||
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
 | 
			
		||||
  const u = getUtils(page)
 | 
			
		||||
  await page.setViewportSize({ width: 1000, height: 500 })
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
 | 
			
		||||
  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.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.type('const topAng = 30')
 | 
			
		||||
  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.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
 | 
			
		||||
  // expand variables section
 | 
			
		||||
@ -211,7 +216,7 @@ test('re-executes', async ({ page, context }) => {
 | 
			
		||||
    localStorage.setItem('persistCode', `const myVar = 5`)
 | 
			
		||||
  })
 | 
			
		||||
  await page.setViewportSize({ width: 1000, height: 500 })
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
 | 
			
		||||
  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 PUR = 400 / 37.5 //pixeltoUnitRatio
 | 
			
		||||
  await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
  await u.openDebugPanel()
 | 
			
		||||
  await u.waitForDefaultPlanesVisibilityChange()
 | 
			
		||||
@ -354,7 +359,7 @@ test('Auto complete works', async ({ page }) => {
 | 
			
		||||
  const u = getUtils(page)
 | 
			
		||||
  // const PUR = 400 / 37.5 //pixeltoUnitRatio
 | 
			
		||||
  await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
  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.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
 | 
			
		||||
  // Test that the redirect happened
 | 
			
		||||
  await expect(page.url()).toBe(
 | 
			
		||||
    `http://localhost:3000/file/new/onboarding/export`
 | 
			
		||||
  await expect(page.url().split(':3000').slice(-1)[0]).toBe(
 | 
			
		||||
    `/file/new/onboarding/export`
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  // Test that you come back to this page when you refresh
 | 
			
		||||
  await page.reload()
 | 
			
		||||
  await expect(page.url()).toBe(
 | 
			
		||||
    `http://localhost:3000/file/new/onboarding/export`
 | 
			
		||||
  await expect(page.url().split(':3000').slice(-1)[0]).toBe(
 | 
			
		||||
    `/file/new/onboarding/export`
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  // 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 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 }) => {
 | 
			
		||||
  const u = getUtils(page)
 | 
			
		||||
  await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
  await page.goto('localhost:3000')
 | 
			
		||||
  await page.goto('/')
 | 
			
		||||
  await u.waitForAuthSkipAppStart()
 | 
			
		||||
  await u.openAndClearDebugPanel()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,6 @@ import { expect, Page } from '@playwright/test'
 | 
			
		||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
 | 
			
		||||
 | 
			
		||||
async function waitForPageLoad(page: Page) {
 | 
			
		||||
  // wait for 'Loading KittyCAD Modeling App...' spinner
 | 
			
		||||
  await page.getByTestId('initial-load').waitFor()
 | 
			
		||||
  // wait for 'Loading stream...' spinner
 | 
			
		||||
  await page.getByTestId('loading-stream').waitFor()
 | 
			
		||||
  // 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. */
 | 
			
		||||
  use: {
 | 
			
		||||
    /* 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 */
 | 
			
		||||
    trace: 'on-first-retry',
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ export const EngineCommands = () => {
 | 
			
		||||
          const stringer = JSON.stringify(command)
 | 
			
		||||
          if (containsFilter && !stringer.includes(containsFilter)) return null
 | 
			
		||||
          return (
 | 
			
		||||
            <pre className="text-xs">
 | 
			
		||||
            <pre className="text-xs" key={index}>
 | 
			
		||||
              <code
 | 
			
		||||
                key={index}
 | 
			
		||||
                data-message-type={command.type}
 | 
			
		||||
 | 
			
		||||
@ -27,4 +27,7 @@ export const lineHighlightField = StateField.define({
 | 
			
		||||
  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
 | 
			
		||||
// for the EngineCommandManager; namely, the underlying WebSocket
 | 
			
		||||
// and WebRTC connections.
 | 
			
		||||
export class EngineConnection {
 | 
			
		||||
class EngineConnection {
 | 
			
		||||
  websocket?: WebSocket
 | 
			
		||||
  pc?: RTCPeerConnection
 | 
			
		||||
  unreliableDataChannel?: RTCDataChannel
 | 
			
		||||
@ -610,10 +610,9 @@ export type CommandLog =
 | 
			
		||||
      data: null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
type CommandLogTypes = Extract<CommandLog, { type: string }>['type']
 | 
			
		||||
 | 
			
		||||
export class EngineCommandManager {
 | 
			
		||||
  artifactMap: ArtifactMap = {}
 | 
			
		||||
  lastArtifactMap: ArtifactMap = {}
 | 
			
		||||
  outSequence = 1
 | 
			
		||||
  inSequence = 1
 | 
			
		||||
  engineConnection?: EngineConnection
 | 
			
		||||
@ -810,17 +809,17 @@ export class EngineCommandManager {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const modelingResponse = message.data.modeling_response
 | 
			
		||||
    const command = this.artifactMap[id]
 | 
			
		||||
    this.addCommandLog({
 | 
			
		||||
      type: 'receive-reliable',
 | 
			
		||||
      data: message,
 | 
			
		||||
      id,
 | 
			
		||||
      cmd_type: this.artifactMap[id]?.commandType,
 | 
			
		||||
      cmd_type: command?.commandType || this.lastArtifactMap[id]?.commandType,
 | 
			
		||||
    })
 | 
			
		||||
    Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
 | 
			
		||||
      (callback) => callback(modelingResponse)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const command = this.artifactMap[id]
 | 
			
		||||
    if (command && command.type === 'pending') {
 | 
			
		||||
      const resolve = command.resolve
 | 
			
		||||
      this.artifactMap[id] = {
 | 
			
		||||
@ -884,6 +883,7 @@ export class EngineCommandManager {
 | 
			
		||||
    this.engineConnection?.tearDown()
 | 
			
		||||
  }
 | 
			
		||||
  startNewSession() {
 | 
			
		||||
    this.lastArtifactMap = this.artifactMap
 | 
			
		||||
    this.artifactMap = {}
 | 
			
		||||
  }
 | 
			
		||||
  subscribeTo<T extends ModelTypes>({
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,7 @@
 | 
			
		||||
    "jsx": "react-jsx"
 | 
			
		||||
  },
 | 
			
		||||
  "include": [
 | 
			
		||||
    "src",
 | 
			
		||||
    "e2e"
 | 
			
		||||
    "src"
 | 
			
		||||
  ],
 | 
			
		||||
  "references": [{ "path": "./tsconfig.node.json" }]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user