* circle * fix another example * fix bad comment * toPoint fix * cargo fmt * resolve most of the tests * fix last test * missed circle in bracket * remove console error * fmt * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest) * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest) * trigger ci * remove three dot menu for circle * make sure circle can be extruded * fix up after merge * add extrude test for circle * clean up * typo * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest) * Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)" This reverts commit03f8eeb542. * update docs again * cmd bar test serialisation improvements * tiny clean up * fix after: Replace kittycad crate with kittycad-modeling-cmds * fmt * rename fix * Update src/lib/toolbar.ts Co-authored-by: Frank Noirot <frank@zoo.dev> * add another error to list * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest) * image updates * Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)" This reverts commit505bb20bea. * update markdown * skip un reproducable windows test failure * rust review * leave issue todo comment --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Frank Noirot <frank@zoo.dev>
		
			
				
	
	
		
			217 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import type { Page, Locator } from '@playwright/test'
 | 
						|
import { expect, test as base } from '@playwright/test'
 | 
						|
import { getUtils, setup, tearDown } from './test-utils'
 | 
						|
import fsp from 'fs/promises'
 | 
						|
import { join } from 'path'
 | 
						|
 | 
						|
type CmdBarSerilised =
 | 
						|
  | {
 | 
						|
      stage: 'commandBarClosed'
 | 
						|
      // TODO no more properties needed but needs to be implemented in _serialiseCmdBar
 | 
						|
    }
 | 
						|
  | {
 | 
						|
      stage: 'pickCommand'
 | 
						|
      // TODO this will need more properties when implemented in _serialiseCmdBar
 | 
						|
    }
 | 
						|
  | {
 | 
						|
      stage: 'arguments'
 | 
						|
      currentArgKey: string
 | 
						|
      currentArgValue: string
 | 
						|
      headerArguments: Record<string, string>
 | 
						|
      highlightedHeaderArg: string
 | 
						|
      commandName: string
 | 
						|
    }
 | 
						|
  | {
 | 
						|
      stage: 'review'
 | 
						|
      headerArguments: Record<string, string>
 | 
						|
      commandName: string
 | 
						|
    }
 | 
						|
 | 
						|
export class AuthenticatedApp {
 | 
						|
  private readonly codeContent: Locator
 | 
						|
  private readonly extrudeButton: Locator
 | 
						|
 | 
						|
  constructor(public readonly page: Page) {
 | 
						|
    this.codeContent = page.locator('.cm-content')
 | 
						|
    this.extrudeButton = page.getByTestId('extrude')
 | 
						|
  }
 | 
						|
 | 
						|
  async initialise(code = '') {
 | 
						|
    const u = await getUtils(this.page)
 | 
						|
    await this.page.addInitScript(async (code) => {
 | 
						|
      localStorage.setItem('persistCode', code)
 | 
						|
      ;(window as any).playwrightSkipFilePicker = true
 | 
						|
    }, code)
 | 
						|
 | 
						|
    await this.page.setViewportSize({ width: 1000, height: 500 })
 | 
						|
 | 
						|
    await u.waitForAuthSkipAppStart()
 | 
						|
  }
 | 
						|
  getInputFile = (fileName: string) => {
 | 
						|
    return fsp.readFile(
 | 
						|
      join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName),
 | 
						|
      'utf-8'
 | 
						|
    )
 | 
						|
  }
 | 
						|
  makeMouseHelpers = (x: number, y: number) => [
 | 
						|
    () => this.page.mouse.click(x, y),
 | 
						|
    () => this.page.mouse.move(x, y),
 | 
						|
  ]
 | 
						|
 | 
						|
  /** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
 | 
						|
   *
 | 
						|
   * Expects the viewPort to be 1000x500 */
 | 
						|
  clickNoWhere = () => this.page.mouse.click(998, 60)
 | 
						|
 | 
						|
  // Toolbars
 | 
						|
  expectExtrudeButtonToBeDisabled = async () =>
 | 
						|
    await expect(this.extrudeButton).toBeDisabled()
 | 
						|
  expectExtrudeButtonToBeEnabled = async () =>
 | 
						|
    await expect(this.extrudeButton).not.toBeDisabled()
 | 
						|
  clickExtrudeButton = async () => await this.extrudeButton.click()
 | 
						|
 | 
						|
  private _serialiseCmdBar = async (): Promise<CmdBarSerilised> => {
 | 
						|
    const reviewForm = await this.page.locator('#review-form')
 | 
						|
    const getHeaderArgs = async () => {
 | 
						|
      const inputs = await this.page.getByTestId('cmd-bar-input-tab').all()
 | 
						|
      const entries = await Promise.all(
 | 
						|
        inputs.map((input) => {
 | 
						|
          const key = input
 | 
						|
            .locator('[data-test-name="arg-name"]')
 | 
						|
            .innerText()
 | 
						|
            .then((a) => a.trim())
 | 
						|
          const value = input
 | 
						|
            .getByTestId('header-arg-value')
 | 
						|
            .innerText()
 | 
						|
            .then((a) => a.trim())
 | 
						|
          return Promise.all([key, value])
 | 
						|
        })
 | 
						|
      )
 | 
						|
      return Object.fromEntries(entries)
 | 
						|
    }
 | 
						|
    const getCommandName = () =>
 | 
						|
      this.page.getByTestId('command-name').textContent()
 | 
						|
    if (await reviewForm.isVisible()) {
 | 
						|
      const [headerArguments, commandName] = await Promise.all([
 | 
						|
        getHeaderArgs(),
 | 
						|
        getCommandName(),
 | 
						|
      ])
 | 
						|
      return {
 | 
						|
        stage: 'review',
 | 
						|
        headerArguments,
 | 
						|
        commandName: commandName || '',
 | 
						|
      }
 | 
						|
    }
 | 
						|
    const [
 | 
						|
      currentArgKey,
 | 
						|
      currentArgValue,
 | 
						|
      headerArguments,
 | 
						|
      highlightedHeaderArg,
 | 
						|
      commandName,
 | 
						|
    ] = await Promise.all([
 | 
						|
      this.page.getByTestId('cmd-bar-arg-name').textContent(),
 | 
						|
      this.page.getByTestId('cmd-bar-arg-value').textContent(),
 | 
						|
      getHeaderArgs(),
 | 
						|
      this.page
 | 
						|
        .locator('[data-is-current-arg="true"]')
 | 
						|
        .locator('[data-test-name="arg-name"]')
 | 
						|
        .textContent(),
 | 
						|
      getCommandName(),
 | 
						|
    ])
 | 
						|
    return {
 | 
						|
      stage: 'arguments',
 | 
						|
      currentArgKey: currentArgKey || '',
 | 
						|
      currentArgValue: currentArgValue || '',
 | 
						|
      headerArguments,
 | 
						|
      highlightedHeaderArg: highlightedHeaderArg || '',
 | 
						|
      commandName: commandName || '',
 | 
						|
    }
 | 
						|
  }
 | 
						|
  expectCmdBarToBe = async (expected: CmdBarSerilised) => {
 | 
						|
    return expect.poll(() => this._serialiseCmdBar()).toEqual(expected)
 | 
						|
  }
 | 
						|
  progressCmdBar = async () => {
 | 
						|
    if (Math.random() > 0.5) {
 | 
						|
      const arrowButton = this.page.getByRole('button', {
 | 
						|
        name: 'arrow right Continue',
 | 
						|
      })
 | 
						|
      if (await arrowButton.isVisible()) {
 | 
						|
        await arrowButton.click()
 | 
						|
      } else {
 | 
						|
        await this.page
 | 
						|
          .getByRole('button', { name: 'checkmark Submit command' })
 | 
						|
          .click()
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      await this.page.keyboard.press('Enter')
 | 
						|
    }
 | 
						|
  }
 | 
						|
  expectCodeHighlightedToBe = async (code: string) =>
 | 
						|
    await expect
 | 
						|
      .poll(async () => {
 | 
						|
        const texts = (
 | 
						|
          await this.page.getByTestId('hover-highlight').allInnerTexts()
 | 
						|
        ).map((s) => s.replace(/\s+/g, '').trim())
 | 
						|
        return texts.join('')
 | 
						|
      })
 | 
						|
      .toBe(code.replace(/\s+/g, '').trim())
 | 
						|
  expectActiveLinesToBe = async (lines: Array<string>) => {
 | 
						|
    await expect
 | 
						|
      .poll(async () => {
 | 
						|
        return (await this.page.locator('.cm-activeLine').allInnerTexts()).map(
 | 
						|
          (l) => l.trim()
 | 
						|
        )
 | 
						|
      })
 | 
						|
      .toEqual(lines.map((l) => l.trim()))
 | 
						|
  }
 | 
						|
  private _expectEditorToContain =
 | 
						|
    (not = false) =>
 | 
						|
    (
 | 
						|
      code: string,
 | 
						|
      {
 | 
						|
        shouldNormalise = false,
 | 
						|
        timeout = 5_000,
 | 
						|
      }: { shouldNormalise?: boolean; timeout?: number } = {}
 | 
						|
    ) => {
 | 
						|
      if (!shouldNormalise) {
 | 
						|
        const expectStart = expect(this.codeContent)
 | 
						|
        if (not) {
 | 
						|
          return expectStart.not.toContainText(code, { timeout })
 | 
						|
        }
 | 
						|
        return expectStart.toContainText(code, { timeout })
 | 
						|
      }
 | 
						|
      const normalisedCode = code.replaceAll(/\s+/g, ' ').trim()
 | 
						|
      const expectStart = expect.poll(() => this.codeContent.textContent(), {
 | 
						|
        timeout,
 | 
						|
      })
 | 
						|
      if (not) {
 | 
						|
        return expectStart.not.toContain(normalisedCode)
 | 
						|
      }
 | 
						|
      return expectStart.toContain(normalisedCode)
 | 
						|
    }
 | 
						|
 | 
						|
  expectEditor = {
 | 
						|
    toContain: this._expectEditorToContain(),
 | 
						|
    not: { toContain: this._expectEditorToContain(true) },
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export const test = base.extend<{
 | 
						|
  app: AuthenticatedApp
 | 
						|
}>({
 | 
						|
  app: async ({ page }, use) => {
 | 
						|
    const authenticatedApp = new AuthenticatedApp(page)
 | 
						|
    await use(authenticatedApp)
 | 
						|
  },
 | 
						|
})
 | 
						|
 | 
						|
test.beforeEach(async ({ context, page }, testInfo) => {
 | 
						|
  await setup(context, page, testInfo)
 | 
						|
})
 | 
						|
 | 
						|
test.afterEach(async ({ page }, testInfo) => {
 | 
						|
  await tearDown(page, testInfo)
 | 
						|
})
 | 
						|
 | 
						|
export { expect } from '@playwright/test'
 |