Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions packages/davinci-client/src/lib/client.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,16 +346,8 @@ export async function davinci<ActionType extends ActionTypes = ActionTypes>({
| FidoAuthenticationInputValue,
index?: number,
) {
try {
store.dispatch(nodeSlice.actions.update({ id, value, index }));
return null;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
return {
type: 'internal_error',
error: { message: errorMessage, type: 'internal_error' },
};
}
store.dispatch(nodeSlice.actions.update({ id, value, index }));
return null;
Comment on lines +349 to +350
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the DX and might be breaking. Previously, in an application you would check for the error like so:

      const result = updater(value);
      if (result && 'error' in result) {
        // Handle error
      }

But now we are always returning null so this error check can't be made. Perhaps we should return the collector here so that customers can still make the if ('error' in result) check. Or alternatively we can do the check for them and return an error object if there is an error on the collector.

};
},

Expand Down
146 changes: 141 additions & 5 deletions packages/davinci-client/src/lib/node.reducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ describe('The node collector reducer', () => {
]);
});

it('should throw with no collectors', () => {
it('should add an UnknownCollector with error when no matching collector is found', () => {
const action = {
type: 'node/update',
payload: {
Expand Down Expand Up @@ -378,10 +378,13 @@ describe('The node collector reducer', () => {
},
},
];
expect(() => nodeCollectorReducer(state, action)).toThrowError('No collector found to update');
const result = nodeCollectorReducer(state, action);
const errorCollector = result.find((c) => c.id === 'submit-1');
expect(errorCollector?.error).toBe('No collector found to update');
expect(errorCollector?.category).toBe('UnknownCollector');
});

it('should throw with no Action Collector', () => {
it('should set error on ActionCollector when update is attempted', () => {
const action = {
type: 'node/update',
payload: {
Expand Down Expand Up @@ -421,8 +424,141 @@ describe('The node collector reducer', () => {
},
},
];
expect(() => nodeCollectorReducer(state, action)).toThrowError(
'ActionCollectors are read-only',
const result = nodeCollectorReducer(state, action);
const collector = result.find((c) => c.id === 'submit-1');
expect(collector?.error).toBe('ActionCollectors are read-only');
});

it('should set error on NoValueCollector when update is attempted', () => {
const state: QrCodeCollector[] = [
{
category: 'NoValueCollector',
error: null,
type: 'QrCodeCollector',
id: 'qr-0',
name: 'qr',
output: {
key: 'qr',
label: 'QR Code',
type: 'QR_CODE',
src: 'data:image/png;base64,abc',
},
},
];
const action = { type: 'node/update', payload: { id: 'qr-0', value: 'anything' } };
const result = nodeCollectorReducer(state, action);
expect(result.find((c) => c.id === 'qr-0')?.error).toBe(
'NoValueCollectors, like ReadOnlyCollectors, are read-only',
);
});

it('should set error on collector when value is undefined', () => {
const state: TextCollector[] = [
{
category: 'SingleValueCollector',
error: null,
type: 'TextCollector',
id: 'username-0',
name: 'username',
input: { key: 'username', value: '', type: 'TEXT' },
output: { key: 'username', label: 'Username', type: 'TEXT', value: '' },
},
];
const action = {
type: 'node/update',
payload: { id: 'username-0', value: undefined as unknown as string },
};
const result = nodeCollectorReducer(state, action);
expect(result.find((c) => c.id === 'username-0')?.error).toBe(
'Value argument cannot be undefined',
);
});

it('should clear error on collector when a valid value is provided after an error', () => {
const state: TextCollector[] = [
{
category: 'SingleValueCollector',
error: 'Value argument must be a string',
type: 'TextCollector',
id: 'username-0',
name: 'username',
input: { key: 'username', value: '', type: 'TEXT' },
output: { key: 'username', label: 'Username', type: 'TEXT', value: '' },
},
];
const action = { type: 'node/update', payload: { id: 'username-0', value: 'validString' } };
const result = nodeCollectorReducer(state, action);
const collector = result.find((c) => c.id === 'username-0') as TextCollector | undefined;
expect(collector?.error).toBeNull();
expect(collector?.input.value).toBe('validString');
});

it('should set error on SingleValueCollector when value is not a string', () => {
const state: TextCollector[] = [
{
category: 'SingleValueCollector',
error: null,
type: 'TextCollector',
id: 'username-0',
name: 'username',
input: { key: 'username', value: '', type: 'TEXT' },
output: { key: 'username', label: 'Username', type: 'TEXT', value: '' },
},
];
const action = {
type: 'node/update',
payload: { id: 'username-0', value: 42 as unknown as string },
};
const result = nodeCollectorReducer(state, action);
expect(result.find((c) => c.id === 'username-0')?.error).toBe(
'Value argument must be a string',
);
});

it('should set error on MultiValueCollector when value is an object', () => {
const state: MultiSelectCollector[] = [
{
category: 'MultiValueCollector',
error: null,
type: 'MultiSelectCollector',
id: 'multi-0',
name: 'multi',
input: { key: 'multi', value: [], type: 'MULTI_SELECT', validation: null },
output: {
key: 'multi',
label: 'Multi',
type: 'MULTI_SELECT',
value: [],
options: [{ label: 'A', value: 'a' }],
},
},
];
const action = {
type: 'node/update',
payload: { id: 'multi-0', value: { bad: true } as unknown as string },
};
const result = nodeCollectorReducer(state, action);
expect(result.find((c) => c.id === 'multi-0')?.error).toBe(
'MultiValueCollector does not accept an object',
);
});

it('should set error on PollingCollector when update is attempted', () => {
const state: PollingCollector[] = [
{
category: 'SingleValueAutoCollector',
error: null,
type: 'PollingCollector',
id: 'poll-0',
name: 'poll',
input: { key: 'poll', value: '', type: 'POLLING' },
output: { key: 'poll', type: 'POLLING', config: { pollInterval: 1000, pollRetries: 3 } },
},
];
const action = { type: 'node/update', payload: { id: 'poll-0', value: 'anything' } };
const result = nodeCollectorReducer(state, action);
expect(result.find((c) => c.id === 'poll-0')?.error).toBe(
'This collector type does not support value updates',
);
});

Expand Down
Loading
Loading