* Rename desktop e2e scripts and tags for consistency * Show local command in main test step * Restore 'e2e' prefix to clarify GitHub UI * Add web script to contributor guide
		
			
				
	
	
		
			1059 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1059 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import path from 'path'
 | 
						|
import * as fsp from 'fs/promises'
 | 
						|
 | 
						|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
 | 
						|
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
 | 
						|
import {
 | 
						|
  executorInputPath,
 | 
						|
  getUtils,
 | 
						|
  kclSamplesPath,
 | 
						|
  testsInputPath,
 | 
						|
} from '@e2e/playwright/test-utils'
 | 
						|
import { expect, test } from '@e2e/playwright/zoo-test'
 | 
						|
import type { Page } from '@playwright/test'
 | 
						|
 | 
						|
async function insertPartIntoAssembly(
 | 
						|
  path: string,
 | 
						|
  alias: string,
 | 
						|
  toolbar: ToolbarFixture,
 | 
						|
  cmdBar: CmdBarFixture,
 | 
						|
  page: Page
 | 
						|
) {
 | 
						|
  await toolbar.insertButton.click()
 | 
						|
  await cmdBar.selectOption({ name: path }).click()
 | 
						|
  await cmdBar.expectState({
 | 
						|
    stage: 'arguments',
 | 
						|
    currentArgKey: 'localName',
 | 
						|
    currentArgValue: '',
 | 
						|
    headerArguments: { Path: path, LocalName: '' },
 | 
						|
    highlightedHeaderArg: 'localName',
 | 
						|
    commandName: 'Insert',
 | 
						|
  })
 | 
						|
  await page.keyboard.insertText(alias)
 | 
						|
  await cmdBar.progressCmdBar()
 | 
						|
  await cmdBar.expectState({
 | 
						|
    stage: 'review',
 | 
						|
    headerArguments: { Path: path, LocalName: alias },
 | 
						|
    commandName: 'Insert',
 | 
						|
  })
 | 
						|
  await cmdBar.progressCmdBar()
 | 
						|
}
 | 
						|
 | 
						|
// test file is for testing point an click code gen functionality that's assemblies related
 | 
						|
test.describe('Point-and-click assemblies tests', () => {
 | 
						|
  test(
 | 
						|
    `Insert kcl parts into assembly as whole module import`,
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({
 | 
						|
      context,
 | 
						|
      page,
 | 
						|
      homePage,
 | 
						|
      scene,
 | 
						|
      editor,
 | 
						|
      toolbar,
 | 
						|
      cmdBar,
 | 
						|
      tronApp,
 | 
						|
    }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      const midPoint = { x: 500, y: 250 }
 | 
						|
      const partPoint = { x: midPoint.x + 30, y: midPoint.y - 30 } // mid point, just off top right
 | 
						|
      const defaultPlanesColor: [number, number, number] = [180, 220, 180]
 | 
						|
      const partColor: [number, number, number] = [100, 100, 100]
 | 
						|
      const tolerance = 50
 | 
						|
      const u = await getUtils(page)
 | 
						|
      const gizmo = page.locator('[aria-label*=gizmo]')
 | 
						|
      const resetCameraButton = page.getByRole('button', { name: 'Reset view' })
 | 
						|
 | 
						|
      await test.step('Setup parts and expect empty assembly scene', async () => {
 | 
						|
        const projectName = 'assembly'
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const projDir = path.join(dir, projectName)
 | 
						|
          const nestedProjDir = path.join(dir, projectName, 'nested', 'twice')
 | 
						|
          await fsp.mkdir(projDir, { recursive: true })
 | 
						|
          await fsp.mkdir(nestedProjDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              executorInputPath('cylinder.kcl'),
 | 
						|
              path.join(projDir, 'cylinder.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              executorInputPath('cylinder.kcl'),
 | 
						|
              path.join(nestedProjDir, 'main.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
 | 
						|
              path.join(projDir, 'bracket.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              testsInputPath('cube.step'),
 | 
						|
              path.join(projDir, 'cube.step')
 | 
						|
            ),
 | 
						|
            fsp.writeFile(path.join(projDir, 'main.kcl'), ''),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(defaultPlanesColor, midPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert kcl as first part as module', async () => {
 | 
						|
        await insertPartIntoAssembly(
 | 
						|
          'cylinder.kcl',
 | 
						|
          'cylinder',
 | 
						|
          toolbar,
 | 
						|
          cmdBar,
 | 
						|
          page
 | 
						|
        )
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          import "cylinder.kcl" as cylinder
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
 | 
						|
        // Check scene for changes
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await u.doAndWaitForCmd(async () => {
 | 
						|
          await gizmo.click({ button: 'right' })
 | 
						|
          await resetCameraButton.click()
 | 
						|
        }, 'zoom_to_fit')
 | 
						|
        await toolbar.closePane('debug')
 | 
						|
        await scene.expectPixelColor(partColor, partPoint, tolerance)
 | 
						|
        await toolbar.openPane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert a second part with the same name and expect error', async () => {
 | 
						|
        await toolbar.insertButton.click()
 | 
						|
        await cmdBar.selectOption({ name: 'bracket.kcl' }).click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'localName',
 | 
						|
          currentArgValue: '',
 | 
						|
          headerArguments: { Path: 'bracket.kcl', LocalName: '' },
 | 
						|
          highlightedHeaderArg: 'localName',
 | 
						|
          commandName: 'Insert',
 | 
						|
        })
 | 
						|
        await page.keyboard.insertText('cylinder')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await expect(
 | 
						|
          page.getByText('This variable name is already in use')
 | 
						|
        ).toBeVisible()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Fix the name and expect the second part inserted', async () => {
 | 
						|
        await cmdBar.argumentInput.clear()
 | 
						|
        await page.keyboard.insertText('bracket')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: { Path: 'bracket.kcl', LocalName: 'bracket' },
 | 
						|
          commandName: 'Insert',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "cylinder.kcl" as cylinder
 | 
						|
            import "bracket.kcl" as bracket
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert a second time and expect error', async () => {
 | 
						|
        await toolbar.insertButton.click()
 | 
						|
        await cmdBar.selectOption({ name: 'bracket.kcl' }).click()
 | 
						|
        await expect(
 | 
						|
          page.getByText('This file is already imported')
 | 
						|
        ).toBeVisible()
 | 
						|
        await cmdBar.closeCmdBar()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert a nested kcl part', async () => {
 | 
						|
        await insertPartIntoAssembly(
 | 
						|
          'nested/twice/main.kcl',
 | 
						|
          'main',
 | 
						|
          toolbar,
 | 
						|
          cmdBar,
 | 
						|
          page
 | 
						|
        )
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await page.waitForTimeout(10000)
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          import "nested/twice/main.kcl" as main
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Can still translate, rotate, and delete inserted parts even with non standard code`,
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({
 | 
						|
      context,
 | 
						|
      page,
 | 
						|
      homePage,
 | 
						|
      scene,
 | 
						|
      editor,
 | 
						|
      toolbar,
 | 
						|
      cmdBar,
 | 
						|
      tronApp,
 | 
						|
    }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await test.step('Setup parts and expect empty assembly scene', async () => {
 | 
						|
        const projectName = 'assembly'
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const projectDir = path.join(dir, projectName)
 | 
						|
          await fsp.mkdir(projectDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              executorInputPath('cylinder.kcl'),
 | 
						|
              path.join(projectDir, 'cylinder.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              testsInputPath('cube.step'),
 | 
						|
              path.join(projectDir, 'cube.step')
 | 
						|
            ),
 | 
						|
            fsp.writeFile(
 | 
						|
              path.join(projectDir, 'main.kcl'),
 | 
						|
              `
 | 
						|
                import "cube.step" as cube
 | 
						|
                import "cylinder.kcl" as cylinder
 | 
						|
                cylinder
 | 
						|
                  |> translate(x = 1)
 | 
						|
                cube
 | 
						|
                  |> rotate(pitch = 2)
 | 
						|
                  |> translate(y = 2)
 | 
						|
                cylinder
 | 
						|
                  |> rotate(roll = 1)
 | 
						|
                cylinder
 | 
						|
                  |> translate(x = 0.1)
 | 
						|
              `
 | 
						|
            ),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set translate on cylinder', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('cylinder', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-translate').click()
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('10')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            X: '0.1',
 | 
						|
            Y: '0',
 | 
						|
            Z: '10',
 | 
						|
          },
 | 
						|
          commandName: 'Translate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "cube.step" as cube
 | 
						|
            import "cylinder.kcl" as cylinder
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 1)
 | 
						|
            cube
 | 
						|
              |> rotate(pitch = 2)
 | 
						|
              |> translate(y = 2)
 | 
						|
            cylinder
 | 
						|
              |> rotate(roll = 1)
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 0.1, y = 0, z = 10)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set rotate on cylinder', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('cylinder', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-rotate').click()
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('100')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            Roll: '1',
 | 
						|
            Pitch: '0',
 | 
						|
            Yaw: '100',
 | 
						|
          },
 | 
						|
          commandName: 'Rotate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "cube.step" as cube
 | 
						|
            import "cylinder.kcl" as cylinder
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 1)
 | 
						|
            cube
 | 
						|
              |> rotate(pitch = 2)
 | 
						|
              |> translate(y = 2)
 | 
						|
            cylinder
 | 
						|
              |> rotate(roll = 1, pitch = 0, yaw = 100)
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 0.1, y = 0, z = 10)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set rotate on cube', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('cube', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-rotate').click()
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('200')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            Roll: '0',
 | 
						|
            Pitch: '2',
 | 
						|
            Yaw: '200',
 | 
						|
          },
 | 
						|
          commandName: 'Rotate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "cube.step" as cube
 | 
						|
            import "cylinder.kcl" as cylinder
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 1)
 | 
						|
            cube
 | 
						|
              |> rotate(roll = 0, pitch = 2, yaw = 200)
 | 
						|
              |> translate(y = 2)
 | 
						|
            cylinder
 | 
						|
              |> rotate(roll = 1, pitch = 0, yaw = 100)
 | 
						|
            cylinder
 | 
						|
              |> translate(x = 0.1, y = 0, z = 10)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete cylinder using the feature tree', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('cylinder', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-delete').click()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "cube.step" as cube
 | 
						|
            cube
 | 
						|
              |> rotate(roll = 0, pitch = 2, yaw = 200)
 | 
						|
              |> translate(y = 2)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Insert the bracket part into an assembly and transform it`,
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({
 | 
						|
      context,
 | 
						|
      page,
 | 
						|
      homePage,
 | 
						|
      scene,
 | 
						|
      editor,
 | 
						|
      toolbar,
 | 
						|
      cmdBar,
 | 
						|
      tronApp,
 | 
						|
    }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      const midPoint = { x: 500, y: 250 }
 | 
						|
      const moreToTheRightPoint = { x: 900, y: 250 }
 | 
						|
      const bgColor: [number, number, number] = [30, 30, 30]
 | 
						|
      const partColor: [number, number, number] = [100, 100, 100]
 | 
						|
      const tolerance = 30
 | 
						|
      const u = await getUtils(page)
 | 
						|
      const gizmo = page.locator('[aria-label*=gizmo]')
 | 
						|
      const resetCameraButton = page.getByRole('button', { name: 'Reset view' })
 | 
						|
 | 
						|
      await test.step('Setup parts and expect empty assembly scene', async () => {
 | 
						|
        const projectName = 'assembly'
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const bracketDir = path.join(dir, projectName)
 | 
						|
          await fsp.mkdir(bracketDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              path.join('public', 'kcl-samples', 'bracket', 'main.kcl'),
 | 
						|
              path.join(bracketDir, 'bracket.kcl')
 | 
						|
            ),
 | 
						|
            fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert kcl as module', async () => {
 | 
						|
        await insertPartIntoAssembly(
 | 
						|
          'bracket.kcl',
 | 
						|
          'bracket',
 | 
						|
          toolbar,
 | 
						|
          cmdBar,
 | 
						|
          page
 | 
						|
        )
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
            import "bracket.kcl" as bracket
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
 | 
						|
        // Check scene for changes
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await u.doAndWaitForCmd(async () => {
 | 
						|
          await gizmo.click({ button: 'right' })
 | 
						|
          await resetCameraButton.click()
 | 
						|
        }, 'zoom_to_fit')
 | 
						|
        await toolbar.closePane('debug')
 | 
						|
        await scene.expectPixelColor(partColor, midPoint, tolerance)
 | 
						|
        await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set translate on module', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('bracket', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-translate').click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'x',
 | 
						|
          currentArgValue: '0',
 | 
						|
          headerArguments: {
 | 
						|
            X: '',
 | 
						|
            Y: '',
 | 
						|
            Z: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'x',
 | 
						|
          commandName: 'Translate',
 | 
						|
        })
 | 
						|
        await page.keyboard.insertText('100')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('0.1')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('0.2')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            X: '100',
 | 
						|
            Y: '0.1',
 | 
						|
            Z: '0.2',
 | 
						|
          },
 | 
						|
          commandName: 'Translate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          bracket
 | 
						|
            |> translate(x = 100, y = 0.1, z = 0.2)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        // Expect translated part in the scene
 | 
						|
        await scene.expectPixelColor(bgColor, midPoint, tolerance)
 | 
						|
        await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set rotate on module', async () => {
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('bracket', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-rotate').click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'roll',
 | 
						|
          currentArgValue: '0',
 | 
						|
          headerArguments: {
 | 
						|
            Roll: '',
 | 
						|
            Pitch: '',
 | 
						|
            Yaw: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'roll',
 | 
						|
          commandName: 'Rotate',
 | 
						|
        })
 | 
						|
        await page.keyboard.insertText('0.1')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('0.2')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('0.3')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            Roll: '0.1',
 | 
						|
            Pitch: '0.2',
 | 
						|
            Yaw: '0.3',
 | 
						|
          },
 | 
						|
          commandName: 'Rotate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          bracket
 | 
						|
            |> translate(x = 100, y = 0.1, z = 0.2)
 | 
						|
            |> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3)
 | 
						|
          `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        // Expect no change in the scene as the rotations are tiny
 | 
						|
        await scene.expectPixelColor(bgColor, midPoint, tolerance)
 | 
						|
        await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete the part using the feature tree', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('bracket', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-delete').click()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
        // Expect empty editor and scene
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.not.toContain('import')
 | 
						|
        await editor.expectEditor.not.toContain('bracket')
 | 
						|
        await editor.expectEditor.not.toContain('|> translate')
 | 
						|
        await editor.expectEditor.not.toContain('|> rotate')
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColorNotToBe(partColor, midPoint, tolerance)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Insert foreign parts into assembly and delete them`,
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({
 | 
						|
      context,
 | 
						|
      page,
 | 
						|
      homePage,
 | 
						|
      scene,
 | 
						|
      editor,
 | 
						|
      toolbar,
 | 
						|
      cmdBar,
 | 
						|
      tronApp,
 | 
						|
    }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      const midPoint = { x: 500, y: 250 }
 | 
						|
      const partPoint = { x: midPoint.x + 30, y: midPoint.y - 30 } // mid point, just off top right
 | 
						|
      const defaultPlanesColor: [number, number, number] = [180, 220, 180]
 | 
						|
      const partColor: [number, number, number] = [150, 150, 150]
 | 
						|
      const tolerance = 50
 | 
						|
 | 
						|
      const complexPlmFileName = 'cube_Complex-PLM_Name_-001.sldprt'
 | 
						|
      const camelCasedSolidworksFileName = 'cubeComplexPLMName001'
 | 
						|
 | 
						|
      await test.step('Setup parts and expect empty assembly scene', async () => {
 | 
						|
        const projectName = 'assembly'
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const bracketDir = path.join(dir, projectName)
 | 
						|
          await fsp.mkdir(bracketDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              testsInputPath('cube.step'),
 | 
						|
              path.join(bracketDir, 'cube.step')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              testsInputPath('cube.sldprt'),
 | 
						|
              path.join(bracketDir, complexPlmFileName)
 | 
						|
            ),
 | 
						|
            fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(defaultPlanesColor, midPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert step part as module', async () => {
 | 
						|
        await insertPartIntoAssembly('cube.step', 'cube', toolbar, cmdBar, page)
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          import "cube.step" as cube
 | 
						|
        `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
 | 
						|
        await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(partColor, partPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Insert second foreign part by clicking', async () => {
 | 
						|
        await toolbar.openPane('files')
 | 
						|
        await toolbar.expectFileTreeState([
 | 
						|
          complexPlmFileName,
 | 
						|
          'cube.step',
 | 
						|
          'main.kcl',
 | 
						|
        ])
 | 
						|
        await toolbar.openFile(complexPlmFileName)
 | 
						|
 | 
						|
        // Go through the ToastInsert prompt
 | 
						|
        await page.getByText('Insert into my current file').click()
 | 
						|
 | 
						|
        // Check getPathFilenameInVariableCase output
 | 
						|
        const parsedValueFromFile =
 | 
						|
          await cmdBar.currentArgumentInput.inputValue()
 | 
						|
        expect(parsedValueFromFile).toEqual(camelCasedSolidworksFileName)
 | 
						|
 | 
						|
        // Continue on with the flow
 | 
						|
        await page.keyboard.insertText('cubeSw')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: { Path: complexPlmFileName, LocalName: 'cubeSw' },
 | 
						|
          commandName: 'Insert',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await toolbar.closePane('files')
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          import "cube.step" as cube
 | 
						|
          import "${complexPlmFileName}" as cubeSw
 | 
						|
        `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
 | 
						|
        await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(partColor, partPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      // TODO: enable once deleting the first import is fixed
 | 
						|
      // await test.step('Delete first part using the feature tree', async () => {
 | 
						|
      //   page.on('console', console.log)
 | 
						|
      //   await toolbar.openPane('feature-tree')
 | 
						|
      //   const op = await toolbar.getFeatureTreeOperation('cube', 0)
 | 
						|
      //   await op.click({ button: 'right' })
 | 
						|
      //   await page.getByTestId('context-menu-delete').click()
 | 
						|
      //   await scene.settled(cmdBar)
 | 
						|
      //   await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
      //   // Expect only the import statement to be there
 | 
						|
      //   await toolbar.openPane('code')
 | 
						|
      //   await editor.expectEditor.not.toContain(`import "cube.step" as cube`)
 | 
						|
      //   await toolbar.closePane('code')
 | 
						|
      //   await editor.expectEditor.toContain(
 | 
						|
      //     `
 | 
						|
      //     import "${complexPlmFileName}" as cubeSw
 | 
						|
      //   `,
 | 
						|
      //     { shouldNormalise: true }
 | 
						|
      //   )
 | 
						|
      //   await toolbar.closePane('code')
 | 
						|
      // })
 | 
						|
 | 
						|
      await test.step('Delete second part using the feature tree', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('cube_Complex', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-delete').click()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
        // Expect empty editor and scene
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.not.toContain(
 | 
						|
          `import "${complexPlmFileName}" as cubeSw`
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        // TODO: enable once deleting the first import is fixed
 | 
						|
        // await scene.expectPixelColorNotToBe(partColor, midPoint, tolerance)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'Assembly gets reexecuted when imported models are updated externally',
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({ context, page, homePage, scene, toolbar, cmdBar, tronApp }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      const midPoint = { x: 500, y: 250 }
 | 
						|
      const washerPoint = { x: 645, y: 250 }
 | 
						|
      const partColor: [number, number, number] = [120, 120, 120]
 | 
						|
      const redPartColor: [number, number, number] = [200, 0, 0]
 | 
						|
      const bgColor: [number, number, number] = [30, 30, 30]
 | 
						|
      const tolerance = 50
 | 
						|
      const projectName = 'assembly'
 | 
						|
 | 
						|
      await test.step('Setup parts and expect imported model', async () => {
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const projectDir = path.join(dir, projectName)
 | 
						|
          await fsp.mkdir(projectDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              executorInputPath('cube.kcl'),
 | 
						|
              path.join(projectDir, 'cube.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              kclSamplesPath(
 | 
						|
                path.join(
 | 
						|
                  'pipe-flange-assembly',
 | 
						|
                  'mcmaster-parts',
 | 
						|
                  '98017a257-washer.step'
 | 
						|
                )
 | 
						|
              ),
 | 
						|
              path.join(projectDir, 'foreign.step')
 | 
						|
            ),
 | 
						|
            fsp.writeFile(
 | 
						|
              path.join(projectDir, 'main.kcl'),
 | 
						|
              `
 | 
						|
import "cube.kcl" as cube
 | 
						|
import "foreign.step" as foreign
 | 
						|
cube
 | 
						|
foreign
 | 
						|
  |> translate(x = 40, z = 10)`
 | 
						|
            ),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(partColor, midPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Change imported kcl file and expect change', async () => {
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          // Append appearance to the cube.kcl file
 | 
						|
          await fsp.appendFile(
 | 
						|
            path.join(dir, projectName, 'cube.kcl'),
 | 
						|
            `\n  |> appearance(color = "#ff0000")`
 | 
						|
          )
 | 
						|
        })
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        await scene.expectPixelColor(redPartColor, midPoint, tolerance)
 | 
						|
        await scene.expectPixelColor(partColor, washerPoint, tolerance)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Change imported step file and expect change', async () => {
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          // Replace the washer with a pipe
 | 
						|
          await fsp.copyFile(
 | 
						|
            kclSamplesPath(
 | 
						|
              path.join(
 | 
						|
                'pipe-flange-assembly',
 | 
						|
                'mcmaster-parts',
 | 
						|
                '1120t74-pipe.step'
 | 
						|
              )
 | 
						|
            ),
 | 
						|
            path.join(dir, projectName, 'foreign.step')
 | 
						|
          )
 | 
						|
        })
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
        // Expect pipe to take over the red cube but leave some space where the washer was
 | 
						|
        await scene.expectPixelColor(partColor, midPoint, tolerance)
 | 
						|
        await scene.expectPixelColor(bgColor, washerPoint, tolerance)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Point-and-click clone`,
 | 
						|
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
						|
    async ({
 | 
						|
      context,
 | 
						|
      page,
 | 
						|
      homePage,
 | 
						|
      scene,
 | 
						|
      editor,
 | 
						|
      toolbar,
 | 
						|
      cmdBar,
 | 
						|
      tronApp,
 | 
						|
    }) => {
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      const projectName = 'assembly'
 | 
						|
      const midPoint = { x: 500, y: 250 }
 | 
						|
      const [clickMidPoint] = scene.makeMouseHelpers(midPoint.x, midPoint.y)
 | 
						|
 | 
						|
      await test.step('Setup parts and expect imported model', async () => {
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const projectDir = path.join(dir, projectName)
 | 
						|
          await fsp.mkdir(projectDir, { recursive: true })
 | 
						|
          await Promise.all([
 | 
						|
            fsp.copyFile(
 | 
						|
              path.join('public', 'kcl-samples', 'washer', 'main.kcl'),
 | 
						|
              path.join(projectDir, 'washer.kcl')
 | 
						|
            ),
 | 
						|
            fsp.copyFile(
 | 
						|
              path.join(
 | 
						|
                'public',
 | 
						|
                'kcl-samples',
 | 
						|
                'socket-head-cap-screw',
 | 
						|
                'main.kcl'
 | 
						|
              ),
 | 
						|
              path.join(projectDir, 'screw.kcl')
 | 
						|
            ),
 | 
						|
            fsp.writeFile(
 | 
						|
              path.join(projectDir, 'main.kcl'),
 | 
						|
              `
 | 
						|
import "washer.kcl" as washer
 | 
						|
import "screw.kcl" as screw
 | 
						|
screw
 | 
						|
washer
 | 
						|
  |> rotate(roll = 90, pitch = 0, yaw = 0)`
 | 
						|
            ),
 | 
						|
          ])
 | 
						|
        })
 | 
						|
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Try to clone from scene selection and expect error', async () => {
 | 
						|
        await cmdBar.openCmdBar()
 | 
						|
        await cmdBar.chooseCommand('Clone a solid')
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'selection',
 | 
						|
          currentArgValue: '',
 | 
						|
          headerArguments: {
 | 
						|
            Selection: '',
 | 
						|
            VariableName: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'selection',
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await clickMidPoint()
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'variableName',
 | 
						|
          currentArgValue: '',
 | 
						|
          headerArguments: {
 | 
						|
            Selection: '1 path',
 | 
						|
            VariableName: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'variableName',
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            Selection: '1 path',
 | 
						|
            VariableName: 'clone001',
 | 
						|
          },
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await expect(
 | 
						|
          page.getByText(
 | 
						|
            "Couldn't retrieve selection. If you're trying to transform an import, use the feature tree."
 | 
						|
          )
 | 
						|
        ).toBeVisible()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Clone the part using the feature tree', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('washer', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-clone').click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'variableName',
 | 
						|
          currentArgValue: '',
 | 
						|
          headerArguments: {
 | 
						|
            VariableName: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'variableName',
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            VariableName: 'clone001',
 | 
						|
          },
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
        // Expect changes
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
        washer
 | 
						|
          |> rotate(roll = 90, pitch = 0, yaw = 0)
 | 
						|
        clone001 = clone(washer)
 | 
						|
        `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
        await toolbar.closePane('code')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Set translate on clone', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('Clone', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-set-translate').click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'x',
 | 
						|
          currentArgValue: '0',
 | 
						|
          headerArguments: {
 | 
						|
            X: '',
 | 
						|
            Y: '',
 | 
						|
            Z: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'x',
 | 
						|
          commandName: 'Translate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await page.keyboard.insertText('-3')
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            X: '0',
 | 
						|
            Y: '-3',
 | 
						|
            Z: '0',
 | 
						|
          },
 | 
						|
          commandName: 'Translate',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
        // Expect changes
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          screw
 | 
						|
          washer
 | 
						|
            |> rotate(roll = 90, pitch = 0, yaw = 0)
 | 
						|
          clone001 = clone(washer)
 | 
						|
            |> translate(x = 0, y = -3, z = 0)
 | 
						|
        `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Clone the translated clone', async () => {
 | 
						|
        await toolbar.openPane('feature-tree')
 | 
						|
        const op = await toolbar.getFeatureTreeOperation('Clone', 0)
 | 
						|
        await op.click({ button: 'right' })
 | 
						|
        await page.getByTestId('context-menu-clone').click()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'arguments',
 | 
						|
          currentArgKey: 'variableName',
 | 
						|
          currentArgValue: '',
 | 
						|
          headerArguments: {
 | 
						|
            VariableName: '',
 | 
						|
          },
 | 
						|
          highlightedHeaderArg: 'variableName',
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await cmdBar.expectState({
 | 
						|
          stage: 'review',
 | 
						|
          headerArguments: {
 | 
						|
            VariableName: 'clone002',
 | 
						|
          },
 | 
						|
          commandName: 'Clone',
 | 
						|
        })
 | 
						|
        await cmdBar.progressCmdBar()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await toolbar.closePane('feature-tree')
 | 
						|
 | 
						|
        // Expect changes
 | 
						|
        await toolbar.openPane('code')
 | 
						|
        await editor.expectEditor.toContain(
 | 
						|
          `
 | 
						|
          screw
 | 
						|
          washer
 | 
						|
            |> rotate(roll = 90, pitch = 0, yaw = 0)
 | 
						|
          clone001 = clone(washer)
 | 
						|
            |> translate(x = 0, y = -3, z = 0)
 | 
						|
          clone002 = clone(clone001)
 | 
						|
        `,
 | 
						|
          { shouldNormalise: true }
 | 
						|
        )
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 |