@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
1
|
import { render, screen } 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 type { TrialContribution } from '../chrome/types';
|
|
4
5
|
import type { Instrument } from '../instrument/types';
|
|
@@ -19,23 +20,27 @@ function silenceRenderError(): void {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
describe('<Lab> chrome', () => {
|
|
22
|
-
it('renders a consumer contribution', () => {
|
|
23
|
+
it('renders a consumer contribution', async () => {
|
|
23
24
|
const extra: TrialContribution = {
|
|
24
25
|
id: 'export',
|
|
25
26
|
region: 'toolbar',
|
|
26
27
|
item: { icon: Glyph, label: 'Export', onActivate: () => {} },
|
|
27
28
|
};
|
|
28
|
-
|
|
29
|
+
await renderSettled(
|
|
30
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" chrome={[extra]} />,
|
|
31
|
+
);
|
|
29
32
|
expect(screen.getByRole('button', { name: 'Export' })).toBeInTheDocument();
|
|
30
33
|
});
|
|
31
34
|
|
|
32
|
-
it('suppresses a built-in by id', () => {
|
|
33
|
-
|
|
35
|
+
it('suppresses a built-in by id', async () => {
|
|
36
|
+
await renderSettled(
|
|
37
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" suppress={['snapshot']} />,
|
|
38
|
+
);
|
|
34
39
|
expect(screen.queryByRole('button', { name: 'Save snapshot' })).toBeNull();
|
|
35
40
|
expect(screen.getByRole('button', { name: 'Clone trial' })).toBeInTheDocument();
|
|
36
41
|
});
|
|
37
42
|
|
|
38
|
-
it('throws when a consumer id collides with a built-in', () => {
|
|
43
|
+
it('throws when a consumer id collides with a built-in', async () => {
|
|
39
44
|
silenceRenderError();
|
|
40
45
|
const clash: TrialContribution = {
|
|
41
46
|
id: 'clone',
|
|
@@ -47,24 +52,24 @@ describe('<Lab> chrome', () => {
|
|
|
47
52
|
).toThrow(/duplicate contribution id "clone"/);
|
|
48
53
|
});
|
|
49
54
|
|
|
50
|
-
it('throws when suppressing an id that is not there', () => {
|
|
55
|
+
it('throws when suppressing an id that is not there', async () => {
|
|
51
56
|
silenceRenderError();
|
|
52
57
|
expect(() =>
|
|
53
58
|
render(<Lab title="T" instruments={[bare]} defaultInstrument="Bare" suppress={['nope']} />),
|
|
54
59
|
).toThrow(/cannot suppress "nope"/);
|
|
55
60
|
});
|
|
56
61
|
|
|
57
|
-
it('renders an instrument-declared contribution', () => {
|
|
62
|
+
it('renders an instrument-declared contribution', async () => {
|
|
58
63
|
const withChrome: Instrument = {
|
|
59
64
|
...bare,
|
|
60
65
|
chrome: [{ id: 'mine', region: 'status', item: { text: 'ready' } }],
|
|
61
66
|
};
|
|
62
|
-
|
|
67
|
+
await renderSettled(<Lab title="T" instruments={[withChrome]} defaultInstrument="Bare" />);
|
|
63
68
|
expect(screen.getByText('ready')).toBeInTheDocument();
|
|
64
69
|
});
|
|
65
70
|
|
|
66
|
-
it('renders an aside contribution in its own pane, after the workspace', () => {
|
|
67
|
-
|
|
71
|
+
it('renders an aside contribution in its own pane, after the workspace', async () => {
|
|
72
|
+
await renderSettled(
|
|
68
73
|
<Lab
|
|
69
74
|
title="T"
|
|
70
75
|
instruments={[bare]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
2
|
import { resolveTheme } from '@weasel-js/theme';
|
|
3
3
|
import { describe, expect, it } from 'vitest';
|
|
4
4
|
import type { Instrument } from '../instrument/types';
|
|
@@ -18,8 +18,8 @@ const backdrop = (container: HTMLElement) =>
|
|
|
18
18
|
.find(Boolean) ?? '';
|
|
19
19
|
|
|
20
20
|
describe('<Lab nebula>', () => {
|
|
21
|
-
it('draws one blob per literal color', () => {
|
|
22
|
-
const { container } =
|
|
21
|
+
it('draws one blob per literal color', async () => {
|
|
22
|
+
const { container } = await renderSettled(
|
|
23
23
|
<Lab
|
|
24
24
|
instruments={[bare]}
|
|
25
25
|
defaultInstrument="Bare"
|
|
@@ -30,8 +30,8 @@ describe('<Lab nebula>', () => {
|
|
|
30
30
|
expect(backdrop(container).match(/radial-gradient/g)).toHaveLength(3);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it('resolves a theme ramp against the lab’s theme', () => {
|
|
34
|
-
const { container } =
|
|
33
|
+
it('resolves a theme ramp against the lab’s theme', async () => {
|
|
34
|
+
const { container } = await renderSettled(
|
|
35
35
|
<Lab instruments={[bare]} defaultInstrument="Bare" mode="dark" nebula={{ ramp: 'swatch' }} />,
|
|
36
36
|
);
|
|
37
37
|
const sky = resolveTheme(interstellarTheme, { mode: 'dark' })['--wzl-swatch-sky'];
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* workspace: `.lk-lab__body > .lk-workspace { min-width: 0 }` is a direct-child
|
|
8
8
|
* selector, so an extra element between them silently drops that rule.
|
|
9
9
|
*/
|
|
10
|
-
import { act
|
|
10
|
+
import { act } from '@testing-library/react';
|
|
11
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
11
12
|
import type { ReactNode } from 'react';
|
|
12
13
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
14
|
import { defineInstrument } from '../instrument/defineInstrument';
|
|
@@ -75,16 +76,18 @@ const probeInstrument = defineInstrument<Record<string, never>, Record<string, n
|
|
|
75
76
|
});
|
|
76
77
|
|
|
77
78
|
describe('<Lab> surface provider', () => {
|
|
78
|
-
it('puts a surface handle in reach of an instrument', () => {
|
|
79
|
+
it('puts a surface handle in reach of an instrument', async () => {
|
|
79
80
|
seen = 'not-rendered';
|
|
80
|
-
|
|
81
|
+
await renderSettled(<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />);
|
|
81
82
|
expect(seen).not.toBe('not-rendered');
|
|
82
83
|
expect(seen).not.toBeNull();
|
|
83
84
|
expect(typeof (seen as unknown as SurfaceHandle).registerTile).toBe('function');
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
it('stacks an under and an over buffer inside the lab body', () => {
|
|
87
|
-
const { container } =
|
|
87
|
+
it('stacks an under and an over buffer inside the lab body', async () => {
|
|
88
|
+
const { container } = await renderSettled(
|
|
89
|
+
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
90
|
+
);
|
|
88
91
|
const canvases = container.querySelectorAll('.lk-lab__body > canvas.lk-lab__surface');
|
|
89
92
|
expect(canvases).toHaveLength(2);
|
|
90
93
|
// Source order, because the z-index that separates them is in a stylesheet
|
|
@@ -96,24 +99,28 @@ describe('<Lab> surface provider', () => {
|
|
|
96
99
|
]);
|
|
97
100
|
});
|
|
98
101
|
|
|
99
|
-
it('hands a tenant whichever of the two it asks for', () => {
|
|
100
|
-
const { container } =
|
|
102
|
+
it('hands a tenant whichever of the two it asks for', async () => {
|
|
103
|
+
const { container } = await renderSettled(
|
|
104
|
+
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
105
|
+
);
|
|
101
106
|
const [underEl, overEl] = [...container.querySelectorAll('canvas.lk-lab__surface')];
|
|
102
107
|
expect(seenCanvases.over).toBe(overEl);
|
|
103
108
|
expect(seenCanvases.under).toBe(underEl);
|
|
104
109
|
expect(overEl).not.toBe(underEl);
|
|
105
110
|
});
|
|
106
111
|
|
|
107
|
-
it('defaults a tenant to the buffer over the trials', () => {
|
|
112
|
+
it('defaults a tenant to the buffer over the trials', async () => {
|
|
108
113
|
// Every tenant predating the split wants the over one: the surface was
|
|
109
114
|
// built for marks that annotate an instrument.
|
|
110
|
-
const { container } =
|
|
115
|
+
const { container } = await renderSettled(
|
|
116
|
+
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
117
|
+
);
|
|
111
118
|
const [underEl, overEl] = [...container.querySelectorAll('canvas.lk-lab__surface')];
|
|
112
119
|
expect(seenCanvases.byDefault).toBe(overEl);
|
|
113
120
|
expect(seenCanvases.byDefault).not.toBe(underEl);
|
|
114
121
|
});
|
|
115
122
|
|
|
116
|
-
it('mounts no buffer of its own when a host already owns the surface', () => {
|
|
123
|
+
it('mounts no buffer of its own when a host already owns the surface', async () => {
|
|
117
124
|
function Host({ children }: { children: ReactNode }) {
|
|
118
125
|
const surface = useTiledSurface({ onFrame: () => {} });
|
|
119
126
|
return (
|
|
@@ -122,7 +129,7 @@ describe('<Lab> surface provider', () => {
|
|
|
122
129
|
</SurfaceContext.Provider>
|
|
123
130
|
);
|
|
124
131
|
}
|
|
125
|
-
const { container } =
|
|
132
|
+
const { container } = await renderSettled(
|
|
126
133
|
<Host>
|
|
127
134
|
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />
|
|
128
135
|
</Host>,
|
|
@@ -130,11 +137,13 @@ describe('<Lab> surface provider', () => {
|
|
|
130
137
|
expect(container.querySelectorAll('canvas.lk-lab__surface')).toHaveLength(0);
|
|
131
138
|
});
|
|
132
139
|
|
|
133
|
-
it('keeps the workspace a direct child of the lab body', () => {
|
|
140
|
+
it('keeps the workspace a direct child of the lab body', async () => {
|
|
134
141
|
// The min-width:0 rule that keeps the flex row from overflowing is
|
|
135
142
|
// `.lk-lab__body > .lk-workspace`. An element inserted between them drops
|
|
136
143
|
// it, and jsdom cannot see the overflow that follows.
|
|
137
|
-
const { container } =
|
|
144
|
+
const { container } = await renderSettled(
|
|
145
|
+
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
146
|
+
);
|
|
138
147
|
const body = container.querySelector('.lk-lab__body');
|
|
139
148
|
expect(body).not.toBeNull();
|
|
140
149
|
expect(body?.querySelector(':scope > .lk-workspace')).not.toBeNull();
|
|
@@ -155,7 +164,7 @@ describe('<Lab> tile round trip', () => {
|
|
|
155
164
|
vi.restoreAllMocks();
|
|
156
165
|
});
|
|
157
166
|
|
|
158
|
-
it('publishes a rect for a tile an instrument registered', () => {
|
|
167
|
+
it('publishes a rect for a tile an instrument registered', async () => {
|
|
159
168
|
const frames: SurfaceFrame[] = [];
|
|
160
169
|
let handle: SurfaceHandle | null = null;
|
|
161
170
|
|
|
@@ -177,7 +186,7 @@ describe('<Lab> tile round trip', () => {
|
|
|
177
186
|
);
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
|
|
189
|
+
await renderSettled(
|
|
181
190
|
<Host>
|
|
182
191
|
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />
|
|
183
192
|
</Host>,
|
|
@@ -189,11 +198,11 @@ describe('<Lab> tile round trip', () => {
|
|
|
189
198
|
expect(pane).not.toBeNull();
|
|
190
199
|
stubBox(pane, 240, 100, 320, 200);
|
|
191
200
|
|
|
192
|
-
act(() => {
|
|
201
|
+
await act(async () => {
|
|
193
202
|
handle?.invalidateRects();
|
|
194
203
|
handle?.invalidate(paneTileId);
|
|
195
204
|
});
|
|
196
|
-
act(() => {
|
|
205
|
+
await act(async () => {
|
|
197
206
|
vi.advanceTimersByTime(64);
|
|
198
207
|
});
|
|
199
208
|
|
|
@@ -208,8 +217,8 @@ describe('<Lab> tile round trip', () => {
|
|
|
208
217
|
});
|
|
209
218
|
|
|
210
219
|
describe('<Lab> pages', () => {
|
|
211
|
-
it("gives the title a way back to the project's other labs", () => {
|
|
212
|
-
const { getByRole } =
|
|
220
|
+
it("gives the title a way back to the project's other labs", async () => {
|
|
221
|
+
const { getByRole } = await renderSettled(
|
|
213
222
|
<Lab
|
|
214
223
|
instruments={[probeInstrument]}
|
|
215
224
|
defaultInstrument="Probe"
|
|
@@ -225,8 +234,8 @@ describe('<Lab> pages', () => {
|
|
|
225
234
|
expect(getByRole('button', { name: /kliegsminister/ })).toBeTruthy();
|
|
226
235
|
});
|
|
227
236
|
|
|
228
|
-
it('leaves the title a plain heading when there are no other labs', () => {
|
|
229
|
-
const { getByRole, queryByRole } =
|
|
237
|
+
it('leaves the title a plain heading when there are no other labs', async () => {
|
|
238
|
+
const { getByRole, queryByRole } = await renderSettled(
|
|
230
239
|
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" title="solo" />,
|
|
231
240
|
);
|
|
232
241
|
expect(getByRole('heading', { name: 'solo' })).toBeTruthy();
|
package/src/lab/Lab.test.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
2
3
|
import { type ReactNode, useEffect } from 'react';
|
|
3
4
|
import { describe, expect, it } from 'vitest';
|
|
4
5
|
import type { Instrument } from '../instrument/types';
|
|
@@ -32,9 +33,9 @@ function CaptureLab({ children }: { children?: ReactNode }) {
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
function mountLab(props: Partial<Record<keyof LabProps, unknown>> = {}) {
|
|
36
|
+
async function mountLab(props: Partial<Record<keyof LabProps, unknown>> = {}) {
|
|
36
37
|
labRef = null;
|
|
37
|
-
return
|
|
38
|
+
return renderSettled(
|
|
38
39
|
<Lab {...({ instruments: [stub, stubB], defaultInstrument: 'Stub', ...props } as LabProps)}>
|
|
39
40
|
<CaptureLab />
|
|
40
41
|
</Lab>,
|
|
@@ -42,86 +43,86 @@ function mountLab(props: Partial<Record<keyof LabProps, unknown>> = {}) {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
describe('<Lab>', () => {
|
|
45
|
-
it('renders one trial by default', () => {
|
|
46
|
-
mountLab();
|
|
46
|
+
it('renders one trial by default', async () => {
|
|
47
|
+
await mountLab();
|
|
47
48
|
expect(screen.getAllByRole('region', { name: /trial/i })).toHaveLength(1);
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
it('addTrial adds a second trial', () => {
|
|
51
|
-
mountLab();
|
|
52
|
-
act(() => labRef?.addTrial('Stub'));
|
|
51
|
+
it('addTrial adds a second trial', async () => {
|
|
52
|
+
await mountLab();
|
|
53
|
+
await act(async () => labRef?.addTrial('Stub'));
|
|
53
54
|
expect(screen.getAllByRole('region', { name: /trial/i })).toHaveLength(2);
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
it('addTrial opens each trial on the config it was given', () => {
|
|
57
|
-
mountLab();
|
|
58
|
-
act(() => labRef?.addTrial('Stub', { config: { count: 7 } }));
|
|
57
|
+
it('addTrial opens each trial on the config it was given', async () => {
|
|
58
|
+
await mountLab();
|
|
59
|
+
await act(async () => labRef?.addTrial('Stub', { config: { count: 7 } }));
|
|
59
60
|
expect(labRef?.trials.map((t) => (t.config as { count: number }).count)).toEqual([0, 7]);
|
|
60
61
|
expect(labRef?.trials.map((t) => (t.state as { value: number }).value)).toEqual([0, 7]);
|
|
61
62
|
});
|
|
62
63
|
|
|
63
|
-
it('closeTrial removes one when more than one exists', () => {
|
|
64
|
-
mountLab();
|
|
65
|
-
act(() => labRef?.addTrial('Stub'));
|
|
64
|
+
it('closeTrial removes one when more than one exists', async () => {
|
|
65
|
+
await mountLab();
|
|
66
|
+
await act(async () => labRef?.addTrial('Stub'));
|
|
66
67
|
const first = labRef?.trials[0];
|
|
67
|
-
act(() => labRef?.closeTrial(first?.id ?? ''));
|
|
68
|
+
await act(async () => labRef?.closeTrial(first?.id ?? ''));
|
|
68
69
|
expect(screen.getAllByRole('region', { name: /trial/i })).toHaveLength(1);
|
|
69
70
|
});
|
|
70
71
|
|
|
71
|
-
it('closeTrial is a no-op on the last trial', () => {
|
|
72
|
-
mountLab();
|
|
72
|
+
it('closeTrial is a no-op on the last trial', async () => {
|
|
73
|
+
await mountLab();
|
|
73
74
|
const only = labRef?.trials[0];
|
|
74
|
-
act(() => labRef?.closeTrial(only?.id ?? ''));
|
|
75
|
+
await act(async () => labRef?.closeTrial(only?.id ?? ''));
|
|
75
76
|
expect(screen.getAllByRole('region', { name: /trial/i })).toHaveLength(1);
|
|
76
77
|
});
|
|
77
78
|
|
|
78
|
-
it('cloneTrial inserts immediately after source', () => {
|
|
79
|
-
mountLab();
|
|
80
|
-
act(() => labRef?.addTrial('StubB'));
|
|
79
|
+
it('cloneTrial inserts immediately after source', async () => {
|
|
80
|
+
await mountLab();
|
|
81
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
81
82
|
const ws0 = labRef?.trials[0];
|
|
82
83
|
const ws1 = labRef?.trials[1];
|
|
83
|
-
act(() => labRef?.cloneTrial(ws0?.id ?? ''));
|
|
84
|
+
await act(async () => labRef?.cloneTrial(ws0?.id ?? ''));
|
|
84
85
|
const ids = labRef?.trials.map((w) => w.id) ?? [];
|
|
85
86
|
expect(ids).toHaveLength(3);
|
|
86
87
|
expect(ids[0]).toBe(ws0?.id);
|
|
87
88
|
expect(ids[2]).toBe(ws1?.id);
|
|
88
89
|
});
|
|
89
90
|
|
|
90
|
-
it('resetTrial restores defaults', () => {
|
|
91
|
-
mountLab();
|
|
91
|
+
it('resetTrial restores defaults', async () => {
|
|
92
|
+
await mountLab();
|
|
92
93
|
const ws = labRef?.trials[0];
|
|
93
94
|
if (!ws) throw new Error('no trial');
|
|
94
|
-
act(() => labRef?.resetTrial(ws.id));
|
|
95
|
+
await act(async () => labRef?.resetTrial(ws.id));
|
|
95
96
|
const reset = labRef?.trials[0];
|
|
96
97
|
expect(reset?.config).toEqual({ count: 0 });
|
|
97
98
|
expect(reset?.state).toEqual({ value: 0 });
|
|
98
99
|
});
|
|
99
100
|
|
|
100
|
-
it('mode="light" stamps light on the lab root', () => {
|
|
101
|
-
const { container } = mountLab({ mode: 'light' });
|
|
101
|
+
it('mode="light" stamps light on the lab root', async () => {
|
|
102
|
+
const { container } = await mountLab({ mode: 'light' });
|
|
102
103
|
expect(container.querySelector('.lk-lab')?.getAttribute('data-wzl-mode')).toBe('light');
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
it('mode="dark" stamps dark on the lab root', () => {
|
|
106
|
-
const { container } = mountLab({ mode: 'dark' });
|
|
106
|
+
it('mode="dark" stamps dark on the lab root', async () => {
|
|
107
|
+
const { container } = await mountLab({ mode: 'dark' });
|
|
107
108
|
expect(container.querySelector('.lk-lab')?.getAttribute('data-wzl-mode')).toBe('dark');
|
|
108
109
|
});
|
|
109
110
|
|
|
110
|
-
it('mode="auto" resolves to a concrete mode', () => {
|
|
111
|
-
const { container } = mountLab({ mode: 'auto' });
|
|
111
|
+
it('mode="auto" resolves to a concrete mode', async () => {
|
|
112
|
+
const { container } = await mountLab({ mode: 'auto' });
|
|
112
113
|
expect(container.querySelector('.lk-lab')?.getAttribute('data-wzl-mode')).toMatch(
|
|
113
114
|
/^(light|dark)$/,
|
|
114
115
|
);
|
|
115
116
|
});
|
|
116
117
|
|
|
117
|
-
it('applies the interstellar theme in every mode', () => {
|
|
118
|
-
const { container } = mountLab({ mode: 'light' });
|
|
118
|
+
it('applies the interstellar theme in every mode', async () => {
|
|
119
|
+
const { container } = await mountLab({ mode: 'light' });
|
|
119
120
|
expect(container.querySelector('.lk-lab')?.getAttribute('data-wzl-theme')).toBe('interstellar');
|
|
120
121
|
});
|
|
121
122
|
|
|
122
|
-
it('setMode updates the stamped mode at runtime', () => {
|
|
123
|
-
const { container } = mountLab({ mode: 'auto' });
|
|
124
|
-
act(() => labRef?.setMode('light'));
|
|
123
|
+
it('setMode updates the stamped mode at runtime', async () => {
|
|
124
|
+
const { container } = await mountLab({ mode: 'auto' });
|
|
125
|
+
await act(async () => labRef?.setMode('light'));
|
|
125
126
|
expect(container.querySelector('.lk-lab')?.getAttribute('data-wzl-mode')).toBe('light');
|
|
126
127
|
});
|
|
127
128
|
|
|
@@ -152,13 +153,13 @@ describe('<Lab>', () => {
|
|
|
152
153
|
};
|
|
153
154
|
const storage = () => createMemoryAdapter(backing);
|
|
154
155
|
|
|
155
|
-
const first =
|
|
156
|
+
const first = await renderSettled(
|
|
156
157
|
<Lab instruments={[mapped]} defaultInstrument="Mapped" storage={storage()} storageKey="s" />,
|
|
157
158
|
);
|
|
158
159
|
await waitFor(() => expect(JSON.stringify([...backing.values()])).toContain('"seen":["a"]'));
|
|
159
160
|
first.unmount();
|
|
160
161
|
|
|
161
|
-
mountLab({
|
|
162
|
+
await mountLab({
|
|
162
163
|
instruments: [mapped],
|
|
163
164
|
defaultInstrument: 'Mapped',
|
|
164
165
|
storage: storage(),
|
|
@@ -172,7 +173,7 @@ describe('<Lab>', () => {
|
|
|
172
173
|
});
|
|
173
174
|
|
|
174
175
|
describe('instruments that change while mounted', () => {
|
|
175
|
-
it('fills an open trial’s config before its replaced instrument renders', () => {
|
|
176
|
+
it('fills an open trial’s config before its replaced instrument renders', async () => {
|
|
176
177
|
const v1: Instrument = {
|
|
177
178
|
name: 'Grow',
|
|
178
179
|
defaultConfig: () => ({ size: 1 }),
|
|
@@ -188,7 +189,7 @@ describe('instruments that change while mounted', () => {
|
|
|
188
189
|
return null;
|
|
189
190
|
},
|
|
190
191
|
};
|
|
191
|
-
const view =
|
|
192
|
+
const view = await renderSettled(
|
|
192
193
|
<Lab instruments={[v1]} defaultInstrument="Grow">
|
|
193
194
|
<CaptureLab />
|
|
194
195
|
</Lab>,
|
|
@@ -203,9 +204,9 @@ describe('instruments that change while mounted', () => {
|
|
|
203
204
|
expect(seenByV2.every((c) => c.color === 'red')).toBe(true);
|
|
204
205
|
});
|
|
205
206
|
|
|
206
|
-
it('opens a trial of an instrument added after mount', () => {
|
|
207
|
+
it('opens a trial of an instrument added after mount', async () => {
|
|
207
208
|
const later: Instrument = { ...stub, name: 'Later' };
|
|
208
|
-
const view =
|
|
209
|
+
const view = await renderSettled(
|
|
209
210
|
<Lab instruments={[stub]} defaultInstrument="Stub">
|
|
210
211
|
<CaptureLab />
|
|
211
212
|
</Lab>,
|
|
@@ -215,12 +216,12 @@ describe('instruments that change while mounted', () => {
|
|
|
215
216
|
<CaptureLab />
|
|
216
217
|
</Lab>,
|
|
217
218
|
);
|
|
218
|
-
act(() => labRef?.addTrial('Later'));
|
|
219
|
+
await act(async () => labRef?.addTrial('Later'));
|
|
219
220
|
expect(labRef?.trials.map((t) => t.instrumentName)).toEqual(['Stub', 'Later']);
|
|
220
221
|
expect(screen.queryByText(/Unknown instrument/)).toBeNull();
|
|
221
222
|
});
|
|
222
223
|
|
|
223
|
-
it('keeps a trial’s content mounted across an instrument swap', () => {
|
|
224
|
+
it('keeps a trial’s content mounted across an instrument swap', async () => {
|
|
224
225
|
let mounts = 0;
|
|
225
226
|
function Probe() {
|
|
226
227
|
useEffect(() => {
|
|
@@ -235,7 +236,7 @@ describe('instruments that change while mounted', () => {
|
|
|
235
236
|
render: () => <Probe />,
|
|
236
237
|
};
|
|
237
238
|
const v2: Instrument = { ...v1, defaultConfig: () => ({ extra: 1 }) };
|
|
238
|
-
const view =
|
|
239
|
+
const view = await renderSettled(<Lab instruments={[v1]} defaultInstrument="Keep" />);
|
|
239
240
|
view.rerender(<Lab instruments={[v2]} defaultInstrument="Keep" />);
|
|
240
241
|
expect(mounts).toBe(1);
|
|
241
242
|
});
|
|
@@ -249,72 +250,72 @@ describe('focused trial', () => {
|
|
|
249
250
|
render: () => <iframe title="story frame" />,
|
|
250
251
|
};
|
|
251
252
|
|
|
252
|
-
it('is the only trial until another opens, then the one that opened', () => {
|
|
253
|
-
mountLab();
|
|
253
|
+
it('is the only trial until another opens, then the one that opened', async () => {
|
|
254
|
+
await mountLab();
|
|
254
255
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id);
|
|
255
|
-
act(() => labRef?.addTrial('StubB'));
|
|
256
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
256
257
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[1]?.id);
|
|
257
|
-
act(() => labRef?.cloneTrial(labRef?.trials[0]?.id ?? ''));
|
|
258
|
+
await act(async () => labRef?.cloneTrial(labRef?.trials[0]?.id ?? ''));
|
|
258
259
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[1]?.id);
|
|
259
260
|
expect(labRef?.trials.map((t) => t.instrumentName)).toEqual(['Stub', 'Stub', 'StubB']);
|
|
260
261
|
});
|
|
261
262
|
|
|
262
|
-
it('moves to a trial a pointer goes down in', () => {
|
|
263
|
-
mountLab();
|
|
264
|
-
act(() => labRef?.addTrial('StubB'));
|
|
263
|
+
it('moves to a trial a pointer goes down in', async () => {
|
|
264
|
+
await mountLab();
|
|
265
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
265
266
|
fireEvent.pointerDown(within(regions()[0] as HTMLElement).getByTestId('stub-content'));
|
|
266
267
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id);
|
|
267
268
|
});
|
|
268
269
|
|
|
269
|
-
it('moves to a trial focus moves into', () => {
|
|
270
|
-
mountLab();
|
|
271
|
-
act(() => labRef?.addTrial('StubB'));
|
|
272
|
-
act(() => (regions()[0] as HTMLElement).focus());
|
|
270
|
+
it('moves to a trial focus moves into', async () => {
|
|
271
|
+
await mountLab();
|
|
272
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
273
|
+
await act(async () => (regions()[0] as HTMLElement).focus());
|
|
273
274
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id);
|
|
274
275
|
});
|
|
275
276
|
|
|
276
277
|
it('moves to a trial whose frame takes focus, which fires nothing in the lab’s document', async () => {
|
|
277
|
-
mountLab({ instruments: [framed, stubB], defaultInstrument: 'Framed' });
|
|
278
|
-
act(() => labRef?.addTrial('StubB'));
|
|
279
|
-
act(() => screen.getByTitle('story frame').focus());
|
|
278
|
+
await mountLab({ instruments: [framed, stubB], defaultInstrument: 'Framed' });
|
|
279
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
280
|
+
await act(async () => screen.getByTitle('story frame').focus());
|
|
280
281
|
fireEvent.blur(window);
|
|
281
282
|
await waitFor(() => expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id));
|
|
282
283
|
});
|
|
283
284
|
|
|
284
|
-
it('falls back to the first trial when the focused one closes', () => {
|
|
285
|
-
mountLab();
|
|
286
|
-
act(() => labRef?.addTrial('StubB'));
|
|
287
|
-
act(() => labRef?.closeTrial(labRef?.trials[1]?.id ?? ''));
|
|
285
|
+
it('falls back to the first trial when the focused one closes', async () => {
|
|
286
|
+
await mountLab();
|
|
287
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
288
|
+
await act(async () => labRef?.closeTrial(labRef?.trials[1]?.id ?? ''));
|
|
288
289
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id);
|
|
289
290
|
});
|
|
290
291
|
|
|
291
|
-
it('is marked on its trial only when there is more than one', () => {
|
|
292
|
-
mountLab();
|
|
292
|
+
it('is marked on its trial only when there is more than one', async () => {
|
|
293
|
+
await mountLab();
|
|
293
294
|
expect(regions()[0]).not.toHaveAttribute('data-focused');
|
|
294
|
-
act(() => labRef?.addTrial('StubB'));
|
|
295
|
+
await act(async () => labRef?.addTrial('StubB'));
|
|
295
296
|
expect(regions()[1]).toHaveAttribute('data-focused', 'true');
|
|
296
297
|
expect(regions()[0]).not.toHaveAttribute('data-focused');
|
|
297
298
|
});
|
|
298
299
|
});
|
|
299
300
|
|
|
300
301
|
describe('swapTrial', () => {
|
|
301
|
-
it('runs another instrument in the trial’s place, and keeps focus on that place', () => {
|
|
302
|
-
mountLab();
|
|
303
|
-
act(() => labRef?.addTrial('Stub'));
|
|
302
|
+
it('runs another instrument in the trial’s place, and keeps focus on that place', async () => {
|
|
303
|
+
await mountLab();
|
|
304
|
+
await act(async () => labRef?.addTrial('Stub'));
|
|
304
305
|
const second = labRef?.trials[1]?.id;
|
|
305
|
-
act(() => labRef?.focusTrial(labRef?.trials[0]?.id ?? ''));
|
|
306
|
-
act(() => labRef?.swapTrial(labRef?.trials[0]?.id ?? '', 'StubB'));
|
|
306
|
+
await act(async () => labRef?.focusTrial(labRef?.trials[0]?.id ?? ''));
|
|
307
|
+
await act(async () => labRef?.swapTrial(labRef?.trials[0]?.id ?? '', 'StubB'));
|
|
307
308
|
expect(labRef?.trials.map((t) => t.instrumentName)).toEqual(['StubB', 'Stub']);
|
|
308
309
|
expect(labRef?.trials[1]?.id).toBe(second);
|
|
309
310
|
expect(labRef?.focusedTrialId).toBe(labRef?.trials[0]?.id);
|
|
310
311
|
expect(screen.getByTestId('stub-b-content')).toBeInTheDocument();
|
|
311
312
|
});
|
|
312
313
|
|
|
313
|
-
it('leaves focus where it was when the swapped trial did not have it', () => {
|
|
314
|
-
mountLab();
|
|
315
|
-
act(() => labRef?.addTrial('Stub'));
|
|
314
|
+
it('leaves focus where it was when the swapped trial did not have it', async () => {
|
|
315
|
+
await mountLab();
|
|
316
|
+
await act(async () => labRef?.addTrial('Stub'));
|
|
316
317
|
const focused = labRef?.focusedTrialId;
|
|
317
|
-
act(() => labRef?.swapTrial(labRef?.trials[0]?.id ?? '', 'StubB'));
|
|
318
|
+
await act(async () => labRef?.swapTrial(labRef?.trials[0]?.id ?? '', 'StubB'));
|
|
318
319
|
expect(labRef?.focusedTrialId).toBe(focused);
|
|
319
320
|
});
|
|
320
321
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { screen } from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
3
4
|
import { describe, expect, it } from 'vitest';
|
|
4
5
|
import type { TrialContribution } from '../chrome/types';
|
|
5
6
|
import type { Instrument } from '../instrument/types';
|
|
@@ -34,19 +35,21 @@ const pinned: TrialContribution = {
|
|
|
34
35
|
item: { title: 'Pinned', undockable: false, body: <p>stay</p> },
|
|
35
36
|
};
|
|
36
37
|
|
|
37
|
-
function renderLab() {
|
|
38
|
-
return
|
|
38
|
+
async function renderLab() {
|
|
39
|
+
return renderSettled(
|
|
40
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" chrome={[notes]} />,
|
|
41
|
+
);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
describe('undocking a sidebar section', () => {
|
|
42
|
-
it('starts docked inside the trial sidebar', () => {
|
|
43
|
-
const { container } = renderLab();
|
|
45
|
+
it('starts docked inside the trial sidebar', async () => {
|
|
46
|
+
const { container } = await renderLab();
|
|
44
47
|
const sidebar = container.querySelector('.lk-trial__sidebar');
|
|
45
48
|
expect(sidebar).toContainElement(screen.getByTestId('notes-body'));
|
|
46
49
|
});
|
|
47
50
|
|
|
48
51
|
it('moves the section out of the sidebar and into a workspace panel', async () => {
|
|
49
|
-
const { container } = renderLab();
|
|
52
|
+
const { container } = await renderLab();
|
|
50
53
|
await userEvent.click(screen.getByRole('button', { name: 'Undock Notes' }));
|
|
51
54
|
|
|
52
55
|
expect(container.querySelector('.lk-trial__sidebar')).not.toContainElement(
|
|
@@ -57,7 +60,7 @@ describe('undocking a sidebar section', () => {
|
|
|
57
60
|
});
|
|
58
61
|
|
|
59
62
|
it('docks it back', async () => {
|
|
60
|
-
const { container } = renderLab();
|
|
63
|
+
const { container } = await renderLab();
|
|
61
64
|
await userEvent.click(screen.getByRole('button', { name: 'Undock Notes' }));
|
|
62
65
|
await userEvent.click(screen.getByRole('button', { name: 'Dock Notes' }));
|
|
63
66
|
|
|
@@ -68,7 +71,7 @@ describe('undocking a sidebar section', () => {
|
|
|
68
71
|
});
|
|
69
72
|
|
|
70
73
|
it('sends a section declaring undockAs floating to the floating layer', async () => {
|
|
71
|
-
const { container } =
|
|
74
|
+
const { container } = await renderSettled(
|
|
72
75
|
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" chrome={[floater]} />,
|
|
73
76
|
);
|
|
74
77
|
await userEvent.click(screen.getByRole('button', { name: 'Undock Floater' }));
|
|
@@ -78,8 +81,10 @@ describe('undocking a sidebar section', () => {
|
|
|
78
81
|
expect(container.querySelector('.lk-panel-tile--floating')).not.toBeNull();
|
|
79
82
|
});
|
|
80
83
|
|
|
81
|
-
it('offers no tear-out control on a section that opts out', () => {
|
|
82
|
-
|
|
84
|
+
it('offers no tear-out control on a section that opts out', async () => {
|
|
85
|
+
await renderSettled(
|
|
86
|
+
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" chrome={[pinned]} />,
|
|
87
|
+
);
|
|
83
88
|
expect(screen.queryByRole('button', { name: 'Undock Pinned' })).toBeNull();
|
|
84
89
|
});
|
|
85
90
|
});
|