native-document 1.0.169 → 1.0.172
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 +62 -1
- package/docs/vitepress-conventions.md +2 -2
- package/package.json +11 -4
- package/src/components/BaseComponent.js +43 -1
- package/src/components/accordion/Accordion.js +1 -0
- package/src/components/accordion/AccordionItem.js +1 -0
- package/src/components/alert/Alert.js +4 -0
- package/src/components/avatar/Avatar.js +1 -0
- package/src/components/avatar/AvatarGroup.js +1 -0
- package/src/components/badge/Badge.js +1 -0
- package/src/components/breadcrumb/BreadCrumb.js +4 -0
- package/src/components/button/Button.js +1 -0
- package/src/components/card/Card.js +1 -0
- package/src/components/context-menu/ContextMenu.js +2 -0
- package/src/components/context-menu/ContextMenuGroup.js +2 -0
- package/src/components/context-menu/ContextMenuItem.js +2 -0
- package/src/components/divider/Divider.js +2 -0
- package/src/components/dropdown/Dropdown.js +1 -0
- package/src/components/dropdown/DropdownDivider.js +1 -0
- package/src/components/dropdown/DropdownGroup.js +1 -0
- package/src/components/dropdown/DropdownItem.js +1 -0
- package/src/components/dropdown/DropdownTrigger.js +1 -0
- package/src/components/form/FormControl.js +1 -0
- package/src/components/form/field/Field.js +1 -0
- package/src/components/form/field/types/StringField.js +1 -1
- package/src/components/icon/Icon.js +1 -0
- package/src/components/list/ListItem.js +1 -0
- package/src/components/menu/Menu.js +3 -0
- package/src/components/menu/MenuDivider.js +1 -0
- package/src/components/menu/MenuGroup.js +1 -0
- package/src/components/menu/MenuItem.js +1 -0
- package/src/components/menu/MenuLink.js +1 -0
- package/src/components/modal/Modal.js +4 -0
- package/src/components/pagination/Pagination.js +1 -0
- package/src/components/popover/Popover.js +1 -0
- package/src/components/popover/PopoverFooter.js +1 -0
- package/src/components/popover/PopoverHeader.js +1 -0
- package/src/components/progress/Progress.js +5 -0
- package/src/components/skeleton/Skeleton.js +4 -0
- package/src/components/slider/Slider.js +1 -0
- package/src/components/spacer/Spacer.js +1 -0
- package/src/components/spinner/Spinner.js +1 -0
- package/src/components/splitter/Splitter.js +1 -0
- package/src/components/splitter/SplitterGutter.js +6 -0
- package/src/components/splitter/SplitterPanel.js +1 -0
- package/src/components/stacks/AbsoluteStack.js +1 -0
- package/src/components/stacks/FixedStack.js +1 -0
- package/src/components/stacks/HStack.js +1 -0
- package/src/components/stacks/PositionStack.js +1 -0
- package/src/components/stacks/RelativeStack.js +1 -0
- package/src/components/stacks/Stack.js +1 -0
- package/src/components/stacks/VStack.js +1 -0
- package/src/components/stepper/Stepper.js +1 -0
- package/src/components/stepper/StepperStep.js +1 -0
- package/src/components/switch/Switch.js +1 -0
- package/src/components/table/DataTable.js +1 -0
- package/src/components/table/SimpleTable.js +1 -0
- package/src/components/tabs/Tabs.js +1 -0
- package/src/components/toast/Toast.js +1 -0
- package/src/components/tooltip/Tooltip.js +1 -0
- package/src/core/data/MemoryManager.js +6 -1
- package/src/core/data/ObservableItem.js +28 -10
- package/src/core/elements/anchor/anchor.js +4 -3
- package/src/core/elements/anchor/one-child-anchor-overwriting.js +4 -4
- package/src/core/elements/content-formatter.js +19 -0
- package/src/core/elements/control/for-each-array-cache.js +59 -0
- package/src/core/elements/control/for-each-array.js +15 -23
- package/src/core/wrappers/AttributesWrapper.js +12 -12
- package/src/core/wrappers/ElementCreator.js +33 -10
- package/src/core/wrappers/HtmlElementWrapper.js +9 -4
- package/src/core/wrappers/NDElement.js +23 -8
- package/src/core/wrappers/NdPrototype.js +31 -39
- package/src/core/wrappers/constants.js +12 -12
- package/src/core/wrappers/prototypes/nd-element-extensions.js +127 -57
- package/src/ui/components/accordion/AccordionItemRender.js +29 -7
- package/src/ui/components/alert/AlertRender.js +6 -0
- package/src/ui/components/breadcrumb/BreadcrumbRender.js +9 -1
- package/src/ui/components/button/ButtonRender.js +3 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +2 -0
- package/src/ui/components/dropdown/DropdownRender.js +3 -0
- package/src/ui/components/dropdown/item/DropdownItemRender.js +6 -1
- package/src/ui/components/form/FieldCollectionRender.js +4 -0
- package/src/ui/components/form/fields/CheckboxFieldRender.js +4 -0
- package/src/ui/components/form/fields/CheckboxGroupFieldRender.js +4 -0
- package/src/ui/components/form/fields/RadioFieldRender.js +4 -0
- package/src/ui/components/form/fields/RangeFieldRender.js +4 -0
- package/src/ui/components/form/fields/SelectFieldRender.js +5 -0
- package/src/ui/components/form/fields/SliderFieldRender.js +3 -0
- package/src/ui/components/icon/tabler/TablerIconRender.js +0 -1
- package/src/ui/components/list/item/ListItemRender.js +4 -0
- package/src/ui/components/menu/MenuItemRender.js +8 -0
- package/src/ui/components/menu/MenuLinkRender.js +11 -0
- package/src/ui/components/menu/MenuRender.js +5 -0
- package/src/ui/components/menu/helpers.js +2 -1
- package/src/ui/components/modal/ModalRender.js +8 -2
- package/src/ui/components/progress/ProgressRender.js +9 -0
- package/src/ui/components/skeleton/SkeletonRender.js +2 -0
- package/src/ui/components/spinner/SpinnerRender.js +3 -0
- package/src/ui/components/splitter/SplitterGutterRender.js +4 -0
- package/src/ui/components/splitter/SplitterPanelRender.js +4 -0
- package/src/ui/components/splitter/SplitterRender.js +2 -0
- package/src/ui/components/stepper/StepperRender.js +2 -1
- package/src/ui/components/stepper/StepperStepRender.js +8 -1
- package/src/ui/components/switch/SwitchRender.js +3 -0
- package/src/ui/components/table/data-table/DataTableRender.js +4 -0
- package/src/ui/components/table/simple-table/SimpleTableRender.js +2 -0
- package/src/ui/components/toast/ToastRender.js +4 -0
- package/src/ui/utils/aria.js +19 -0
- package/tests/integration/ui/attributes-wrapper.test.js +177 -0
- package/tests/integration/ui/for-each-array.test.js +167 -0
- package/tests/integration/ui/lifecycle.test.js +146 -0
- package/tests/integration/ui/show-if.test.js +168 -0
- package/tests/unit/core/observable-array.test.js +404 -0
- package/tests/unit/core/observable-item.test.js +428 -0
- package/tests/unit/core/observable-object.test.js +239 -0
- package/tests/unit/core/observable-resource.test.js +323 -0
- package/tests/unit/core/store.test.js +347 -0
- package/tests/unit/router/route-matching.test.js +169 -0
- package/tests/unit/router/router-guards.test.js +189 -0
- package/tests/unit/utils/cache.test.js +120 -0
- package/tests/unit/utils/filters.test.js +394 -0
- package/types/elements.d.ts +5 -0
- package/vitest.config.js +15 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { Observable } from '../../../src/core/data/Observable';
|
|
3
|
+
import ObservableArray from '../../../src/core/data/ObservableArray';
|
|
4
|
+
|
|
5
|
+
const arr = (items = []) => new ObservableArray(items);
|
|
6
|
+
|
|
7
|
+
describe('ObservableArray', () => {
|
|
8
|
+
|
|
9
|
+
// -- constructor ----------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
describe('constructor', () => {
|
|
12
|
+
it('initializes with an empty array by default', () => {
|
|
13
|
+
const obs = arr([]);
|
|
14
|
+
expect(obs.val()).toEqual([]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('initializes with provided items', () => {
|
|
18
|
+
const obs = arr([1, 2, 3]);
|
|
19
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('throws if target is not an array', () => {
|
|
23
|
+
expect(() => new ObservableArray('not-an-array')).toThrow();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// -- val() ----------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
describe('val()', () => {
|
|
30
|
+
it('returns the current array', () => {
|
|
31
|
+
const obs = arr([1, 2, 3]);
|
|
32
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns updated array after mutation', () => {
|
|
36
|
+
const obs = arr([1, 2]);
|
|
37
|
+
obs.push(3);
|
|
38
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// -- push() ---------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
describe('push()', () => {
|
|
45
|
+
it('adds an item to the end', () => {
|
|
46
|
+
const obs = arr([1, 2]);
|
|
47
|
+
obs.push(3);
|
|
48
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('notifies subscribers', () => {
|
|
52
|
+
const obs = arr([]);
|
|
53
|
+
const cb = vi.fn();
|
|
54
|
+
obs.subscribe(cb);
|
|
55
|
+
obs.push(1);
|
|
56
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('increases length by 1', () => {
|
|
60
|
+
const obs = arr([1, 2]);
|
|
61
|
+
obs.push(3);
|
|
62
|
+
expect(obs.length).toBe(3);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// -- remove() -------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
describe('remove()', () => {
|
|
69
|
+
it('removes item at given index', () => {
|
|
70
|
+
const obs = arr(['a', 'b', 'c']);
|
|
71
|
+
obs.remove(1);
|
|
72
|
+
expect(obs.val()).toEqual(['a', 'c']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('notifies subscribers', () => {
|
|
76
|
+
const obs = arr([1, 2, 3]);
|
|
77
|
+
const cb = vi.fn();
|
|
78
|
+
obs.subscribe(cb);
|
|
79
|
+
obs.remove(0);
|
|
80
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('returns the removed item', () => {
|
|
84
|
+
const obs = arr(['a', 'b', 'c']);
|
|
85
|
+
const result = obs.remove(1);
|
|
86
|
+
expect(result).toEqual(['b']);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// -- removeItem() ---------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
describe('removeItem()', () => {
|
|
93
|
+
it('removes a specific item by reference', () => {
|
|
94
|
+
const obs = arr([1, 2, 3]);
|
|
95
|
+
obs.removeItem(2);
|
|
96
|
+
expect(obs.val()).toEqual([1, 3]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('does nothing if item not found', () => {
|
|
100
|
+
const obs = arr([1, 2, 3]);
|
|
101
|
+
obs.removeItem(99);
|
|
102
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('notifies subscribers', () => {
|
|
106
|
+
const obs = arr([1, 2, 3]);
|
|
107
|
+
const cb = vi.fn();
|
|
108
|
+
obs.subscribe(cb);
|
|
109
|
+
obs.removeItem(2);
|
|
110
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// -- clear() --------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
describe('clear()', () => {
|
|
117
|
+
it('empties the array', () => {
|
|
118
|
+
const obs = arr([1, 2, 3]);
|
|
119
|
+
obs.clear();
|
|
120
|
+
expect(obs.val()).toEqual([]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('notifies subscribers', () => {
|
|
124
|
+
const obs = arr([1, 2, 3]);
|
|
125
|
+
const cb = vi.fn();
|
|
126
|
+
obs.subscribe(cb);
|
|
127
|
+
obs.clear();
|
|
128
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('does nothing and does not notify if already empty', () => {
|
|
132
|
+
const obs = arr([]);
|
|
133
|
+
const cb = vi.fn();
|
|
134
|
+
obs.subscribe(cb);
|
|
135
|
+
obs.clear();
|
|
136
|
+
expect(cb).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// -- merge() --------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
describe('merge()', () => {
|
|
143
|
+
it('appends multiple items', () => {
|
|
144
|
+
const obs = arr([1, 2]);
|
|
145
|
+
obs.merge([3, 4]);
|
|
146
|
+
expect(obs.val()).toEqual([1, 2, 3, 4]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('notifies subscribers', () => {
|
|
150
|
+
const obs = arr([]);
|
|
151
|
+
const cb = vi.fn();
|
|
152
|
+
obs.subscribe(cb);
|
|
153
|
+
obs.merge([1, 2]);
|
|
154
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// -- set() ----------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
describe('set()', () => {
|
|
161
|
+
it('replaces the entire array', () => {
|
|
162
|
+
const obs = arr([1, 2, 3]);
|
|
163
|
+
obs.set([4, 5]);
|
|
164
|
+
expect(obs.val()).toEqual([4, 5]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('notifies subscribers', () => {
|
|
168
|
+
const obs = arr([1]);
|
|
169
|
+
const cb = vi.fn();
|
|
170
|
+
obs.subscribe(cb);
|
|
171
|
+
obs.set([2, 3]);
|
|
172
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// -- at() -----------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
describe('at()', () => {
|
|
179
|
+
it('returns item at given index', () => {
|
|
180
|
+
const obs = arr(['a', 'b', 'c']);
|
|
181
|
+
expect(obs.at(1)).toBe('b');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('returns undefined for out-of-bounds index', () => {
|
|
185
|
+
const obs = arr(['a']);
|
|
186
|
+
expect(obs.at(99)).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// -- swap() ---------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
describe('swap()', () => {
|
|
193
|
+
it('swaps items at two indices', () => {
|
|
194
|
+
const obs = arr(['a', 'b', 'c']);
|
|
195
|
+
obs.swap(0, 2);
|
|
196
|
+
expect(obs.val()).toEqual(['c', 'b', 'a']);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('notifies subscribers', () => {
|
|
200
|
+
const obs = arr([1, 2, 3]);
|
|
201
|
+
const cb = vi.fn();
|
|
202
|
+
obs.subscribe(cb);
|
|
203
|
+
obs.swap(0, 1);
|
|
204
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// -- swapItems() ----------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
describe('swapItems()', () => {
|
|
211
|
+
it('swaps two items by reference', () => {
|
|
212
|
+
const obs = arr(['a', 'b', 'c']);
|
|
213
|
+
obs.swapItems('a', 'c');
|
|
214
|
+
expect(obs.val()).toEqual(['c', 'b', 'a']);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// -- insertAfter() --------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
describe('insertAfter()', () => {
|
|
221
|
+
it('inserts item after target', () => {
|
|
222
|
+
const obs = arr(['a', 'c']);
|
|
223
|
+
obs.insertAfter('b', 'a');
|
|
224
|
+
expect(obs.val()).toEqual(['a', 'b', 'c']);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('notifies subscribers', () => {
|
|
228
|
+
const obs = arr(['a', 'c']);
|
|
229
|
+
const cb = vi.fn();
|
|
230
|
+
obs.subscribe(cb);
|
|
231
|
+
obs.insertAfter('b', 'a');
|
|
232
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// -- count() --------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
describe('count()', () => {
|
|
239
|
+
it('counts items matching condition', () => {
|
|
240
|
+
const obs = arr([1, 2, 3, 4, 5]);
|
|
241
|
+
expect(obs.count((n) => n > 3)).toBe(2);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// -- isNotEmpty() ---------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
describe('isNotEmpty()', () => {
|
|
248
|
+
it('returns false for empty array', () => {
|
|
249
|
+
const obs = arr([]);
|
|
250
|
+
expect(obs.isNotEmpty().val()).toBe(false);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('returns true for non-empty array', () => {
|
|
254
|
+
const obs = arr([1]);
|
|
255
|
+
expect(obs.isNotEmpty().val()).toBe(true);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('updates reactively when items are added', () => {
|
|
259
|
+
const obs = arr([]);
|
|
260
|
+
const isEmpty = obs.isNotEmpty();
|
|
261
|
+
expect(isEmpty.val()).toBe(false);
|
|
262
|
+
obs.push(1);
|
|
263
|
+
expect(isEmpty.val()).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// -- empty() --------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
describe('empty()', () => {
|
|
270
|
+
it('returns true for empty array', () => {
|
|
271
|
+
expect(arr([]).empty()).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('returns false for non-empty array', () => {
|
|
275
|
+
expect(arr([1]).empty()).toBe(false);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// -- where() --------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
describe('where()', () => {
|
|
282
|
+
it('returns a filtered observable array', () => {
|
|
283
|
+
const obs = arr([1, 2, 3, 4, 5]);
|
|
284
|
+
const filtered = obs.where({ _: (n) => n > 3 });
|
|
285
|
+
expect(filtered.val()).toEqual([4, 5]);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('updates when source changes', () => {
|
|
289
|
+
const obs = arr([1, 2, 3]);
|
|
290
|
+
const filtered = obs.where({ _: (n) => n > 2 });
|
|
291
|
+
obs.push(4);
|
|
292
|
+
expect(filtered.val()).toEqual([3, 4]);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// -- sync() ---------------------------------------------------------------
|
|
297
|
+
|
|
298
|
+
describe('sync()', () => {
|
|
299
|
+
it('mirrors initial content to target', () => {
|
|
300
|
+
const source = arr([1, 2, 3]);
|
|
301
|
+
const target = arr([]);
|
|
302
|
+
source.sync(target);
|
|
303
|
+
expect(target.val()).toEqual([1, 2, 3]);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('mirrors push to target', () => {
|
|
307
|
+
const source = arr([1]);
|
|
308
|
+
const target = arr([]);
|
|
309
|
+
source.sync(target);
|
|
310
|
+
source.push(2);
|
|
311
|
+
expect(target.val()).toEqual([1, 2]);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('stops syncing after unsync()', () => {
|
|
315
|
+
const source = arr([1]);
|
|
316
|
+
const target = arr([]);
|
|
317
|
+
const unsync = source.sync(target);
|
|
318
|
+
unsync();
|
|
319
|
+
source.push(2);
|
|
320
|
+
expect(target.val()).toEqual([1]);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('throws if target is not an ObservableArray', () => {
|
|
324
|
+
const source = arr([1]);
|
|
325
|
+
expect(() => source.sync({})).toThrow();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
// -- reset() --------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
describe('reset()', () => {
|
|
333
|
+
it('restores initial array when { reset: true }', () => {
|
|
334
|
+
const obs = new ObservableArray([1, 2, 3], { reset: true });
|
|
335
|
+
obs.push(4);
|
|
336
|
+
obs.reset();
|
|
337
|
+
expect(obs.val()).toEqual([1, 2, 3]);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('does nothing without { reset: true }', () => {
|
|
341
|
+
const obs = new ObservableArray([1, 2, 3]);
|
|
342
|
+
obs.push(4);
|
|
343
|
+
obs.reset();
|
|
344
|
+
expect(obs.val()).toEqual([1, 2, 3, 4]);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('deep clones the initial array — mutations do not affect reset value', () => {
|
|
348
|
+
const obs = new ObservableArray([{ x: 1 }], { reset: true });
|
|
349
|
+
obs.at(0).x = 99;
|
|
350
|
+
obs.reset();
|
|
351
|
+
expect(obs.val()[0].x).toBe(1);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('notifies subscribers after reset', () => {
|
|
355
|
+
const obs = new ObservableArray([1, 2], { reset: true });
|
|
356
|
+
obs.push(3);
|
|
357
|
+
const cb = vi.fn();
|
|
358
|
+
obs.subscribe(cb);
|
|
359
|
+
obs.reset();
|
|
360
|
+
expect(cb).toHaveBeenCalledOnce();
|
|
361
|
+
expect(obs.val()).toEqual([1, 2]);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// -- clone() --------------------------------------------------------------
|
|
366
|
+
|
|
367
|
+
describe('clone()', () => {
|
|
368
|
+
it('returns a new independent ObservableArray', () => {
|
|
369
|
+
const obs = arr([1, 2, 3]);
|
|
370
|
+
const clone = obs.clone();
|
|
371
|
+
expect(clone.val()).toEqual([1, 2, 3]);
|
|
372
|
+
expect(clone).not.toBe(obs);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('mutations on clone do not affect source', () => {
|
|
376
|
+
const obs = arr([1, 2]);
|
|
377
|
+
const clone = obs.clone();
|
|
378
|
+
clone.push(3);
|
|
379
|
+
expect(obs.val()).toEqual([1, 2]);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// -- deepSubscribe() ------------------------------------------------------
|
|
384
|
+
|
|
385
|
+
describe('deepSubscribe()', () => {
|
|
386
|
+
it('fires callback when a nested observable changes', async () => {
|
|
387
|
+
const inner = Observable(1);
|
|
388
|
+
const obs = arr([inner]);
|
|
389
|
+
const cb = vi.fn();
|
|
390
|
+
obs.deepSubscribe(cb);
|
|
391
|
+
inner.set(2);
|
|
392
|
+
// nextTick is used internally - wait for microtask queue
|
|
393
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
394
|
+
expect(cb).toHaveBeenCalled();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('returns an unsubscribe function', () => {
|
|
398
|
+
const obs = arr([Observable(1)]);
|
|
399
|
+
const unsub = obs.deepSubscribe(() => {});
|
|
400
|
+
expect(typeof unsub).toBe('function');
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
});
|