UNPKG

@scullyio/scully-plugin-playwright

Version:

The playwright renderer is utilizing the [playwright](https://playwright.dev/) engine to render your pages.

144 lines (143 loc) 6.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.launchedBrowser$ = exports.reLaunch = exports.launchedBrowser = exports.waitForIt = exports.browser = void 0; const tslib_1 = require("tslib"); const scully_1 = require("@scullyio/scully"); const cli_options_1 = require("@scullyio/scully/src/lib/utils/cli-options"); const playwright = require("playwright"); const rxjs_1 = require("rxjs"); const defaultConfig = { headless: true, channel: 'chrome', browser: 'chromium', }; const options = Object.assign({}, defaultConfig); const launches = new rxjs_1.BehaviorSubject(undefined); function waitForIt(milliSeconds) { return new Promise((resolve) => setTimeout(() => resolve(), milliSeconds)); } exports.waitForIt = waitForIt; let usageCounter = 0; const launchedBrowser = () => tslib_1.__awaiter(void 0, void 0, void 0, function* () { if (++usageCounter > 500) { launches.next(); usageCounter = 0; } return exports.launchedBrowser$.pipe(rxjs_1.take(1)).toPromise(); }); exports.launchedBrowser = launchedBrowser; const reLaunch = (reason) => { if (reason) { scully_1.logWarn(scully_1.white(` ======================================== Relaunch because of ${reason} ======================================== `)); } launches.next(); return exports.launchedBrowser(); }; exports.reLaunch = reLaunch; const launch = (pluginConfig) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { const browserType = pluginConfig.browser; const playrightBrowser = playwright[browserType]; const browser = yield playrightBrowser.launch({ headless: pluginConfig.headless, channel: pluginConfig.channel }); return browser; }); exports.launchedBrowser$ = rxjs_1.of('').pipe( /** load config only after a subscription is made */ rxjs_1.switchMap(() => scully_1.loadConfig()), /** give the system a bit of breathing room, and prevent race */ rxjs_1.switchMap(() => rxjs_1.from(waitForIt(50))), rxjs_1.switchMap(() => rxjs_1.merge(obsBrowser(), launches)), /** use shareReplay so the browser will stay in memory during the lifetime of the program */ rxjs_1.shareReplay({ refCount: false, bufferSize: 1 }), rxjs_1.filter((e) => e !== undefined)); function obsBrowser() { let isLaunching = false; if (cli_options_1.showBrowser) { options.headless = false; } options.args = options.args || []; return new rxjs_1.Observable((obs) => { const startPlaywright = () => { if (!isLaunching) { isLaunching = true; launchPlayWrightWithRetry(options).then((b) => { /** I will only come here when playwright is actually launched */ exports.browser = b; b.on('disconnected', () => exports.reLaunch('disconnect')); obs.next(b); /** only allow a relaunch in a next cycle */ setTimeout(() => (isLaunching = false), 1000); }); } }; launches .pipe( /** ignore request while the browser is already starting, we can only launch 1 */ rxjs_1.filter(() => !isLaunching), /** the long throttleTime is to cater for the concurrently running browsers to crash and burn. */ rxjs_1.throttleTime(15000), // provide enough time for the current async operations to finish before killing the current browser instance rxjs_1.delayWhen(() => rxjs_1.merge( /** timout at 25 seconds */ rxjs_1.timer(25000)).pipe( /** use take 1 to make sure we complete when one of the above fires */ rxjs_1.take(1), /** if something _really_ unwieldy happens with the browser, ignore and go ahead */ rxjs_1.catchError(() => rxjs_1.of([]))))) .subscribe({ next: () => { try { if (exports.browser && exports.browser.contexts() != null) { exports.browser.close(); } } catch (_a) { /** ignored */ } startPlaywright(); }, }); return () => { if (exports.browser) { exports.browser.close(); exports.browser = undefined; } }; }); } function launchPlayWrightWithRetry(options, failedLaunches = 0) { const timeout = (millisecs) => new Promise((_, reject) => setTimeout(() => reject('timeout'), millisecs)); return Promise.race([ /** use a 1 minute timeout to detect a stalled launch of playwright */ timeout(Math.max(/** serverTimeout,*/ 60 * 1000)), launch(options).then((b) => { return b; }), ]) .catch((e) => { /** first stage catch check for retry */ if (e.message.includes('Could not find browser revision')) { throw new Error('Failed launch'); } if (++failedLaunches < 3) { return launchPlayWrightWithRetry(options, failedLaunches); } throw new Error('failed 3 times to launch'); }) .catch((b) => { /** second stage catch, houston, we have a problem, and will abort */ scully_1.logError(` ================================================================================================= Playwright cannot find or launch the browser. (by default chrome) This might happen because the default timeout (60 seconds) is to short on this system this can be fixed by adding the ${scully_1.yellow('--serverTimeout=x')} cmd line option. (where x = the new timeout in milliseconds) When this happens in CI/CD you can find some additional information here: https://playwright.dev/docs/troubleshooting ================================================================================================= `); process.exit(15); }); } //# sourceMappingURL=plugins-scully-plugin-playwright-utils.js.map