* Make "skip = false" non-required args appear in header * Make non-required, unskippable selection args work * Make prompt-to-edit's selection arg optional but non-skippable * Update src/components/CommandBar/CommandBarSelectionInput.tsx Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Fix dumb logic bug Thanks for user testing @Irev-dev * Update mixed input to show selection Feel free to revert @Irev-Dev if this is the wrong move, but I found it odd that this component doesn't show the current selection in the text like the other selection input does, so I copied that over. * Merge branch 'main' into franknoirot/adhoc/optional-selection-args * Merge branch 'main' into franknoirot/adhoc/optional-selection-args * Merge remote-tracking branch 'origin' into franknoirot/adhoc/optional-selection-args * fix tests * change copy again * Update src/components/CommandBar/CommandBarSelectionMixedInput.tsx Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { expect, test } from '@e2e/playwright/zoo-test'
 | 
						|
import * as fsp from 'fs/promises'
 | 
						|
import * as path from 'path'
 | 
						|
 | 
						|
/* eslint-disable jest/no-conditional-expect */
 | 
						|
 | 
						|
const file = `sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [57.81, 250.51])
 | 
						|
  |> line(end = [121.13, 56.63], tag = $seg02)
 | 
						|
  |> line(end = [83.37, -34.61], tag = $seg01)
 | 
						|
  |> line(end = [19.66, -116.4])
 | 
						|
  |> line(end = [-221.8, -41.69])
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 200)
 | 
						|
sketch002 = startSketchOn(XZ)
 | 
						|
  |> startProfile(at = [-114, 85.52])
 | 
						|
  |> xLine(length = 265.36)
 | 
						|
  |> line(end = [33.17, -261.22])
 | 
						|
  |> xLine(length = -297.25)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude002 = extrude(sketch002, length = 50)
 | 
						|
sketch003 = startSketchOn(XY)
 | 
						|
  |> startProfile(at = [52.92, 157.81])
 | 
						|
  |> angledLine(angle = 0, length = 176.4, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 53.4, tag = $rectangleSegmentB001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude003 = extrude(sketch003, length = 20)
 | 
						|
`
 | 
						|
 | 
						|
test.describe('Prompt-to-edit tests', () => {
 | 
						|
  test.describe('Check the happy path, for basic changing color', () => {
 | 
						|
    const cases = [
 | 
						|
      {
 | 
						|
        desc: 'User accepts change',
 | 
						|
        shouldReject: false,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        desc: 'User rejects change',
 | 
						|
        shouldReject: true,
 | 
						|
      },
 | 
						|
    ] as const
 | 
						|
    for (const { desc, shouldReject } of cases) {
 | 
						|
      test(`${desc}`, async ({
 | 
						|
        context,
 | 
						|
        homePage,
 | 
						|
        cmdBar,
 | 
						|
        editor,
 | 
						|
        page,
 | 
						|
        scene,
 | 
						|
      }) => {
 | 
						|
        await context.folderSetupFn(async (dir) => {
 | 
						|
          const projectDir = path.join(dir, 'test-project')
 | 
						|
          await fsp.mkdir(projectDir, { recursive: true })
 | 
						|
          await fsp.writeFile(path.join(projectDir, 'main.kcl'), file)
 | 
						|
        })
 | 
						|
        await homePage.openProject('test-project')
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
 | 
						|
        const body1CapCoords = { x: 571, y: 311 }
 | 
						|
        const [clickBody1Cap] = scene.makeMouseHelpers(
 | 
						|
          body1CapCoords.x,
 | 
						|
          body1CapCoords.y
 | 
						|
        )
 | 
						|
        const yellow: [number, number, number] = [179, 179, 131]
 | 
						|
        const submittingToast = page.getByText(
 | 
						|
          'Submitting to Text-to-CAD API...'
 | 
						|
        )
 | 
						|
        const successToast = page.getByText('Prompt to edit successful')
 | 
						|
        const acceptBtn = page.getByRole('button', {
 | 
						|
          name: 'checkmark Continue',
 | 
						|
        })
 | 
						|
        const rejectBtn = page.getByRole('button', { name: 'close Revert' })
 | 
						|
 | 
						|
        await test.step('wait for scene to load select body and check selection came through', async () => {
 | 
						|
          await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
 | 
						|
          await clickBody1Cap()
 | 
						|
          await scene.expectPixelColor(yellow, body1CapCoords, 20)
 | 
						|
          await editor.expectState({
 | 
						|
            highlightedCode: '',
 | 
						|
            activeLines: ['|>startProfile(at=[-114,85.52])'],
 | 
						|
            diagnostics: [],
 | 
						|
          })
 | 
						|
        })
 | 
						|
 | 
						|
        await test.step('fire off edit prompt', async () => {
 | 
						|
          await cmdBar.openCmdBar('promptToEdit')
 | 
						|
          await page.waitForTimeout(100)
 | 
						|
          await cmdBar.progressCmdBar()
 | 
						|
          // being specific about the color with a hex means asserting pixel color is more stable
 | 
						|
          await page
 | 
						|
            .getByTestId('cmd-bar-arg-value')
 | 
						|
            .fill('make this neon green please, use #39FF14')
 | 
						|
          await page.waitForTimeout(100)
 | 
						|
          await cmdBar.progressCmdBar()
 | 
						|
          await expect(submittingToast).toBeVisible()
 | 
						|
          await expect(submittingToast).not.toBeVisible({
 | 
						|
            timeout: 2 * 60_000,
 | 
						|
          }) // can take a while
 | 
						|
          await expect(successToast).toBeVisible()
 | 
						|
        })
 | 
						|
 | 
						|
        await test.step('verify initial change', async () => {
 | 
						|
          await editor.expectEditor.toContain('appearance(')
 | 
						|
        })
 | 
						|
 | 
						|
        if (!shouldReject) {
 | 
						|
          await test.step('check accept works', async () => {
 | 
						|
            await acceptBtn.click()
 | 
						|
            await expect(successToast).not.toBeVisible()
 | 
						|
 | 
						|
            await editor.expectEditor.toContain('appearance(')
 | 
						|
          })
 | 
						|
        } else {
 | 
						|
          await test.step('check reject works', async () => {
 | 
						|
            await rejectBtn.click()
 | 
						|
            await expect(successToast).not.toBeVisible()
 | 
						|
 | 
						|
            await editor.expectEditor.not.toContain('appearance(')
 | 
						|
          })
 | 
						|
        }
 | 
						|
      })
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  test('bad edit prompt', async ({
 | 
						|
    context,
 | 
						|
    homePage,
 | 
						|
    cmdBar,
 | 
						|
    editor,
 | 
						|
    toolbar,
 | 
						|
    page,
 | 
						|
    scene,
 | 
						|
  }) => {
 | 
						|
    await context.addInitScript((file) => {
 | 
						|
      localStorage.setItem('persistCode', file)
 | 
						|
    }, file)
 | 
						|
    await homePage.goToModelingScene()
 | 
						|
    await scene.settled(cmdBar)
 | 
						|
 | 
						|
    const body1CapCoords = { x: 571, y: 311 }
 | 
						|
    const [clickBody1Cap] = scene.makeMouseHelpers(
 | 
						|
      body1CapCoords.x,
 | 
						|
      body1CapCoords.y
 | 
						|
    )
 | 
						|
    const yellow: [number, number, number] = [179, 179, 131]
 | 
						|
    const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
 | 
						|
    const failToast = page.getByText(
 | 
						|
      'Failed to edit your KCL code, please try again with a different prompt or selection'
 | 
						|
    )
 | 
						|
 | 
						|
    await test.step('wait for scene to load and select body', async () => {
 | 
						|
      await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
 | 
						|
 | 
						|
      await clickBody1Cap()
 | 
						|
      await scene.expectPixelColor(yellow, body1CapCoords, 20)
 | 
						|
 | 
						|
      await editor.expectState({
 | 
						|
        highlightedCode: '',
 | 
						|
        activeLines: ['|>startProfile(at=[-114,85.52])'],
 | 
						|
        diagnostics: [],
 | 
						|
      })
 | 
						|
    })
 | 
						|
 | 
						|
    await test.step('fire of bad prompt', async () => {
 | 
						|
      await cmdBar.openCmdBar('promptToEdit')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await cmdBar.progressCmdBar()
 | 
						|
      await page
 | 
						|
        .getByTestId('cmd-bar-arg-value')
 | 
						|
        .fill('ansheusha asnthuatshoeuhtaoetuhthaeu laughs in dvorak')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await cmdBar.progressCmdBar()
 | 
						|
      await expect(submittingToast).toBeVisible()
 | 
						|
    })
 | 
						|
    await test.step('check fail toast appeared', async () => {
 | 
						|
      await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
 | 
						|
      await expect(failToast).toBeVisible()
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  test(`manual code selection rename`, async ({
 | 
						|
    context,
 | 
						|
    homePage,
 | 
						|
    cmdBar,
 | 
						|
    editor,
 | 
						|
    page,
 | 
						|
    scene,
 | 
						|
  }) => {
 | 
						|
    const body1CapCoords = { x: 571, y: 311 }
 | 
						|
 | 
						|
    await context.addInitScript((file) => {
 | 
						|
      localStorage.setItem('persistCode', file)
 | 
						|
    }, file)
 | 
						|
    await homePage.goToModelingScene()
 | 
						|
    await scene.settled(cmdBar)
 | 
						|
 | 
						|
    const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
 | 
						|
    const successToast = page.getByText('Prompt to edit successful')
 | 
						|
    const acceptBtn = page.getByRole('button', { name: 'checkmark Continue' })
 | 
						|
 | 
						|
    await test.step('wait for scene to load and select code in editor', async () => {
 | 
						|
      // Find and select the text "sketch002" in the editor
 | 
						|
      await editor.selectText('sketch002')
 | 
						|
 | 
						|
      // Verify the selection was made
 | 
						|
      await editor.expectState({
 | 
						|
        highlightedCode: '',
 | 
						|
        activeLines: ['sketch002 = startSketchOn(XZ)'],
 | 
						|
        diagnostics: [],
 | 
						|
      })
 | 
						|
    })
 | 
						|
 | 
						|
    await test.step('fire off edit prompt', async () => {
 | 
						|
      await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
 | 
						|
      await cmdBar.openCmdBar('promptToEdit')
 | 
						|
      await page
 | 
						|
        .getByTestId('cmd-bar-arg-value')
 | 
						|
        .fill('Please rename to mySketch001')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await cmdBar.progressCmdBar()
 | 
						|
      await expect(submittingToast).toBeVisible()
 | 
						|
      await expect(submittingToast).not.toBeVisible({
 | 
						|
        timeout: 2 * 60_000,
 | 
						|
      })
 | 
						|
      await expect(successToast).toBeVisible()
 | 
						|
    })
 | 
						|
 | 
						|
    await test.step('verify rename change and accept it', async () => {
 | 
						|
      await editor.expectEditor.toContain('mySketch001 = startSketchOn')
 | 
						|
      await editor.expectEditor.not.toContain('sketch002 = startSketchOn')
 | 
						|
      await editor.expectEditor.toContain(
 | 
						|
        'extrude002 = extrude(mySketch001, length = 50)'
 | 
						|
      )
 | 
						|
 | 
						|
      await acceptBtn.click()
 | 
						|
      await expect(successToast).not.toBeVisible()
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  test('multiple body selections', async ({
 | 
						|
    context,
 | 
						|
    homePage,
 | 
						|
    cmdBar,
 | 
						|
    editor,
 | 
						|
    page,
 | 
						|
    scene,
 | 
						|
  }) => {
 | 
						|
    const body1CapCoords = { x: 571, y: 311 }
 | 
						|
    const body2WallCoords = { x: 620, y: 152 }
 | 
						|
    const [clickBody1Cap] = scene.makeMouseHelpers(
 | 
						|
      body1CapCoords.x,
 | 
						|
      body1CapCoords.y
 | 
						|
    )
 | 
						|
    const [clickBody2Cap] = scene.makeMouseHelpers(
 | 
						|
      body2WallCoords.x,
 | 
						|
      body2WallCoords.y
 | 
						|
    )
 | 
						|
    const grey: [number, number, number] = [132, 132, 132]
 | 
						|
 | 
						|
    await context.addInitScript((file) => {
 | 
						|
      localStorage.setItem('persistCode', file)
 | 
						|
    }, file)
 | 
						|
    await homePage.goToModelingScene()
 | 
						|
    await scene.settled(cmdBar)
 | 
						|
 | 
						|
    const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
 | 
						|
    const successToast = page.getByText('Prompt to edit successful')
 | 
						|
    const acceptBtn = page.getByRole('button', { name: 'checkmark Continue' })
 | 
						|
 | 
						|
    await test.step('select multiple bodies and fire prompt', async () => {
 | 
						|
      // Initial color check
 | 
						|
      await scene.expectPixelColor(grey, body1CapCoords, 15)
 | 
						|
 | 
						|
      // Open command bar first (without selection)
 | 
						|
      await cmdBar.openCmdBar('promptToEdit')
 | 
						|
 | 
						|
      // Select first body
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await clickBody1Cap()
 | 
						|
 | 
						|
      // Hold shift and select second body
 | 
						|
      await editor.expectState({
 | 
						|
        highlightedCode: '',
 | 
						|
        activeLines: ['|>startProfile(at=[-114,85.52])'],
 | 
						|
        diagnostics: [],
 | 
						|
      })
 | 
						|
      await page.keyboard.down('Shift')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await clickBody2Cap()
 | 
						|
      await editor.expectState({
 | 
						|
        highlightedCode:
 | 
						|
          'line(end=[121.13,56.63],tag=$seg02)extrude(profile001,length=200)',
 | 
						|
        activeLines: [
 | 
						|
          '|>line(end=[121.13,56.63],tag=$seg02)',
 | 
						|
          '|>startProfile(at=[-114,85.52])',
 | 
						|
        ],
 | 
						|
        diagnostics: [],
 | 
						|
      })
 | 
						|
      await page.keyboard.up('Shift')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await cmdBar.progressCmdBar()
 | 
						|
 | 
						|
      // Enter prompt and submit
 | 
						|
      await page
 | 
						|
        .getByTestId('cmd-bar-arg-value')
 | 
						|
        .fill('make these neon green please, use #39FF14')
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await cmdBar.progressCmdBar()
 | 
						|
 | 
						|
      // Wait for API response
 | 
						|
      await expect(submittingToast).toBeVisible()
 | 
						|
      await expect(submittingToast).not.toBeVisible({
 | 
						|
        timeout: 2 * 60_000,
 | 
						|
      })
 | 
						|
      await expect(successToast).toBeVisible()
 | 
						|
    })
 | 
						|
 | 
						|
    await test.step('verify code changed', async () => {
 | 
						|
      await editor.expectEditor.toContain('appearance(')
 | 
						|
 | 
						|
      // Accept changes
 | 
						|
      await acceptBtn.click()
 | 
						|
      await expect(successToast).not.toBeVisible()
 | 
						|
    })
 | 
						|
  })
 | 
						|
})
 |