@wise/dynamic-flow-client-internal 5.28.2 → 5.28.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/i18n/tr.json +1 -1
- package/build/main.css +1 -0
- package/build/main.js +2 -2
- package/build/main.mjs +2 -2
- package/build/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/build/dynamicFlow/DynamicFlow.js +0 -11
- package/build/dynamicFlow/DynamicFlowModal.js +0 -21
- package/build/dynamicFlow/DynamicFlowWiseProps.js +0 -1
- package/build/dynamicFlow/DynamicFlowWithRef.js +0 -19
- package/build/dynamicFlow/context-menu/useContextMenu.js +0 -33
- package/build/dynamicFlow/context-menu/useDFContextMenu.js +0 -135
- package/build/dynamicFlow/context-menu/useDFContextMenu.test.js +0 -101
- package/build/dynamicFlow/getMergedRenderers.js +0 -36
- package/build/dynamicFlow/messages.js +0 -13
- package/build/dynamicFlow/renderers.js +0 -10
- package/build/dynamicFlow/telemetry/app-version.js +0 -4
- package/build/dynamicFlow/telemetry/dispatchAnalyticsEvent.js +0 -8
- package/build/dynamicFlow/telemetry/getLogEvent.js +0 -22
- package/build/dynamicFlow/telemetry/getTrackEvent.js +0 -39
- package/build/dynamicFlow/telemetry/index.js +0 -2
- package/build/dynamicFlow/useCreateSnackBar.js +0 -9
- package/build/dynamicFlow/useOnCopy.js +0 -16
- package/build/dynamicFlow/useWiseHttpClient.js +0 -24
- package/build/dynamicFlow/useWiseToCoreProps.js +0 -42
- package/build/i18n/index.js +0 -43
- package/build/index.js +0 -6
- package/build/stories/ButtonLoader.story.js +0 -109
- package/build/stories/ContainerQueries.story.js +0 -74
- package/build/stories/DynamicFlow.story.js +0 -37
- package/build/stories/DynamicFlowWithRef.story.js +0 -72
- package/build/stories/Editable.story.js +0 -58
- package/build/stories/Initialisation.story.js +0 -33
- package/build/stories/PinnedButtons.story.js +0 -94
- package/build/stories/SCAIntegration.story.js +0 -67
- package/build/stories/utils/fixtureHttpClient.js +0 -59
- package/build/test-utils/NeptuneProviders.js +0 -11
- package/build/test-utils/index.js +0 -4
- package/build/test-utils/respond-with.js +0 -6
- package/build/test-utils/rtl-utils.js +0 -7
- package/build/test-utils/wait.js +0 -3
- package/build/tests/DynamicFlow.test.js +0 -177
- package/build/tests/HideStepTitle.test.js +0 -58
- package/build/tests/RequestCache.test.js +0 -34
- package/build/tests/SCARejectionHandling.test.js +0 -71
- package/build/tests/Subflow.Wise.test.js +0 -650
- package/build/tests/events.test.js +0 -124
- package/build/tests/logging.test.js +0 -12
- package/build/tests/onNotification.test.js +0 -96
- package/build/tests/renderers/ControlAndTags.test.js +0 -225
- package/build/tests/renderers/ImageRenderer.test.js +0 -129
- package/build/tests/renderers/RadioInputRendererComponent.test.js +0 -89
- package/build/tests/renderers/ReviewRenderer.test.js +0 -41
- package/build/tests/renderers/TextInputRenderer.test.js +0 -44
- package/build/tests/renderers/TimeZoneHeader.test.js +0 -19
- package/build/tests/renderers/UploadInputRenderer.maxSize.test.js +0 -22
- package/build/tests/renderers/value-utils.test.js +0 -24
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
-
import { vi } from 'vitest';
|
|
4
|
-
import { DynamicFlow } from '../dynamicFlow/DynamicFlow';
|
|
5
|
-
import { renderWithProviders } from '../test-utils/rtl-utils';
|
|
6
|
-
describe('events', () => {
|
|
7
|
-
describe('when an event is triggered from core', () => {
|
|
8
|
-
describe('is a theme required event', () => {
|
|
9
|
-
it('triggers an onEvent callback, but not an onAnalytics callback', async () => {
|
|
10
|
-
const onEvent = vi.fn();
|
|
11
|
-
const onAnalytics = vi.fn();
|
|
12
|
-
renderWithProviders(_jsx(DynamicFlow, { flowId: "test-flow", customFetch: fetch, initialStep: {
|
|
13
|
-
id: 'celebration',
|
|
14
|
-
title: '',
|
|
15
|
-
control: 'splash-celebration',
|
|
16
|
-
layout: [],
|
|
17
|
-
schemas: [],
|
|
18
|
-
}, onCompletion: () => { }, onError: () => { }, onEvent: onEvent, onAnalytics: onAnalytics }));
|
|
19
|
-
await waitFor(() => {
|
|
20
|
-
expect(onEvent).toHaveBeenCalledWith('Dynamic Flow - Theme Required', expect.objectContaining({ theme: 'forest-green' }));
|
|
21
|
-
});
|
|
22
|
-
expect(onAnalytics).not.toHaveBeenCalledWith('Dynamic Flow - Theme Required', expect.anything());
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
describe('it is a documented flow event', () => {
|
|
26
|
-
it('triggers onAnalytics and onEvent', async () => {
|
|
27
|
-
const onEvent = vi.fn();
|
|
28
|
-
const onAnalytics = vi.fn();
|
|
29
|
-
renderWithProviders(_jsx(DynamicFlow, { flowId: "test-flow", customFetch: fetch, initialStep: {
|
|
30
|
-
id: 'regular-step',
|
|
31
|
-
title: '',
|
|
32
|
-
layout: [],
|
|
33
|
-
schemas: [],
|
|
34
|
-
}, onCompletion: () => { }, onError: () => { }, onEvent: onEvent, onAnalytics: onAnalytics }));
|
|
35
|
-
await waitFor(() => {
|
|
36
|
-
expect(onEvent).toHaveBeenCalledWith('Dynamic Flow - Step Shown', expect.anything());
|
|
37
|
-
});
|
|
38
|
-
expect(onAnalytics).toHaveBeenCalledWith('Dynamic Flow - Step Shown', expect.anything());
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('onAnalytics', () => {
|
|
43
|
-
let originalMixpanel;
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
originalMixpanel = window.mixpanel;
|
|
46
|
-
});
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
window.mixpanel = originalMixpanel;
|
|
49
|
-
vi.clearAllMocks();
|
|
50
|
-
});
|
|
51
|
-
const initialStep = {
|
|
52
|
-
id: 'step',
|
|
53
|
-
title: 'Step',
|
|
54
|
-
layout: [],
|
|
55
|
-
schemas: [],
|
|
56
|
-
};
|
|
57
|
-
const getProps = () => ({
|
|
58
|
-
flowId: 'test-flow',
|
|
59
|
-
customFetch: vi.fn(),
|
|
60
|
-
initialStep,
|
|
61
|
-
onCompletion: () => { },
|
|
62
|
-
onError: () => { },
|
|
63
|
-
});
|
|
64
|
-
describe('When onAnalytics is provided', () => {
|
|
65
|
-
it('calls onAnalytics with the correct event name and properties', async () => {
|
|
66
|
-
const props = getProps();
|
|
67
|
-
const onAnalyticsMock = vi.fn();
|
|
68
|
-
renderWithProviders(_jsx(DynamicFlow, Object.assign({}, props, { onAnalytics: onAnalyticsMock })));
|
|
69
|
-
await waitFor(() => {
|
|
70
|
-
expect(screen.getByText('Step')).toBeInTheDocument();
|
|
71
|
-
});
|
|
72
|
-
expect(onAnalyticsMock).toHaveBeenCalledWith('Dynamic Flow - Initiated', expect.objectContaining({
|
|
73
|
-
flowId: 'test-flow',
|
|
74
|
-
integrationId: 'test-flow',
|
|
75
|
-
stepCount: 0,
|
|
76
|
-
stepId: 'step',
|
|
77
|
-
}));
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
describe('When onAnalytics is not provided but onEvent is provided', () => {
|
|
81
|
-
it('calls onEvent with the correct event name and properties, and the window.mixpanel.track with an $insert_id', async () => {
|
|
82
|
-
const props = getProps();
|
|
83
|
-
const onEventMock = vi.fn();
|
|
84
|
-
const mixpanelTrackMock = vi.fn();
|
|
85
|
-
window.mixpanel = { track: mixpanelTrackMock };
|
|
86
|
-
renderWithProviders(_jsx(DynamicFlow, Object.assign({}, props, { onEvent: onEventMock })));
|
|
87
|
-
await waitFor(() => {
|
|
88
|
-
expect(screen.getByText('Step')).toBeInTheDocument();
|
|
89
|
-
});
|
|
90
|
-
expect(onEventMock).toHaveBeenCalledWith('Dynamic Flow - Initiated', expect.objectContaining({
|
|
91
|
-
flowId: 'test-flow',
|
|
92
|
-
integrationId: 'test-flow',
|
|
93
|
-
stepCount: 0,
|
|
94
|
-
stepId: 'step',
|
|
95
|
-
}));
|
|
96
|
-
expect(mixpanelTrackMock).toHaveBeenCalledWith('Dynamic Flow - Initiated', expect.objectContaining({
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
98
|
-
$insert_id: expect.any(String),
|
|
99
|
-
dfFallbackAnalytics: true,
|
|
100
|
-
}));
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
describe('When onAnalytics and onEvent are not provided', () => {
|
|
104
|
-
it('calls window.mixpanel.track with the correct event name and properties, including an extra dfFallbackAnalytics property', async () => {
|
|
105
|
-
const props = getProps();
|
|
106
|
-
const mixpanelTrackMock = vi.fn();
|
|
107
|
-
window.mixpanel = { track: mixpanelTrackMock };
|
|
108
|
-
renderWithProviders(_jsx(DynamicFlow, Object.assign({}, props)));
|
|
109
|
-
await waitFor(() => {
|
|
110
|
-
expect(screen.getByText('Step')).toBeInTheDocument();
|
|
111
|
-
});
|
|
112
|
-
expect(mixpanelTrackMock).toHaveBeenCalledWith('Dynamic Flow - Initiated', expect.objectContaining({
|
|
113
|
-
dfFallbackAnalytics: true,
|
|
114
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
115
|
-
$insert_id: expect.any(String),
|
|
116
|
-
flowId: 'test-flow',
|
|
117
|
-
integrationId: 'test-flow',
|
|
118
|
-
stepCount: 0,
|
|
119
|
-
stepId: 'step',
|
|
120
|
-
}));
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { vi } from 'vitest';
|
|
3
|
-
import { DynamicFlow } from '../dynamicFlow/DynamicFlow';
|
|
4
|
-
import { renderWithProviders } from '../test-utils/rtl-utils';
|
|
5
|
-
describe('logging', () => {
|
|
6
|
-
it(`supresses invalid-step "info" events coming from core`, () => {
|
|
7
|
-
const onLog = vi.fn();
|
|
8
|
-
const invalidStep = {};
|
|
9
|
-
renderWithProviders(_jsx(DynamicFlow, { flowId: "flow-id", initialStep: invalidStep, customFetch: vi.fn(), onCompletion: vi.fn(), onError: vi.fn(), onEvent: vi.fn(), onLog: onLog }));
|
|
10
|
-
expect(onLog).not.toHaveBeenCalledWith('info', expect.anything(), expect.anything());
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
-
import { userEvent } from '@testing-library/user-event';
|
|
4
|
-
import { vi } from 'vitest';
|
|
5
|
-
import { DynamicFlow } from '../dynamicFlow/DynamicFlow';
|
|
6
|
-
import { renderWithProviders } from '../test-utils/rtl-utils';
|
|
7
|
-
const createSnackBarMock = vi.fn();
|
|
8
|
-
vi.mock('../dynamicFlow/useCreateSnackBar', async () => {
|
|
9
|
-
return {
|
|
10
|
-
useCreateSnackBar: () => createSnackBarMock,
|
|
11
|
-
};
|
|
12
|
-
});
|
|
13
|
-
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
14
|
-
describe('onNotification', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
createSnackBarMock.mockClear();
|
|
17
|
-
});
|
|
18
|
-
describe('Download Behavior notifications', () => {
|
|
19
|
-
const customFetch = async (input) => {
|
|
20
|
-
switch (input) {
|
|
21
|
-
case '/fake-download-url':
|
|
22
|
-
return new Response(new Blob(['file contents'], { type: 'text/plain' }), {
|
|
23
|
-
headers: { 'Content-Disposition': 'attachment; filename="test.txt"' },
|
|
24
|
-
});
|
|
25
|
-
default:
|
|
26
|
-
return new Response('Not Found', { status: 404 });
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
const initialStep = {
|
|
30
|
-
id: 'regular-step',
|
|
31
|
-
title: 'Download Test',
|
|
32
|
-
layout: [
|
|
33
|
-
{
|
|
34
|
-
type: 'button',
|
|
35
|
-
title: 'Download (correct)',
|
|
36
|
-
behavior: {
|
|
37
|
-
type: 'download',
|
|
38
|
-
request: {
|
|
39
|
-
url: '/fake-download-url',
|
|
40
|
-
method: 'GET',
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
type: 'button',
|
|
46
|
-
title: 'Download (incorrect)',
|
|
47
|
-
behavior: {
|
|
48
|
-
type: 'download',
|
|
49
|
-
request: {
|
|
50
|
-
url: '/non-existent-url',
|
|
51
|
-
method: 'GET',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
schemas: [],
|
|
57
|
-
};
|
|
58
|
-
const getProps = () => ({
|
|
59
|
-
flowId: 'test-flow',
|
|
60
|
-
customFetch,
|
|
61
|
-
initialStep,
|
|
62
|
-
onCompletion: () => { },
|
|
63
|
-
onError: () => { },
|
|
64
|
-
onEvent: vi.fn(),
|
|
65
|
-
onAnalytics: vi.fn(),
|
|
66
|
-
});
|
|
67
|
-
it('displays a snackbar with the specified localised text (success)', async () => {
|
|
68
|
-
const props = getProps();
|
|
69
|
-
renderWithProviders(_jsx(DynamicFlow, Object.assign({}, props)));
|
|
70
|
-
await waitFor(() => {
|
|
71
|
-
expect(screen.getByText('Download Test')).toBeInTheDocument();
|
|
72
|
-
});
|
|
73
|
-
await user.click(screen.getByText('Download (correct)'));
|
|
74
|
-
await waitFor(() => {
|
|
75
|
-
expect(createSnackBarMock).toHaveBeenCalledWith({ text: 'Downloading...' });
|
|
76
|
-
});
|
|
77
|
-
await waitFor(() => {
|
|
78
|
-
expect(createSnackBarMock).toHaveBeenCalledWith({ text: 'Download successful' });
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
it('displays a snackbar with the specified localised text (failure)', async () => {
|
|
82
|
-
const props = getProps();
|
|
83
|
-
renderWithProviders(_jsx(DynamicFlow, Object.assign({}, props)));
|
|
84
|
-
await waitFor(() => {
|
|
85
|
-
expect(screen.getByText('Download Test')).toBeInTheDocument();
|
|
86
|
-
});
|
|
87
|
-
await user.click(screen.getByText('Download (incorrect)'));
|
|
88
|
-
await waitFor(() => {
|
|
89
|
-
expect(createSnackBarMock).toHaveBeenCalledWith({ text: 'Downloading...' });
|
|
90
|
-
});
|
|
91
|
-
await waitFor(() => {
|
|
92
|
-
expect(createSnackBarMock).toHaveBeenCalledWith({ text: 'Download failed' });
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { screen } from '@testing-library/react';
|
|
3
|
-
import { vi } from 'vitest';
|
|
4
|
-
import { DynamicFlow } from '../../dynamicFlow/DynamicFlow';
|
|
5
|
-
import { renderWithProviders } from '../../test-utils/rtl-utils';
|
|
6
|
-
const defaultProps = {
|
|
7
|
-
flowId: 'control-and-tags-flow',
|
|
8
|
-
onCompletion: vi.fn(),
|
|
9
|
-
onError: vi.fn(),
|
|
10
|
-
customFetch: vi.fn(),
|
|
11
|
-
};
|
|
12
|
-
const renderStep = (step) => renderWithProviders(_jsx(DynamicFlow, Object.assign({}, defaultProps, { initialStep: step })));
|
|
13
|
-
describe('control and tags classes', () => {
|
|
14
|
-
it('applies classes for FieldInput-backed controls', () => {
|
|
15
|
-
const step = {
|
|
16
|
-
id: 'text-input-step',
|
|
17
|
-
title: 'Text input step',
|
|
18
|
-
schemas: [
|
|
19
|
-
{
|
|
20
|
-
$id: '#schema',
|
|
21
|
-
type: 'object',
|
|
22
|
-
displayOrder: ['amount'],
|
|
23
|
-
properties: {
|
|
24
|
-
amount: {
|
|
25
|
-
type: 'string',
|
|
26
|
-
title: 'Amount',
|
|
27
|
-
control: 'numeric',
|
|
28
|
-
tags: ['primary', 'desktop'],
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
layout: [{ type: 'form', schemaId: '#schema' }],
|
|
34
|
-
};
|
|
35
|
-
const { container } = renderStep(step);
|
|
36
|
-
expect(screen.getByLabelText('Amount')).toBeInTheDocument();
|
|
37
|
-
expect(container.querySelector('.df-control-numeric.df-tag-primary.df-tag-desktop')).not.toBeNull();
|
|
38
|
-
});
|
|
39
|
-
it('applies classes for radio-item select controls', () => {
|
|
40
|
-
const step = {
|
|
41
|
-
id: 'radio-item-step',
|
|
42
|
-
title: 'Radio item step',
|
|
43
|
-
schemas: [
|
|
44
|
-
{
|
|
45
|
-
$id: '#schema',
|
|
46
|
-
type: 'object',
|
|
47
|
-
displayOrder: ['plan'],
|
|
48
|
-
properties: {
|
|
49
|
-
plan: {
|
|
50
|
-
title: 'Choose a plan',
|
|
51
|
-
control: 'radio-item',
|
|
52
|
-
tags: ['plan-tag'],
|
|
53
|
-
oneOf: [
|
|
54
|
-
{ const: 'STANDARD', title: 'Standard' },
|
|
55
|
-
{ const: 'PREMIUM', title: 'Premium' },
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
layout: [{ type: 'form', schemaId: '#schema' }],
|
|
62
|
-
};
|
|
63
|
-
const { container } = renderStep(step);
|
|
64
|
-
expect(screen.getByText('Standard')).toBeInTheDocument();
|
|
65
|
-
expect(container.querySelector('.df-control-radio-item.df-tag-plan-tag')).not.toBeNull();
|
|
66
|
-
});
|
|
67
|
-
it.each(['checkbox-item', 'switch-item'])('applies classes for %s boolean controls', (control) => {
|
|
68
|
-
const step = {
|
|
69
|
-
id: `${control}-step`,
|
|
70
|
-
title: `${control} step`,
|
|
71
|
-
schemas: [
|
|
72
|
-
{
|
|
73
|
-
$id: '#schema',
|
|
74
|
-
type: 'object',
|
|
75
|
-
displayOrder: ['marketing'],
|
|
76
|
-
properties: {
|
|
77
|
-
marketing: {
|
|
78
|
-
type: 'boolean',
|
|
79
|
-
title: 'Marketing emails',
|
|
80
|
-
description: 'Receive product updates.',
|
|
81
|
-
control,
|
|
82
|
-
tags: ['policy'],
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
layout: [{ type: 'form', schemaId: '#schema' }],
|
|
88
|
-
};
|
|
89
|
-
const { container } = renderStep(step);
|
|
90
|
-
expect(screen.getByText('Marketing emails')).toBeInTheDocument();
|
|
91
|
-
expect(container.querySelector(`.df-control-${control}.df-tag-policy`)).not.toBeNull();
|
|
92
|
-
});
|
|
93
|
-
it('applies classes for repeatable controls', () => {
|
|
94
|
-
const step = {
|
|
95
|
-
id: 'repeatable-step',
|
|
96
|
-
title: 'Repeatable step',
|
|
97
|
-
schemas: [
|
|
98
|
-
{
|
|
99
|
-
$id: '#schema',
|
|
100
|
-
type: 'object',
|
|
101
|
-
displayOrder: ['emails'],
|
|
102
|
-
properties: {
|
|
103
|
-
emails: {
|
|
104
|
-
type: 'array',
|
|
105
|
-
title: 'Emails',
|
|
106
|
-
addItemTitle: 'Add email',
|
|
107
|
-
editItemTitle: 'Edit email',
|
|
108
|
-
control: 'repeatable',
|
|
109
|
-
tags: ['repeatable-tag'],
|
|
110
|
-
items: {
|
|
111
|
-
type: 'string',
|
|
112
|
-
title: 'Email',
|
|
113
|
-
summary: {
|
|
114
|
-
providesTitle: true,
|
|
115
|
-
providesIcon: true,
|
|
116
|
-
},
|
|
117
|
-
icon: {
|
|
118
|
-
name: 'email',
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
layout: [{ type: 'form', schemaId: '#schema' }],
|
|
126
|
-
};
|
|
127
|
-
const { container } = renderStep(step);
|
|
128
|
-
expect(screen.getByText('Add email')).toBeInTheDocument();
|
|
129
|
-
expect(container.querySelector('.df-control-repeatable.df-tag-repeatable-tag')).not.toBeNull();
|
|
130
|
-
});
|
|
131
|
-
it('applies classes for file uploads rendered from a persist-async blob schema', () => {
|
|
132
|
-
const step = {
|
|
133
|
-
id: 'upload-step',
|
|
134
|
-
title: 'Upload step',
|
|
135
|
-
schemas: [
|
|
136
|
-
{
|
|
137
|
-
$id: '#schema',
|
|
138
|
-
type: 'object',
|
|
139
|
-
displayOrder: ['fileToken'],
|
|
140
|
-
properties: {
|
|
141
|
-
fileToken: {
|
|
142
|
-
type: 'string',
|
|
143
|
-
persistAsync: {
|
|
144
|
-
method: 'POST',
|
|
145
|
-
url: '/upload',
|
|
146
|
-
param: 'bodyAttribute',
|
|
147
|
-
idProperty: 'token',
|
|
148
|
-
schema: {
|
|
149
|
-
type: 'blob',
|
|
150
|
-
title: 'Invoice',
|
|
151
|
-
description: 'PNG, JPG, or PDF, less than 5mb',
|
|
152
|
-
accepts: ['image/png', 'image/jpg', 'application/pdf'],
|
|
153
|
-
source: 'file',
|
|
154
|
-
control: 'upload',
|
|
155
|
-
tags: ['upload-tag'],
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
layout: [{ type: 'form', schemaId: '#schema' }],
|
|
163
|
-
};
|
|
164
|
-
const { container } = renderStep(step);
|
|
165
|
-
expect(screen.getByText('Invoice')).toBeInTheDocument();
|
|
166
|
-
expect(container.querySelector('.df-control-upload.df-tag-upload-tag')).not.toBeNull();
|
|
167
|
-
});
|
|
168
|
-
it('applies classes for splash step renderers', () => {
|
|
169
|
-
const step = {
|
|
170
|
-
id: 'splash-step',
|
|
171
|
-
title: 'Welcome',
|
|
172
|
-
control: 'splash',
|
|
173
|
-
tags: ['hero'],
|
|
174
|
-
schemas: [],
|
|
175
|
-
layout: [
|
|
176
|
-
{
|
|
177
|
-
type: 'button',
|
|
178
|
-
title: 'Continue',
|
|
179
|
-
action: { id: 'continue-action', url: '/continue' },
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
};
|
|
183
|
-
const { container } = renderStep(step);
|
|
184
|
-
expect(screen.getByText('Continue')).toBeInTheDocument();
|
|
185
|
-
expect(container.querySelector('.splash-screen.df-control-splash.df-tag-hero')).not.toBeNull();
|
|
186
|
-
});
|
|
187
|
-
it('applies classes for button renderers', () => {
|
|
188
|
-
const step = {
|
|
189
|
-
id: 'button-step',
|
|
190
|
-
title: 'Button step',
|
|
191
|
-
schemas: [],
|
|
192
|
-
layout: [
|
|
193
|
-
{
|
|
194
|
-
type: 'button',
|
|
195
|
-
title: 'Submit',
|
|
196
|
-
control: 'small',
|
|
197
|
-
tags: ['button-tag'],
|
|
198
|
-
action: { id: 'submit-action', url: '/submit' },
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
};
|
|
202
|
-
const { container } = renderStep(step);
|
|
203
|
-
expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
|
|
204
|
-
expect(container.querySelector('.df-button.df-control-small.df-tag-button-tag')).not.toBeNull();
|
|
205
|
-
});
|
|
206
|
-
it('applies classes for list renderers', () => {
|
|
207
|
-
const step = {
|
|
208
|
-
id: 'list-step',
|
|
209
|
-
title: 'List step',
|
|
210
|
-
schemas: [],
|
|
211
|
-
layout: [
|
|
212
|
-
{
|
|
213
|
-
type: 'list',
|
|
214
|
-
title: 'Review items',
|
|
215
|
-
control: 'spotlight',
|
|
216
|
-
tags: ['list-tag'],
|
|
217
|
-
items: [{ title: 'First item' }],
|
|
218
|
-
},
|
|
219
|
-
],
|
|
220
|
-
};
|
|
221
|
-
const { container } = renderStep(step);
|
|
222
|
-
expect(screen.getByText('First item')).toBeInTheDocument();
|
|
223
|
-
expect(container.querySelector('.df-control-spotlight.df-tag-list-tag')).not.toBeNull();
|
|
224
|
-
});
|
|
225
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
-
import ImageRenderer from '@wise/dynamic-flow-renderers/src/ImageRenderer';
|
|
4
|
-
import { vi } from 'vitest';
|
|
5
|
-
import { renderWithProviders } from '../../test-utils/rtl-utils';
|
|
6
|
-
const ImageRendererFunction = ImageRenderer.render;
|
|
7
|
-
class MockFileReaderLoadend {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.result = 'data:image/jpeg';
|
|
10
|
-
this.addEventListener = vi.fn((event, callback) => {
|
|
11
|
-
if (event === 'loadend') {
|
|
12
|
-
callback();
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
this.readAsDataURL = vi.fn();
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
class MockFileReaderError {
|
|
19
|
-
constructor() {
|
|
20
|
-
this.result = 'some-error-message';
|
|
21
|
-
this.addEventListener = vi.fn((event, callback) => {
|
|
22
|
-
if (event === 'error') {
|
|
23
|
-
callback();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
this.readAsDataURL = vi.fn();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
describe('ImageRenderer', () => {
|
|
30
|
-
const getProps = (uri) => ({
|
|
31
|
-
type: 'image',
|
|
32
|
-
uid: 'uid',
|
|
33
|
-
uri,
|
|
34
|
-
align: 'center',
|
|
35
|
-
accessibilityDescription: 'An image',
|
|
36
|
-
size: 'lg',
|
|
37
|
-
margin: 'md',
|
|
38
|
-
render: () => null,
|
|
39
|
-
stepLoadingState: 'idle',
|
|
40
|
-
features: {},
|
|
41
|
-
trackEvent: vi.fn(),
|
|
42
|
-
httpClient: vi.fn(),
|
|
43
|
-
});
|
|
44
|
-
describe('image.uri (with absolute url in a different domain)', () => {
|
|
45
|
-
it('renders an "img" tag with the appropriate "src" attribute', async () => {
|
|
46
|
-
const props = getProps('https://example.com/image.jpg');
|
|
47
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
48
|
-
await waitFor(() => {
|
|
49
|
-
const img = screen.getByRole('img');
|
|
50
|
-
expect(img).toHaveAttribute('src', 'https://example.com/image.jpg');
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe('using httpClient and FileReader', () => {
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
vi.stubGlobal('FileReader', MockFileReaderLoadend);
|
|
57
|
-
});
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
vi.restoreAllMocks();
|
|
60
|
-
vi.unstubAllGlobals();
|
|
61
|
-
});
|
|
62
|
-
describe('image.uri (with relative url that succeeds)', () => {
|
|
63
|
-
it('renders an "img" tag, but fetches the image using httpClient', async () => {
|
|
64
|
-
const props = getProps('/image.jpg');
|
|
65
|
-
vi.mocked(props.httpClient).mockResolvedValueOnce(new Response(new Blob()));
|
|
66
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
67
|
-
await waitFor(() => {
|
|
68
|
-
expect(props.httpClient).toHaveBeenCalledWith('/image.jpg', expect.anything());
|
|
69
|
-
});
|
|
70
|
-
await waitFor(() => {
|
|
71
|
-
const img = screen.getByRole('img');
|
|
72
|
-
expect(img).toHaveAttribute('src', 'data:image/jpeg');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe('image.uri (with relative url that fails to fetch)', () => {
|
|
77
|
-
it('renders an "img" tag, fetches the image using httpClient, but falls back to setting "src"', async () => {
|
|
78
|
-
const props = getProps('/image.jpg');
|
|
79
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
80
|
-
await waitFor(() => {
|
|
81
|
-
expect(props.httpClient).toHaveBeenCalledWith('/image.jpg', expect.anything());
|
|
82
|
-
});
|
|
83
|
-
// because the image fetching fails (it's an empty mock), the image source will be the uri
|
|
84
|
-
expect(screen.getByRole('img')).toHaveAttribute('src', '/image.jpg');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
describe('image.uri (with relative url that fails to decode)', () => {
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
vi.stubGlobal('FileReader', MockFileReaderError);
|
|
90
|
-
});
|
|
91
|
-
afterEach(() => {
|
|
92
|
-
vi.unstubAllGlobals();
|
|
93
|
-
});
|
|
94
|
-
it('renders an "img" tag, fetches the image using httpClient, but falls back to setting "src"', async () => {
|
|
95
|
-
const props = getProps('/image.jpg');
|
|
96
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
97
|
-
await waitFor(() => {
|
|
98
|
-
expect(props.httpClient).toHaveBeenCalledWith('/image.jpg', expect.anything());
|
|
99
|
-
});
|
|
100
|
-
// because the image fetching fails (it's an empty mock), the image source will be the uri
|
|
101
|
-
expect(screen.getByRole('img')).toHaveAttribute('src', '/image.jpg');
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
describe('image.uri (with absolute url in the same domain)', () => {
|
|
105
|
-
it('renders an "img" tag, but fetches the image using httpClient', async () => {
|
|
106
|
-
const props = getProps('http://localhost:3000/image.jpg');
|
|
107
|
-
vi.mocked(props.httpClient).mockResolvedValueOnce(new Response(new Blob()));
|
|
108
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
109
|
-
await waitFor(() => {
|
|
110
|
-
expect(props.httpClient).toHaveBeenCalledWith('http://localhost:3000/image.jpg', expect.anything());
|
|
111
|
-
});
|
|
112
|
-
await waitFor(() => {
|
|
113
|
-
const img = screen.getByRole('img');
|
|
114
|
-
expect(img).toHaveAttribute('src', 'data:image/jpeg');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
describe('image.uri (with urn illustration)', () => {
|
|
120
|
-
it('renders an illustration component which renders an "img" with a specific "src"', async () => {
|
|
121
|
-
const props = getProps('urn:wise:illustrations:confetti');
|
|
122
|
-
renderWithProviders(_jsx(ImageRendererFunction, Object.assign({}, props)));
|
|
123
|
-
await waitFor(() => {
|
|
124
|
-
const img = screen.getByRole('img');
|
|
125
|
-
expect(img).toHaveAttribute('src', expect.stringMatching(/^https:\/\/wise\.com.*illustrations\/confetti/));
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { screen } from '@testing-library/react';
|
|
3
|
-
import { RadioInputRendererComponent } from '@wise/dynamic-flow-renderers/src/SelectInputRenderer/RadioInputRendererComponent';
|
|
4
|
-
import { vi } from 'vitest';
|
|
5
|
-
import { renderWithProviders } from '../../test-utils/rtl-utils';
|
|
6
|
-
describe('RadioInputRendererComponent', () => {
|
|
7
|
-
const getProps = () => ({
|
|
8
|
-
type: 'input-select',
|
|
9
|
-
uid: 'uid',
|
|
10
|
-
id: 'id',
|
|
11
|
-
children: null,
|
|
12
|
-
childrenProps: null,
|
|
13
|
-
description: 'description',
|
|
14
|
-
autoComplete: 'off',
|
|
15
|
-
required: false,
|
|
16
|
-
disabled: false,
|
|
17
|
-
validationState: { status: 'invalid', message: 'error' },
|
|
18
|
-
fieldLoadingState: 'idle',
|
|
19
|
-
help: 'help',
|
|
20
|
-
options: [
|
|
21
|
-
{
|
|
22
|
-
title: 'Option A',
|
|
23
|
-
disabled: false,
|
|
24
|
-
keywords: [],
|
|
25
|
-
children: null,
|
|
26
|
-
childrenProps: null,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
title: 'Option B',
|
|
30
|
-
disabled: false,
|
|
31
|
-
keywords: [],
|
|
32
|
-
children: null,
|
|
33
|
-
childrenProps: null,
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
title: 'label',
|
|
37
|
-
selectedIndex: null,
|
|
38
|
-
render: () => null,
|
|
39
|
-
features: {},
|
|
40
|
-
trackEvent: vi.fn(),
|
|
41
|
-
stepLoadingState: 'idle',
|
|
42
|
-
httpClient: vi.fn(),
|
|
43
|
-
onSelect: vi.fn(),
|
|
44
|
-
onFocus: vi.fn(),
|
|
45
|
-
onBlur: vi.fn(),
|
|
46
|
-
});
|
|
47
|
-
describe('When the value is updated from outside the UI component', () => {
|
|
48
|
-
it('should update the selected value', () => {
|
|
49
|
-
const props = getProps();
|
|
50
|
-
const { rerender } = renderWithProviders(_jsx(RadioInputRendererComponent, Object.assign({}, props)));
|
|
51
|
-
expect(screen.getByLabelText('Option A')).not.toBeChecked();
|
|
52
|
-
expect(screen.getByLabelText('Option B')).not.toBeChecked();
|
|
53
|
-
rerender(_jsx(RadioInputRendererComponent, Object.assign({}, props, { selectedIndex: 1 })));
|
|
54
|
-
expect(screen.getByLabelText('Option B')).toBeChecked();
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe('When the options are updated (e.g. refresh-on-change)', () => {
|
|
58
|
-
it('should update the options', () => {
|
|
59
|
-
const props = Object.assign(Object.assign({}, getProps()), { selectedIndex: 1 });
|
|
60
|
-
const { rerender } = renderWithProviders(_jsx(RadioInputRendererComponent, Object.assign({}, props)));
|
|
61
|
-
expect(screen.getByLabelText('Option B')).toBeChecked();
|
|
62
|
-
const updatedProps = Object.assign(Object.assign({}, props), { options: [
|
|
63
|
-
{
|
|
64
|
-
title: 'Option A',
|
|
65
|
-
disabled: false,
|
|
66
|
-
keywords: [],
|
|
67
|
-
children: null,
|
|
68
|
-
childrenProps: null,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
title: 'Option NEW',
|
|
72
|
-
disabled: false,
|
|
73
|
-
keywords: [],
|
|
74
|
-
children: null,
|
|
75
|
-
childrenProps: null,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
title: 'Option B',
|
|
79
|
-
disabled: false,
|
|
80
|
-
keywords: [],
|
|
81
|
-
children: null,
|
|
82
|
-
childrenProps: null,
|
|
83
|
-
},
|
|
84
|
-
] });
|
|
85
|
-
rerender(_jsx(RadioInputRendererComponent, Object.assign({}, updatedProps)));
|
|
86
|
-
expect(screen.getByLabelText('Option NEW')).toBeChecked();
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
});
|