@design.estate/wcctools 6.1.0 → 7.0.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/changelog.md +41 -0
- package/dist_shell/bundle.js +405 -368
- package/dist_shell/bundle.js.map +1 -1
- package/dist_shell/bundle.js.third-party-notices.json +52 -0
- package/dist_shell/bundle.js.third-party-notices.txt +107 -1
- package/dist_shell/index.html +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/capture/classes.capturebrowser.d.ts +65 -0
- package/dist_ts/capture/classes.capturebrowser.js +251 -0
- package/dist_ts/capture/classes.captureservice.d.ts +39 -0
- package/dist_ts/capture/classes.captureservice.js +119 -0
- package/dist_ts/capture/classes.screencast.d.ts +47 -0
- package/dist_ts/capture/classes.screencast.js +127 -0
- package/dist_ts/capture/errors.d.ts +14 -0
- package/dist_ts/capture/errors.js +23 -0
- package/dist_ts/capture/images.d.ts +3 -0
- package/dist_ts/capture/images.js +58 -0
- package/dist_ts/capture/index.d.ts +4 -0
- package/dist_ts/capture/index.js +5 -0
- package/dist_ts/capture/interaction.d.ts +11 -0
- package/dist_ts/capture/interaction.js +138 -0
- package/dist_ts/capture/navigationguard.d.ts +44 -0
- package/dist_ts/capture/navigationguard.js +129 -0
- package/dist_ts/capture/previewpage.d.ts +29 -0
- package/dist_ts/capture/previewpage.js +96 -0
- package/dist_ts/capture/request.d.ts +69 -0
- package/dist_ts/capture/request.js +239 -0
- package/dist_ts/classes.bundlestatus.d.ts +36 -0
- package/dist_ts/classes.bundlestatus.js +110 -0
- package/dist_ts/classes.devapi.d.ts +3 -0
- package/dist_ts/classes.devapi.js +8 -1
- package/dist_ts/classes.devserver.d.ts +65 -14
- package/dist_ts/classes.devserver.js +157 -52
- package/dist_ts/classes.hostpolicy.d.ts +63 -0
- package/dist_ts/classes.hostpolicy.js +171 -0
- package/dist_ts/cli.js +28 -3
- package/dist_ts/cli.screenshot.d.ts +6 -0
- package/dist_ts/cli.screenshot.js +89 -0
- package/dist_ts/index.d.ts +0 -2
- package/dist_ts/index.js +1 -3
- package/dist_ts/plugins.d.ts +6 -2
- package/dist_ts/plugins.js +8 -3
- package/dist_ts_interfaces/capture.d.ts +151 -0
- package/dist_ts_interfaces/capture.js +2 -0
- package/dist_ts_interfaces/index.d.ts +1 -0
- package/dist_ts_interfaces/index.js +2 -1
- package/dist_ts_interfaces/requests.d.ts +59 -0
- package/dist_ts_shell/bundlestatus.d.ts +15 -0
- package/dist_ts_shell/bundlestatus.js +64 -0
- package/dist_ts_shell/elements/wcc-contextmenu.d.ts +3 -0
- package/dist_ts_shell/elements/wcc-contextmenu.js +18 -3
- package/dist_ts_shell/elements/wcc-preview-frame.d.ts +6 -0
- package/dist_ts_shell/elements/wcc-preview-frame.js +50 -2
- package/dist_ts_shell/elements/wcc-recording-panel.d.ts +9 -0
- package/dist_ts_shell/elements/wcc-recording-panel.js +12 -1
- package/dist_ts_shell/elements/wcc-shell.d.ts +15 -0
- package/dist_ts_shell/elements/wcc-shell.js +45 -3
- package/dist_ts_shell/plugins.d.ts +2 -1
- package/dist_ts_shell/plugins.js +3 -2
- package/dist_ts_shell/services/framesampler.service.d.ts +23 -0
- package/dist_ts_shell/services/framesampler.service.js +101 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +6 -2
- package/readme.md +108 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/capture/classes.capturebrowser.ts +290 -0
- package/ts/capture/classes.captureservice.ts +152 -0
- package/ts/capture/classes.screencast.ts +152 -0
- package/ts/capture/errors.ts +25 -0
- package/ts/capture/images.ts +61 -0
- package/ts/capture/index.ts +4 -0
- package/ts/capture/interaction.ts +146 -0
- package/ts/capture/navigationguard.ts +147 -0
- package/ts/capture/previewpage.ts +130 -0
- package/ts/capture/request.ts +304 -0
- package/ts/classes.bundlestatus.ts +123 -0
- package/ts/classes.devapi.ts +9 -0
- package/ts/classes.devserver.ts +172 -50
- package/ts/classes.hostpolicy.ts +201 -0
- package/ts/cli.screenshot.ts +95 -0
- package/ts/cli.ts +28 -2
- package/ts/index.ts +0 -2
- package/ts/plugins.ts +9 -2
- package/ts_interfaces/bridge.ts +95 -0
- package/ts_interfaces/capture.ts +144 -0
- package/ts_interfaces/catalog.ts +37 -0
- package/ts_interfaces/index.ts +5 -0
- package/ts_interfaces/plugins.ts +3 -0
- package/ts_interfaces/requests.ts +104 -0
- package/ts_interfaces/standard.ts +103 -0
- package/ts_shared/index.ts +1 -0
- package/ts_shared/plugins.ts +3 -0
- package/ts_shared/previewroute.ts +58 -0
- package/ts_shell/bundlestatus.ts +68 -0
- package/ts_shell/elements/wcc-contextmenu.ts +306 -0
- package/ts_shell/elements/wcc-preview-frame.ts +167 -0
- package/ts_shell/elements/wcc-properties.ts +998 -0
- package/ts_shell/elements/wcc-record-button.ts +108 -0
- package/ts_shell/elements/wcc-recording-panel.ts +1017 -0
- package/ts_shell/elements/wcc-shell.ts +485 -0
- package/ts_shell/elements/wcc-sidebar.ts +1419 -0
- package/ts_shell/index.html +21 -0
- package/ts_shell/index.ts +4 -0
- package/ts_shell/plugins.ts +12 -0
- package/ts_shell/previewconnection.ts +103 -0
- package/ts_shell/services/framesampler.service.ts +112 -0
- package/ts_shell/services/recorder.service.ts +451 -0
- package/ts_shell/types/dom-mediacapture-stub/index.d.ts +12 -0
- package/ts_shell/types/dom-mediacapture-stub/package.json +6 -0
- package/ts_shell/types/dom-webcodecs-stub/index.d.ts +2 -0
- package/ts_shell/types/dom-webcodecs-stub/package.json +6 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccCaptureBrowser, captureBrowserClosedMessage } from './classes.capturebrowser.js';
|
|
3
|
+
import { WccCaptureError } from './errors.js';
|
|
4
|
+
import { runInteraction } from './interaction.js';
|
|
5
|
+
import { renderPreview, screenshotPreview } from './previewpage.js';
|
|
6
|
+
import { captureLimits, resolveInteractionRequest, resolveScreenshotRequest } from './request.js';
|
|
7
|
+
/** The longest a screenshot may take, from its request to its image. */
|
|
8
|
+
export const screenshotDeadlineMs = 30_000;
|
|
9
|
+
/** The longest an interaction may take: its steps, its observation and the set-up around them. */
|
|
10
|
+
export const interactionDeadlineMs = 30_000
|
|
11
|
+
+ captureLimits.maxSteps * captureLimits.stepTimeoutMs
|
|
12
|
+
+ captureLimits.maxObserveMs;
|
|
13
|
+
/**
|
|
14
|
+
* The captures of a dev server: screenshots and observed interactions of single demos, taken in a
|
|
15
|
+
* headless browser on the server's preview route. A capture waits while a catalog bundle builds,
|
|
16
|
+
* and is refused when one failed, so it never shows a stale or broken bundle. Every capture has a
|
|
17
|
+
* deadline and ends when its client goes away. The service registers `captureScreenshot` and
|
|
18
|
+
* `captureInteraction` on the dev server's typed API.
|
|
19
|
+
*/
|
|
20
|
+
export class WccCaptureService {
|
|
21
|
+
bundleStatus;
|
|
22
|
+
browser;
|
|
23
|
+
origin = null;
|
|
24
|
+
constructor(bundleStatus, browserOptionsArg = {}) {
|
|
25
|
+
this.bundleStatus = bundleStatus;
|
|
26
|
+
this.browser = new WccCaptureBrowser(browserOptionsArg);
|
|
27
|
+
}
|
|
28
|
+
/** Registers the capture requests on a typed router. */
|
|
29
|
+
addTypedHandlers(typedrouterArg) {
|
|
30
|
+
typedrouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler('captureScreenshot', async (requestArg, typedToolsArg) => {
|
|
31
|
+
return this.captureScreenshot(requestArg, typedToolsArg?.abortSignal);
|
|
32
|
+
}));
|
|
33
|
+
typedrouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler('captureInteraction', async (requestArg, typedToolsArg) => {
|
|
34
|
+
return this.captureInteraction(requestArg, typedToolsArg?.abortSignal);
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
/** How often the capture browser was launched. */
|
|
38
|
+
getBrowserLaunchCount() {
|
|
39
|
+
return this.browser.getLaunchCount();
|
|
40
|
+
}
|
|
41
|
+
/** The process id of the capture browser while it runs. */
|
|
42
|
+
getBrowserProcessId() {
|
|
43
|
+
return this.browser.getProcessId();
|
|
44
|
+
}
|
|
45
|
+
/** Takes captures of the dev server serving at `originArg` (scheme, host and port). */
|
|
46
|
+
open(originArg) {
|
|
47
|
+
this.origin = originArg;
|
|
48
|
+
this.browser.open();
|
|
49
|
+
}
|
|
50
|
+
/** Ends every capture and closes the browser. */
|
|
51
|
+
async close() {
|
|
52
|
+
this.origin = null;
|
|
53
|
+
await this.browser.close();
|
|
54
|
+
}
|
|
55
|
+
async captureScreenshot(requestArg, abortSignalArg) {
|
|
56
|
+
const request = resolveScreenshotRequest(requestArg);
|
|
57
|
+
return this.run(screenshotDeadlineMs, abortSignalArg, async (contextArg, originArg) => {
|
|
58
|
+
const preview = await renderPreview(contextArg, originArg, request.view, request.subject);
|
|
59
|
+
const shot = await screenshotPreview(preview, request.view, request.image);
|
|
60
|
+
await preview.navigationGuard.assertStayed('being captured');
|
|
61
|
+
return { selection: preview.selection, ...shot };
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async captureInteraction(requestArg, abortSignalArg) {
|
|
65
|
+
const request = resolveInteractionRequest(requestArg);
|
|
66
|
+
return this.run(interactionDeadlineMs, abortSignalArg, async (contextArg, originArg, signalArg) => {
|
|
67
|
+
const preview = await renderPreview(contextArg, originArg, request.view, request.subject);
|
|
68
|
+
const observed = await runInteraction(preview, request, signalArg);
|
|
69
|
+
const result = { selection: preview.selection, ...observed };
|
|
70
|
+
if (request.finalScreenshot) {
|
|
71
|
+
result.finalImage = (await screenshotPreview(preview, request.view, request.finalScreenshot)).image;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Runs a capture within its deadline: waits for settled bundles, then runs it in a browser
|
|
78
|
+
* context of its own. The client's abort and the deadline both end it.
|
|
79
|
+
*/
|
|
80
|
+
async run(deadlineMsArg, clientSignalArg, captureArg) {
|
|
81
|
+
const origin = this.origin;
|
|
82
|
+
if (!origin) {
|
|
83
|
+
throw new WccCaptureError(captureBrowserClosedMessage);
|
|
84
|
+
}
|
|
85
|
+
const controller = new AbortController();
|
|
86
|
+
const deadline = setTimeout(() => {
|
|
87
|
+
controller.abort(new WccCaptureError(`wcctools: the capture did not finish within ${deadlineMsArg / 1000} s.`));
|
|
88
|
+
}, deadlineMsArg);
|
|
89
|
+
const onClientAbort = () => controller.abort(new WccCaptureError('wcctools: the client went away; the capture ended.'));
|
|
90
|
+
clientSignalArg?.addEventListener('abort', onClientAbort, { once: true });
|
|
91
|
+
if (clientSignalArg?.aborted) {
|
|
92
|
+
onClientAbort();
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await this.waitForSettledBundles(controller.signal);
|
|
96
|
+
return await this.browser.withContext(controller.signal, (contextArg) => captureArg(contextArg, origin, controller.signal));
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
clearTimeout(deadline);
|
|
100
|
+
clientSignalArg?.removeEventListener('abort', onClientAbort);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Resolves once no bundle builds; refused while a bundle's latest run failed. */
|
|
104
|
+
async waitForSettledBundles(abortSignalArg) {
|
|
105
|
+
for (;;) {
|
|
106
|
+
abortSignalArg.throwIfAborted();
|
|
107
|
+
const snapshot = this.bundleStatus.getSnapshot();
|
|
108
|
+
const failed = snapshot.bundles.filter((bundleArg) => bundleArg.state === 'failed');
|
|
109
|
+
if (failed.length > 0) {
|
|
110
|
+
throw new WccCaptureError(`wcctools: the catalog bundle failed, so there is nothing current to capture: ${failed.map((bundleArg) => `${bundleArg.name}: ${bundleArg.errorMessage ?? 'no message'}`).join('; ')}`);
|
|
111
|
+
}
|
|
112
|
+
if (!snapshot.bundles.some((bundleArg) => bundleArg.state === 'started')) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await this.bundleStatus.waitForChange(snapshot.revision, abortSignalArg);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5jYXB0dXJlc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2NhcHR1cmUvY2xhc3Nlcy5jYXB0dXJlc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsMkJBQTJCLEVBQWtDLE1BQU0sNkJBQTZCLENBQUM7QUFDN0gsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxhQUFhLEVBQUUseUJBQXlCLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFbEcsd0VBQXdFO0FBQ3hFLE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLE1BQU0sQ0FBQztBQUUzQyxrR0FBa0c7QUFDbEcsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsTUFBTTtNQUN2QyxhQUFhLENBQUMsUUFBUSxHQUFHLGFBQWEsQ0FBQyxhQUFhO01BQ3BELGFBQWEsQ0FBQyxZQUFZLENBQUM7QUFFL0I7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLGlCQUFpQjtJQUtUO0lBSkYsT0FBTyxDQUFvQjtJQUNwQyxNQUFNLEdBQWtCLElBQUksQ0FBQztJQUVyQyxZQUNtQixZQUFrQyxFQUNuRCxvQkFBK0MsRUFBRTtRQURoQyxpQkFBWSxHQUFaLFlBQVksQ0FBc0I7UUFHbkQsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLGlCQUFpQixDQUFDLGlCQUFpQixDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVELHdEQUF3RDtJQUNqRCxnQkFBZ0IsQ0FBQyxjQUFnRDtRQUN0RSxjQUFjLENBQUMsZUFBZSxDQUM1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
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
type TPage = plugins.smartbrowser.puppeteer.Page;
|
|
3
|
+
export interface IWccScreencastOptions {
|
|
4
|
+
/** JPEG quality from 1 to 100. */
|
|
5
|
+
quality: number;
|
|
6
|
+
/** Frames are scaled down to fit these device pixels. */
|
|
7
|
+
maxWidth: number;
|
|
8
|
+
maxHeight: number;
|
|
9
|
+
/** At most this many frames per second are kept; default 10. */
|
|
10
|
+
maxFps?: number;
|
|
11
|
+
/** The most frames kept over a whole recording; older ones are thinned out. Default 200. */
|
|
12
|
+
maxKeptFrames?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Records a page's screencast (Chrome DevTools Protocol `Page.startScreencast`): JPEG frames the
|
|
16
|
+
* browser sends whenever the page paints, kept at a bounded rate and count. The page's last
|
|
17
|
+
* painted frame is always kept.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WccScreencastRecorder {
|
|
20
|
+
private readonly page;
|
|
21
|
+
private readonly options;
|
|
22
|
+
private session;
|
|
23
|
+
private startedAt;
|
|
24
|
+
private readonly kept;
|
|
25
|
+
/** The newest frame that arrived too soon after the last kept one. */
|
|
26
|
+
private latest;
|
|
27
|
+
private stopped;
|
|
28
|
+
private ackFailure;
|
|
29
|
+
private readonly minFrameGapMs;
|
|
30
|
+
private readonly maxKeptFrames;
|
|
31
|
+
constructor(page: TPage, options: IWccScreencastOptions);
|
|
32
|
+
/** The time the recording started, as `Date.now()` read it. */
|
|
33
|
+
getStartedAt(): number;
|
|
34
|
+
start(): Promise<void>;
|
|
35
|
+
/** Stops the screencast, detaches from the page and answers the kept frames in time order. */
|
|
36
|
+
stop(): Promise<plugins.interfaces.IWccTimedFrame[]>;
|
|
37
|
+
private readonly onFrame;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Picks at most `maxFramesArg` frames spread evenly over the time the frames cover: for each of
|
|
41
|
+
* that many points in time, the frame on screen at that point. The first and the last frame are
|
|
42
|
+
* always among them.
|
|
43
|
+
*/
|
|
44
|
+
export declare const selectFrames: <T extends {
|
|
45
|
+
timestampMs: number;
|
|
46
|
+
}>(framesArg: T[], maxFramesArg: number) => T[];
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { toWccImage } from './images.js';
|
|
3
|
+
/**
|
|
4
|
+
* Records a page's screencast (Chrome DevTools Protocol `Page.startScreencast`): JPEG frames the
|
|
5
|
+
* browser sends whenever the page paints, kept at a bounded rate and count. The page's last
|
|
6
|
+
* painted frame is always kept.
|
|
7
|
+
*/
|
|
8
|
+
export class WccScreencastRecorder {
|
|
9
|
+
page;
|
|
10
|
+
options;
|
|
11
|
+
session = null;
|
|
12
|
+
startedAt = 0;
|
|
13
|
+
kept = [];
|
|
14
|
+
/** The newest frame that arrived too soon after the last kept one. */
|
|
15
|
+
latest = null;
|
|
16
|
+
stopped = false;
|
|
17
|
+
ackFailure = null;
|
|
18
|
+
minFrameGapMs;
|
|
19
|
+
maxKeptFrames;
|
|
20
|
+
constructor(page, options) {
|
|
21
|
+
this.page = page;
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.minFrameGapMs = 1000 / (options.maxFps ?? 10);
|
|
24
|
+
this.maxKeptFrames = options.maxKeptFrames ?? 200;
|
|
25
|
+
}
|
|
26
|
+
/** The time the recording started, as `Date.now()` read it. */
|
|
27
|
+
getStartedAt() {
|
|
28
|
+
return this.startedAt;
|
|
29
|
+
}
|
|
30
|
+
async start() {
|
|
31
|
+
const session = await this.page.createCDPSession();
|
|
32
|
+
this.session = session;
|
|
33
|
+
session.on('Page.screencastFrame', this.onFrame);
|
|
34
|
+
this.startedAt = Date.now();
|
|
35
|
+
await session.send('Page.startScreencast', {
|
|
36
|
+
format: 'jpeg',
|
|
37
|
+
quality: this.options.quality,
|
|
38
|
+
maxWidth: this.options.maxWidth,
|
|
39
|
+
maxHeight: this.options.maxHeight,
|
|
40
|
+
everyNthFrame: 1,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Stops the screencast, detaches from the page and answers the kept frames in time order. */
|
|
44
|
+
async stop() {
|
|
45
|
+
const session = this.session;
|
|
46
|
+
this.session = null;
|
|
47
|
+
if (session && !this.stopped) {
|
|
48
|
+
this.stopped = true;
|
|
49
|
+
session.off('Page.screencastFrame', this.onFrame);
|
|
50
|
+
if (!session.detached) {
|
|
51
|
+
try {
|
|
52
|
+
await session.send('Page.stopScreencast');
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
await session.detach();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (this.ackFailure) {
|
|
60
|
+
throw this.ackFailure;
|
|
61
|
+
}
|
|
62
|
+
if (this.latest && this.latest.timestampMs > (this.kept.at(-1)?.timestampMs ?? -1)) {
|
|
63
|
+
this.kept.push(this.latest);
|
|
64
|
+
this.latest = null;
|
|
65
|
+
}
|
|
66
|
+
return this.kept.map((frameArg) => ({
|
|
67
|
+
...toWccImage(Buffer.from(frameArg.dataBase64, 'base64'), 'jpeg'),
|
|
68
|
+
timestampMs: frameArg.timestampMs,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
onFrame = (eventArg) => {
|
|
72
|
+
const session = this.session;
|
|
73
|
+
if (!session) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Chrome sends the next frame only once this one is acknowledged
|
|
77
|
+
session.send('Page.screencastFrameAck', { sessionId: eventArg.sessionId }).catch((error) => {
|
|
78
|
+
if (!this.stopped) {
|
|
79
|
+
this.ackFailure ??= error;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const paintedAt = eventArg.metadata.timestamp === undefined ? Date.now() : eventArg.metadata.timestamp * 1000;
|
|
83
|
+
const frame = {
|
|
84
|
+
timestampMs: Math.max(0, Math.round(paintedAt - this.startedAt)),
|
|
85
|
+
dataBase64: eventArg.data,
|
|
86
|
+
};
|
|
87
|
+
const lastKept = this.kept.at(-1);
|
|
88
|
+
if (lastKept && frame.timestampMs - lastKept.timestampMs < this.minFrameGapMs) {
|
|
89
|
+
this.latest = frame;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.latest = null;
|
|
93
|
+
this.kept.push(frame);
|
|
94
|
+
if (this.kept.length > this.maxKeptFrames) {
|
|
95
|
+
// Thin out evenly: drop every other frame, keeping the first
|
|
96
|
+
for (let index = this.kept.length - 1; index > 0; index -= 2) {
|
|
97
|
+
this.kept.splice(index, 1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Picks at most `maxFramesArg` frames spread evenly over the time the frames cover: for each of
|
|
104
|
+
* that many points in time, the frame on screen at that point. The first and the last frame are
|
|
105
|
+
* always among them.
|
|
106
|
+
*/
|
|
107
|
+
export const selectFrames = (framesArg, maxFramesArg) => {
|
|
108
|
+
if (framesArg.length <= maxFramesArg) {
|
|
109
|
+
return [...framesArg];
|
|
110
|
+
}
|
|
111
|
+
if (maxFramesArg === 1) {
|
|
112
|
+
return [framesArg[framesArg.length - 1]];
|
|
113
|
+
}
|
|
114
|
+
const first = framesArg[0].timestampMs;
|
|
115
|
+
const span = framesArg[framesArg.length - 1].timestampMs - first;
|
|
116
|
+
const picked = new Set();
|
|
117
|
+
let frameIndex = 0;
|
|
118
|
+
for (let point = 0; point < maxFramesArg; point++) {
|
|
119
|
+
const time = point === maxFramesArg - 1 ? Number.POSITIVE_INFINITY : first + (span * point) / (maxFramesArg - 1);
|
|
120
|
+
while (frameIndex + 1 < framesArg.length && framesArg[frameIndex + 1].timestampMs <= time) {
|
|
121
|
+
frameIndex++;
|
|
122
|
+
}
|
|
123
|
+
picked.add(frameIndex);
|
|
124
|
+
}
|
|
125
|
+
return [...picked].sort((a, b) => a - b).map((indexArg) => framesArg[indexArg]);
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zY3JlZW5jYXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY2FwdHVyZS9jbGFzc2VzLnNjcmVlbmNhc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQXVCekM7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxxQkFBcUI7SUFZYjtJQUNBO0lBWlgsT0FBTyxHQUF1QixJQUFJLENBQUM7SUFDbkMsU0FBUyxHQUFHLENBQUMsQ0FBQztJQUNMLElBQUksR0FBdUIsRUFBRSxDQUFDO0lBQy9DLHNFQUFzRTtJQUM5RCxNQUFNLEdBQTRCLElBQUksQ0FBQztJQUN2QyxPQUFPLEdBQUcsS0FBSyxDQUFDO0lBQ2hCLFVBQVUsR0FBWSxJQUFJLENBQUM7SUFDbEIsYUFBYSxDQUFTO0lBQ3RCLGFBQWEsQ0FBUztJQUV2QyxZQUNtQixJQUFXLEVBQ1gsT0FBOEI7UUFEOUIsU0FBSSxHQUFKLElBQUksQ0FBTztRQUNYLFlBQU8sR0FBUCxPQUFPLENBQXVCO1FBRS9DLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNuRCxJQUFJLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLElBQUksR0FBRyxDQUFDO0lBQ3BELENBQUM7SUFFRCwrREFBK0Q7SUFDeEQsWUFBWTtRQUNqQixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDeEIsQ0FBQztJQUVNLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ25ELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLE9BQU8sQ0FBQyxFQUFFLENBQUMsc0JB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
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* A capture that failed: the typed API answers it as a typed error with this message, the CLI
|
|
4
|
+
* prints it and exits with 1.
|
|
5
|
+
*/
|
|
6
|
+
export declare class WccCaptureError extends plugins.typedrequest.TypedResponseError {
|
|
7
|
+
constructor(messageArg: string);
|
|
8
|
+
}
|
|
9
|
+
/** A capture request that is invalid as given; the CLI exits with 2. */
|
|
10
|
+
export declare class WccCaptureRequestError extends WccCaptureError {
|
|
11
|
+
constructor(messageArg: string);
|
|
12
|
+
}
|
|
13
|
+
/** The message of any thrown value. */
|
|
14
|
+
export declare const getErrorMessage: (errorArg: unknown) => string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* A capture that failed: the typed API answers it as a typed error with this message, the CLI
|
|
4
|
+
* prints it and exits with 1.
|
|
5
|
+
*/
|
|
6
|
+
export class WccCaptureError extends plugins.typedrequest.TypedResponseError {
|
|
7
|
+
constructor(messageArg) {
|
|
8
|
+
super(messageArg);
|
|
9
|
+
this.name = 'WccCaptureError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** A capture request that is invalid as given; the CLI exits with 2. */
|
|
13
|
+
export class WccCaptureRequestError extends WccCaptureError {
|
|
14
|
+
constructor(messageArg) {
|
|
15
|
+
super(messageArg);
|
|
16
|
+
this.name = 'WccCaptureRequestError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** The message of any thrown value. */
|
|
20
|
+
export const getErrorMessage = (errorArg) => {
|
|
21
|
+
return errorArg instanceof Error ? errorArg.message : String(errorArg);
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY2FwdHVyZS9lcnJvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLGVBQWdCLFNBQVEsT0FBTyxDQUFDLFlBQVksQ0FBQyxrQkFBa0I7SUFDMUUsWUFBWSxVQUFrQjtRQUM1QixLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEIsSUFBSSxDQUFDLElBQUksR0FBRyxpQkFBaUIsQ0FBQztJQUNoQyxDQUFDO0NBQ0Y7QUFFRCx3RUFBd0U7QUFDeEUsTUFBTSxPQUFPLHNCQUF1QixTQUFRLGVBQWU7SUFDekQsWUFBWSxVQUFrQjtRQUM1QixLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEIsSUFBSSxDQUFDLElBQUksR0FBRyx3QkFBd0IsQ0FBQztJQUN2QyxDQUFDO0NBQ0Y7QUFFRCx1Q0FBdUM7QUFDdkMsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLENBQUMsUUFBaUIsRUFBVSxFQUFFO0lBQzNELE9BQU8sUUFBUSxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ3pFLENBQUMsQ0FBQyJ9
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccCaptureError } from './errors.js';
|
|
3
|
+
const pngSignature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
4
|
+
/** The pixel size of a PNG, from its IHDR chunk. */
|
|
5
|
+
const readPngSize = (bytesArg) => {
|
|
6
|
+
if (bytesArg.length < 24 || pngSignature.some((byteArg, indexArg) => bytesArg[indexArg] !== byteArg)) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const view = new DataView(bytesArg.buffer, bytesArg.byteOffset, bytesArg.byteLength);
|
|
10
|
+
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
11
|
+
};
|
|
12
|
+
/** The pixel size of a JPEG, from its first start-of-frame segment. */
|
|
13
|
+
const readJpegSize = (bytesArg) => {
|
|
14
|
+
if (bytesArg.length < 4 || bytesArg[0] !== 0xff || bytesArg[1] !== 0xd8) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const view = new DataView(bytesArg.buffer, bytesArg.byteOffset, bytesArg.byteLength);
|
|
18
|
+
let offset = 2;
|
|
19
|
+
while (offset + 4 <= bytesArg.length) {
|
|
20
|
+
if (bytesArg[offset] !== 0xff) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const marker = bytesArg[offset + 1];
|
|
24
|
+
// Markers without a length: fill bytes, TEM and RSTn
|
|
25
|
+
if (marker === 0xff) {
|
|
26
|
+
offset += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
30
|
+
offset += 2;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const length = view.getUint16(offset + 2);
|
|
34
|
+
const isStartOfFrame = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
35
|
+
if (isStartOfFrame) {
|
|
36
|
+
if (offset + 9 > bytesArg.length) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return { width: view.getUint16(offset + 7), height: view.getUint16(offset + 5) };
|
|
40
|
+
}
|
|
41
|
+
offset += 2 + length;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
/** An encoded image with its pixel size read from its own header. */
|
|
46
|
+
export const toWccImage = (bytesArg, formatArg) => {
|
|
47
|
+
const size = formatArg === 'png' ? readPngSize(bytesArg) : readJpegSize(bytesArg);
|
|
48
|
+
if (!size) {
|
|
49
|
+
throw new WccCaptureError(`wcctools: the browser returned no valid ${formatArg.toUpperCase()} image.`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
mimeType: formatArg === 'png' ? 'image/png' : 'image/jpeg',
|
|
53
|
+
width: size.width,
|
|
54
|
+
height: size.height,
|
|
55
|
+
dataBase64: Buffer.from(bytesArg.buffer, bytesArg.byteOffset, bytesArg.byteLength).toString('base64'),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY2FwdHVyZS9pbWFnZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUU5QyxNQUFNLFlBQVksR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztBQUV0RSxvREFBb0Q7QUFDcEQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxRQUFvQixFQUE0QyxFQUFFO0lBQ3JGLElBQUksUUFBUSxDQUFDLE1BQU0sR0FBRyxFQUFFLElBQUksWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsS0FBSyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ3JHLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDckYsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7QUFDbkUsQ0FBQyxDQUFDO0FBRUYsdUVBQXVFO0FBQ3ZFLE1BQU0sWUFBWSxHQUFHLENBQUMsUUFBb0IsRUFBNEMsRUFBRTtJQUN0RixJQUFJLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLElBQUksUUFBUSxDQUFDLENB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
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './classes.captureservice.js';
|
|
2
|
+
export * from './classes.capturebrowser.js';
|
|
3
|
+
export * from './errors.js';
|
|
4
|
+
export { captureLimits, captureViewportWidths, resolveScreenshotRequest, resolveInteractionRequest } from './request.js';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jYXB0dXJlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyw2QkFBNkIsQ0FBQztBQUM1QyxjQUFjLGFBQWEsQ0FBQztBQUM1QixPQUFPLEVBQUUsYUFBYSxFQUFFLHFCQUFxQixFQUFFLHdCQUF3QixFQUFFLHlCQUF5QixFQUFFLE1BQU0sY0FBYyxDQUFDIn0=
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { IWccRenderedPreview } from './previewpage.js';
|
|
3
|
+
import { type IWccResolvedInteractionRequest } from './request.js';
|
|
4
|
+
/**
|
|
5
|
+
* Runs the steps on a rendered preview while recording its screencast, observes for a while after
|
|
6
|
+
* the last one and answers the steps' results and a bounded selection of the frames. A failed step
|
|
7
|
+
* ends the script; an abort ends it with the signal's reason. The page stays on the preview: a step
|
|
8
|
+
* that starts a navigation fails with the blocked navigation and ends the script; a navigation
|
|
9
|
+
* blocked during the observation fails the last step.
|
|
10
|
+
*/
|
|
11
|
+
export declare const runInteraction: (previewArg: IWccRenderedPreview, requestArg: IWccResolvedInteractionRequest, abortSignalArg: AbortSignal) => Promise<Omit<plugins.interfaces.IWccInteractionResult, "selection" | "finalImage">>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { getErrorMessage } from './errors.js';
|
|
3
|
+
import { WccScreencastRecorder, selectFrames } from './classes.screencast.js';
|
|
4
|
+
import { formatBlockedNavigation } from './navigationguard.js';
|
|
5
|
+
import { captureLimits } from './request.js';
|
|
6
|
+
/** A selector matched in the document and in every open shadow root below it. */
|
|
7
|
+
const piercing = (selectorArg) => `pierce/${selectorArg}`;
|
|
8
|
+
/** Resolves after `msArg`, or rejects with the signal's reason once it aborts; never leaves its timer behind. */
|
|
9
|
+
const observe = (msArg, abortSignalArg) => {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
if (abortSignalArg.aborted) {
|
|
12
|
+
reject(abortSignalArg.reason);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const onAbort = () => {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
reject(abortSignalArg.reason);
|
|
18
|
+
};
|
|
19
|
+
const timer = setTimeout(() => {
|
|
20
|
+
abortSignalArg.removeEventListener('abort', onAbort);
|
|
21
|
+
resolve();
|
|
22
|
+
}, msArg);
|
|
23
|
+
abortSignalArg.addEventListener('abort', onAbort, { once: true });
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
/** A step as its error messages name it: its action and its selector or key. */
|
|
27
|
+
const describeStep = (stepArg) => {
|
|
28
|
+
switch (stepArg.action) {
|
|
29
|
+
case 'press':
|
|
30
|
+
return stepArg.selector === undefined ? `press "${stepArg.key}"` : `press "${stepArg.key}" on "${stepArg.selector}"`;
|
|
31
|
+
case 'wait':
|
|
32
|
+
return `wait ${stepArg.ms} ms`;
|
|
33
|
+
default:
|
|
34
|
+
return `${stepArg.action} "${stepArg.selector}"`;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const runStep = async (pageArg, stepArg, abortSignalArg) => {
|
|
38
|
+
const timeout = captureLimits.stepTimeoutMs;
|
|
39
|
+
switch (stepArg.action) {
|
|
40
|
+
case 'click':
|
|
41
|
+
await pageArg.locator(piercing(stepArg.selector)).setTimeout(timeout).click({ signal: abortSignalArg });
|
|
42
|
+
return;
|
|
43
|
+
case 'hover':
|
|
44
|
+
await pageArg.locator(piercing(stepArg.selector)).setTimeout(timeout).hover({ signal: abortSignalArg });
|
|
45
|
+
return;
|
|
46
|
+
case 'fill':
|
|
47
|
+
await pageArg.locator(piercing(stepArg.selector)).setTimeout(timeout).fill(stepArg.text, { signal: abortSignalArg });
|
|
48
|
+
return;
|
|
49
|
+
case 'press': {
|
|
50
|
+
if (stepArg.selector !== undefined) {
|
|
51
|
+
const handle = await pageArg.locator(piercing(stepArg.selector)).setTimeout(timeout).waitHandle({ signal: abortSignalArg });
|
|
52
|
+
try {
|
|
53
|
+
await handle.focus();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await handle.dispose();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Puppeteer refuses keys it does not know with an error naming the key
|
|
60
|
+
await pageArg.keyboard.press(stepArg.key);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
case 'waitFor': {
|
|
64
|
+
const handle = await pageArg.waitForSelector(piercing(stepArg.selector), {
|
|
65
|
+
timeout,
|
|
66
|
+
signal: abortSignalArg,
|
|
67
|
+
...(stepArg.state === 'hidden' ? { hidden: true } : { visible: true }),
|
|
68
|
+
});
|
|
69
|
+
await handle?.dispose();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
case 'wait':
|
|
73
|
+
await observe(stepArg.ms, abortSignalArg);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Runs the steps on a rendered preview while recording its screencast, observes for a while after
|
|
79
|
+
* the last one and answers the steps' results and a bounded selection of the frames. A failed step
|
|
80
|
+
* ends the script; an abort ends it with the signal's reason. The page stays on the preview: a step
|
|
81
|
+
* that starts a navigation fails with the blocked navigation and ends the script; a navigation
|
|
82
|
+
* blocked during the observation fails the last step.
|
|
83
|
+
*/
|
|
84
|
+
export const runInteraction = async (previewArg, requestArg, abortSignalArg) => {
|
|
85
|
+
const { page, navigationGuard } = previewArg;
|
|
86
|
+
const recorder = new WccScreencastRecorder(page, {
|
|
87
|
+
quality: requestArg.frameQuality,
|
|
88
|
+
maxWidth: requestArg.view.width * requestArg.view.scale,
|
|
89
|
+
maxHeight: requestArg.view.height * requestArg.view.scale,
|
|
90
|
+
});
|
|
91
|
+
const steps = [];
|
|
92
|
+
let frames;
|
|
93
|
+
await recorder.start();
|
|
94
|
+
try {
|
|
95
|
+
const startedAt = recorder.getStartedAt();
|
|
96
|
+
for (const [index, step] of requestArg.steps.entries()) {
|
|
97
|
+
const startMs = Date.now() - startedAt;
|
|
98
|
+
let error = null;
|
|
99
|
+
try {
|
|
100
|
+
await runStep(page, step, abortSignalArg);
|
|
101
|
+
}
|
|
102
|
+
catch (stepError) {
|
|
103
|
+
abortSignalArg.throwIfAborted();
|
|
104
|
+
error = getErrorMessage(stepError);
|
|
105
|
+
}
|
|
106
|
+
const blockedUrl = await navigationGuard.takeBlockedNavigation();
|
|
107
|
+
if (blockedUrl !== null) {
|
|
108
|
+
error = formatBlockedNavigation(blockedUrl);
|
|
109
|
+
}
|
|
110
|
+
const endMs = Date.now() - startedAt;
|
|
111
|
+
if (error === null) {
|
|
112
|
+
steps.push({ index, ok: true, startMs, endMs });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
steps.push({ index, ok: false, error: `${describeStep(step)}: ${error}`, startMs, endMs });
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
await observe(requestArg.observeMs, abortSignalArg);
|
|
119
|
+
const lastStep = steps.at(-1);
|
|
120
|
+
if (lastStep === undefined) {
|
|
121
|
+
await navigationGuard.assertStayed('being observed');
|
|
122
|
+
}
|
|
123
|
+
else if (lastStep.ok) {
|
|
124
|
+
const blockedUrl = await navigationGuard.takeBlockedNavigation();
|
|
125
|
+
if (blockedUrl !== null) {
|
|
126
|
+
// Started after the last step ended: by that step, or by the demo on its own
|
|
127
|
+
lastStep.ok = false;
|
|
128
|
+
lastStep.error = `${describeStep(requestArg.steps[lastStep.index])}: ${formatBlockedNavigation(blockedUrl)}`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
frames = await recorder.stop();
|
|
134
|
+
}
|
|
135
|
+
const completed = steps.length === requestArg.steps.length && steps.every((stepArg) => stepArg.ok);
|
|
136
|
+
return { frames: selectFrames(frames, requestArg.maxFrames), steps, completed };
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJhY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jYXB0dXJlL2ludGVyYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDOUMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzlFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRS9ELE9BQU8sRUFBRSxhQUFhLEVBQXVDLE1BQU0sY0FBYyxDQUFDO0FBS2xGLGlGQUFpRjtBQUNqRixNQUFNLFFBQVEsR0FBRyxDQUFDLFdBQW1CLEVBQUUsRUFBRSxDQUFDLFVBQVUsV0FBVyxFQUFFLENBQUM7QUFFbEUsaUhBQWlIO0FBQ2pILE1BQU0sT0FBTyxHQUFHLENBQUMsS0FBYSxFQUFFLGNBQTJCLEVBQWlCLEVBQUU7SUFDNUUsT0FBTyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUMzQyxJQUFJLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUMzQixNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzlCLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQ25CLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLENBQUMsQ0FBQztRQUNGLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDNUIsY0FBYyxDQUFDLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNyRCxPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUMsRUFBRSxLQUFLLENBQUMs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
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
type TPage = plugins.smartbrowser.puppeteer.Page;
|
|
3
|
+
/** The message of a navigation the capture blocked. */
|
|
4
|
+
export declare const formatBlockedNavigation: (urlArg: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Keeps a capture page on the preview. Its main frame loads the preview document once; after that,
|
|
7
|
+
* the page script above cancels every navigation that would replace the document before it starts.
|
|
8
|
+
* Request interception backs it up: a main-frame navigation request the page could not cancel is
|
|
9
|
+
* aborted before it leaves. The preview's history holds the preview alone, so there is no earlier
|
|
10
|
+
* document to go back to. Subframes and all other requests are left alone.
|
|
11
|
+
*
|
|
12
|
+
* `takeBlockedNavigation()` answers the navigation blocked since its last call, so each step of an
|
|
13
|
+
* interaction learns about the navigation it started.
|
|
14
|
+
*/
|
|
15
|
+
export declare class WccNavigationGuard {
|
|
16
|
+
private readonly page;
|
|
17
|
+
/** Navigation requests aborted by the backstop since the last `takeBlockedNavigation()`. */
|
|
18
|
+
private abortedUrls;
|
|
19
|
+
private interceptionFailure;
|
|
20
|
+
private previewUrl;
|
|
21
|
+
private constructor();
|
|
22
|
+
/**
|
|
23
|
+
* Installs the page script and intercepts the page's requests; the page's first main-frame
|
|
24
|
+
* navigation may load `previewUrlArg` only. Install before loading the preview.
|
|
25
|
+
*/
|
|
26
|
+
static install(pageArg: TPage, previewUrlArg: string): Promise<WccNavigationGuard>;
|
|
27
|
+
/** Drops the history entries before the loaded preview (the blank page it opened as), so going back leaves nothing. */
|
|
28
|
+
forgetEarlierHistory(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Lets the page run the tasks queued so far (an animation frame, then a task), then answers the
|
|
31
|
+
* URL of the first navigation blocked since the last call, by the page script or the backstop,
|
|
32
|
+
* or null when none was.
|
|
33
|
+
*/
|
|
34
|
+
takeBlockedNavigation(): Promise<string | null>;
|
|
35
|
+
/** Throws when the page tried to leave the preview while `whileArg` (e.g. "rendering"). */
|
|
36
|
+
assertStayed(whileArg: string): Promise<void>;
|
|
37
|
+
private decide;
|
|
38
|
+
/**
|
|
39
|
+
* Keeps the first failure of an interception decision. Puppeteer itself tolerates a request
|
|
40
|
+
* that ended or a page that closed meanwhile; what reaches here fails the capture.
|
|
41
|
+
*/
|
|
42
|
+
private settle;
|
|
43
|
+
}
|
|
44
|
+
export {};
|