web-snaps
Version:
Browser automation with automatic snapshotting.
107 lines (106 loc) • 4.68 kB
JavaScript
import { check, checkWrap } from '@augment-vir/assert';
import { ensureErrorAndPrependMessage, log as logImport, wrapInTry, } from '@augment-vir/common';
import { getNowInIsoString, getNowInUtcTimezone } from 'date-vir';
import { JSDOM } from 'jsdom';
import { getAllPageHtml } from '../web-snap/get-html.js';
import { saveWebSnap } from '../web-snap/save-web-snap.js';
/**
* Run a single {@link WebFlow}. A browser must already be loaded beforehand.
*
* @category Internal
*/
export async function runWebFlow({ browserParams, webFlow, options = {}, }) {
const log = logImport.if(!options.silent);
/* node:coverage ignore next 1: not testing the user provided page */
const page = options.existingPage || (await browserParams.browserContext.newPage());
const createdPage = !options.existingPage;
try {
try {
const webFlowStartedAt = getNowInUtcTimezone();
await page.goto(webFlow.startUrl);
log.faint(`${webFlow.flowKey}: start`);
let wasSnapshotBlocked = false;
const params = {
...browserParams,
originalUrl: webFlow.startUrl,
page,
webFlowStartedAt,
webFlowKey: webFlow.flowKey,
silent: !!options.silent,
blockSnapshot(shouldBlockSnapshot) {
wasSnapshotBlocked = shouldBlockSnapshot;
},
};
const phaseOutputs = [];
const webSnapInProgress = {
webFlow: {
flowKey: webFlow.flowKey,
startUrl: webFlow.startUrl,
phaseNames: webFlow.phaseNames,
},
generatedAt: getNowInIsoString(),
phaseSnaps: [],
};
try {
for (const [index, phase,] of webFlow.phases.entries()) {
try {
const phaseParams = {
...params,
phaseStartedAt: getNowInUtcTimezone(),
};
log.faint(`${webFlow.flowKey}: phase ${index}: ${phase.name}`);
const phaseResult = await wrapInTry(() => phase.run(phaseParams));
const error = checkWrap.instanceOf(phaseResult, Error);
const { disableSnapshot, output } = checkWrap.notInstanceOf(phaseResult, Error) || {
disableSnapshot: false,
output: undefined,
};
phaseOutputs.push(output);
if (!wasSnapshotBlocked &&
!options.disableSnapshots &&
!phase.disableSnapshot &&
!disableSnapshot) {
const rawHtml = await getAllPageHtml(page, browserParams.storeKey);
const finalHtml = phase.sanitizeSnapshot
? await phase.sanitizeSnapshot({
...phaseParams,
get dom() {
return new JSDOM(rawHtml);
},
domString: rawHtml,
})
: rawHtml;
webSnapInProgress.phaseSnaps.push({
pageHtml: check.isString(finalHtml)
? finalHtml
: finalHtml.serialize(),
phaseName: phase.name,
url: page.url(),
});
}
if (error) {
throw error;
}
}
catch (error) {
throw ensureErrorAndPrependMessage(error, `Phase '${phase.name}' in WebFlow '${webFlow.flowKey}' failed:`);
}
}
}
finally {
if (webSnapInProgress.phaseSnaps.length && options.webSnapDirPath) {
await saveWebSnap(webFlow, webSnapInProgress, !!options.silent);
}
}
return phaseOutputs;
}
catch (error) {
throw ensureErrorAndPrependMessage(error, `WebFlow '${webFlow.flowKey}' failed:`);
}
}
finally {
if (createdPage) {
await page.close();
}
}
}