Files
modeling-app/e2e/playwright/prompt-to-edit.spec.ts
Frank Noirot ebf048478d Make Modify with Text-to-CAD selection arg optional by honoring skip: false on non-required args (#6992)
* 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>
2025-05-16 05:15:52 -04:00

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()
})
})
})