@happyvertical/smrt-ui 0.53.0 → 0.53.2
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/package.json +3 -3
- package/dist/actions/__tests__/ripple.test.js +0 -82
- package/dist/actions/__tests__/swipe-dismiss.test.js +0 -69
- package/dist/components/calendar/__tests__/CalendarView.test.js +0 -226
- package/dist/components/calendar/__tests__/calendar-model.test.js +0 -138
- package/dist/components/chat/__tests__/chat-primitives.test.js +0 -118
- package/dist/components/data/__tests__/CollectionList.test.js +0 -37
- package/dist/components/data/__tests__/DataTable.test.js +0 -807
- package/dist/components/data/__tests__/DataTableConformance.test.js +0 -212
- package/dist/components/data/__tests__/DataTableController.test.js +0 -371
- package/dist/components/data/__tests__/DataTableIdentity.test.js +0 -29
- package/dist/components/data/__tests__/DataTableLayout.test.js +0 -195
- package/dist/components/data/__tests__/DataTablePerformance.test.js +0 -21
- package/dist/components/data/__tests__/DataTableResponsive.test.js +0 -80
- package/dist/components/data/__tests__/DataTableVirtualization.test.js +0 -80
- package/dist/components/data/__tests__/DataTableVirtualizationComponent.test.js +0 -255
- package/dist/components/data/__tests__/SortableHeader.test.js +0 -101
- package/dist/components/data/__tests__/data-surface.test.js +0 -746
- package/dist/components/data/__tests__/data-table-helpers.test.js +0 -36
- package/dist/components/data/__tests__/data-table-surface.test.js +0 -49
- package/dist/components/data/__tests__/link-step-surface.test.js +0 -172
- package/dist/components/data/__tests__/list-sort-state.svelte.test.js +0 -71
- package/dist/components/data/__tests__/list-sort.test.js +0 -91
- package/dist/components/data/__tests__/mounted-data-surface.test.js +0 -794
- package/dist/components/data/__tests__/responsive-table.fixture.svelte +0 -29
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +0 -7
- package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +0 -1
- package/dist/components/data/__tests__/surface-navigation.test.js +0 -121
- package/dist/components/display/__tests__/ConfidenceBadge.test.js +0 -109
- package/dist/components/display/__tests__/CurrencyDisplay.test.js +0 -304
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte +0 -20
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts +0 -19
- package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts.map +0 -1
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte +0 -7
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts +0 -19
- package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts.map +0 -1
- package/dist/components/display/__tests__/DateDisplay.test.js +0 -195
- package/dist/components/display/__tests__/Icon.test.js +0 -223
- package/dist/components/display/__tests__/StatusBadge.test.js +0 -191
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +0 -181
- package/dist/components/feedback/__tests__/LoadingOverlay.test.js +0 -113
- package/dist/components/feedback/__tests__/Modal.test.js +0 -72
- package/dist/components/feedback/__tests__/PhoneSheet.test.js +0 -101
- package/dist/components/feedback/__tests__/ProgressBar.test.js +0 -89
- package/dist/components/feedback/__tests__/WorkingStrip.test.js +0 -172
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +0 -9
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +0 -19
- package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +0 -1
- package/dist/components/feedback/__tests__/toast.test.js +0 -190
- package/dist/components/forms/__tests__/CameraCapture.test.js +0 -504
- package/dist/components/forms/__tests__/Combobox.test.js +0 -354
- package/dist/components/forms/__tests__/ControlHitArea.test.js +0 -73
- package/dist/components/forms/__tests__/DictationButton.test.js +0 -176
- package/dist/components/forms/__tests__/Fieldset.test.js +0 -37
- package/dist/components/forms/__tests__/Form.test.js +0 -201
- package/dist/components/forms/__tests__/FormActionBar.test.js +0 -22
- package/dist/components/forms/__tests__/FormGroup.test.js +0 -48
- package/dist/components/forms/__tests__/Input.test.js +0 -49
- package/dist/components/forms/__tests__/Listbox.test.js +0 -303
- package/dist/components/forms/__tests__/MultiSelect.test.js +0 -281
- package/dist/components/forms/__tests__/SearchInput.test.js +0 -92
- package/dist/components/forms/__tests__/Select.test.js +0 -37
- package/dist/components/forms/__tests__/SignaturePad.test.js +0 -338
- package/dist/components/forms/__tests__/StagedControlReview.test.js +0 -702
- package/dist/components/forms/__tests__/Textarea.test.js +0 -39
- package/dist/components/forms/__tests__/Toggle.test.js +0 -87
- package/dist/components/forms/__tests__/camera-capture-session.test.js +0 -207
- package/dist/components/forms/__tests__/capture-form-retry.test.js +0 -166
- package/dist/components/forms/__tests__/capture-form.fixture.svelte +0 -38
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +0 -10
- package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/capture-test-env.d.ts +0 -70
- package/dist/components/forms/__tests__/capture-test-env.d.ts.map +0 -1
- package/dist/components/forms/__tests__/capture-test-env.js +0 -122
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte +0 -15
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +0 -14
- package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +0 -19
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +0 -11
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte +0 -47
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-controls.test.js +0 -128
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte +0 -41
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/control-interaction.test.js +0 -3421
- package/dist/components/forms/__tests__/control-proposals.test.js +0 -63
- package/dist/components/forms/__tests__/core-controls.fixture.svelte +0 -50
- package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/core-controls.test.js +0 -311
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte +0 -25
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/dictation-recording.test.js +0 -582
- package/dist/components/forms/__tests__/dictation.test.js +0 -486
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte +0 -28
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +0 -11
- package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte +0 -16
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +0 -31
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/form-retry-form.test.js +0 -107
- package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +0 -69
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +0 -29
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte +0 -27
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/long-press.test.js +0 -175
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +0 -27
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +0 -13
- package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/native-file-field.test.js +0 -177
- package/dist/components/forms/__tests__/place-picker.fixture.svelte +0 -33
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +0 -28
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/segmented-posting.test.js +0 -92
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte +0 -25
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts +0 -9
- package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/signature-pad-logic.test.js +0 -54
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte +0 -21
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte +0 -23
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/staged-review.fixture.svelte +0 -56
- package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts +0 -12
- package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/touch-density.fixture.svelte +0 -34
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +0 -8
- package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +0 -1
- package/dist/components/forms/__tests__/touch-density.test.js +0 -66
- package/dist/components/forms/form-retry/__tests__/boundaries.test.js +0 -23
- package/dist/components/forms/form-retry/__tests__/content-digest.test.js +0 -70
- package/dist/components/forms/form-retry/__tests__/controller.test.js +0 -802
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +0 -82
- package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +0 -1
- package/dist/components/forms/form-retry/__tests__/fake-kit.js +0 -180
- package/dist/components/forms/form-retry/__tests__/form-draft.test.js +0 -169
- package/dist/components/forms/form-retry/__tests__/ssr.test.js +0 -37
- package/dist/components/forms/form-retry/__tests__/storage.d.ts +0 -26
- package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +0 -1
- package/dist/components/forms/form-retry/__tests__/storage.js +0 -63
- package/dist/components/forms/form-retry/__tests__/submission-key.test.js +0 -236
- package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +0 -334
- package/dist/components/layout/__tests__/Container.test.js +0 -62
- package/dist/components/layout/__tests__/EmptyState.test.js +0 -83
- package/dist/components/layout/__tests__/Footer.test.js +0 -50
- package/dist/components/layout/__tests__/Grid.test.js +0 -121
- package/dist/components/layout/__tests__/Header.test.js +0 -48
- package/dist/components/layout/__tests__/Masthead.test.js +0 -93
- package/dist/components/layout/__tests__/PageHeader.test.js +0 -166
- package/dist/components/layout/__tests__/SummaryCard.test.js +0 -104
- package/dist/components/nav/__tests__/FilterChips.test.js +0 -94
- package/dist/components/nav/__tests__/LinkTabs.test.js +0 -117
- package/dist/components/nav/__tests__/Tabs.test.js +0 -128
- package/dist/components/roles/__tests__/RoleSelector.test.js +0 -134
- package/dist/components/ui/__tests__/Badge.test.js +0 -76
- package/dist/components/ui/__tests__/Button.test.js +0 -166
- package/dist/components/ui/__tests__/Card.test.js +0 -103
- package/dist/components/ui/__tests__/IconToggle.test.js +0 -59
- package/dist/components/ui/__tests__/IconToggleGroup.test.js +0 -30
- package/dist/components/ui/__tests__/Pagination.test.js +0 -113
- package/dist/components/ui/__tests__/Popover.test.js +0 -44
- package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +0 -125
- package/dist/components/ui/__tests__/gap-primitives-interactive.test.js +0 -112
- package/dist/components/ui/__tests__/gap-primitives.test.js +0 -84
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte +0 -13
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts +0 -19
- package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts.map +0 -1
- package/dist/components/ui/__tests__/interaction-primitives.test.js +0 -31
- package/dist/components/ui/__tests__/phone-touch-targets.test.js +0 -39
- package/dist/i18n/__tests__/i18n.test.js +0 -74
- package/dist/i18n/__tests__/render-parity.spec.js +0 -37
- package/dist/svelte/__tests__/playground.test.js +0 -20
- package/dist/themes/__tests__/control-boundary-contrast.test.js +0 -112
- package/dist/themes/__tests__/create-theme.test.js +0 -79
- package/dist/themes/__tests__/css-generator.test.js +0 -129
- package/dist/themes/__tests__/happyvertical-theme.test.js +0 -231
- package/dist/themes/__tests__/registry.test.js +0 -43
- package/dist/themes/__tests__/styles-lockstep.test.js +0 -32
- package/dist/themes/__tests__/theme-provider.test.js +0 -147
- package/dist/themes/__tests__/theme-script.test.js +0 -130
- package/dist/themes/__tests__/token-aliases.test.js +0 -178
- package/dist/utils/forms/__tests__/formatters.test.js +0 -27
- package/dist/utils/theme/__tests__/color.test.js +0 -89
- package/dist/utils/theme/__tests__/typography.test.js +0 -11
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `createFormRetry()` with smrt-ui's own `Form` (#3291, deferred on #3265).
|
|
3
|
-
*
|
|
4
|
-
* `Form` forwards Svelte attachments to its native `<form>` (#3265, shipped in
|
|
5
|
-
* #3331), so the pairing is SvelteKit's `enhance` through `fromAction` with
|
|
6
|
-
* the retry submit function, plus `retry.attach` for the hidden key field.
|
|
7
|
-
* `enhance` is the structural fake from the form-retry tests, attached as the
|
|
8
|
-
* real action and driven by real submit events; the action behind it records
|
|
9
|
-
* rows the way `runOnce()` does.
|
|
10
|
-
*/
|
|
11
|
-
import { fireEvent, render, screen } from '@testing-library/svelte';
|
|
12
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
|
-
import { fakeEnhanceAction, fakeRunOnceServer, } from '../form-retry/__tests__/fake-kit.js';
|
|
14
|
-
import { memoryStorage } from '../form-retry/__tests__/storage.js';
|
|
15
|
-
import { createFormRetry } from '../form-retry/controller.js';
|
|
16
|
-
import { resetFormRetryMemory } from '../form-retry/submission-key.js';
|
|
17
|
-
import FormRetryFormFixture from './form-retry-form.fixture.svelte';
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
resetFormRetryMemory();
|
|
20
|
-
sessionStorage.clear();
|
|
21
|
-
});
|
|
22
|
-
function setup() {
|
|
23
|
-
const server = fakeRunOnceServer();
|
|
24
|
-
const kit = fakeEnhanceAction(server);
|
|
25
|
-
const retry = createFormRetry({ form: 'report', storage: memoryStorage() });
|
|
26
|
-
const { container } = render(FormRetryFormFixture, {
|
|
27
|
-
props: { enhance: kit.action, retry },
|
|
28
|
-
});
|
|
29
|
-
const form = container.querySelector('form');
|
|
30
|
-
const title = screen.getByRole('textbox', { name: 'Title' });
|
|
31
|
-
const send = async () => {
|
|
32
|
-
await fireEvent.click(screen.getByRole('button', { name: 'Send' }));
|
|
33
|
-
};
|
|
34
|
-
const type = async (value) => {
|
|
35
|
-
await fireEvent.input(title, { target: { value } });
|
|
36
|
-
};
|
|
37
|
-
return { server, kit, retry, form, title, send, type };
|
|
38
|
-
}
|
|
39
|
-
describe('createFormRetry with an enhanced smrt-ui Form', () => {
|
|
40
|
-
it('keeps the key field inside Form and posts the key in the body', async () => {
|
|
41
|
-
const { server, kit, retry, form, send, type } = setup();
|
|
42
|
-
const token = retry.token;
|
|
43
|
-
const fields = form.querySelectorAll('input[name="submissionKey"]');
|
|
44
|
-
expect(fields).toHaveLength(1);
|
|
45
|
-
expect(fields[0].value).toBe(token);
|
|
46
|
-
await type('Hose');
|
|
47
|
-
await send();
|
|
48
|
-
await kit.settle();
|
|
49
|
-
expect(server.requests).toHaveLength(1);
|
|
50
|
-
expect(server.requests[0].get('submissionKey')).toBe(token);
|
|
51
|
-
expect(server.requests[0].get('title')).toBe('Hose');
|
|
52
|
-
});
|
|
53
|
-
it('refuses a double submit while the first is in flight', async () => {
|
|
54
|
-
const { server, kit, retry, send, type } = setup();
|
|
55
|
-
await type('Hose');
|
|
56
|
-
const held = server.hold();
|
|
57
|
-
await send();
|
|
58
|
-
await vi.waitFor(() => expect(retry.state.status).toBe('submitting'));
|
|
59
|
-
await send();
|
|
60
|
-
expect(retry.state.status).toBe('busy');
|
|
61
|
-
held.release();
|
|
62
|
-
await kit.settle();
|
|
63
|
-
expect(kit.enhanced.cancelled).toBe(1);
|
|
64
|
-
expect(server.requests).toHaveLength(1);
|
|
65
|
-
expect(server.rows).toHaveLength(1);
|
|
66
|
-
});
|
|
67
|
-
it('retries a lost response under the same key, recording one row', async () => {
|
|
68
|
-
const { server, kit, retry, title, send, type } = setup();
|
|
69
|
-
await type('Hose');
|
|
70
|
-
const token = retry.token;
|
|
71
|
-
server.next('lost-response');
|
|
72
|
-
await send();
|
|
73
|
-
await kit.settle();
|
|
74
|
-
expect(retry.state.status).toBe('transport-error');
|
|
75
|
-
expect(kit.enhanced.unmounted).toBe(0);
|
|
76
|
-
expect(title.value).toBe('Hose');
|
|
77
|
-
await send();
|
|
78
|
-
await kit.settle();
|
|
79
|
-
expect(server.requests.map((body) => body.get('submissionKey'))).toEqual([
|
|
80
|
-
token,
|
|
81
|
-
token,
|
|
82
|
-
]);
|
|
83
|
-
expect(server.rows).toHaveLength(1);
|
|
84
|
-
});
|
|
85
|
-
it('resets on success only when the form is unchanged', async () => {
|
|
86
|
-
const { server, kit, retry, title, send, type } = setup();
|
|
87
|
-
await type('First');
|
|
88
|
-
const token = retry.token;
|
|
89
|
-
const held = server.hold();
|
|
90
|
-
await send();
|
|
91
|
-
await vi.waitFor(() => expect(retry.state.status).toBe('submitting'));
|
|
92
|
-
await type('Second');
|
|
93
|
-
held.release();
|
|
94
|
-
await kit.settle();
|
|
95
|
-
expect(retry.state.status).toBe('success');
|
|
96
|
-
expect(title.value).toBe('Second');
|
|
97
|
-
expect(retry.token).toBe(token);
|
|
98
|
-
await send();
|
|
99
|
-
await kit.settle();
|
|
100
|
-
expect(server.rows.map((row) => row.fields.title)).toEqual([
|
|
101
|
-
['First'],
|
|
102
|
-
['Second'],
|
|
103
|
-
]);
|
|
104
|
-
expect(title.value).toBe('');
|
|
105
|
-
expect(retry.token).not.toBe(token);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `createFormRetry()` around #3331's choice controls (#3291). `Listbox`,
|
|
3
|
-
* `Combobox` and `MultiSelect` post their selection through hidden inputs, so
|
|
4
|
-
* the "unchanged since submit" check must compare hidden inputs too — every
|
|
5
|
-
* one except the retry helper's own key field, which rotates by design.
|
|
6
|
-
*
|
|
7
|
-
* A selection changed while a submit is in flight was never sent: the success
|
|
8
|
-
* must keep it (and its key), and the next submit must post it.
|
|
9
|
-
*/
|
|
10
|
-
import { render } from '@testing-library/svelte';
|
|
11
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
12
|
-
import { fakeEnhance, fakeRunOnceServer, } from '../form-retry/__tests__/fake-kit.js';
|
|
13
|
-
import { memoryStorage } from '../form-retry/__tests__/storage.js';
|
|
14
|
-
import { createFormRetry } from '../form-retry/controller.js';
|
|
15
|
-
import { resetFormRetryMemory } from '../form-retry/submission-key.js';
|
|
16
|
-
import HiddenControlsForm from './hidden-controls-form.fixture.svelte';
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
resetFormRetryMemory();
|
|
19
|
-
sessionStorage.clear();
|
|
20
|
-
});
|
|
21
|
-
function setup() {
|
|
22
|
-
const view = render(HiddenControlsForm);
|
|
23
|
-
const form = view.container.querySelector('form');
|
|
24
|
-
const server = fakeRunOnceServer();
|
|
25
|
-
const retry = createFormRetry({ form: 'choices', storage: memoryStorage() });
|
|
26
|
-
retry.attach(form);
|
|
27
|
-
const kit = fakeEnhance(form, retry.enhance(), server);
|
|
28
|
-
const title = form.querySelector('[name=title]');
|
|
29
|
-
title.value = 'Hose';
|
|
30
|
-
return { ...view, form, server, retry, kit, title };
|
|
31
|
-
}
|
|
32
|
-
const posted = (body, name) => body.getAll(name);
|
|
33
|
-
describe('hidden-input-backed controls under createFormRetry', () => {
|
|
34
|
-
it.each([
|
|
35
|
-
['Listbox', 'listbox', 'b'],
|
|
36
|
-
['Combobox', 'combobox', 'b'],
|
|
37
|
-
['MultiSelect', 'multi', ['a', 'b']],
|
|
38
|
-
])('a %s change during an in-flight submit survives the success', async (_name, prop, next) => {
|
|
39
|
-
const { form, server, retry, kit, rerender } = setup();
|
|
40
|
-
const token = retry.token;
|
|
41
|
-
const field = prop;
|
|
42
|
-
const before = posted(new FormData(form), field);
|
|
43
|
-
const held = server.hold();
|
|
44
|
-
const first = kit.submit();
|
|
45
|
-
await vi.waitFor(() => expect(retry.state.status).toBe('submitting'));
|
|
46
|
-
await rerender({ [prop]: next });
|
|
47
|
-
const changed = posted(new FormData(form), field);
|
|
48
|
-
expect(changed).not.toEqual(before);
|
|
49
|
-
held.release();
|
|
50
|
-
await first;
|
|
51
|
-
expect(retry.state.status).toBe('success');
|
|
52
|
-
expect(kit.updates).toEqual([{ reset: false }]);
|
|
53
|
-
expect(posted(new FormData(form), field)).toEqual(changed);
|
|
54
|
-
expect(retry.token).toBe(token);
|
|
55
|
-
await kit.submit();
|
|
56
|
-
expect(posted(server.requests[0], field)).toEqual(before);
|
|
57
|
-
expect(posted(server.requests[1], field)).toEqual(changed);
|
|
58
|
-
expect(server.rows).toHaveLength(2);
|
|
59
|
-
});
|
|
60
|
-
it('still resets when only the key field changed', async () => {
|
|
61
|
-
const { form, retry, kit, title } = setup();
|
|
62
|
-
const token = retry.token;
|
|
63
|
-
await kit.submit();
|
|
64
|
-
expect(kit.updates).toEqual([{ reset: true }]);
|
|
65
|
-
expect(title.value).toBe('');
|
|
66
|
-
expect(retry.token).not.toBe(token);
|
|
67
|
-
expect(form.querySelector('[name=submissionKey]').value).toBe(retry.token);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import Combobox from '../Combobox.svelte';
|
|
3
|
-
import Listbox from '../Listbox.svelte';
|
|
4
|
-
import MultiSelect from '../MultiSelect.svelte';
|
|
5
|
-
|
|
6
|
-
let {
|
|
7
|
-
listbox = $bindable('a'),
|
|
8
|
-
combobox = $bindable('a'),
|
|
9
|
-
multi = $bindable<Array<string | number>>(['a']),
|
|
10
|
-
}: {
|
|
11
|
-
listbox?: string | number;
|
|
12
|
-
combobox?: string;
|
|
13
|
-
multi?: Array<string | number>;
|
|
14
|
-
} = $props();
|
|
15
|
-
|
|
16
|
-
const options = [
|
|
17
|
-
{ value: 'a', label: 'Alpha' },
|
|
18
|
-
{ value: 'b', label: 'Bravo' },
|
|
19
|
-
];
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<!-- #3331's choice controls post through hidden inputs, not visible fields. -->
|
|
23
|
-
<form method="POST">
|
|
24
|
-
<input name="title" aria-label="Title" />
|
|
25
|
-
<Listbox name="listbox" label="Listbox" bind:value={listbox} {options} interaction={false} />
|
|
26
|
-
<Combobox name="combobox" label="Combobox" bind:value={combobox} {options} interaction={false} />
|
|
27
|
-
<MultiSelect name="multi" label="Multi" bind:values={multi} {options} interaction={false} />
|
|
28
|
-
<button type="submit">Send</button>
|
|
29
|
-
</form>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
type $$ComponentProps = {
|
|
2
|
-
listbox?: string | number;
|
|
3
|
-
combobox?: string;
|
|
4
|
-
multi?: Array<string | number>;
|
|
5
|
-
};
|
|
6
|
-
declare const HiddenControlsForm: import("svelte").Component<$$ComponentProps, {}, "combobox" | "listbox" | "multi">;
|
|
7
|
-
type HiddenControlsForm = ReturnType<typeof HiddenControlsForm>;
|
|
8
|
-
export default HiddenControlsForm;
|
|
9
|
-
//# sourceMappingURL=hidden-controls-form.fixture.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hidden-controls-form.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/hidden-controls-form.fixture.svelte.ts"],"names":[],"mappings":"AAOC,KAAK,gBAAgB,GAAI;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CAChC,CAAC;AA6BF,QAAA,MAAM,kBAAkB,oFAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { ControlInteractionRegistry } from '../control-interaction.js';
|
|
3
|
-
import Form from '../Form.svelte';
|
|
4
|
-
import Listbox from '../Listbox.svelte';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
name?: string;
|
|
8
|
-
unnamed?: boolean;
|
|
9
|
-
value?: string | number;
|
|
10
|
-
disabled?: boolean;
|
|
11
|
-
fieldsetDisabled?: boolean;
|
|
12
|
-
registry?: ControlInteractionRegistry;
|
|
13
|
-
}
|
|
14
|
-
let {
|
|
15
|
-
name = 'vendor',
|
|
16
|
-
unnamed = false,
|
|
17
|
-
value = $bindable(),
|
|
18
|
-
disabled = false,
|
|
19
|
-
fieldsetDisabled = false,
|
|
20
|
-
registry,
|
|
21
|
-
}: Props = $props();
|
|
22
|
-
</script>
|
|
23
|
-
<Form formId="vendor-form" interactionRegistry={registry} aria-label="Vendor form">
|
|
24
|
-
<fieldset disabled={fieldsetDisabled}>
|
|
25
|
-
<Listbox name={unnamed ? undefined : name} label="Vendor" options={[{ value: 42, label: 'Plate supplier' }, { value: 'steel', label: 'Steel supplier' }, { value: 'blocked', label: 'Disabled supplier', disabled: true }]} bind:value {disabled} interaction={{ id: 'vendor' }} />
|
|
26
|
-
</fieldset>
|
|
27
|
-
</Form>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ControlInteractionRegistry } from '../control-interaction.js';
|
|
2
|
-
interface Props {
|
|
3
|
-
name?: string;
|
|
4
|
-
unnamed?: boolean;
|
|
5
|
-
value?: string | number;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
fieldsetDisabled?: boolean;
|
|
8
|
-
registry?: ControlInteractionRegistry;
|
|
9
|
-
}
|
|
10
|
-
declare const ListboxForm: import("svelte").Component<Props, {}, "value">;
|
|
11
|
-
type ListboxForm = ReturnType<typeof ListboxForm>;
|
|
12
|
-
export default ListboxForm;
|
|
13
|
-
//# sourceMappingURL=listbox-form.fixture.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"listbox-form.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/listbox-form.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAK5E,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,0BAA0B,CAAC;CACvC;AAuBD,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Long press vs tap vs drag: only a still, primary press held for the delay
|
|
3
|
-
* fires; moving past the tolerance (a drag or scroll) or a native dragstart
|
|
4
|
-
* cancels it; the click after a fired press is swallowed; the context menu is
|
|
5
|
-
* suppressed only when opted in and only during a tracked press.
|
|
6
|
-
*/
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
|
-
import { createLongPress, longPress } from '../long-press.js';
|
|
9
|
-
function pointer(type, init = {}) {
|
|
10
|
-
return {
|
|
11
|
-
type,
|
|
12
|
-
isPrimary: true,
|
|
13
|
-
pointerId: 1,
|
|
14
|
-
pointerType: 'touch',
|
|
15
|
-
button: 0,
|
|
16
|
-
clientX: 100,
|
|
17
|
-
clientY: 100,
|
|
18
|
-
target: document.body,
|
|
19
|
-
...init,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
describe('createLongPress', () => {
|
|
23
|
-
beforeEach(() => vi.useFakeTimers());
|
|
24
|
-
afterEach(() => vi.useRealTimers());
|
|
25
|
-
it('fires after the delay when the pointer stays still', () => {
|
|
26
|
-
const onLongPress = vi.fn();
|
|
27
|
-
const press = createLongPress({ onLongPress });
|
|
28
|
-
press.handlePointerDown(pointer('pointerdown'));
|
|
29
|
-
vi.advanceTimersByTime(499);
|
|
30
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
31
|
-
// Small wobble inside the tolerance still counts as still.
|
|
32
|
-
press.handlePointerMove(pointer('pointermove', { clientX: 106, clientY: 104 }));
|
|
33
|
-
vi.advanceTimersByTime(1);
|
|
34
|
-
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
35
|
-
expect(onLongPress.mock.calls[0][0]).toMatchObject({
|
|
36
|
-
pointerType: 'touch',
|
|
37
|
-
});
|
|
38
|
-
expect(press.fired).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
it('a quick tap never fires and reports a cancel', () => {
|
|
41
|
-
const onLongPress = vi.fn();
|
|
42
|
-
const onCancel = vi.fn();
|
|
43
|
-
const press = createLongPress({ onLongPress, onCancel });
|
|
44
|
-
press.handlePointerDown(pointer('pointerdown'));
|
|
45
|
-
vi.advanceTimersByTime(200);
|
|
46
|
-
press.handlePointerUp(pointer('pointerup'));
|
|
47
|
-
vi.advanceTimersByTime(1000);
|
|
48
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
49
|
-
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
50
|
-
});
|
|
51
|
-
it('moving past the tolerance is a drag, not a long press', () => {
|
|
52
|
-
const onLongPress = vi.fn();
|
|
53
|
-
const press = createLongPress({ onLongPress, moveTolerance: 10 });
|
|
54
|
-
press.handlePointerDown(pointer('pointerdown'));
|
|
55
|
-
vi.advanceTimersByTime(300);
|
|
56
|
-
press.handlePointerMove(pointer('pointermove', { clientX: 115 }));
|
|
57
|
-
vi.advanceTimersByTime(1000);
|
|
58
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
it('a native dragstart cancels a pending press', () => {
|
|
61
|
-
const onLongPress = vi.fn();
|
|
62
|
-
const press = createLongPress({ onLongPress });
|
|
63
|
-
press.handlePointerDown(pointer('pointerdown', { pointerType: 'mouse' }));
|
|
64
|
-
press.handleDragStart();
|
|
65
|
-
vi.advanceTimersByTime(1000);
|
|
66
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
67
|
-
});
|
|
68
|
-
it('ignores right clicks and secondary pointers', () => {
|
|
69
|
-
const onLongPress = vi.fn();
|
|
70
|
-
const press = createLongPress({ onLongPress });
|
|
71
|
-
press.handlePointerDown(pointer('pointerdown', { pointerType: 'mouse', button: 2 }));
|
|
72
|
-
press.handlePointerDown(pointer('pointerdown', { isPrimary: false }));
|
|
73
|
-
vi.advanceTimersByTime(1000);
|
|
74
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
75
|
-
});
|
|
76
|
-
it('only counts presses the filter accepts, about the element it returns', () => {
|
|
77
|
-
const picture = document.createElement('img');
|
|
78
|
-
const onLongPress = vi.fn();
|
|
79
|
-
const press = createLongPress({
|
|
80
|
-
onLongPress,
|
|
81
|
-
filter: (event) => (event.target === picture ? picture : null),
|
|
82
|
-
});
|
|
83
|
-
press.handlePointerDown(pointer('pointerdown', { target: document.body }));
|
|
84
|
-
vi.advanceTimersByTime(600);
|
|
85
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
86
|
-
press.handlePointerDown(pointer('pointerdown', { target: picture }));
|
|
87
|
-
vi.advanceTimersByTime(600);
|
|
88
|
-
expect(onLongPress.mock.calls[0][0].target).toBe(picture);
|
|
89
|
-
});
|
|
90
|
-
it('reports the release after a fired press and swallows the next click', () => {
|
|
91
|
-
const onRelease = vi.fn();
|
|
92
|
-
const press = createLongPress({ onLongPress: () => { }, onRelease });
|
|
93
|
-
press.handlePointerDown(pointer('pointerdown'));
|
|
94
|
-
vi.advanceTimersByTime(500);
|
|
95
|
-
press.handlePointerUp(pointer('pointerup'));
|
|
96
|
-
expect(onRelease).toHaveBeenCalledTimes(1);
|
|
97
|
-
const click = {
|
|
98
|
-
preventDefault: vi.fn(),
|
|
99
|
-
stopPropagation: vi.fn(),
|
|
100
|
-
};
|
|
101
|
-
press.handleClick(click);
|
|
102
|
-
expect(click.preventDefault).toHaveBeenCalled();
|
|
103
|
-
// Only the one click right after the release.
|
|
104
|
-
const later = {
|
|
105
|
-
preventDefault: vi.fn(),
|
|
106
|
-
stopPropagation: vi.fn(),
|
|
107
|
-
};
|
|
108
|
-
press.handleClick(later);
|
|
109
|
-
expect(later.preventDefault).not.toHaveBeenCalled();
|
|
110
|
-
});
|
|
111
|
-
it('suppresses the context menu only when opted in and during a press', () => {
|
|
112
|
-
const plain = createLongPress({ onLongPress: () => { } });
|
|
113
|
-
const guarded = createLongPress({
|
|
114
|
-
onLongPress: () => { },
|
|
115
|
-
preventContextMenu: true,
|
|
116
|
-
});
|
|
117
|
-
const idleMenu = { preventDefault: vi.fn() };
|
|
118
|
-
guarded.handleContextMenu(idleMenu);
|
|
119
|
-
expect(idleMenu.preventDefault).not.toHaveBeenCalled();
|
|
120
|
-
plain.handlePointerDown(pointer('pointerdown'));
|
|
121
|
-
guarded.handlePointerDown(pointer('pointerdown'));
|
|
122
|
-
vi.advanceTimersByTime(500);
|
|
123
|
-
const plainMenu = { preventDefault: vi.fn() };
|
|
124
|
-
const guardedMenu = { preventDefault: vi.fn() };
|
|
125
|
-
plain.handleContextMenu(plainMenu);
|
|
126
|
-
guarded.handleContextMenu(guardedMenu);
|
|
127
|
-
expect(plainMenu.preventDefault).not.toHaveBeenCalled();
|
|
128
|
-
expect(guardedMenu.preventDefault).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe('longPress action', () => {
|
|
132
|
-
beforeEach(() => vi.useFakeTimers());
|
|
133
|
-
afterEach(() => vi.useRealTimers());
|
|
134
|
-
function dispatch(target, type, init = {}) {
|
|
135
|
-
const event = new Event(type, { bubbles: true, cancelable: true });
|
|
136
|
-
Object.assign(event, {
|
|
137
|
-
isPrimary: true,
|
|
138
|
-
pointerId: 7,
|
|
139
|
-
pointerType: 'mouse',
|
|
140
|
-
button: 0,
|
|
141
|
-
clientX: 10,
|
|
142
|
-
clientY: 10,
|
|
143
|
-
...init,
|
|
144
|
-
});
|
|
145
|
-
target.dispatchEvent(event);
|
|
146
|
-
return event;
|
|
147
|
-
}
|
|
148
|
-
it('wires pointer events on the node and the window, and cleans up', () => {
|
|
149
|
-
const node = document.createElement('div');
|
|
150
|
-
document.body.append(node);
|
|
151
|
-
const onLongPress = vi.fn();
|
|
152
|
-
const action = longPress(node, { onLongPress });
|
|
153
|
-
dispatch(node, 'pointerdown');
|
|
154
|
-
vi.advanceTimersByTime(500);
|
|
155
|
-
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
156
|
-
dispatch(window, 'pointerup');
|
|
157
|
-
action.destroy();
|
|
158
|
-
dispatch(node, 'pointerdown');
|
|
159
|
-
vi.advanceTimersByTime(500);
|
|
160
|
-
expect(onLongPress).toHaveBeenCalledTimes(1);
|
|
161
|
-
node.remove();
|
|
162
|
-
});
|
|
163
|
-
it('a drag on the node (move past tolerance) never fires', () => {
|
|
164
|
-
const node = document.createElement('div');
|
|
165
|
-
document.body.append(node);
|
|
166
|
-
const onLongPress = vi.fn();
|
|
167
|
-
const action = longPress(node, { onLongPress });
|
|
168
|
-
dispatch(node, 'pointerdown');
|
|
169
|
-
dispatch(window, 'pointermove', { clientX: 40 });
|
|
170
|
-
vi.advanceTimersByTime(800);
|
|
171
|
-
expect(onLongPress).not.toHaveBeenCalled();
|
|
172
|
-
action.destroy();
|
|
173
|
-
node.remove();
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { ControlInteractionRegistry } from '../control-interaction.js';
|
|
3
|
-
import Form from '../Form.svelte';
|
|
4
|
-
import MultiSelect from '../MultiSelect.svelte';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
name?: string;
|
|
8
|
-
unnamed?: boolean;
|
|
9
|
-
values?: Array<string | number>;
|
|
10
|
-
disabled?: boolean;
|
|
11
|
-
fieldsetDisabled?: boolean;
|
|
12
|
-
registry?: ControlInteractionRegistry;
|
|
13
|
-
}
|
|
14
|
-
let {
|
|
15
|
-
name = 'roles',
|
|
16
|
-
unnamed = false,
|
|
17
|
-
values = $bindable([]),
|
|
18
|
-
disabled = false,
|
|
19
|
-
fieldsetDisabled = false,
|
|
20
|
-
registry,
|
|
21
|
-
}: Props = $props();
|
|
22
|
-
</script>
|
|
23
|
-
<Form formId="roles-form" interactionRegistry={registry} aria-label="Roles form">
|
|
24
|
-
<fieldset disabled={fieldsetDisabled}>
|
|
25
|
-
<MultiSelect name={unnamed ? undefined : name} label="Roles" options={[{ value: 42, label: 'Operator' }, { value: 'admin', label: 'Administrator' }, { value: 'blocked', label: 'Disabled role', disabled: true }]} bind:values {disabled} interaction={{ id: 'roles' }} />
|
|
26
|
-
</fieldset>
|
|
27
|
-
</Form>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ControlInteractionRegistry } from '../control-interaction.js';
|
|
2
|
-
interface Props {
|
|
3
|
-
name?: string;
|
|
4
|
-
unnamed?: boolean;
|
|
5
|
-
values?: Array<string | number>;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
fieldsetDisabled?: boolean;
|
|
8
|
-
registry?: ControlInteractionRegistry;
|
|
9
|
-
}
|
|
10
|
-
declare const MultiselectForm: import("svelte").Component<Props, {}, "values">;
|
|
11
|
-
type MultiselectForm = ReturnType<typeof MultiselectForm>;
|
|
12
|
-
export default MultiselectForm;
|
|
13
|
-
//# sourceMappingURL=multiselect-form.fixture.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multiselect-form.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/multiselect-form.fixture.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAK5E,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,0BAA0B,CAAC;CACvC;AAuBD,QAAA,MAAM,eAAe,iDAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Native file-field posting strategies (smrt#3290).
|
|
3
|
-
*/
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { assignFileWithDataTransfer, attachFormDataFallback, attachFormResetListener, clearFileInput, detectNativeFileFieldStrategy, fileNameForType, } from '../native-file-field.js';
|
|
6
|
-
import { submittedEntries, TestDataTransfer, TestFormDataEvent, } from './capture-test-env.js';
|
|
7
|
-
function fileInput() {
|
|
8
|
-
const input = document.createElement('input');
|
|
9
|
-
input.type = 'file';
|
|
10
|
-
return input;
|
|
11
|
-
}
|
|
12
|
-
const env = (overrides) => ({
|
|
13
|
-
File,
|
|
14
|
-
createFileInput: fileInput,
|
|
15
|
-
...overrides,
|
|
16
|
-
});
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
document.body.innerHTML = '';
|
|
19
|
-
});
|
|
20
|
-
describe('detectNativeFileFieldStrategy', () => {
|
|
21
|
-
it('prefers DataTransfer when an input accepts its files', () => {
|
|
22
|
-
expect(detectNativeFileFieldStrategy(env({
|
|
23
|
-
DataTransfer: TestDataTransfer,
|
|
24
|
-
FormDataEvent: TestFormDataEvent,
|
|
25
|
-
}))).toBe('data-transfer');
|
|
26
|
-
});
|
|
27
|
-
it('falls back to the formdata event without DataTransfer', () => {
|
|
28
|
-
expect(detectNativeFileFieldStrategy(env({ FormDataEvent: TestFormDataEvent }))).toBe('formdata-event');
|
|
29
|
-
});
|
|
30
|
-
it('falls back when the DataTransfer constructor throws (old engines)', () => {
|
|
31
|
-
class IllegalConstructor {
|
|
32
|
-
constructor() {
|
|
33
|
-
throw new TypeError('Illegal constructor');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
expect(detectNativeFileFieldStrategy(env({
|
|
37
|
-
DataTransfer: IllegalConstructor,
|
|
38
|
-
FormDataEvent: TestFormDataEvent,
|
|
39
|
-
}))).toBe('formdata-event');
|
|
40
|
-
});
|
|
41
|
-
it('falls back when the input rejects the files assignment', () => {
|
|
42
|
-
class PlainDataTransfer {
|
|
43
|
-
items = { add: () => null };
|
|
44
|
-
files = [];
|
|
45
|
-
}
|
|
46
|
-
expect(detectNativeFileFieldStrategy(env({
|
|
47
|
-
DataTransfer: PlainDataTransfer,
|
|
48
|
-
FormDataEvent: TestFormDataEvent,
|
|
49
|
-
}))).toBe('formdata-event');
|
|
50
|
-
});
|
|
51
|
-
it('reports none when neither API exists, and during SSR', () => {
|
|
52
|
-
expect(detectNativeFileFieldStrategy(env({}))).toBe('none');
|
|
53
|
-
expect(detectNativeFileFieldStrategy({})).toBe('none');
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe('assignFileWithDataTransfer / clearFileInput', () => {
|
|
57
|
-
it('puts the file into the input so a native submit posts it, then clears it', () => {
|
|
58
|
-
const form = document.createElement('form');
|
|
59
|
-
const input = fileInput();
|
|
60
|
-
input.name = 'photo';
|
|
61
|
-
form.append(input);
|
|
62
|
-
const file = new File(['jpeg'], 'photo.jpg', { type: 'image/jpeg' });
|
|
63
|
-
expect(assignFileWithDataTransfer(input, file, TestDataTransfer)).toBe(true);
|
|
64
|
-
expect(new FormData(form).get('photo')).toBe(file);
|
|
65
|
-
clearFileInput(input);
|
|
66
|
-
const empty = new FormData(form).get('photo');
|
|
67
|
-
expect(empty.name).toBe('');
|
|
68
|
-
expect(empty.size).toBe(0);
|
|
69
|
-
});
|
|
70
|
-
it('returns false without a DataTransfer constructor', () => {
|
|
71
|
-
expect(assignFileWithDataTransfer(fileInput(), new File([], 'x'), undefined)).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
describe('attachFormDataFallback', () => {
|
|
75
|
-
it('appends the committed file to its own form only, until disposed', () => {
|
|
76
|
-
const form = document.createElement('form');
|
|
77
|
-
const other = document.createElement('form');
|
|
78
|
-
document.body.append(form, other);
|
|
79
|
-
const file = new File(['png'], 'signature.png', { type: 'image/png' });
|
|
80
|
-
const dispose = attachFormDataFallback(document, () => ({
|
|
81
|
-
form,
|
|
82
|
-
name: 'signature',
|
|
83
|
-
file,
|
|
84
|
-
}));
|
|
85
|
-
expect(submittedEntries(form).getAll('signature')).toEqual([file]);
|
|
86
|
-
expect(submittedEntries(other).has('signature')).toBe(false);
|
|
87
|
-
dispose();
|
|
88
|
-
expect(submittedEntries(form).has('signature')).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
it('posts the native empty-file entry when nothing is committed', () => {
|
|
91
|
-
const form = document.createElement('form');
|
|
92
|
-
document.body.append(form);
|
|
93
|
-
attachFormDataFallback(document, () => ({
|
|
94
|
-
form,
|
|
95
|
-
name: 'photo',
|
|
96
|
-
file: null,
|
|
97
|
-
}));
|
|
98
|
-
const entry = submittedEntries(form).get('photo');
|
|
99
|
-
expect(entry.name).toBe('');
|
|
100
|
-
expect(entry.size).toBe(0);
|
|
101
|
-
expect(entry.type).toBe('application/octet-stream');
|
|
102
|
-
});
|
|
103
|
-
it('contributes nothing while its source input is disabled by a fieldset', () => {
|
|
104
|
-
document.body.innerHTML =
|
|
105
|
-
'<form><fieldset><input type="file" hidden /></fieldset></form>';
|
|
106
|
-
const form = document.querySelector('form');
|
|
107
|
-
const fieldset = form.querySelector('fieldset');
|
|
108
|
-
const source = form.querySelector('input');
|
|
109
|
-
const file = new File(['png'], 'signature.png', { type: 'image/png' });
|
|
110
|
-
let committed = file;
|
|
111
|
-
attachFormDataFallback(document, () => ({
|
|
112
|
-
form,
|
|
113
|
-
name: 'signature',
|
|
114
|
-
file: committed,
|
|
115
|
-
source,
|
|
116
|
-
}));
|
|
117
|
-
expect(submittedEntries(form).getAll('signature')).toEqual([file]);
|
|
118
|
-
fieldset.disabled = true;
|
|
119
|
-
// Both a committed file and the empty entry are filtered, as the browser
|
|
120
|
-
// filters a disabled native control.
|
|
121
|
-
expect(submittedEntries(form).has('signature')).toBe(false);
|
|
122
|
-
committed = null;
|
|
123
|
-
expect(submittedEntries(form).has('signature')).toBe(false);
|
|
124
|
-
fieldset.disabled = false;
|
|
125
|
-
expect(submittedEntries(form).get('signature').size).toBe(0);
|
|
126
|
-
});
|
|
127
|
-
it('contributes nothing when the entry is unavailable', () => {
|
|
128
|
-
const form = document.createElement('form');
|
|
129
|
-
document.body.append(form);
|
|
130
|
-
const read = vi.fn(() => null);
|
|
131
|
-
attachFormDataFallback(document, read);
|
|
132
|
-
expect([...submittedEntries(form).keys()]).toEqual([]);
|
|
133
|
-
expect(read).toHaveBeenCalled();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe('fileNameForType', () => {
|
|
137
|
-
it('maps known image types and falls back to bin', () => {
|
|
138
|
-
expect(fileNameForType('photo', 'image/jpeg')).toBe('photo.jpg');
|
|
139
|
-
expect(fileNameForType('photo', 'image/png')).toBe('photo.png');
|
|
140
|
-
expect(fileNameForType('photo', 'image/webp')).toBe('photo.webp');
|
|
141
|
-
expect(fileNameForType('photo', 'image/heic')).toBe('photo.bin');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
describe('attachFormResetListener', () => {
|
|
145
|
-
it('fires for the owning form only, read at dispatch time, until disposed', () => {
|
|
146
|
-
document.body.innerHTML = '<form id="a"></form><form id="b"></form>';
|
|
147
|
-
const a = document.getElementById('a');
|
|
148
|
-
const b = document.getElementById('b');
|
|
149
|
-
let owner = a;
|
|
150
|
-
const onReset = vi.fn();
|
|
151
|
-
const dispose = attachFormResetListener(document, () => owner, onReset);
|
|
152
|
-
b.reset();
|
|
153
|
-
expect(onReset).not.toHaveBeenCalled();
|
|
154
|
-
a.reset();
|
|
155
|
-
expect(onReset).toHaveBeenCalledTimes(1);
|
|
156
|
-
owner = b;
|
|
157
|
-
b.reset();
|
|
158
|
-
expect(onReset).toHaveBeenCalledTimes(2);
|
|
159
|
-
owner = null;
|
|
160
|
-
b.reset();
|
|
161
|
-
expect(onReset).toHaveBeenCalledTimes(2);
|
|
162
|
-
owner = a;
|
|
163
|
-
dispose();
|
|
164
|
-
a.reset();
|
|
165
|
-
expect(onReset).toHaveBeenCalledTimes(2);
|
|
166
|
-
});
|
|
167
|
-
it('is not hidden by a page listener that stops propagation', () => {
|
|
168
|
-
document.body.innerHTML = '<form></form>';
|
|
169
|
-
const form = document.querySelector('form');
|
|
170
|
-
form.addEventListener('reset', (event) => event.stopPropagation());
|
|
171
|
-
const onReset = vi.fn();
|
|
172
|
-
const dispose = attachFormResetListener(document, () => form, onReset);
|
|
173
|
-
form.reset();
|
|
174
|
-
expect(onReset).toHaveBeenCalledTimes(1);
|
|
175
|
-
dispose();
|
|
176
|
-
});
|
|
177
|
-
});
|