Skip to content
This repository has been archived by the owner on Feb 25, 2024. It is now read-only.

Commit

Permalink
Fixed the app crashing when processing invalid actions (#332)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Jan 3, 2022
1 parent 1bd42b1 commit 9f93d67
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-teachers-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate-viz-app': patch
---

Fixed the app crashing when processing invalid actions - like when using a guard accidentally in a place of an action.
32 changes: 32 additions & 0 deletions cypress/integration/invalid-configs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
describe('Invalid configurations', () => {
it('invalid actions should not crash the app', () => {
cy.setLocalStorage(
'xstate_viz_raw_source|no_source',
JSON.stringify({
date: new Date(),
sourceRawContent: `
import { createMachine } from 'xstate';
createMachine({
id: 'invalidEntryAction',
initial: 'a',
states: {
a: {
// accidentally used guard as an action
entry: [{
cond: 'isOdd',
target: 'b',
}]
},
b: {},
},
});
`,
}),
);

cy.visit('/viz');

cy.getCanvas().contains('invalidEntryAction');
});
});
16 changes: 14 additions & 2 deletions src/ActionViz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ type PotentiallyStructurallyCloned<T> = {
[K in keyof T]: AnyFunction extends T[K] ? T[K] | undefined : T[K];
};

// at the moment a lot of invalid values can be passed through `createMachine` and reach lines like here
// so we need to be defensive about this before we implement some kind of a validation so we could raise such problems early and discard the invalid values
export function getActionLabel(action: ActionObject<any, any>): string | null {
if (!action) {
return null;
}
if (typeof action.exec === 'function') {
return isStringifiedFunction(action.type) ? null : action.type;
return isStringifiedFunction(action.type) ? 'anonymous' : action.type;
}
if (!action.type) {
return null;
}
if (action.type.startsWith('xstate.')) {
return action.type.match(/^xstate\.(.+)$/)![1];
Expand Down Expand Up @@ -167,9 +175,13 @@ export const CustomActionLabel: React.FC<{
}> = ({ action }) => {
const label = getActionLabel(action);

if (label === null) {
return null;
}

return (
<ActionType>
{label !== null ? <strong>{label}</strong> : <em>anonymous</em>}
{label === 'anonymous' ? <em>anonymous</em> : <strong>{label}</strong>}
</ActionType>
);
};
Expand Down

0 comments on commit 9f93d67

Please sign in to comment.