@oanda/labs-widget-common 1.0.200 → 1.0.202
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/CHANGELOG.md +1588 -0
- package/dist/main/providers/Layout/LayoutProvider.test.js +181 -0
- package/dist/main/providers/Layout/LayoutProvider.test.js.map +1 -0
- package/dist/module/providers/Layout/LayoutProvider.test.js +178 -0
- package/dist/module/providers/Layout/LayoutProvider.test.js.map +1 -0
- package/dist/types/providers/Layout/LayoutProvider.test.d.ts +1 -0
- package/package.json +2 -2
- package/src/providers/Layout/LayoutProvider.test.tsx +191 -0
- package/test/components/SwitchItem.test.tsx +239 -0
- package/test/utils/useNumberFormat.test.tsx +127 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { SwitchItem } from '../../src/components/Switch/SwitchItem';
|
|
8
|
+
|
|
9
|
+
jest.mock('usehooks-ts', () => ({
|
|
10
|
+
...jest.requireActual('usehooks-ts'),
|
|
11
|
+
useResizeObserver: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const mockItem = { id: 'item1', label: 'Test Item 1' };
|
|
15
|
+
|
|
16
|
+
describe('SwitchItem', () => {
|
|
17
|
+
let originalGetBoundingClientRect: () => DOMRect;
|
|
18
|
+
|
|
19
|
+
const mockedUseResizeObserver = jest.requireMock('usehooks-ts')
|
|
20
|
+
.useResizeObserver as jest.Mock;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
|
|
24
|
+
|
|
25
|
+
mockedUseResizeObserver.mockClear();
|
|
26
|
+
|
|
27
|
+
mockedUseResizeObserver.mockImplementation(
|
|
28
|
+
(options: { ref: React.RefObject<Element>; onResize: () => void }) => {
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
if (options.ref.current && typeof options.onResize === 'function') {
|
|
31
|
+
options.onResize();
|
|
32
|
+
}
|
|
33
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
|
+
}, [options.ref.current]);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
|
|
41
|
+
jest.restoreAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should render correctly with basic props (inactive)', () => {
|
|
45
|
+
render(
|
|
46
|
+
<SwitchItem
|
|
47
|
+
handleClick={jest.fn()}
|
|
48
|
+
handleRender={jest.fn()}
|
|
49
|
+
isActive={false}
|
|
50
|
+
item={mockItem}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
const listItem = screen.getByTestId('switch-item');
|
|
54
|
+
expect(listItem).toBeInTheDocument();
|
|
55
|
+
expect(listItem).not.toHaveClass('lw-text-text-negative');
|
|
56
|
+
|
|
57
|
+
const button = screen.getByRole('button', { name: 'Test Item 1' });
|
|
58
|
+
expect(button).toBeInTheDocument();
|
|
59
|
+
expect(button).not.toBeDisabled();
|
|
60
|
+
expect(button).not.toHaveAttribute('aria-current');
|
|
61
|
+
expect(button).toHaveValue(mockItem.id);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should render correctly when active', () => {
|
|
65
|
+
render(
|
|
66
|
+
<SwitchItem
|
|
67
|
+
handleClick={jest.fn()}
|
|
68
|
+
handleRender={jest.fn()}
|
|
69
|
+
isActive={true}
|
|
70
|
+
item={mockItem}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
const listItem = screen.getByTestId('switch-item');
|
|
74
|
+
expect(listItem).toHaveClass('lw-text-text-negative');
|
|
75
|
+
|
|
76
|
+
const button = screen.getByRole('button', { name: 'Test Item 1' });
|
|
77
|
+
expect(button).toBeDisabled();
|
|
78
|
+
expect(button).toHaveAttribute('aria-current', 'true');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should call handleClick when button is clicked and not active', () => {
|
|
82
|
+
const handleClickMock = jest.fn();
|
|
83
|
+
render(
|
|
84
|
+
<SwitchItem
|
|
85
|
+
handleClick={handleClickMock}
|
|
86
|
+
handleRender={jest.fn()}
|
|
87
|
+
isActive={false}
|
|
88
|
+
item={mockItem}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
const button = screen.getByRole('button', { name: 'Test Item 1' });
|
|
92
|
+
fireEvent.click(button);
|
|
93
|
+
expect(handleClickMock).toHaveBeenCalledTimes(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should not call handleClick when button is clicked and active (button is disabled)', () => {
|
|
97
|
+
const handleClickMock = jest.fn();
|
|
98
|
+
render(
|
|
99
|
+
<SwitchItem
|
|
100
|
+
handleClick={handleClickMock}
|
|
101
|
+
handleRender={jest.fn()}
|
|
102
|
+
isActive={true}
|
|
103
|
+
item={mockItem}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
const button = screen.getByRole('button', { name: 'Test Item 1' });
|
|
107
|
+
fireEvent.click(button);
|
|
108
|
+
expect(handleClickMock).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should apply equalSize styles when equalSize prop is true', () => {
|
|
112
|
+
render(
|
|
113
|
+
<SwitchItem
|
|
114
|
+
equalSize={true}
|
|
115
|
+
handleClick={jest.fn()}
|
|
116
|
+
handleRender={jest.fn()}
|
|
117
|
+
isActive={false}
|
|
118
|
+
item={mockItem}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
const listItem = screen.getByTestId('switch-item');
|
|
122
|
+
expect(listItem).toHaveClass('lw-shrink-0', 'lw-basis-0');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should not apply equalSize styles when equalSize prop is false or undefined', () => {
|
|
126
|
+
const { rerender } = render(
|
|
127
|
+
<SwitchItem
|
|
128
|
+
equalSize={false}
|
|
129
|
+
handleClick={jest.fn()}
|
|
130
|
+
handleRender={jest.fn()}
|
|
131
|
+
isActive={false}
|
|
132
|
+
item={mockItem}
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
let listItem = screen.getByTestId('switch-item');
|
|
136
|
+
expect(listItem).not.toHaveClass('lw-shrink-0');
|
|
137
|
+
expect(listItem).not.toHaveClass('lw-basis-0');
|
|
138
|
+
|
|
139
|
+
rerender(
|
|
140
|
+
<SwitchItem
|
|
141
|
+
handleClick={jest.fn()}
|
|
142
|
+
handleRender={jest.fn()}
|
|
143
|
+
isActive={false}
|
|
144
|
+
item={mockItem}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
listItem = screen.getByTestId('switch-item');
|
|
148
|
+
expect(listItem).not.toHaveClass('lw-shrink-0');
|
|
149
|
+
expect(listItem).not.toHaveClass('lw-basis-0');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should use labelCallback to modify the label', () => {
|
|
153
|
+
const labelCallback = (label: string) => `Callback: ${label}`;
|
|
154
|
+
render(
|
|
155
|
+
<SwitchItem
|
|
156
|
+
handleClick={jest.fn()}
|
|
157
|
+
handleRender={jest.fn()}
|
|
158
|
+
isActive={false}
|
|
159
|
+
item={mockItem}
|
|
160
|
+
labelCallback={labelCallback}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
const button = screen.getByRole('button', {
|
|
164
|
+
name: 'Callback: Test Item 1',
|
|
165
|
+
});
|
|
166
|
+
expect(button).toBeInTheDocument();
|
|
167
|
+
expect(screen.getByText('Callback: Test Item 1')).toBeInTheDocument();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('handleRender calls (via useResizeObserver mock)', () => {
|
|
171
|
+
it('should call handleRender with correct dimensions when active and component mounts', () => {
|
|
172
|
+
const handleRenderMock = jest.fn();
|
|
173
|
+
const mockLiRect = {
|
|
174
|
+
left: 50,
|
|
175
|
+
width: 120,
|
|
176
|
+
top: 0,
|
|
177
|
+
height: 40,
|
|
178
|
+
x: 0,
|
|
179
|
+
y: 0,
|
|
180
|
+
bottom: 0,
|
|
181
|
+
right: 0,
|
|
182
|
+
toJSON: () => {},
|
|
183
|
+
} as DOMRect;
|
|
184
|
+
|
|
185
|
+
// eslint-disable-next-line func-names
|
|
186
|
+
Element.prototype.getBoundingClientRect = jest.fn(function (
|
|
187
|
+
this: Element
|
|
188
|
+
) {
|
|
189
|
+
if (
|
|
190
|
+
this.tagName === 'LI' &&
|
|
191
|
+
(this as HTMLElement).dataset.testid === 'switch-item'
|
|
192
|
+
) {
|
|
193
|
+
return mockLiRect;
|
|
194
|
+
}
|
|
195
|
+
return originalGetBoundingClientRect.call(this);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
render(
|
|
199
|
+
<SwitchItem
|
|
200
|
+
handleClick={jest.fn()}
|
|
201
|
+
handleRender={handleRenderMock}
|
|
202
|
+
isActive={true}
|
|
203
|
+
item={mockItem}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(handleRenderMock).toHaveBeenCalledTimes(1);
|
|
208
|
+
expect(handleRenderMock).toHaveBeenCalledWith({
|
|
209
|
+
left: mockLiRect.left,
|
|
210
|
+
width: mockLiRect.width,
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should NOT call handleRender when inactive, even on mount', () => {
|
|
215
|
+
const handleRenderMock = jest.fn();
|
|
216
|
+
Element.prototype.getBoundingClientRect = jest.fn().mockReturnValue({
|
|
217
|
+
left: 50,
|
|
218
|
+
width: 120,
|
|
219
|
+
top: 0,
|
|
220
|
+
height: 40,
|
|
221
|
+
x: 0,
|
|
222
|
+
y: 0,
|
|
223
|
+
bottom: 0,
|
|
224
|
+
right: 0,
|
|
225
|
+
toJSON: () => {},
|
|
226
|
+
} as DOMRect);
|
|
227
|
+
|
|
228
|
+
render(
|
|
229
|
+
<SwitchItem
|
|
230
|
+
handleClick={jest.fn()}
|
|
231
|
+
handleRender={handleRenderMock}
|
|
232
|
+
isActive={false}
|
|
233
|
+
item={mockItem}
|
|
234
|
+
/>
|
|
235
|
+
);
|
|
236
|
+
expect(handleRenderMock).not.toHaveBeenCalled();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
|
|
3
|
+
import { useNumberFormat } from '../../src';
|
|
4
|
+
|
|
5
|
+
describe('useNumberFormat', () => {
|
|
6
|
+
it('should initialize with undefined if no initialValue is provided', () => {
|
|
7
|
+
const { result } = renderHook(() => useNumberFormat({}));
|
|
8
|
+
expect(result.current.value).toBeUndefined();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should initialize with the provided initialValue', () => {
|
|
12
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: 10 }));
|
|
13
|
+
expect(result.current.value).toBe(10);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should update value on handleValueChange', () => {
|
|
17
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: 10 }));
|
|
18
|
+
act(() => {
|
|
19
|
+
result.current.handleValueChange({
|
|
20
|
+
formattedValue: '20',
|
|
21
|
+
value: '20',
|
|
22
|
+
floatValue: 20,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
expect(result.current.value).toBe(20);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should increment value by default step (1)', () => {
|
|
29
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: 5 }));
|
|
30
|
+
act(() => {
|
|
31
|
+
result.current.increment();
|
|
32
|
+
});
|
|
33
|
+
expect(result.current.value).toBe(6);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should increment value by provided step', () => {
|
|
37
|
+
const { result } = renderHook(() =>
|
|
38
|
+
useNumberFormat({ initialValue: 5, step: 5 })
|
|
39
|
+
);
|
|
40
|
+
act(() => {
|
|
41
|
+
result.current.increment();
|
|
42
|
+
});
|
|
43
|
+
expect(result.current.value).toBe(10);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should not increment if value is undefined', () => {
|
|
47
|
+
const { result } = renderHook(() => useNumberFormat({}));
|
|
48
|
+
act(() => {
|
|
49
|
+
result.current.increment();
|
|
50
|
+
});
|
|
51
|
+
expect(result.current.value).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should decrement value by default step (1)', () => {
|
|
55
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: 5 }));
|
|
56
|
+
act(() => {
|
|
57
|
+
result.current.decrement();
|
|
58
|
+
});
|
|
59
|
+
expect(result.current.value).toBe(4);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should decrement value by provided step', () => {
|
|
63
|
+
const { result } = renderHook(() =>
|
|
64
|
+
useNumberFormat({ initialValue: 10, step: 5 })
|
|
65
|
+
);
|
|
66
|
+
act(() => {
|
|
67
|
+
result.current.decrement();
|
|
68
|
+
});
|
|
69
|
+
expect(result.current.value).toBe(5);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should not decrement if value is undefined', () => {
|
|
73
|
+
const { result } = renderHook(() => useNumberFormat({}));
|
|
74
|
+
act(() => {
|
|
75
|
+
result.current.decrement();
|
|
76
|
+
});
|
|
77
|
+
expect(result.current.value).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should not decrement below zero', () => {
|
|
81
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: 0 }));
|
|
82
|
+
act(() => {
|
|
83
|
+
result.current.decrement();
|
|
84
|
+
});
|
|
85
|
+
expect(result.current.value).toBe(0);
|
|
86
|
+
|
|
87
|
+
const { result: result2 } = renderHook(() =>
|
|
88
|
+
useNumberFormat({ initialValue: 2, step: 3 })
|
|
89
|
+
);
|
|
90
|
+
act(() => {
|
|
91
|
+
result2.current.decrement();
|
|
92
|
+
});
|
|
93
|
+
expect(result2.current.value).toBe(2); // Stays at 2 because 2 - 3 < 0
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should not decrement if value is 0 and step is positive', () => {
|
|
97
|
+
const { result } = renderHook(() =>
|
|
98
|
+
useNumberFormat({ initialValue: 0, step: 5 })
|
|
99
|
+
);
|
|
100
|
+
act(() => {
|
|
101
|
+
result.current.decrement();
|
|
102
|
+
});
|
|
103
|
+
expect(result.current.value).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle decrement when value - step is less than 0', () => {
|
|
107
|
+
const { result } = renderHook(() =>
|
|
108
|
+
useNumberFormat({ initialValue: 1, step: 2 })
|
|
109
|
+
);
|
|
110
|
+
act(() => {
|
|
111
|
+
result.current.decrement();
|
|
112
|
+
});
|
|
113
|
+
// The current logic: `prevValue !== undefined && prevValue - step >= 0 ? prevValue - step : prevValue`
|
|
114
|
+
// So, if 1 - 2 < 0, it should return prevValue (1)
|
|
115
|
+
expect(result.current.value).toBe(1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('increment should set value to undefined if prevValue is negative (though this state is unlikely given NumericFormat constraints)', () => {
|
|
119
|
+
// This tests the hook's internal logic, even if NumericFormat prevents negative inputs.
|
|
120
|
+
const { result } = renderHook(() => useNumberFormat({ initialValue: -5 }));
|
|
121
|
+
act(() => {
|
|
122
|
+
result.current.increment();
|
|
123
|
+
});
|
|
124
|
+
// prevValue (-5) is not >= 0, so it becomes undefined
|
|
125
|
+
expect(result.current.value).toBeUndefined();
|
|
126
|
+
});
|
|
127
|
+
});
|