Assemblies: UX improvements around foreign file imports (#6159)
* WIP: Add point-and-click Import for geometry Will eventually fix #6120 Right now the whole loop is there but the codemod doesn't work yet * Better pathToNOde, log on non-working cm dispatch call * Add workaround to updateModelingState not working * Back to updateModelingState with a skip flag * Better todo * Change working from Import to Insert, cleanups * Sister command in kclCommands to populate file options * Improve path selector * Unsure: move importAstMod to kclCommands onSubmit 😶 * Add e2e test * Clean up for review * Add native file menu entry and test * No await yo lint said so * WIP: UX improvements around foreign file imports Fixes #6152 * @lrev-Dev's suggestion to remove a comment Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch> * Update to scene.settled(cmdBar) * Add partNNN default name for alias * Lint * Lint * Fix unit tests * Add sad path insert test Thanks @Irev-Dev for the suggestion * Add step insert test * Lint * Add test for second foreign import thru file tree click * Add default value for local name alias * Aligning tests * Fix tests * Add padding for filenames starting with a digit --------- Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit is contained in:
		@ -1,13 +1,47 @@
 | 
			
		||||
import * as fsp from 'fs/promises'
 | 
			
		||||
import path from 'path'
 | 
			
		||||
 | 
			
		||||
import { executorInputPath } from '@e2e/playwright/test-utils'
 | 
			
		||||
import { test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
 | 
			
		||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
 | 
			
		||||
import {
 | 
			
		||||
  executorInputPath,
 | 
			
		||||
  getUtils,
 | 
			
		||||
  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 part into assembly as whole module import`,
 | 
			
		||||
    `Insert kcl parts into assembly as whole module import`,
 | 
			
		||||
    { tag: ['@electron'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
@ -23,11 +57,14 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
        fail()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // One dumb hardcoded screen pixel value
 | 
			
		||||
      const testPoint = { x: 575, y: 200 }
 | 
			
		||||
      const initialColor: [number, number, number] = [50, 50, 50]
 | 
			
		||||
      const partColor: [number, number, number] = [150, 150, 150]
 | 
			
		||||
      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'
 | 
			
		||||
@ -36,41 +73,36 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
          await fsp.mkdir(bracketDir, { recursive: true })
 | 
			
		||||
          await Promise.all([
 | 
			
		||||
            fsp.copyFile(
 | 
			
		||||
              executorInputPath('cylinder-inches.kcl'),
 | 
			
		||||
              executorInputPath('cylinder.kcl'),
 | 
			
		||||
              path.join(bracketDir, 'cylinder.kcl')
 | 
			
		||||
            ),
 | 
			
		||||
            fsp.copyFile(
 | 
			
		||||
              executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
 | 
			
		||||
              path.join(bracketDir, 'bracket.kcl')
 | 
			
		||||
            ),
 | 
			
		||||
            fsp.copyFile(
 | 
			
		||||
              testsInputPath('cube.step'),
 | 
			
		||||
              path.join(bracketDir, 'cube.step')
 | 
			
		||||
            ),
 | 
			
		||||
            fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
 | 
			
		||||
          ])
 | 
			
		||||
        })
 | 
			
		||||
        await page.setBodyDimensions({ width: 1000, height: 500 })
 | 
			
		||||
        await homePage.openProject(projectName)
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
        await scene.expectPixelColor(initialColor, testPoint, tolerance)
 | 
			
		||||
        await toolbar.closePane('code')
 | 
			
		||||
        await scene.expectPixelColor(defaultPlanesColor, midPoint, tolerance)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Insert first part into the assembly', async () => {
 | 
			
		||||
        await toolbar.insertButton.click()
 | 
			
		||||
        await cmdBar.selectOption({ name: 'cylinder.kcl' }).click()
 | 
			
		||||
        await cmdBar.expectState({
 | 
			
		||||
          stage: 'arguments',
 | 
			
		||||
          currentArgKey: 'localName',
 | 
			
		||||
          currentArgValue: '',
 | 
			
		||||
          headerArguments: { Path: 'cylinder.kcl', LocalName: '' },
 | 
			
		||||
          highlightedHeaderArg: 'localName',
 | 
			
		||||
          commandName: 'Insert',
 | 
			
		||||
        })
 | 
			
		||||
        await page.keyboard.insertText('cylinder')
 | 
			
		||||
        await cmdBar.progressCmdBar()
 | 
			
		||||
        await cmdBar.expectState({
 | 
			
		||||
          stage: 'review',
 | 
			
		||||
          headerArguments: { Path: 'cylinder.kcl', LocalName: 'cylinder' },
 | 
			
		||||
          commandName: 'Insert',
 | 
			
		||||
        })
 | 
			
		||||
        await cmdBar.progressCmdBar()
 | 
			
		||||
      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
 | 
			
		||||
@ -78,28 +110,27 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
      `,
 | 
			
		||||
          { shouldNormalise: true }
 | 
			
		||||
        )
 | 
			
		||||
        await scene.expectPixelColor(partColor, testPoint, tolerance)
 | 
			
		||||
        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 second part into the assembly', 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('bracket')
 | 
			
		||||
        await cmdBar.progressCmdBar()
 | 
			
		||||
        await cmdBar.expectState({
 | 
			
		||||
          stage: 'review',
 | 
			
		||||
          headerArguments: { Path: 'bracket.kcl', LocalName: 'bracket' },
 | 
			
		||||
          commandName: 'Insert',
 | 
			
		||||
        })
 | 
			
		||||
        await cmdBar.progressCmdBar()
 | 
			
		||||
      await test.step('Insert kcl second part as module', async () => {
 | 
			
		||||
        await insertPartIntoAssembly(
 | 
			
		||||
          'bracket.kcl',
 | 
			
		||||
          'bracket',
 | 
			
		||||
          toolbar,
 | 
			
		||||
          cmdBar,
 | 
			
		||||
          page
 | 
			
		||||
        )
 | 
			
		||||
        await editor.expectEditor.toContain(
 | 
			
		||||
          `
 | 
			
		||||
        import "cylinder.kcl" as cylinder
 | 
			
		||||
@ -109,6 +140,152 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
      `,
 | 
			
		||||
          { shouldNormalise: true }
 | 
			
		||||
        )
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Insert a second time and expect error', async () => {
 | 
			
		||||
        // TODO: revisit once we have clone with #6209
 | 
			
		||||
        await insertPartIntoAssembly(
 | 
			
		||||
          'bracket.kcl',
 | 
			
		||||
          'bracket',
 | 
			
		||||
          toolbar,
 | 
			
		||||
          cmdBar,
 | 
			
		||||
          page
 | 
			
		||||
        )
 | 
			
		||||
        await editor.expectEditor.toContain(
 | 
			
		||||
          `
 | 
			
		||||
        import "cylinder.kcl" as cylinder
 | 
			
		||||
        import "bracket.kcl" as bracket
 | 
			
		||||
        import "bracket.kcl" as bracket
 | 
			
		||||
        cylinder
 | 
			
		||||
        bracket
 | 
			
		||||
        bracket
 | 
			
		||||
      `,
 | 
			
		||||
          { shouldNormalise: true }
 | 
			
		||||
        )
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
        await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Insert foreign parts into assembly as whole module import`,
 | 
			
		||||
    { tag: ['@electron'] },
 | 
			
		||||
    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
 | 
			
		||||
        cube
 | 
			
		||||
      `,
 | 
			
		||||
          { shouldNormalise: true }
 | 
			
		||||
        )
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
        // TODO: remove this once #5780 is fixed
 | 
			
		||||
        await page.reload()
 | 
			
		||||
 | 
			
		||||
        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 step 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
 | 
			
		||||
        cube
 | 
			
		||||
        cubeSw
 | 
			
		||||
      `,
 | 
			
		||||
          { shouldNormalise: true }
 | 
			
		||||
        )
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
        // TODO: remove this once #5780 is fixed
 | 
			
		||||
        await page.reload()
 | 
			
		||||
        await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
        await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
 | 
			
		||||
        await toolbar.closePane('code')
 | 
			
		||||
        await scene.expectPixelColor(partColor, partPoint, tolerance)
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user