Compare commits

...

32 Commits

Author SHA1 Message Date
ca1fd14308 Merge branch 'main' into pierremtb/nadro-ubuntu-e2e-local-fixes 2025-02-07 09:36:46 -05:00
b2fea24fc4 fix: trying to resolve more flakes when running with more workers 2025-02-06 20:33:43 -06:00
1829c1047a fix: last wait fix 2025-02-06 15:52:52 -06:00
d5c7a1cef3 fix: Fixed testing selections with wait 2025-02-06 15:49:09 -06:00
23ea59cf58 Merge branch 'nadro/adhoc/ubuntu-e2e-local-fixes' of github.com:KittyCAD/modeling-app into nadro/adhoc/ubuntu-e2e-local-fixes 2025-02-06 15:44:56 -06:00
3d6fb4e6e6 fix: bad prompt fix 2025-02-06 15:44:06 -06:00
157ee93032 A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:46:06 +00:00
6c4c59ced9 A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:38:33 +00:00
925cc892a7 A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:30:09 +00:00
08ae896b65 A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:23:17 +00:00
2073df8a3f A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:15:22 +00:00
743732ba15 A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) 2025-02-06 20:07:54 +00:00
b8525ccd61 fix: auto fixes 2025-02-06 14:00:42 -06:00
0188d6c065 fix: resolved main conflicts 2025-02-06 13:58:49 -06:00
ddc7d5b20a fix: restoring name 2025-02-06 13:01:40 -06:00
81c2be78bb fix: not waiting for scene, not waiting for command bar 2025-02-06 13:01:03 -06:00
0f85e85c57 fix: wait for execution done 2025-02-06 12:51:53 -06:00
55a302bd75 fix: wait for execution done 2025-02-06 11:36:48 -06:00
21e710ee41 fix: updating wait for execution 2025-02-06 11:32:10 -06:00
bb51c8d269 fix: adding a wait for execution 2025-02-06 11:27:26 -06:00
d9d970b436 fix: add 1250ms before every progresscmdbar, removed model loaded scene state that is flaky, added toast if someone presses the command bar too quickly 2025-02-06 09:52:09 -06:00
1e9a2e3a1a fix: only fixes the chamfer point and click delete 2025-02-06 09:01:59 -06:00
e9ca2469c5 fix: adding execution done wait 2025-02-06 08:50:48 -06:00
1248623922 fix: adding wait for execution done 2025-02-06 08:47:21 -06:00
49ba5d31c7 fix: removing testing code 2025-02-06 08:44:55 -06:00
af7d0e6b66 fix: added wait for execution done 2025-02-06 08:43:57 -06:00
004898e0c7 fix: saving off debugging... 2025-02-05 15:33:33 -06:00
e7457dac0d fix: adding comment to clarify the gotcha 2025-02-05 12:43:25 -06:00
a8d76950d0 fix: fixing react race condition on parsing numeric literals in command bar on open 2025-02-05 12:34:08 -06:00
2ac836c0b8 fix: bug that desyncs codeManager, executeCode, lspPlugin 2025-02-05 11:07:27 -06:00
25c73aae6c fix: again another wait for execution does not work 2025-02-04 15:05:30 -06:00
65682e755a fix: Needed to increase timeout for this test. waitForExecutionDone does not work since there are errors in the KCL code 2025-02-04 14:47:36 -06:00
16 changed files with 138 additions and 55 deletions

View File

@ -19,6 +19,8 @@ test.describe(
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// FIXME: Cannot use scene.waitForExecutionDone() since there is no KCL code
await page.waitForTimeout(10000)
await u.openDebugPanel()
const coord =

View File

@ -10,6 +10,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
test('Typing KCL errors induces a badge on the code pane button', async ({
page,
homePage,
scene,
}) => {
const u = await getUtils(page)
@ -30,11 +31,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await scene.waitForExecutionDone()
// Ensure no badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
@ -175,7 +172,9 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await page.waitForTimeout(1000)
// FIXME: await scene.waitForExecutionDone() does not work. It still fails.
// I needed to increase this timeout to get this to pass.
await page.waitForTimeout(10000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
@ -187,7 +186,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
// click in the editor to focus it
await page.locator('.cm-content').click()
await page.waitForTimeout(500)
await page.waitForTimeout(2000)
// go to the start of the editor and enter more text which will trigger
// a lint error.
@ -204,8 +203,9 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
await page.keyboard.press('ArrowUp')
await page.keyboard.press('Home')
await page.keyboard.type('foo_bar = 1')
await page.waitForTimeout(500)
await page.waitForTimeout(2000)
await page.keyboard.press('Enter')
await page.waitForTimeout(2000)
// ensure we have a lint error
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()

View File

@ -174,6 +174,9 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// FIXME: No KCL code, unable to wait for engine execution
await page.waitForTimeout(10000)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()

View File

@ -10,7 +10,7 @@ import fsp from 'fs/promises'
test(
'export works on the first try',
{ tag: '@electron' },
async ({ page, context }, testInfo) => {
async ({ page, context, scene }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
@ -118,8 +118,9 @@ test(
// Close the file pane
await u.closeFilePanel()
// wait for it to finish executing (todo: make this more robust)
await page.waitForTimeout(1000)
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done
await page.waitForTimeout(10000)
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()

View File

@ -490,6 +490,11 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
await page.keyboard.press('ArrowLeft')
await page.keyboard.press('ArrowRight')
// FIXME: lsp errors do not propagate to the frontend until engine is connected and code is executed
// This timeout is to wait for engine connection. LSP and code execution errors should be handled differently
// LSP can emit errors as fast as it waits and show them in the editor
await page.waitForTimeout(10000)
// error in guter
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()

View File

@ -112,6 +112,9 @@ export class CmdBarFixture {
* and assumes we are past the `pickCommand` step.
*/
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
// FIXME: Progressing the command bar is a race condition. We have an async useEffect that reports the final state via useCalculateKclExpression. If this does not run quickly enough, it will not "fail" the continue because you can press continue if the state is not ready. E2E tests do not know this.
// Wait 1250ms to assume the await executeAst of the KCL input field is finished
await this.page.waitForTimeout(1250)
if (shouldFuzzProgressMethod || Math.random() > 0.5) {
const arrowButton = this.page.getByRole('button', {
name: 'arrow right Continue',
@ -128,6 +131,23 @@ export class CmdBarFixture {
}
}
// Added data-testid to the command bar buttons
// command-bar-continue are the buttons to go to the next step
// does not include the submit which is the final button press
// aka the right arrow button
continue = async () => {
const continueButton = this.page.getByTestId('command-bar-continue')
await continueButton.click()
}
// Added data-testid to the commad bar buttons
// command-bar-submit is the button for the final step to submit
// the command bar flow aka the checkmark button.
submit = async () => {
const submitButton = this.page.getByTestId('command-bar-submit')
await submitButton.click()
}
openCmdBar = async (selectCmd?: 'promptToEdit') => {
// TODO why does this button not work in electron tests?
// await this.cmdBarOpenBtn.click()

View File

@ -29,11 +29,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
await scene.clickNoWhere()
// FIXME: Do not click, clicking removes the activeLines in future checks
// await scene.clickNoWhere()
await expect(toolbar.extrudeButton).toBeEnabled()
})
@ -199,6 +201,7 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -422,6 +425,7 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -727,6 +731,9 @@ openSketch = startSketchOn('XY')
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
await homePage.goToModelingScene()
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue.
// The engine may not be connected
await page.waitForTimeout(15000)
await test.step(`Look for the blue of the XZ plane`, async () => {
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
@ -887,6 +894,7 @@ loft001 = loft([sketch001, sketch002])
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
@ -1529,16 +1537,7 @@ extrude001 = extrude(sketch001, length = -12)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// verify modeling scene is loaded
await scene.expectPixelColor(
backgroundColor,
secondEdgeLocation,
lowTolerance
)
// wait for stream to load
await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance)
await scene.waitForExecutionDone()
})
// Test 1: Command bar flow with preselected edges
@ -1763,6 +1762,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// verify modeling scene is loaded
await scene.expectPixelColor(
@ -1885,6 +1885,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
@ -1983,6 +1984,7 @@ extrude001 = extrude(sketch001, length = 40)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 }

View File

@ -60,6 +60,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
const body1CapCoords = { x: 571, y: 351 }
const greenCheckCoords = { x: 565, y: 345 }
@ -76,7 +77,9 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
'Submitting to Text-to-CAD API...'
)
const successToast = page.getByText('Prompt to edit successful')
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
const acceptBtn = page.getByRole('button', {
name: 'checkmark Accept',
})
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
await test.step('wait for scene to load select body and check selection came through', async () => {
@ -99,7 +102,9 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
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(submittingToast).not.toBeVisible({
timeout: 2 * 60_000,
}) // can take a while
await expect(successToast).toBeVisible()
})
@ -150,6 +155,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
const body1CapCoords = { x: 571, y: 351 }
const [clickBody1Cap] = scene.makeMouseHelpers(

View File

@ -9,6 +9,7 @@ import {
PERSIST_MODELING_CONTEXT,
} from './test-utils'
import { uuidv4, roundOff } from 'lib/utils'
import { SceneFixture } from './fixtures/sceneFixture'
test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
@ -184,7 +185,8 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
const doEditSegmentsByDraggingHandle = async (
page: Page,
homePage: HomePageFixture,
openPanes: string[]
openPanes: string[],
scene: SceneFixture
) => {
// Load the app with the code panes
await page.addInitScript(async () => {
@ -200,6 +202,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
const u = await getUtils(page)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
@ -317,7 +320,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
test(
'code pane open at start-handles',
{ tag: ['@skipWin'] },
async ({ page, homePage }) => {
async ({ page, homePage, scene }) => {
// Load the app with the code panes
await page.addInitScript(async () => {
localStorage.setItem(
@ -330,14 +333,14 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
})
)
})
await doEditSegmentsByDraggingHandle(page, homePage, ['code'])
await doEditSegmentsByDraggingHandle(page, homePage, ['code'], scene)
}
)
test(
'code pane closed at start-handles',
{ tag: ['@skipWin'] },
async ({ page, homePage }) => {
async ({ page, homePage, scene }) => {
// Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem(
@ -345,7 +348,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
JSON.stringify({ openPanes: [] })
)
}, PERSIST_MODELING_CONTEXT)
await doEditSegmentsByDraggingHandle(page, homePage, [])
await doEditSegmentsByDraggingHandle(page, homePage, [], scene)
}
)
})
@ -547,6 +550,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
test('Can edit a sketch that has been revolved in the same pipe', async ({
page,
homePage,
scene,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
@ -562,6 +566,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await expect(
page.getByRole('button', { name: 'Start Sketch' })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -248,7 +248,11 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
})
})
test('Solids should be select and deletable', async ({ page, homePage }) => {
test('Solids should be select and deletable', async ({
page,
homePage,
scene,
}) => {
test.setTimeout(90_000)
const u = await getUtils(page)
await page.addInitScript(async () => {
@ -320,10 +324,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await scene.waitForExecutionDone()
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
@ -902,6 +903,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
page,
homePage,
scene,
}) => {
const cases = [
{
@ -937,6 +939,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
@ -970,6 +973,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
page,
homePage,
scene,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
@ -988,6 +992,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
@ -1021,19 +1026,19 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
.poll(() => u.getGreatestPixDiff(extrudeWall, noHoverColor))
.toBeLessThan(15)
await page.mouse.move(nothing.x, nothing.y)
await page.waitForTimeout(100)
await page.waitForTimeout(1000)
await page.mouse.move(extrudeWall.x, extrudeWall.y)
await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
await expect(page.getByTestId('hover-highlight').first()).toContainText(
removeAfterFirstParenthesis(extrudeText)
)
await page.waitForTimeout(200)
await page.waitForTimeout(1000)
await expect(
await u.getGreatestPixDiff(extrudeWall, hoverColor)
).toBeLessThan(15)
await page.mouse.click(extrudeWall.x, extrudeWall.y)
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${extrudeText}`)
await page.waitForTimeout(200)
await page.waitForTimeout(1000)
await expect(
await u.getGreatestPixDiff(extrudeWall, selectColor)
).toBeLessThan(15)
@ -1044,7 +1049,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
).toBeLessThan(15)
await page.mouse.move(nothing.x, nothing.y)
await page.waitForTimeout(300)
await page.waitForTimeout(1000)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
// because of shading, color is not exact everywhere on the face
@ -1058,11 +1063,11 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
await expect(page.getByTestId('hover-highlight').first()).toContainText(
removeAfterFirstParenthesis(capText)
)
await page.waitForTimeout(200)
await page.waitForTimeout(1000)
await expect(await u.getGreatestPixDiff(cap, hoverColor)).toBeLessThan(15)
await page.mouse.click(cap.x, cap.y)
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${capText}`)
await page.waitForTimeout(200)
await page.waitForTimeout(1000)
await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15)
await page.waitForTimeout(1000)
// check color stays there, i.e. not overridden (this was a bug previously)

View File

@ -444,7 +444,7 @@ test('Delete key does not navigate back', async ({ page, homePage }) => {
await expect.poll(() => page.url()).not.toContain('/settings')
})
test('Sketch on face', async ({ page, homePage }) => {
test('Sketch on face', async ({ page, homePage, scene, cmdBar }) => {
test.setTimeout(90_000)
const u = await getUtils(page)
await page.addInitScript(async () => {
@ -470,11 +470,7 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await scene.waitForExecutionDone()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
@ -579,10 +575,9 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
await expect(page.getByTestId('command-bar')).toBeVisible()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'arrow right Continue' }).click()
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
await expect(page.getByText('Confirm Extrude')).toBeVisible()
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
await cmdBar.progressCmdBar()
const result2 = result.genNext`
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`

View File

@ -196,6 +196,7 @@ function ReviewingButton() {
type="submit"
form="review-form"
className="w-fit !p-0 rounded-sm hover:shadow"
data-testid="command-bar-submit"
iconStart={{
icon: 'checkmark',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
@ -214,6 +215,7 @@ function GatheringArgsButton() {
type="submit"
form="arg-form"
className="w-fit !p-0 rounded-sm hover:shadow"
data-testid="command-bar-continue"
iconStart={{
icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',

View File

@ -20,6 +20,7 @@ import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
import { useSelector } from '@xstate/react'
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
import toast from 'react-hot-toast'
const machineContextSelector = (snapshot?: {
context: Record<string, unknown>
@ -97,6 +98,7 @@ function CommandBarKclInput({
value,
initialVariableName,
})
const varMentionData: Completion[] = prevVariables.map((v) => ({
label: v.key,
detail: String(roundOff(v.value as number)),
@ -170,7 +172,15 @@ function CommandBarKclInput({
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
e?.preventDefault()
if (!canSubmit || valueNode === null) return
if (!canSubmit || valueNode === null) {
// Gotcha: Our application can attempt to submit a command value before the command bar kcl input is ready. Notify the scene and user.
if (!canSubmit) {
toast.error('Unable to submit command')
} else if (valueNode === null) {
toast.error('Unable to submit undefined command value')
}
return
}
onSubmit(
createNewVariable

View File

@ -25,6 +25,18 @@ export class KclPlugin implements PluginValue {
constructor(client: LanguageServerClient) {
this.client = client
// Gotcha: Code can be written into the CodeMirror editor but not propagated to codeManager.code
// because the update function has not run. We need to initialize the codeManager.code when lsp initializes
// because new code could have been written into the editor before the update callback is initialized.
// There appears to be limited ways to safely get the current doc content. This appears to be sync and safe.
const kclLspPlugin = this.client.plugins.find((plugin) => {
return plugin.client.name === 'kcl'
})
if (kclLspPlugin) {
// @ts-ignore Ignoring this private dereference of .view on the plugin. I do not have another helper method that can give me doc string
codeManager.code = kclLspPlugin.view.state.doc.toString()
}
}
// When a doc update needs to be sent to the server, this holds the

View File

@ -9,6 +9,8 @@ import {
getCalculatedKclExpressionValue,
programMemoryFromVariables,
} from './kclHelpers'
import { parse, resultIsOk } from 'lang/wasm'
import { err } from 'lib/trap'
const isValidVariableName = (name: string) =>
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
@ -50,7 +52,20 @@ export function useCalculateKclExpression({
bodyPath: [],
})
const [valueNode, setValueNode] = useState<Expr | null>(null)
const [calcResult, setCalcResult] = useState('NAN')
// Gotcha: If we do not attempt to parse numeric literals instantly it means that there is an async action to verify
// the value is good. This means all E2E tests have a race condition on when they can hit "next" in the commad bar.
// Most scenarios automatically pass a numeric literal. We can try to parse that first, otherwise make it go through the slow
// async method.
// If we pass in numeric literals, we should instantly parse them, they have nothing to do with application memory
const _code_value = `const __result__ = ${value}`
const codeValueParseResult = parse(_code_value)
let isValueParsable = true
if (err(codeValueParseResult) || !resultIsOk(codeValueParseResult)) {
isValueParsable = false
}
const initialCalcResult: number | string =
Number.isNaN(Number(value)) || !isValueParsable ? 'NAN' : value
const [calcResult, setCalcResult] = useState(initialCalcResult)
const [newVariableName, setNewVariableName] = useState('')
const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true)