@flighthq/tool-capture 0.3.0-next.906.07cea63 → 0.3.0
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/README.md +1 -1
- package/dist/baselineStore.d.ts +1 -1
- package/dist/baselineStore.d.ts.map +1 -1
- package/dist/bin.js +19 -3
- package/dist/bin.js.map +1 -1
- package/dist/captureBrowser.d.ts +1 -0
- package/dist/captureBrowser.d.ts.map +1 -1
- package/dist/captureBrowser.js +20 -4
- package/dist/captureBrowser.js.map +1 -1
- package/dist/captureEntry.d.ts +3 -1
- package/dist/captureEntry.d.ts.map +1 -1
- package/dist/captureEntry.js +72 -23
- package/dist/captureEntry.js.map +1 -1
- package/dist/captureFlightPreset.js +12 -2
- package/dist/captureFlightPreset.js.map +1 -1
- package/dist/captureFormat.d.ts +1 -1
- package/dist/captureFormat.d.ts.map +1 -1
- package/dist/captureFormat.js +2 -1
- package/dist/captureFormat.js.map +1 -1
- package/dist/capturePage.d.ts +4 -2
- package/dist/capturePage.d.ts.map +1 -1
- package/dist/capturePage.js +14 -0
- package/dist/capturePage.js.map +1 -1
- package/dist/captureProtocol.d.ts +2 -0
- package/dist/captureProtocol.d.ts.map +1 -1
- package/dist/captureProtocol.js.map +1 -1
- package/dist/captureRegistryMiss.d.ts +8 -0
- package/dist/captureRegistryMiss.d.ts.map +1 -0
- package/dist/captureRegistryMiss.js +41 -0
- package/dist/captureRegistryMiss.js.map +1 -0
- package/dist/captureServer.d.ts.map +1 -1
- package/dist/captureServer.js +13 -19
- package/dist/captureServer.js.map +1 -1
- package/dist/captureSourceHash.d.ts +4 -0
- package/dist/captureSourceHash.d.ts.map +1 -0
- package/dist/captureSourceHash.js +22 -0
- package/dist/captureSourceHash.js.map +1 -0
- package/dist/captureSuite.d.ts.map +1 -1
- package/dist/captureSuite.js +6 -2
- package/dist/captureSuite.js.map +1 -1
- package/dist/captureTimeout.d.ts +4 -0
- package/dist/captureTimeout.d.ts.map +1 -0
- package/dist/captureTimeout.js +39 -0
- package/dist/captureTimeout.js.map +1 -0
- package/dist/captureValidation.d.ts +39 -0
- package/dist/captureValidation.d.ts.map +1 -1
- package/dist/captureValidation.js +274 -32
- package/dist/captureValidation.js.map +1 -1
- package/dist/captureWorkerCount.d.ts +2 -0
- package/dist/captureWorkerCount.d.ts.map +1 -0
- package/dist/captureWorkerCount.js +20 -0
- package/dist/captureWorkerCount.js.map +1 -0
- package/dist/captureWorkflow.d.ts +8 -0
- package/dist/captureWorkflow.d.ts.map +1 -1
- package/dist/captureWorkflow.js +15 -5
- package/dist/captureWorkflow.js.map +1 -1
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/functionalVerify.d.ts +3 -3
- package/dist/functionalVerify.d.ts.map +1 -1
- package/dist/functionalVerify.js +93 -33
- package/dist/functionalVerify.js.map +1 -1
- package/package.json +7 -5
- package/src/captureEntry.test.ts +36 -0
- package/src/captureEyes.e2e.test.ts +32 -1
- package/src/captureFlightPreset.test.ts +12 -0
- package/src/captureFormat.test.ts +22 -0
- package/src/capturePage.test.ts +25 -2
- package/src/captureRegistryMiss.test.ts +106 -0
- package/src/captureServer.test.ts +31 -5
- package/src/captureSourceHash.test.ts +62 -0
- package/src/captureTimeout.test.ts +77 -0
- package/src/captureValidation.test.ts +221 -1
- package/src/captureWorkerCount.test.ts +28 -0
- package/src/captureWorkflow.test.ts +8 -1
- package/src/functionalVerify.test.ts +73 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functionalVerify.js","sourceRoot":"","sources":["../src/functionalVerify.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"functionalVerify.js","sourceRoot":"","sources":["../src/functionalVerify.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/tool-capture",
|
|
3
|
-
"version": "0.3.0
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"author": "Joshua Granick and other contributors",
|
|
5
|
+
"license": "MIT",
|
|
4
6
|
"repository": {
|
|
5
7
|
"type": "git",
|
|
6
8
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -40,10 +42,10 @@
|
|
|
40
42
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
|
-
"@flighthq/capture": "0.3.0
|
|
44
|
-
"@flighthq/render-wgpu": "0.3.0
|
|
45
|
-
"@flighthq/
|
|
46
|
-
"@flighthq/types": "0.3.0
|
|
45
|
+
"@flighthq/capture": "0.3.0",
|
|
46
|
+
"@flighthq/render-wgpu": "0.3.0",
|
|
47
|
+
"@flighthq/bitmap": "0.3.0",
|
|
48
|
+
"@flighthq/types": "0.3.0",
|
|
47
49
|
"@playwright/test": "^1.50.0",
|
|
48
50
|
"picocolors": "^1.1.1"
|
|
49
51
|
},
|
package/src/captureEntry.test.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
captureUrl,
|
|
10
10
|
getCaptureOutputPaths,
|
|
11
11
|
isTransientCaptureError,
|
|
12
|
+
isRejectedCaptureBaselineHash,
|
|
13
|
+
isVerifiedCaptureTool,
|
|
12
14
|
} from './captureEntry';
|
|
13
15
|
|
|
14
16
|
describe('buildCaptureObserveDiagnostics', () => {
|
|
@@ -150,6 +152,24 @@ describe('getCaptureOutputPaths', () => {
|
|
|
150
152
|
});
|
|
151
153
|
});
|
|
152
154
|
|
|
155
|
+
describe('isRejectedCaptureBaselineHash', () => {
|
|
156
|
+
it('rejects the known blank frame a software WebGPU adapter produces', () => {
|
|
157
|
+
// Nearly committed as ground truth once; the write path must refuse it without anyone looking.
|
|
158
|
+
expect(isRejectedCaptureBaselineHash('a4f2105ecdefec94c5fe749c1dc5f2fb9dd74b9832cba0afcd3434f38c0380d0')).toBe(
|
|
159
|
+
true,
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('accepts an ordinary hash, including one differing only in its last character', () => {
|
|
164
|
+
expect(isRejectedCaptureBaselineHash('a4f2105ecdefec94c5fe749c1dc5f2fb9dd74b9832cba0afcd3434f38c0380d1')).toBe(
|
|
165
|
+
false,
|
|
166
|
+
);
|
|
167
|
+
expect(isRejectedCaptureBaselineHash('0b7af17177ffeb0f0f88c03546a86af9a9ef9274116cb05c0d82561b5c1a51be')).toBe(
|
|
168
|
+
false,
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
153
173
|
describe('isTransientCaptureError', () => {
|
|
154
174
|
it('retries infrastructure failures but not deterministic render assertions', () => {
|
|
155
175
|
expect(isTransientCaptureError('page.goto: Timeout 15000ms exceeded')).toBe(true);
|
|
@@ -157,3 +177,19 @@ describe('isTransientCaptureError', () => {
|
|
|
157
177
|
expect(isTransientCaptureError('[mesh] expected red, got blue')).toBe(false);
|
|
158
178
|
});
|
|
159
179
|
});
|
|
180
|
+
|
|
181
|
+
describe('isVerifiedCaptureTool', () => {
|
|
182
|
+
it('verifies the monorepo subjects whose pages register an in-page verifier', () => {
|
|
183
|
+
// Examples earn this for a reason of their own: a software WebGPU adapter cannot present to the
|
|
184
|
+
// swapchain, so an unverified screenshot is blank for every scene alike.
|
|
185
|
+
expect(isVerifiedCaptureTool('examples')).toBe(true);
|
|
186
|
+
expect(isVerifiedCaptureTool('functional')).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('leaves an external subject to opt in explicitly', () => {
|
|
190
|
+
// reference pages only register a target once their harness does; defaulting them on would fail
|
|
191
|
+
// every capture waiting for a verifier that never arrives.
|
|
192
|
+
expect(isVerifiedCaptureTool('reference')).toBe(false);
|
|
193
|
+
expect(isVerifiedCaptureTool('unknown-subject')).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -6,7 +6,8 @@ import { join } from 'node:path';
|
|
|
6
6
|
|
|
7
7
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { launchBrowser } from './captureBrowser';
|
|
10
|
+
import { captureEntry, captureUrl } from './captureEntry';
|
|
10
11
|
import { CAPTURE_PROTOCOL_VERSION } from './captureProtocol';
|
|
11
12
|
|
|
12
13
|
const pages: Record<string, string> = {
|
|
@@ -27,6 +28,14 @@ const pages: Record<string, string> = {
|
|
|
27
28
|
'/flaky': `<!doctype html><canvas width="320" height="180"></canvas><script>
|
|
28
29
|
const ctx=document.querySelector('canvas').getContext('2d'); ctx.fillStyle='#123'; ctx.fillRect(0,0,320,180); ctx.fillStyle='#0cf'; ctx.fillRect(40,30,240,120);
|
|
29
30
|
</script>`,
|
|
31
|
+
'/loop': `<!doctype html><canvas width="320" height="180"></canvas><script>
|
|
32
|
+
const canvas=document.querySelector('canvas'); const ctx=canvas.getContext('2d'); let frame=0;
|
|
33
|
+
function draw() { frame++; ctx.fillStyle='#123'; ctx.fillRect(0,0,320,180); ctx.fillStyle='#0cf'; ctx.fillRect(frame,30,240,120); requestAnimationFrame(draw); }
|
|
34
|
+
requestAnimationFrame(draw);
|
|
35
|
+
</script>`,
|
|
36
|
+
'/static': `<!doctype html><canvas width="320" height="180"></canvas><script>
|
|
37
|
+
const ctx=document.querySelector('canvas').getContext('2d'); ctx.fillStyle='#123'; ctx.fillRect(0,0,320,180); ctx.fillStyle='#0cf'; ctx.fillRect(40,30,240,120);
|
|
38
|
+
</script>`,
|
|
30
39
|
};
|
|
31
40
|
|
|
32
41
|
describe('capture eyes browser contract', () => {
|
|
@@ -92,4 +101,26 @@ describe('capture eyes browser contract', () => {
|
|
|
92
101
|
expect(diagnostics).toMatchObject({ attempts: 2, blank: false, usable: true });
|
|
93
102
|
expect(diagnostics.attemptErrors.join(' ')).toMatch(/ERR_|failed/i);
|
|
94
103
|
}, 15_000);
|
|
104
|
+
|
|
105
|
+
it('drives static pages and waits for the halt outside the halted animation frame queue', async () => {
|
|
106
|
+
const session = await launchBrowser({ captureFrames: 5, verify: false });
|
|
107
|
+
try {
|
|
108
|
+
for (const name of ['loop', 'static']) {
|
|
109
|
+
const result = await captureEntry({
|
|
110
|
+
baseUrl,
|
|
111
|
+
captureFrames: 5,
|
|
112
|
+
context: session.context,
|
|
113
|
+
entry: { name, renderers: ['canvas'], route: () => name },
|
|
114
|
+
outBase: artifactRoot,
|
|
115
|
+
renderers: ['canvas'],
|
|
116
|
+
root: artifactRoot,
|
|
117
|
+
tool: 'examples',
|
|
118
|
+
verify: false,
|
|
119
|
+
});
|
|
120
|
+
expect(result).toBe('ok');
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
await session.browser.close();
|
|
124
|
+
}
|
|
125
|
+
}, 20_000);
|
|
95
126
|
});
|
|
@@ -10,6 +10,18 @@ describe('getFlightCaptureValidationPreset', () => {
|
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
it('gives examples the same-run parity topology, since no example carries a committed baseline', () => {
|
|
14
|
+
// Without a group, every example renderer takes the not-baselined `continue` and the leg compares
|
|
15
|
+
// nothing while reporting success — the defect this topology closes.
|
|
16
|
+
expect(getFlightCaptureValidationPreset('examples').parityGroups).toMatchObject({
|
|
17
|
+
visual: { targets: ['dom', 'canvas', 'webgl', 'webgpu'], reference: 'canvas' },
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('leaves an unknown subject without a parity topology to inherit', () => {
|
|
22
|
+
expect(getFlightCaptureValidationPreset('custom').parityGroups).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
13
25
|
it('keeps example-only fingerprint exceptions scoped to examples', () => {
|
|
14
26
|
expect(getFlightCaptureValidationPreset('examples').fingerprintSkip).toEqual(['playingsound']);
|
|
15
27
|
expect(getFlightCaptureValidationPreset('custom').fingerprintSkip).toEqual([]);
|
|
@@ -30,6 +30,28 @@ describe('formatStatusLine', () => {
|
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
describe('formatStatusLine changed tone', () => {
|
|
34
|
+
it('gives a drifted hash a DIFFERENT glyph from a clean pass', () => {
|
|
35
|
+
// The whole point: a reader scanning a wall of ticks must be able to see this one without reading
|
|
36
|
+
// the message. Same-glyph-different-text is what let a changed hash read as unremarkable.
|
|
37
|
+
const clean = strip(formatStatusLine('pass', 'canvas', 6, ''));
|
|
38
|
+
const drifted = strip(formatStatusLine('changed', 'canvas', 6, 'changed (hash differs from baseline)'));
|
|
39
|
+
expect(clean).toContain('✓');
|
|
40
|
+
expect(drifted).not.toContain('✓');
|
|
41
|
+
expect(drifted).toContain('±');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('keeps the drift message visible rather than dimming it away like a routine pass', () => {
|
|
45
|
+
expect(strip(formatStatusLine('changed', 'canvas', 6, 'changed (hash differs from baseline)'))).toContain(
|
|
46
|
+
'changed (hash differs from baseline)',
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not borrow the failure glyph, since a changed hash is not a failure', () => {
|
|
51
|
+
expect(strip(formatStatusLine('changed', 'canvas', 6, 'x'))).not.toContain('✗');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
33
55
|
describe('formatSummaryCount', () => {
|
|
34
56
|
it('formats a value/label pair', () => {
|
|
35
57
|
expect(strip(formatSummaryCount(3, 'captured', 'pass'))).toBe('3 captured');
|
package/src/capturePage.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
2
|
|
|
3
3
|
import type { DomRenderState } from '@flighthq/types/contract';
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
+
failCaptureTargetVerification,
|
|
7
8
|
installCaptureElementTarget,
|
|
8
9
|
installCaptureTarget,
|
|
9
10
|
registerCaptureBenchmarkTarget,
|
|
@@ -11,15 +12,37 @@ import {
|
|
|
11
12
|
} from './capturePage';
|
|
12
13
|
import { CAPTURE_PROTOCOL_VERSION } from './captureProtocol';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
function resetCapturePageWindow(): void {
|
|
15
16
|
const flags = window as typeof window & {
|
|
17
|
+
__flightCapture?: boolean;
|
|
18
|
+
__flightCaptureVerify?: boolean;
|
|
16
19
|
__ftTarget?: unknown;
|
|
17
20
|
__ftVerification?: unknown;
|
|
18
21
|
__ftBenchmarkTarget?: unknown;
|
|
19
22
|
};
|
|
23
|
+
flags.__flightCapture = undefined;
|
|
24
|
+
flags.__flightCaptureVerify = undefined;
|
|
20
25
|
flags.__ftTarget = undefined;
|
|
21
26
|
flags.__ftVerification = undefined;
|
|
22
27
|
flags.__ftBenchmarkTarget = undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
beforeEach(resetCapturePageWindow);
|
|
31
|
+
afterEach(resetCapturePageWindow);
|
|
32
|
+
|
|
33
|
+
describe('failCaptureTargetVerification', () => {
|
|
34
|
+
it('publishes a terminal reason when setup fails before verification starts', () => {
|
|
35
|
+
const result = failCaptureTargetVerification('canvas', new Error('unsupported backend capability'));
|
|
36
|
+
|
|
37
|
+
expect(result).toMatchObject({
|
|
38
|
+
protocolVersion: CAPTURE_PROTOCOL_VERSION,
|
|
39
|
+
render: 'canvas',
|
|
40
|
+
state: 'failed',
|
|
41
|
+
stage: 'done',
|
|
42
|
+
error: 'unsupported backend capability',
|
|
43
|
+
});
|
|
44
|
+
expect((window as typeof window & { __ftVerification?: unknown }).__ftVerification).toBe(result);
|
|
45
|
+
});
|
|
23
46
|
});
|
|
24
47
|
|
|
25
48
|
describe('installCaptureElementTarget', () => {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { RenderRegistry } from '@flighthq/types/contract';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { findUndrawnRegistryMisses, formatUndrawnRegistryMisses } from './captureRegistryMiss.js';
|
|
5
|
+
|
|
6
|
+
// A verbatim line from a captured page, so the shape this parses is the shape the guards actually emit
|
|
7
|
+
// rather than one invented to match the parser.
|
|
8
|
+
//
|
|
9
|
+
// `registry` is the one field NOT kept as the captured literal. RenderRegistry takes its values from
|
|
10
|
+
// declaration order and nothing persists them, so the captured `2` meant NodeRenderer only against the
|
|
11
|
+
// enum as it stood that day; inserting an alphabetically earlier member silently repoints the literal at
|
|
12
|
+
// a different registry — which is exactly what happened, moving NodeRenderer to 5 and making 2
|
|
13
|
+
// MaterialRenderer, so this fixture started asserting that a material-renderer miss is ignored. Name the
|
|
14
|
+
// member. The captured shape is what this test is about; the ordinal never was.
|
|
15
|
+
const OBSERVED_NODE_RENDERER_MISS = {
|
|
16
|
+
__flight: true,
|
|
17
|
+
t: 176,
|
|
18
|
+
level: 'warn',
|
|
19
|
+
channel: 'render',
|
|
20
|
+
data: {
|
|
21
|
+
kind: 'DisplayObject',
|
|
22
|
+
message: 'createRenderProxy: node kind has no registered renderer — call registerRenderer(state, kind, renderer)',
|
|
23
|
+
registry: RenderRegistry.NodeRenderer,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function miss(kind: string, registry: RenderRegistry) {
|
|
28
|
+
return { __flight: true, level: 'warn', channel: 'render', data: { kind, message: '…', registry } };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('findUndrawnRegistryMisses', () => {
|
|
32
|
+
it('finds a shape rasterizer miss', () => {
|
|
33
|
+
// The regression this gate exists for: a shape whose fill has no tessellated form, with no
|
|
34
|
+
// rasterizer registered, draws nothing while the run stays green.
|
|
35
|
+
expect(findUndrawnRegistryMisses([miss('Shape', RenderRegistry.ShapeRasterizer)])).toEqual([
|
|
36
|
+
{ kind: 'Shape', registry: RenderRegistry.ShapeRasterizer },
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('finds a material renderer miss', () => {
|
|
41
|
+
expect(findUndrawnRegistryMisses([miss('Standard', RenderRegistry.MaterialRenderer)])).toEqual([
|
|
42
|
+
{ kind: 'Standard', registry: RenderRegistry.MaterialRenderer },
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('ignores a node renderer miss, which every passing target reports', () => {
|
|
47
|
+
// Load-bearing: a container has no visual of its own, so every example logs this for DisplayObject.
|
|
48
|
+
// Gating on it would fail the whole suite, which is why the filter is the safety of this gate.
|
|
49
|
+
expect(findUndrawnRegistryMisses([OBSERVED_NODE_RENDERER_MISS])).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('ignores the registries whose absence does not mean undrawn output', () => {
|
|
53
|
+
expect(
|
|
54
|
+
findUndrawnRegistryMisses([
|
|
55
|
+
miss('Blur', RenderRegistry.EffectPaddingResolver),
|
|
56
|
+
miss('roundRect', RenderRegistry.ShapeCommandHandler),
|
|
57
|
+
miss('Image', RenderRegistry.TextureResolver),
|
|
58
|
+
]),
|
|
59
|
+
).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('reports one defect once when a target drives more than one state', () => {
|
|
63
|
+
const logs = [miss('Shape', RenderRegistry.ShapeRasterizer), miss('Shape', RenderRegistry.ShapeRasterizer)];
|
|
64
|
+
expect(findUndrawnRegistryMisses(logs)).toHaveLength(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('keeps distinct kinds and distinct registries apart', () => {
|
|
68
|
+
const logs = [
|
|
69
|
+
miss('Shape', RenderRegistry.ShapeRasterizer),
|
|
70
|
+
miss('MorphShape', RenderRegistry.ShapeRasterizer),
|
|
71
|
+
miss('Shape', RenderRegistry.MaterialRenderer),
|
|
72
|
+
];
|
|
73
|
+
expect(findUndrawnRegistryMisses(logs)).toHaveLength(3);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('ignores entries that are not registry misses', () => {
|
|
77
|
+
// Console lines, page errors, and screenshots share the log stream; none carry a numeric registry.
|
|
78
|
+
expect(
|
|
79
|
+
findUndrawnRegistryMisses([
|
|
80
|
+
{ level: 'error', data: { msg: 'boom' } },
|
|
81
|
+
{ level: 'warn', channel: 'console', data: { msg: '[render] {kind: Shape, registry: 4}' } },
|
|
82
|
+
{ level: 'warn', data: { kind: 'Shape', registry: 'ShapeRasterizer' } },
|
|
83
|
+
{},
|
|
84
|
+
null,
|
|
85
|
+
]),
|
|
86
|
+
).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('formatUndrawnRegistryMisses', () => {
|
|
91
|
+
it('names the registry rather than printing its numeric enum value', () => {
|
|
92
|
+
const message = formatUndrawnRegistryMisses([{ kind: 'Shape', registry: RenderRegistry.ShapeRasterizer }]);
|
|
93
|
+
expect(message).toContain('no shape rasterizer');
|
|
94
|
+
expect(message).toContain('Shape');
|
|
95
|
+
expect(message).not.toContain('4');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('lists every miss so one run reports every undrawn kind', () => {
|
|
99
|
+
const message = formatUndrawnRegistryMisses([
|
|
100
|
+
{ kind: 'Shape', registry: RenderRegistry.ShapeRasterizer },
|
|
101
|
+
{ kind: 'Standard', registry: RenderRegistry.MaterialRenderer },
|
|
102
|
+
]);
|
|
103
|
+
expect(message).toContain('Shape');
|
|
104
|
+
expect(message).toContain('Standard');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
const childProcessMocks = vi.hoisted(() => ({ spawnSync: vi.fn(() => ({ status: 0 })) }));
|
|
2
|
+
|
|
3
|
+
vi.mock('node:child_process', async (importOriginal) => ({
|
|
4
|
+
...(await importOriginal()),
|
|
5
|
+
spawnSync: childProcessMocks.spawnSync,
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
9
|
import { tmpdir } from 'node:os';
|
|
3
10
|
import { join } from 'node:path';
|
|
4
11
|
|
|
@@ -29,9 +36,28 @@ describe('resolveServer', () => {
|
|
|
29
36
|
});
|
|
30
37
|
|
|
31
38
|
describe('resolveStaticServer', () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
it('uses npm rather than treating an inherited npx CLI as the npm CLI', async () => {
|
|
40
|
+
const root = mkdtempSync(join(tmpdir(), 'capture-static-'));
|
|
41
|
+
const dist = join(root, 'examples', 'runners', 'web', 'dist');
|
|
42
|
+
mkdirSync(dist, { recursive: true });
|
|
43
|
+
writeFileSync(join(dist, 'index.html'), '<h1>built</h1>');
|
|
44
|
+
const originalNpmExecPath = process.env['npm_execpath'];
|
|
45
|
+
process.env['npm_execpath'] = '/usr/local/lib/node_modules/npm/bin/npx-cli.js';
|
|
46
|
+
|
|
47
|
+
let server;
|
|
48
|
+
try {
|
|
49
|
+
server = await resolveStaticServer({ tool: 'examples', root, forceBuild: true });
|
|
50
|
+
expect(childProcessMocks.spawnSync).toHaveBeenCalledWith(
|
|
51
|
+
'npm',
|
|
52
|
+
['run', 'build', '--workspace=@flighthq/examples'],
|
|
53
|
+
{ cwd: root, stdio: 'inherit', shell: true },
|
|
54
|
+
);
|
|
55
|
+
expect(await (await fetch(server.url)).text()).toContain('built');
|
|
56
|
+
} finally {
|
|
57
|
+
server?.kill();
|
|
58
|
+
if (originalNpmExecPath === undefined) delete process.env['npm_execpath'];
|
|
59
|
+
else process.env['npm_execpath'] = originalNpmExecPath;
|
|
60
|
+
rmSync(root, { recursive: true, force: true });
|
|
61
|
+
}
|
|
36
62
|
});
|
|
37
63
|
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { getCaptureSceneSourceHash } from './captureSourceHash';
|
|
9
|
+
|
|
10
|
+
let root: string;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
root = mkdtempSync(join(tmpdir(), 'capture-source-hash-'));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
rmSync(root, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('getCaptureSceneSourceHash', () => {
|
|
21
|
+
it('hashes the backend-specific functional scene when one exists', () => {
|
|
22
|
+
const scenes = join(root, 'functional', 'scenes');
|
|
23
|
+
mkdirSync(scenes, { recursive: true });
|
|
24
|
+
writeFileSync(join(scenes, 'sample.ts'), 'shared');
|
|
25
|
+
writeFileSync(join(scenes, 'sample.webgl.ts'), 'specific');
|
|
26
|
+
|
|
27
|
+
expect(getCaptureSceneSourceHash(root, 'functional', { name: 'sample', renderers: ['webgl'] }, 'webgl')).toBe(
|
|
28
|
+
sha256('specific'),
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('hashes the backend-agnostic functional scene for a prefixed renderer', () => {
|
|
33
|
+
const scenes = join(root, 'functional', 'scenes');
|
|
34
|
+
mkdirSync(scenes, { recursive: true });
|
|
35
|
+
writeFileSync(join(scenes, 'sample.ts'), 'shared');
|
|
36
|
+
|
|
37
|
+
expect(
|
|
38
|
+
getCaptureSceneSourceHash(root, 'functional', { name: 'sample', renderers: ['flight:webgl'] }, 'flight:webgl'),
|
|
39
|
+
).toBe(sha256('shared'));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('hashes an example app as the shared scene source', () => {
|
|
43
|
+
const source = join(root, 'examples', 'packages', 'clock', 'src');
|
|
44
|
+
mkdirSync(source, { recursive: true });
|
|
45
|
+
writeFileSync(join(source, 'app.ts'), 'clock scene');
|
|
46
|
+
|
|
47
|
+
expect(getCaptureSceneSourceHash(root, 'examples', { name: 'clock', renderers: ['canvas'] }, 'canvas')).toBe(
|
|
48
|
+
sha256('clock scene'),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns null for an unknown subject or a missing scene', () => {
|
|
53
|
+
expect(getCaptureSceneSourceHash(root, 'custom', { name: 'sample', renderers: ['canvas'] }, 'canvas')).toBeNull();
|
|
54
|
+
expect(
|
|
55
|
+
getCaptureSceneSourceHash(root, 'functional', { name: 'missing', renderers: ['canvas'] }, 'canvas'),
|
|
56
|
+
).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function sha256(source: string): string {
|
|
61
|
+
return createHash('sha256').update(source).digest('hex');
|
|
62
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getCaptureTimeoutMs, resolveCaptureTimeoutMs, setCaptureTimeoutMs } from './captureTimeout';
|
|
2
|
+
|
|
3
|
+
describe('getCaptureTimeoutMs', () => {
|
|
4
|
+
let originalEnvironmentTimeout: string | undefined;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
originalEnvironmentTimeout = process.env['FLIGHT_CAPTURE_TIMEOUT_MS'];
|
|
8
|
+
setCaptureTimeoutMs(null);
|
|
9
|
+
delete process.env['FLIGHT_CAPTURE_TIMEOUT_MS'];
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
setCaptureTimeoutMs(null);
|
|
14
|
+
if (originalEnvironmentTimeout === undefined) delete process.env['FLIGHT_CAPTURE_TIMEOUT_MS'];
|
|
15
|
+
else process.env['FLIGHT_CAPTURE_TIMEOUT_MS'] = originalEnvironmentTimeout;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('defaults to the budget the edited constants used, so an unconfigured run is unchanged', () => {
|
|
19
|
+
expect(getCaptureTimeoutMs()).toBe(15_000);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('reads the environment when nothing is pinned', () => {
|
|
23
|
+
process.env['FLIGHT_CAPTURE_TIMEOUT_MS'] = '45000';
|
|
24
|
+
expect(getCaptureTimeoutMs()).toBe(45_000);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('prefers a pinned budget over the environment', () => {
|
|
28
|
+
process.env['FLIGHT_CAPTURE_TIMEOUT_MS'] = '45000';
|
|
29
|
+
setCaptureTimeoutMs(9_000);
|
|
30
|
+
expect(getCaptureTimeoutMs()).toBe(9_000);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('falls back to the environment again once the pin is cleared', () => {
|
|
34
|
+
process.env['FLIGHT_CAPTURE_TIMEOUT_MS'] = '45000';
|
|
35
|
+
setCaptureTimeoutMs(9_000);
|
|
36
|
+
setCaptureTimeoutMs(null);
|
|
37
|
+
expect(getCaptureTimeoutMs()).toBe(45_000);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('resolveCaptureTimeoutMs', () => {
|
|
42
|
+
it('defaults when neither flag nor environment is given', () => {
|
|
43
|
+
expect(resolveCaptureTimeoutMs(undefined, undefined)).toBe(15_000);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('takes the environment when no flag is given', () => {
|
|
47
|
+
expect(resolveCaptureTimeoutMs(undefined, '30000')).toBe(30_000);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('lets the flag win over the environment', () => {
|
|
51
|
+
expect(resolveCaptureTimeoutMs('20000', '30000')).toBe(20_000);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// A zero or negative budget expires every wait immediately, so every page reports as stalled — which
|
|
55
|
+
// reads as a fleet-wide rendering failure rather than as the typo it is.
|
|
56
|
+
it('ignores a non-positive budget rather than honoring it', () => {
|
|
57
|
+
expect(resolveCaptureTimeoutMs('0', undefined)).toBe(15_000);
|
|
58
|
+
expect(resolveCaptureTimeoutMs('-1', undefined)).toBe(15_000);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('ignores a non-numeric budget', () => {
|
|
62
|
+
expect(resolveCaptureTimeoutMs('soon', undefined)).toBe(15_000);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('falls through a rejected flag to the environment', () => {
|
|
66
|
+
expect(resolveCaptureTimeoutMs('0', '30000')).toBe(30_000);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('setCaptureTimeoutMs', () => {
|
|
71
|
+
afterEach(() => setCaptureTimeoutMs(null));
|
|
72
|
+
|
|
73
|
+
it('pins the budget every capture wait reads', () => {
|
|
74
|
+
setCaptureTimeoutMs(1_234);
|
|
75
|
+
expect(getCaptureTimeoutMs()).toBe(1_234);
|
|
76
|
+
});
|
|
77
|
+
});
|