Add engine message to dry run validation error toasts (#5175)

* Add engine message to dry run validation error toasts
Fixes #5174

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Add unit tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Reset snapshots

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Revert snapshot changes

* Fix lint

* Fix test

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Pierre Jacquier
2025-01-31 16:13:35 -05:00
committed by GitHub
parent 2f72a8ef14
commit 2fac213c58
4 changed files with 59 additions and 23 deletions

View File

@ -1078,7 +1078,7 @@ sketch002 = startSketchOn('XZ')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await expect( await expect(
page.getByText('Unable to sweep with the provided selection') page.getByText('Unable to sweep with the current selection. Reason:')
).toBeVisible() ).toBeVisible()
}) })
}) })
@ -1846,7 +1846,7 @@ sweep001 = sweep({ path = sketch002 }, sketch001)
await page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await expect( await expect(
page.getByText('Unable to shell with the provided selection') page.getByText('Unable to shell with the current selection. Reason:')
).toBeVisible() ).toBeVisible()
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
}) })

View File

@ -1999,7 +1999,7 @@ export class EngineCommandManager extends EventTarget {
.catch((e) => { .catch((e) => {
// TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point. // TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point.
/*noop*/ /*noop*/
return null return e
}) })
} }
/** /**

View File

@ -0,0 +1,19 @@
import { parseEngineErrorMessage } from './validators'
describe('parseEngineErrorMessage', () => {
it('takes an engine error string and parses its json message', () => {
const engineError =
'engine error: [{"error_code":"internal_engine","message":"Trajectory curve must be G1 continuous (with continuous tangents)"}]'
const message = parseEngineErrorMessage(engineError)
expect(message).toEqual(
'Trajectory curve must be G1 continuous (with continuous tangents)'
)
})
it('retuns undefined on strings with different formats', () => {
const s1 = 'engine error: []'
const s2 = 'blabla'
expect(parseEngineErrorMessage(s1)).toBeUndefined()
expect(parseEngineErrorMessage(s2)).toBeUndefined()
})
})

View File

@ -3,6 +3,7 @@ import { engineCommandManager } from 'lib/singletons'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { CommandBarContext } from 'machines/commandBarMachine' import { CommandBarContext } from 'machines/commandBarMachine'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { ApiError_type } from '@kittycad/lib/dist/types/src/models'
export const disableDryRunWithRetry = async (numberOfRetries = 3) => { export const disableDryRunWithRetry = async (numberOfRetries = 3) => {
for (let tries = 0; tries < numberOfRetries; tries++) { for (let tries = 0; tries < numberOfRetries; tries++) {
@ -46,6 +47,20 @@ function isSelections(selections: unknown): selections is Selections {
) )
} }
export function parseEngineErrorMessage(engineError: string) {
const parts = engineError.split('engine error: ')
if (parts.length < 2) {
return undefined
}
const errors = JSON.parse(parts[1]) as ApiError_type[]
if (!errors[0]) {
return undefined
}
return errors[0].message
}
export const revolveAxisValidator = async ({ export const revolveAxisValidator = async ({
data, data,
context, context,
@ -83,7 +98,7 @@ export const revolveAxisValidator = async ({
value: 360, value: 360,
} }
const revolveAboutEdgeCommand = async () => { const command = async () => {
return await engineCommandManager.sendSceneCommand({ return await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd_id: uuidv4(), cmd_id: uuidv4(),
@ -96,13 +111,13 @@ export const revolveAxisValidator = async ({
}, },
}) })
} }
const attemptRevolve = await dryRunWrapper(revolveAboutEdgeCommand) const result = await dryRunWrapper(command)
if (attemptRevolve?.success) { if (result?.success) {
return true return true
} else {
// return error message for the toast
return 'Unable to revolve with selected edge'
} }
const reason = parseEngineErrorMessage(result) || 'unknown'
return `Unable to revolve with the current selection. Reason: ${reason}`
} }
export const loftValidator = async ({ export const loftValidator = async ({
@ -128,7 +143,7 @@ export const loftValidator = async ({
return 'Unable to loft, selection contains less than two solid2ds' return 'Unable to loft, selection contains less than two solid2ds'
} }
const loftCommand = async () => { const command = async () => {
// TODO: check what to do with these // TODO: check what to do with these
const DEFAULT_V_DEGREE = 2 const DEFAULT_V_DEGREE = 2
const DEFAULT_TOLERANCE = 2 const DEFAULT_TOLERANCE = 2
@ -145,13 +160,13 @@ export const loftValidator = async ({
}, },
}) })
} }
const attempt = await dryRunWrapper(loftCommand) const result = await dryRunWrapper(command)
if (attempt?.success) { if (result?.success) {
return true return true
} else {
// return error message for the toast
return 'Unable to loft with selected sketches'
} }
const reason = parseEngineErrorMessage(result) || 'unknown'
return `Unable to loft with the current selection. Reason: ${reason}`
} }
export const shellValidator = async ({ export const shellValidator = async ({
@ -180,7 +195,7 @@ export const shellValidator = async ({
return "Unable to shell, couldn't find the solid" return "Unable to shell, couldn't find the solid"
} }
const shellCommand = async () => { const command = async () => {
// TODO: figure out something better than an arbitrarily small value // TODO: figure out something better than an arbitrarily small value
const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9 const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9
const DEFAULT_HOLLOW = false const DEFAULT_HOLLOW = false
@ -200,12 +215,13 @@ export const shellValidator = async ({
}) })
} }
const attemptShell = await dryRunWrapper(shellCommand) const result = await dryRunWrapper(command)
if (attemptShell?.success) { if (result?.success) {
return true return true
} }
return 'Unable to shell with the provided selection' const reason = parseEngineErrorMessage(result) || 'unknown'
return `Unable to shell with the current selection. Reason: ${reason}`
} }
export const sweepValidator = async ({ export const sweepValidator = async ({
@ -241,7 +257,7 @@ export const sweepValidator = async ({
} }
const target = targetArtifact.pathId const target = targetArtifact.pathId
const sweepCommand = async () => { const command = async () => {
// TODO: second look on defaults here // TODO: second look on defaults here
const DEFAULT_TOLERANCE: Models['LengthUnit_type'] = 1e-7 const DEFAULT_TOLERANCE: Models['LengthUnit_type'] = 1e-7
const DEFAULT_SECTIONAL = false const DEFAULT_SECTIONAL = false
@ -261,10 +277,11 @@ export const sweepValidator = async ({
}) })
} }
const attemptSweep = await dryRunWrapper(sweepCommand) const result = await dryRunWrapper(command)
if (attemptSweep?.success) { if (result?.success) {
return true return true
} }
return 'Unable to sweep with the provided selection' const reason = parseEngineErrorMessage(result) || 'unknown'
return `Unable to sweep with the current selection. Reason: ${reason}`
} }