@weasel-js/labkit 1.7.1 → 1.7.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/dist/chrome/index.js +4 -4
- package/dist/{chunk-GAHELLJT.js → chunk-RODIY7HI.js} +7 -7
- package/dist/{chunk-GAHELLJT.js.map → chunk-RODIY7HI.js.map} +1 -1
- package/dist/{chunk-4KSC7NXL.js → chunk-U6UQVGFP.js} +1984 -1892
- package/dist/chunk-U6UQVGFP.js.map +1 -0
- package/dist/chunk-WFALF2X2.js +302 -0
- package/dist/chunk-WFALF2X2.js.map +1 -0
- package/dist/{chunk-JFOJESJF.js → chunk-WWJDPNZZ.js} +22 -22
- package/dist/chunk-WWJDPNZZ.js.map +1 -0
- package/dist/{chunk-CEGFXTBD.js → chunk-XRX5S5NT.js} +4 -4
- package/dist/{chunk-CEGFXTBD.js.map → chunk-XRX5S5NT.js.map} +1 -1
- package/dist/{chunk-FB4MIZOO.js → chunk-XZDCEMEL.js} +6 -6
- package/dist/chunk-XZDCEMEL.js.map +1 -0
- package/dist/controls/index.d.ts +55 -3
- package/dist/controls/index.js +3 -2
- package/dist/fonts/oswald-tabular-digits.woff2 +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +135 -134
- package/dist/index.js.map +1 -1
- package/dist/layers/index.js +1 -1
- package/dist/passthrough/weasel-ui.js +1 -1
- package/dist/primitives/index.js +3 -3
- package/dist/styles.css +228 -11
- package/package.json +11 -11
- package/src/annotations/capture.browser.test.ts +9 -3
- package/src/canvas/Stage.test.tsx +34 -25
- package/src/chrome/labRegions.test.tsx +37 -26
- package/src/chrome/regions/PaletteRegion.test.tsx +2 -2
- package/src/controls/ControlMatrix.less +245 -0
- package/src/controls/ControlMatrix.stories.less +13 -0
- package/src/controls/ControlMatrix.stories.tsx +135 -0
- package/src/controls/ControlMatrix.test.tsx +206 -0
- package/src/controls/ControlMatrix.tsx +446 -0
- package/src/controls/ControlPanel.tsx +4 -2
- package/src/controls/index.ts +6 -0
- package/src/dragdrop/dragDrop.test.tsx +34 -33
- package/src/index.ts +6 -0
- package/src/lab/Lab.chrome.test.tsx +15 -10
- package/src/lab/Lab.nebula.test.tsx +5 -5
- package/src/lab/Lab.surface.test.tsx +30 -21
- package/src/lab/Lab.test.tsx +75 -74
- package/src/lab/Lab.undock.test.tsx +15 -10
- package/src/lab/LabHeader.test.tsx +15 -14
- package/src/lab/LabPalette.test.tsx +12 -7
- package/src/lab/LabShell.less +1 -2
- package/src/lab/LabZoom.test.tsx +55 -44
- package/src/lab/Workspace.surface.test.tsx +16 -13
- package/src/lab/Workspace.test.tsx +32 -27
- package/src/overview/OverviewMarks.test.tsx +9 -8
- package/src/overview/TrialOverview.test.tsx +18 -17
- package/src/primitives/FpsMeter.less +1 -2
- package/src/primitives/JobProgress.less +1 -2
- package/src/primitives/ScaleIndicator.less +1 -1
- package/src/primitives/StatusBar.less +1 -1
- package/src/specimen/Specimen.less +1 -0
- package/src/styles.less +3 -0
- package/src/trial/Trial.annotations.persist.test.tsx +20 -9
- package/src/trial/Trial.annotations.test.tsx +46 -41
- package/src/trial/Trial.canvas.test.tsx +21 -17
- package/src/trial/Trial.config.test.tsx +34 -27
- package/src/trial/Trial.loupe.test.tsx +27 -18
- package/src/trial/Trial.targets.test.tsx +9 -8
- package/src/trial/Trial.test.tsx +51 -46
- package/src/trial/Trial.trialId.test.tsx +7 -6
- package/dist/chunk-4KSC7NXL.js.map +0 -1
- package/dist/chunk-FB4MIZOO.js.map +0 -1
- package/dist/chunk-JFOJESJF.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { screen } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
3
|
import { describe, expect, it } from 'vitest';
|
|
3
4
|
import type { Instrument } from '../instrument/types';
|
|
4
5
|
import { Lab } from './Lab';
|
|
@@ -12,13 +13,13 @@ const Stub: Instrument = {
|
|
|
12
13
|
const Other: Instrument = { ...Stub, name: 'Other' };
|
|
13
14
|
|
|
14
15
|
describe('<LabHeader>', () => {
|
|
15
|
-
it('offers an add-trial button when the lab has one instrument', () => {
|
|
16
|
-
|
|
16
|
+
it('offers an add-trial button when the lab has one instrument', async () => {
|
|
17
|
+
await renderSettled(<Lab instruments={[Stub]} defaultInstrument="Stub" />);
|
|
17
18
|
expect(screen.getByRole('button', { name: /add trial/i })).toBeInTheDocument();
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
it('offers a menu instead when the lab has several', () => {
|
|
21
|
-
|
|
21
|
+
it('offers a menu instead when the lab has several', async () => {
|
|
22
|
+
await renderSettled(<Lab instruments={[Stub, Other]} defaultInstrument="Stub" />);
|
|
22
23
|
// Which instrument to add is now a choice, so the button opens a list.
|
|
23
24
|
expect(screen.getByRole('button', { name: 'Add trial' })).toHaveAttribute('aria-haspopup');
|
|
24
25
|
});
|
|
@@ -26,7 +27,7 @@ describe('<LabHeader>', () => {
|
|
|
26
27
|
it('adds a trial of the instrument chosen from the menu', async () => {
|
|
27
28
|
const { default: userEvent } = await import('@testing-library/user-event');
|
|
28
29
|
const user = userEvent.setup();
|
|
29
|
-
|
|
30
|
+
await renderSettled(<Lab instruments={[Stub, Other]} defaultInstrument="Stub" />);
|
|
30
31
|
expect(screen.queryAllByLabelText(/^Trial Other$/)).toHaveLength(0);
|
|
31
32
|
await user.click(screen.getByRole('button', { name: 'Add trial' }));
|
|
32
33
|
await user.click(screen.getByRole('menuitem', { name: 'Other' }));
|
|
@@ -37,7 +38,7 @@ describe('<LabHeader>', () => {
|
|
|
37
38
|
const { default: userEvent } = await import('@testing-library/user-event');
|
|
38
39
|
const user = userEvent.setup();
|
|
39
40
|
const titled: Instrument = { ...Other, title: 'Other / Titled' };
|
|
40
|
-
|
|
41
|
+
await renderSettled(<Lab instruments={[Stub, titled]} defaultInstrument="Stub" />);
|
|
41
42
|
await user.click(screen.getByRole('button', { name: 'Add trial' }));
|
|
42
43
|
expect(screen.getByRole('menuitem', { name: 'Stub' })).toBeInTheDocument();
|
|
43
44
|
await user.click(screen.getByRole('menuitem', { name: 'Other / Titled' }));
|
|
@@ -47,27 +48,27 @@ describe('<LabHeader>', () => {
|
|
|
47
48
|
it('adds a trial when the button is used', async () => {
|
|
48
49
|
const { default: userEvent } = await import('@testing-library/user-event');
|
|
49
50
|
const user = userEvent.setup();
|
|
50
|
-
|
|
51
|
+
await renderSettled(<Lab instruments={[Stub]} defaultInstrument="Stub" />);
|
|
51
52
|
expect(screen.getAllByLabelText(/^Trial Stub$/)).toHaveLength(1);
|
|
52
53
|
await user.click(screen.getByRole('button', { name: /add trial/i }));
|
|
53
54
|
expect(screen.getAllByLabelText(/^Trial Stub$/)).toHaveLength(2);
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
it('leaves the add-trial control out when the lab says so', () => {
|
|
57
|
-
const { unmount } =
|
|
57
|
+
it('leaves the add-trial control out when the lab says so', async () => {
|
|
58
|
+
const { unmount } = await renderSettled(
|
|
58
59
|
<Lab instruments={[Stub, Other]} defaultInstrument="Stub" addTrial={false} />,
|
|
59
60
|
);
|
|
60
61
|
expect(screen.queryByRole('button', { name: /add trial/i })).toBeNull();
|
|
61
62
|
expect(screen.getByRole('button', { name: /^Theme: Auto/ })).toBeInTheDocument();
|
|
62
63
|
unmount();
|
|
63
|
-
|
|
64
|
+
await renderSettled(<Lab instruments={[Stub]} defaultInstrument="Stub" addTrial={false} />);
|
|
64
65
|
expect(screen.queryByRole('button', { name: /add trial/i })).toBeNull();
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
it('rotates the color mode from one button', async () => {
|
|
68
69
|
const { default: userEvent } = await import('@testing-library/user-event');
|
|
69
70
|
const user = userEvent.setup();
|
|
70
|
-
|
|
71
|
+
await renderSettled(<Lab instruments={[Stub]} defaultInstrument="Stub" />);
|
|
71
72
|
expect(screen.queryByRole('radiogroup')).toBeNull();
|
|
72
73
|
for (const [now, next] of [
|
|
73
74
|
['Auto', 'Light'],
|
|
@@ -82,8 +83,8 @@ describe('<LabHeader>', () => {
|
|
|
82
83
|
expect(screen.getByRole('button', { name: /^Theme: Auto/ })).toBeInTheDocument();
|
|
83
84
|
});
|
|
84
85
|
|
|
85
|
-
it('puts the theme switch after the header content a consumer passes', () => {
|
|
86
|
-
|
|
86
|
+
it('puts the theme switch after the header content a consumer passes', async () => {
|
|
87
|
+
await renderSettled(
|
|
87
88
|
<Lab instruments={[Stub]} defaultInstrument="Stub">
|
|
88
89
|
<button type="button">Consumer</button>
|
|
89
90
|
</Lab>,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { fireEvent,
|
|
1
|
+
import { fireEvent, screen } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
3
|
import { describe, expect, it } from 'vitest';
|
|
3
4
|
import type { Instrument } from '../instrument/types';
|
|
4
5
|
import { Lab } from './Lab';
|
|
@@ -16,19 +17,23 @@ const tools = [
|
|
|
16
17
|
];
|
|
17
18
|
|
|
18
19
|
describe('the lab palette', () => {
|
|
19
|
-
it('does not render when the lab declares no tools', () => {
|
|
20
|
-
|
|
20
|
+
it('does not render when the lab declares no tools', async () => {
|
|
21
|
+
await renderSettled(<Lab title="T" instruments={[bare]} defaultInstrument="Bare" />);
|
|
21
22
|
expect(screen.queryByRole('toolbar', { name: 'Tools' })).toBeNull();
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
it('renders one button per declared tool', () => {
|
|
25
|
-
|
|
25
|
+
it('renders one button per declared tool', async () => {
|
|
26
|
+
await renderSettled(
|
|
27
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" tools={tools} />,
|
|
28
|
+
);
|
|
26
29
|
expect(screen.getByRole('button', { name: 'Pick' })).toBeInTheDocument();
|
|
27
30
|
expect(screen.getByRole('button', { name: 'Pan' })).toBeInTheDocument();
|
|
28
31
|
});
|
|
29
32
|
|
|
30
|
-
it('marks the chosen tool current after a click', () => {
|
|
31
|
-
|
|
33
|
+
it('marks the chosen tool current after a click', async () => {
|
|
34
|
+
await renderSettled(
|
|
35
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" tools={tools} />,
|
|
36
|
+
);
|
|
32
37
|
fireEvent.click(screen.getByRole('button', { name: 'Pan' }));
|
|
33
38
|
expect(screen.getByRole('button', { name: 'Pan' })).toHaveAttribute('aria-current', 'true');
|
|
34
39
|
});
|
package/src/lab/LabShell.less
CHANGED
package/src/lab/LabZoom.test.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { act, fireEvent,
|
|
1
|
+
import { act, fireEvent, screen, within } from '@testing-library/react';
|
|
2
2
|
import {
|
|
3
3
|
ActionsProvider,
|
|
4
4
|
type ActionsRegistry,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useActionsRegistry,
|
|
9
9
|
useGestureDispatcher,
|
|
10
10
|
} from '@weasel-js/core';
|
|
11
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
11
12
|
import { type ReactNode, useEffect } from 'react';
|
|
12
13
|
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
|
13
14
|
import { defineInstrument } from '../instrument/defineInstrument';
|
|
@@ -54,16 +55,16 @@ function Capture({ children }: { children?: ReactNode }) {
|
|
|
54
55
|
const zoomBar = () => within(screen.getByRole('toolbar', { name: 'Zoom' }));
|
|
55
56
|
const readout = () => zoomBar().getByRole('button', { name: /^Zoom \d+%|^Zoom unavailable/ });
|
|
56
57
|
const press = (name: string) =>
|
|
57
|
-
act(() => {
|
|
58
|
+
act(async () => {
|
|
58
59
|
fireEvent.click(zoomBar().getByRole('button', { name }));
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
const MOD = /mac/i.test(navigator.platform || navigator.userAgent)
|
|
62
63
|
? { metaKey: true }
|
|
63
64
|
: { ctrlKey: true };
|
|
64
|
-
function key(k: string): boolean {
|
|
65
|
+
async function key(k: string): Promise<boolean> {
|
|
65
66
|
let prevented = false;
|
|
66
|
-
act(() => {
|
|
67
|
+
await act(async () => {
|
|
67
68
|
prevented = !fireEvent.keyDown(window, { key: k, ...MOD });
|
|
68
69
|
});
|
|
69
70
|
return prevented;
|
|
@@ -111,113 +112,123 @@ function stageZoom(container: HTMLElement, index = 0): number {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
describe("the lab header's zoom controls", () => {
|
|
114
|
-
it('step the focused trial through its camera and read its zoom back', () => {
|
|
115
|
-
const { container } =
|
|
115
|
+
it('step the focused trial through its camera and read its zoom back', async () => {
|
|
116
|
+
const { container } = await renderSettled(
|
|
117
|
+
<Lab instruments={[staged]} defaultInstrument="Staged" />,
|
|
118
|
+
);
|
|
116
119
|
expect(readout()).toHaveTextContent('100%');
|
|
117
|
-
press('Zoom in');
|
|
120
|
+
await press('Zoom in');
|
|
118
121
|
expect(stageZoom(container)).toBeCloseTo(1.25);
|
|
119
122
|
expect(readout()).toHaveTextContent('125%');
|
|
120
|
-
press('Zoom out');
|
|
123
|
+
await press('Zoom out');
|
|
121
124
|
expect(stageZoom(container)).toBeCloseTo(1);
|
|
122
|
-
press('Zoom out');
|
|
125
|
+
await press('Zoom out');
|
|
123
126
|
expect(readout()).toHaveTextContent('80%');
|
|
124
127
|
});
|
|
125
128
|
|
|
126
|
-
it('reset to actual size from the readout', () => {
|
|
127
|
-
const { container } =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
it('reset to actual size from the readout', async () => {
|
|
130
|
+
const { container } = await renderSettled(
|
|
131
|
+
<Lab instruments={[staged]} defaultInstrument="Staged" />,
|
|
132
|
+
);
|
|
133
|
+
await press('Zoom out');
|
|
134
|
+
await press('Zoom out');
|
|
135
|
+
await act(async () => {
|
|
131
136
|
fireEvent.click(readout());
|
|
132
137
|
});
|
|
133
138
|
expect(stageZoom(container)).toBe(1);
|
|
134
139
|
expect(readout()).toHaveTextContent('100%');
|
|
135
140
|
});
|
|
136
141
|
|
|
137
|
-
it("stop at the camera's own limit, and say so", () => {
|
|
138
|
-
const { container } =
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
it("stop at the camera's own limit, and say so", async () => {
|
|
143
|
+
const { container } = await renderSettled(
|
|
144
|
+
<Lab instruments={[staged]} defaultInstrument="Staged" />,
|
|
145
|
+
);
|
|
146
|
+
await press('Zoom in');
|
|
147
|
+
await press('Zoom in');
|
|
141
148
|
expect(stageZoom(container)).toBe(1.5);
|
|
142
149
|
expect(zoomBar().getByRole('button', { name: 'Zoom in' })).toBeDisabled();
|
|
143
150
|
expect(zoomBar().getByRole('button', { name: 'Zoom out' })).toBeEnabled();
|
|
144
151
|
});
|
|
145
152
|
|
|
146
|
-
it('answer Mod+=, Mod+- and Mod+0, claiming the key from the browser', () => {
|
|
147
|
-
const { container } =
|
|
148
|
-
|
|
153
|
+
it('answer Mod+=, Mod+- and Mod+0, claiming the key from the browser', async () => {
|
|
154
|
+
const { container } = await renderSettled(
|
|
155
|
+
<Lab instruments={[staged]} defaultInstrument="Staged" />,
|
|
156
|
+
);
|
|
157
|
+
expect(await key('=')).toBe(true);
|
|
149
158
|
expect(stageZoom(container)).toBeCloseTo(1.25);
|
|
150
|
-
key('-');
|
|
151
|
-
key('-');
|
|
159
|
+
await key('-');
|
|
160
|
+
await key('-');
|
|
152
161
|
expect(stageZoom(container)).toBeCloseTo(0.8);
|
|
153
|
-
key('0');
|
|
162
|
+
await key('0');
|
|
154
163
|
expect(stageZoom(container)).toBe(1);
|
|
155
164
|
});
|
|
156
165
|
|
|
157
|
-
it('go inert over a trial with no camera, and leave its keys to the browser', () => {
|
|
158
|
-
|
|
166
|
+
it('go inert over a trial with no camera, and leave its keys to the browser', async () => {
|
|
167
|
+
await renderSettled(<Lab instruments={[plain]} defaultInstrument="Plain" />);
|
|
159
168
|
for (const name of ['Zoom out', 'Zoom in']) {
|
|
160
169
|
expect(zoomBar().getByRole('button', { name })).toBeDisabled();
|
|
161
170
|
}
|
|
162
171
|
expect(readout()).toBeDisabled();
|
|
163
172
|
expect(readout()).toHaveTextContent('–');
|
|
164
|
-
expect(key('=')).toBe(false);
|
|
173
|
+
expect(await key('=')).toBe(false);
|
|
165
174
|
});
|
|
166
175
|
|
|
167
|
-
it("take the key ahead of a story's own binding in a trial with a camera", () => {
|
|
176
|
+
it("take the key ahead of a story's own binding in a trial with a camera", async () => {
|
|
168
177
|
const story = vi.fn();
|
|
169
178
|
const zooming = defineInstrument<Record<string, never>, Record<string, never>>({
|
|
170
179
|
...staged,
|
|
171
180
|
name: 'Zooming',
|
|
172
181
|
render: () => <StoryZoomKey run={story} />,
|
|
173
182
|
});
|
|
174
|
-
const { container } =
|
|
175
|
-
|
|
183
|
+
const { container } = await renderSettled(
|
|
184
|
+
<Lab instruments={[zooming]} defaultInstrument="Zooming" />,
|
|
185
|
+
);
|
|
186
|
+
await key('=');
|
|
176
187
|
expect(stageZoom(container)).toBeCloseTo(1.25);
|
|
177
188
|
expect(story).not.toHaveBeenCalled();
|
|
178
189
|
});
|
|
179
190
|
|
|
180
|
-
it("leave the key to a story's own binding in a trial with no camera", () => {
|
|
191
|
+
it("leave the key to a story's own binding in a trial with no camera", async () => {
|
|
181
192
|
const story = vi.fn();
|
|
182
193
|
const zooming = defineInstrument<Record<string, never>, Record<string, never>>({
|
|
183
194
|
...plain,
|
|
184
195
|
name: 'ZoomingPlain',
|
|
185
196
|
render: () => <StoryZoomKey run={story} />,
|
|
186
197
|
});
|
|
187
|
-
|
|
188
|
-
key('=');
|
|
198
|
+
await renderSettled(<Lab instruments={[zooming]} defaultInstrument="ZoomingPlain" />);
|
|
199
|
+
await key('=');
|
|
189
200
|
expect(story).toHaveBeenCalledTimes(1);
|
|
190
201
|
});
|
|
191
202
|
|
|
192
|
-
it('follow the focus from trial to trial', () => {
|
|
193
|
-
const { container } =
|
|
203
|
+
it('follow the focus from trial to trial', async () => {
|
|
204
|
+
const { container } = await renderSettled(
|
|
194
205
|
<Lab instruments={[staged, plain]} defaultInstrument="Staged">
|
|
195
206
|
<Capture />
|
|
196
207
|
</Lab>,
|
|
197
208
|
);
|
|
198
|
-
act(() => lab?.addTrial('Staged'));
|
|
199
|
-
act(() => lab?.addTrial('Plain'));
|
|
209
|
+
await act(async () => lab?.addTrial('Staged'));
|
|
210
|
+
await act(async () => lab?.addTrial('Plain'));
|
|
200
211
|
const [first, second, third] = lab?.trials.map((t) => t.id) ?? [];
|
|
201
212
|
expect(zoomBar().getByRole('button', { name: 'Zoom in' })).toBeDisabled();
|
|
202
213
|
|
|
203
|
-
act(() => lab?.focusTrial(second ?? ''));
|
|
204
|
-
press('Zoom out');
|
|
214
|
+
await act(async () => lab?.focusTrial(second ?? ''));
|
|
215
|
+
await press('Zoom out');
|
|
205
216
|
expect(stageZoom(container, 1)).toBeCloseTo(0.8);
|
|
206
217
|
expect(stageZoom(container, 0)).toBe(1);
|
|
207
218
|
expect(readout()).toHaveTextContent('80%');
|
|
208
219
|
|
|
209
|
-
act(() => lab?.focusTrial(first ?? ''));
|
|
220
|
+
await act(async () => lab?.focusTrial(first ?? ''));
|
|
210
221
|
expect(readout()).toHaveTextContent('100%');
|
|
211
|
-
key('=');
|
|
222
|
+
await key('=');
|
|
212
223
|
expect(stageZoom(container, 0)).toBeCloseTo(1.25);
|
|
213
224
|
expect(stageZoom(container, 1)).toBeCloseTo(0.8);
|
|
214
225
|
|
|
215
|
-
act(() => lab?.focusTrial(third ?? ''));
|
|
226
|
+
await act(async () => lab?.focusTrial(third ?? ''));
|
|
216
227
|
expect(readout()).toBeDisabled();
|
|
217
228
|
});
|
|
218
229
|
|
|
219
|
-
it('are left out when the lab says so', () => {
|
|
220
|
-
|
|
230
|
+
it('are left out when the lab says so', async () => {
|
|
231
|
+
await renderSettled(<Lab instruments={[staged]} defaultInstrument="Staged" zoom={false} />);
|
|
221
232
|
expect(screen.queryByRole('toolbar', { name: 'Zoom' })).toBeNull();
|
|
222
233
|
});
|
|
223
234
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { act } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
3
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
4
|
import { SurfaceContext } from '../surface/SurfaceContext';
|
|
4
5
|
import type { SurfaceHandle } from '../surface/useTiledSurface';
|
|
@@ -18,9 +19,9 @@ function fakeHandle(): SurfaceHandle {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
describe('Workspace with a surface above it', () => {
|
|
21
|
-
it('invalidates rects when the tile set changes, because a re-tile moves tiles', () => {
|
|
22
|
+
it('invalidates rects when the tile set changes, because a re-tile moves tiles', async () => {
|
|
22
23
|
const handle = fakeHandle();
|
|
23
|
-
const { rerender } =
|
|
24
|
+
const { rerender } = await renderSettled(
|
|
24
25
|
<SurfaceContext.Provider value={handle}>
|
|
25
26
|
<Workspace ids={['a']} viewport={{ w: 800, h: 600 }}>
|
|
26
27
|
<div>a</div>
|
|
@@ -29,20 +30,22 @@ describe('Workspace with a surface above it', () => {
|
|
|
29
30
|
);
|
|
30
31
|
(handle.invalidateRects as ReturnType<typeof vi.fn>).mockClear();
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
await act(async () => {
|
|
34
|
+
rerender(
|
|
35
|
+
<SurfaceContext.Provider value={handle}>
|
|
36
|
+
<Workspace ids={['a', 'b']} viewport={{ w: 800, h: 600 }}>
|
|
37
|
+
<div>a</div>
|
|
38
|
+
<div>b</div>
|
|
39
|
+
</Workspace>
|
|
40
|
+
</SurfaceContext.Provider>,
|
|
41
|
+
);
|
|
42
|
+
});
|
|
40
43
|
|
|
41
44
|
expect(handle.invalidateRects).toHaveBeenCalled();
|
|
42
45
|
});
|
|
43
46
|
|
|
44
|
-
it('renders unchanged with no surface above it', () => {
|
|
45
|
-
const { getByText } =
|
|
47
|
+
it('renders unchanged with no surface above it', async () => {
|
|
48
|
+
const { getByText } = await renderSettled(
|
|
46
49
|
<Workspace ids={['a']} viewport={{ w: 800, h: 600 }}>
|
|
47
50
|
<div>a</div>
|
|
48
51
|
</Workspace>,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { act, screen } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
3
|
import { describe, expect, test, vi } from 'vitest';
|
|
3
4
|
import { useTrialDrag } from '../trial/TrialDragContext';
|
|
4
5
|
import { Workspace } from './Workspace';
|
|
@@ -13,8 +14,8 @@ function DragProbe({ label }: { label: string }) {
|
|
|
13
14
|
const VIEWPORT = { w: 800, h: 600 };
|
|
14
15
|
|
|
15
16
|
describe('Workspace', () => {
|
|
16
|
-
test('renders all children', () => {
|
|
17
|
-
|
|
17
|
+
test('renders all children', async () => {
|
|
18
|
+
await renderSettled(
|
|
18
19
|
<Workspace viewport={VIEWPORT}>
|
|
19
20
|
<div>one</div>
|
|
20
21
|
<div>two</div>
|
|
@@ -26,9 +27,9 @@ describe('Workspace', () => {
|
|
|
26
27
|
expect(screen.getByText('three')).toBeInTheDocument();
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
test('keeps a child with the tile its id names when an earlier one closes', () => {
|
|
30
|
+
test('keeps a child with the tile its id names when an earlier one closes', async () => {
|
|
30
31
|
const ids = ['a', 'b', 'c'];
|
|
31
|
-
const { rerender, container } =
|
|
32
|
+
const { rerender, container } = await renderSettled(
|
|
32
33
|
<Workspace ids={ids} viewport={VIEWPORT}>
|
|
33
34
|
<div>a</div>
|
|
34
35
|
<div>b</div>
|
|
@@ -37,18 +38,20 @@ describe('Workspace', () => {
|
|
|
37
38
|
);
|
|
38
39
|
expect(container.querySelector('[data-node="c"]')).toHaveTextContent('c');
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
await act(async () => {
|
|
42
|
+
rerender(
|
|
43
|
+
<Workspace ids={['b', 'c']} viewport={VIEWPORT}>
|
|
44
|
+
<div>b</div>
|
|
45
|
+
<div>c</div>
|
|
46
|
+
</Workspace>,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
46
49
|
expect(container.querySelector('[data-node="c"]')).toHaveTextContent('c');
|
|
47
50
|
expect(container.querySelector('[data-node="a"]')).toBeNull();
|
|
48
51
|
});
|
|
49
52
|
|
|
50
|
-
test('uses lk-workspace class', () => {
|
|
51
|
-
const { container } =
|
|
53
|
+
test('uses lk-workspace class', async () => {
|
|
54
|
+
const { container } = await renderSettled(
|
|
52
55
|
<Workspace viewport={VIEWPORT}>
|
|
53
56
|
<div />
|
|
54
57
|
</Workspace>,
|
|
@@ -56,8 +59,8 @@ describe('Workspace', () => {
|
|
|
56
59
|
expect((container.firstChild as HTMLElement).className).toContain('lk-workspace');
|
|
57
60
|
});
|
|
58
61
|
|
|
59
|
-
test('renders resize affordances only when resizable', () => {
|
|
60
|
-
const { container, rerender } =
|
|
62
|
+
test('renders resize affordances only when resizable', async () => {
|
|
63
|
+
const { container, rerender } = await renderSettled(
|
|
61
64
|
<Workspace ids={['a', 'b']} viewport={VIEWPORT}>
|
|
62
65
|
<div>a</div>
|
|
63
66
|
<div>b</div>
|
|
@@ -65,17 +68,19 @@ describe('Workspace', () => {
|
|
|
65
68
|
);
|
|
66
69
|
expect(container.querySelectorAll('[role="separator"]')).toHaveLength(0);
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
await act(async () => {
|
|
72
|
+
rerender(
|
|
73
|
+
<Workspace ids={['a', 'b']} resizable viewport={VIEWPORT}>
|
|
74
|
+
<div>a</div>
|
|
75
|
+
<div>b</div>
|
|
76
|
+
</Workspace>,
|
|
77
|
+
);
|
|
78
|
+
});
|
|
74
79
|
expect(container.querySelectorAll('[role="separator"]').length).toBeGreaterThan(0);
|
|
75
80
|
});
|
|
76
81
|
|
|
77
|
-
test('applies a saved extent to a tile as it registers', () => {
|
|
78
|
-
const { container } =
|
|
82
|
+
test('applies a saved extent to a tile as it registers', async () => {
|
|
83
|
+
const { container } = await renderSettled(
|
|
79
84
|
<Workspace
|
|
80
85
|
ids={['a', 'b']}
|
|
81
86
|
resizable
|
|
@@ -92,9 +97,9 @@ describe('Workspace', () => {
|
|
|
92
97
|
expect(Number.parseFloat(a.style.width)).toBeGreaterThan(Number.parseFloat(b.style.width));
|
|
93
98
|
});
|
|
94
99
|
|
|
95
|
-
test('reports the order a drop would produce instead of applying it', () => {
|
|
100
|
+
test('reports the order a drop would produce instead of applying it', async () => {
|
|
96
101
|
const onReorder = vi.fn();
|
|
97
|
-
const { container } =
|
|
102
|
+
const { container } = await renderSettled(
|
|
98
103
|
<Workspace ids={['a', 'b']} reorderable onReorder={onReorder} viewport={VIEWPORT}>
|
|
99
104
|
<DragProbe label="a" />
|
|
100
105
|
<DragProbe label="b" />
|
|
@@ -111,8 +116,8 @@ describe('Workspace', () => {
|
|
|
111
116
|
expect(nodes).toEqual(['a', 'b']);
|
|
112
117
|
});
|
|
113
118
|
|
|
114
|
-
test('offers no drag source unless reorderable', () => {
|
|
115
|
-
|
|
119
|
+
test('offers no drag source unless reorderable', async () => {
|
|
120
|
+
await renderSettled(
|
|
116
121
|
<Workspace ids={['a', 'b']} viewport={VIEWPORT}>
|
|
117
122
|
<DragProbe label="a" />
|
|
118
123
|
<DragProbe label="b" />
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* and redraws when one is added. jsdom lays nothing out, so the target's rect
|
|
4
4
|
* and the stage's are mocked; the stage's camera is the identity there.
|
|
5
5
|
*/
|
|
6
|
-
import { act
|
|
6
|
+
import { act } from '@testing-library/react';
|
|
7
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
7
8
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
8
9
|
import { useAnnotations } from '../annotations/AnnotationsContext';
|
|
9
10
|
import type { AnnotationsApi } from '../annotations/types';
|
|
@@ -33,7 +34,7 @@ let restore: (() => void) | null = null;
|
|
|
33
34
|
afterEach(() => restore?.());
|
|
34
35
|
|
|
35
36
|
/** Stage 400×300, the pane at stage (50, 20) and 200×100. */
|
|
36
|
-
function mount() {
|
|
37
|
+
async function mount() {
|
|
37
38
|
const pane = { current: null as HTMLDivElement | null };
|
|
38
39
|
let api: AnnotationsApi | null = null;
|
|
39
40
|
function Body() {
|
|
@@ -68,21 +69,21 @@ function mount() {
|
|
|
68
69
|
restore = () => {
|
|
69
70
|
HTMLElement.prototype.getBoundingClientRect = original;
|
|
70
71
|
};
|
|
71
|
-
const r =
|
|
72
|
+
const r = await renderSettled(<Lab instruments={[marked]} defaultInstrument="Marked" />);
|
|
72
73
|
return { ...r, api: () => api as AnnotationsApi };
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
const px = (el: HTMLElement, name: string) => Number.parseFloat(el.style.getPropertyValue(name));
|
|
76
77
|
|
|
77
78
|
describe('marks on the overview', () => {
|
|
78
|
-
it('draws nothing for a target with no marks', () => {
|
|
79
|
-
const h = mount();
|
|
79
|
+
it('draws nothing for a target with no marks', async () => {
|
|
80
|
+
const h = await mount();
|
|
80
81
|
expect(h.container.querySelector('.lk-overview__marks')).toBeNull();
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
it("places a target's marks at the target's rect, through the fit", () => {
|
|
84
|
-
const h = mount();
|
|
85
|
-
act(() => {
|
|
84
|
+
it("places a target's marks at the target's rect, through the fit", async () => {
|
|
85
|
+
const h = await mount();
|
|
86
|
+
await act(async () => {
|
|
86
87
|
h.api().add({ target: 'pane', kind: 'rect', frac: { x: 0.1, y: 0.1, w: 0.2, h: 0.2 } });
|
|
87
88
|
});
|
|
88
89
|
const marks = h.container.querySelector<HTMLElement>('.lk-overview__marks[data-target="pane"]');
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* centering puts the pressed world point at the stage's origin instead.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { act
|
|
10
|
+
import { act } from '@testing-library/react';
|
|
11
11
|
import type { PointerContextValue } from '@weasel-js/core';
|
|
12
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
12
13
|
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
|
13
14
|
import { defineInstrument } from '../instrument/defineInstrument';
|
|
14
15
|
import { Lab } from '../lab/Lab';
|
|
@@ -40,7 +41,7 @@ function pointer(el: Element, type: string, x: number, y: number, buttons = 1) {
|
|
|
40
41
|
|
|
41
42
|
/** Content 400×300 in a 200×150 box, 6px in: scale 0.46, so box (100, 75)
|
|
42
43
|
* is content (200, 150). */
|
|
43
|
-
function mount() {
|
|
44
|
+
async function mount() {
|
|
44
45
|
let store: PointerContextValue | null = null;
|
|
45
46
|
const pictured = defineInstrument<Record<string, never>, Record<string, never>>({
|
|
46
47
|
name: 'Pictured',
|
|
@@ -57,7 +58,7 @@ function mount() {
|
|
|
57
58
|
),
|
|
58
59
|
},
|
|
59
60
|
});
|
|
60
|
-
const r =
|
|
61
|
+
const r = await renderSettled(<Lab instruments={[pictured]} defaultInstrument="Pictured" />);
|
|
61
62
|
const box = r.container.querySelector('.lk-overview__box');
|
|
62
63
|
const content = r.container.querySelector('.lk-stage__content') as HTMLElement | null;
|
|
63
64
|
if (!box || !content) throw new Error('no overview or stage');
|
|
@@ -67,15 +68,15 @@ function mount() {
|
|
|
67
68
|
const px = (el: HTMLElement, name: string) => Number.parseFloat(el.style.getPropertyValue(name));
|
|
68
69
|
|
|
69
70
|
describe('<TrialOverview>', () => {
|
|
70
|
-
it('draws the instrument-supplied content, not the instrument itself', () => {
|
|
71
|
-
const h = mount();
|
|
71
|
+
it('draws the instrument-supplied content, not the instrument itself', async () => {
|
|
72
|
+
const h = await mount();
|
|
72
73
|
expect(h.box.querySelector('[data-testid="thumb"]')).not.toBeNull();
|
|
73
74
|
expect(h.box.querySelector('[data-testid="picture"]')).toBeNull();
|
|
74
75
|
});
|
|
75
76
|
|
|
76
|
-
it('moves the stage camera to the pressed point', () => {
|
|
77
|
-
const h = mount();
|
|
78
|
-
act(() => {
|
|
77
|
+
it('moves the stage camera to the pressed point', async () => {
|
|
78
|
+
const h = await mount();
|
|
79
|
+
await act(async () => {
|
|
79
80
|
pointer(h.box, 'pointerdown', 100, 75);
|
|
80
81
|
pointer(h.box, 'pointerup', 100, 75, 0);
|
|
81
82
|
});
|
|
@@ -83,9 +84,9 @@ describe('<TrialOverview>', () => {
|
|
|
83
84
|
expect(px(h.content, '--lk-stage-y')).toBeCloseTo(-150);
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
it('follows a drag with the camera, and never pans the stage by it', () => {
|
|
87
|
-
const h = mount();
|
|
88
|
-
act(() => {
|
|
87
|
+
it('follows a drag with the camera, and never pans the stage by it', async () => {
|
|
88
|
+
const h = await mount();
|
|
89
|
+
await act(async () => {
|
|
89
90
|
pointer(h.box, 'pointerdown', 100, 75);
|
|
90
91
|
pointer(h.box, 'pointermove', 110, 75);
|
|
91
92
|
pointer(h.box, 'pointermove', 123, 75);
|
|
@@ -96,9 +97,9 @@ describe('<TrialOverview>', () => {
|
|
|
96
97
|
expect(px(h.content, '--lk-stage-x')).toBeCloseTo(-250);
|
|
97
98
|
});
|
|
98
99
|
|
|
99
|
-
it("publishes the pointer over it as the overview's, and the stage draws it", () => {
|
|
100
|
-
const h = mount();
|
|
101
|
-
act(() => pointer(h.box, 'pointermove', 100, 75, 0));
|
|
100
|
+
it("publishes the pointer over it as the overview's, and the stage draws it", async () => {
|
|
101
|
+
const h = await mount();
|
|
102
|
+
await act(async () => pointer(h.box, 'pointermove', 100, 75, 0));
|
|
102
103
|
const p = h.store()?.get();
|
|
103
104
|
expect(p?.viewId).toBe('overview');
|
|
104
105
|
expect(p?.worldX).toBeCloseTo(200);
|
|
@@ -106,10 +107,10 @@ describe('<TrialOverview>', () => {
|
|
|
106
107
|
expect(h.container.querySelector('.lk-stage .lk-linked-cursor')).not.toBeNull();
|
|
107
108
|
});
|
|
108
109
|
|
|
109
|
-
it("publishes the stage's pointer as the stage's, so the stage draws none", () => {
|
|
110
|
-
const h = mount();
|
|
110
|
+
it("publishes the stage's pointer as the stage's, so the stage draws none", async () => {
|
|
111
|
+
const h = await mount();
|
|
111
112
|
const host = h.container.querySelector('.lk-stage') as HTMLElement;
|
|
112
|
-
act(() => pointer(host, 'pointermove', 10, 10, 0));
|
|
113
|
+
await act(async () => pointer(host, 'pointermove', 10, 10, 0));
|
|
113
114
|
expect(h.store()?.get()?.viewId).toBe('stage');
|
|
114
115
|
expect(h.container.querySelector('.lk-stage .lk-linked-cursor')).toBeNull();
|
|
115
116
|
});
|