Skip to content

Commit

Permalink
Update visual-diff dependencies and code (#39024)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrozenberg committed May 24, 2023
1 parent b5419db commit 320404e
Show file tree
Hide file tree
Showing 8 changed files with 909 additions and 1,369 deletions.
97 changes: 52 additions & 45 deletions build-system/tasks/visual-diff/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
const argv = require('minimist')(process.argv.slice(2));
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer');
const PuppeteerExtraPluginUserPreferences = require('puppeteer-extra-plugin-user-preferences');
const {addExtra} = require('puppeteer-extra');
const {
Browser: BrowserEnum,
computeExecutablePath,
getInstalledBrowsers,
install,
} = require('@puppeteer/browsers');
const {cyan, yellow} = require('kleur/colors');
const {HOST} = require('./consts');
const {log} = require('./log');
const puppeteer = require('puppeteer-core');

const cacheDir = path.join(__dirname, '.cache');

// REPEATING TODO(@ampproject/wg-infra): Update this whenever the Percy backend
// starts using a new version of Chrome to render DOM snapshots.
Expand All @@ -32,14 +38,10 @@ const VIEWPORT_HEIGHT = 100000;
/**
* Launches a Puppeteer controlled browser.
*
* Waits until the browser is up and reachable, and ties its lifecycle to this
* process's lifecycle.
*
* @param {!puppeteer.BrowserFetcher} browserFetcher Puppeteer browser binaries
* manager.
* @return {Promise<!puppeteer.Browser>} a Puppeteer controlled browser.
* @param {string} executablePath browser executable path.
* @return {!Promise<!puppeteer.Browser>} a Puppeteer controlled browser.
*/
async function launchBrowser(browserFetcher) {
async function launchBrowser(executablePath) {
/** @type {"new" | false} */
const headless = !argv.dev && 'new';
const browserOptions = {
Expand All @@ -55,24 +57,10 @@ async function launchBrowser(browserFetcher) {
],
dumpio: argv.chrome_debug,
headless,
executablePath: browserFetcher.revisionInfo(PUPPETEER_CHROMIUM_REVISION)
.executablePath,
executablePath,
waitForInitialPage: false,
};

const puppeteerExtra = addExtra(puppeteer);
puppeteerExtra.use(
PuppeteerExtraPluginUserPreferences({
userPrefs: {
devtools: {
preferences: {
currentDockState: '"undocked"',
},
},
},
})
);
return await puppeteerExtra.launch(browserOptions);
return puppeteer.launch(browserOptions);
}

/**
Expand Down Expand Up @@ -158,43 +146,62 @@ async function resetPage(page, viewport = null) {
}

/**
* Loads task-specific dependencies are returns an instance of BrowserFetcher.
* Returns the executable path of the browser, potentially installing and caching it.
*
* @return {Promise<!puppeteer.BrowserFetcher>}
* @return {Promise<string>}
*/
async function loadBrowserFetcher() {
// @ts-ignore Valid method in Puppeteer's nodejs interface.
// https://github.com/puppeteer/puppeteer/blob/main/src/node/Puppeteer.ts
const browserFetcher = puppeteer.createBrowserFetcher();
const chromiumRevisions = await browserFetcher.localRevisions();
if (chromiumRevisions.includes(PUPPETEER_CHROMIUM_REVISION)) {
async function fetchBrowserExecutablePath() {
const installedBrowsers = await getInstalledBrowsers({cacheDir});
const installedBrowser = installedBrowsers.find(
({browser, buildId}) =>
browser == BrowserEnum.CHROMIUM && buildId == PUPPETEER_CHROMIUM_REVISION
);
if (installedBrowser) {
log(
'info',
'Using Percy-compatible version of Chromium',
'Using cached Percy-compatible version of Chromium',
cyan(PUPPETEER_CHROMIUM_REVISION)
);
} else {
log(
'info',
'Percy-compatible version of Chromium',
cyan(PUPPETEER_CHROMIUM_REVISION),
'was not found. Downloading...'
);
await browserFetcher.download(
PUPPETEER_CHROMIUM_REVISION,
(/* downloadedBytes, totalBytes */) => {
// TODO(@ampproject/wg-infra): display download progress.
// Logging every call is too verbose.
}
'was not found in cache. Downloading...'
);
let logThrottler = true;
await install({
cacheDir,
browser: BrowserEnum.CHROMIUM,
buildId: PUPPETEER_CHROMIUM_REVISION,
downloadProgressCallback(downloadedBytes, totalBytes) {
if (logThrottler) {
log('info', downloadedBytes, '/', totalBytes, 'bytes');
logThrottler = false;
setTimeout(() => {
logThrottler = true;
}, 1000);
} else if (downloadedBytes == totalBytes) {
log(
'info',
'Finished downloading Chromium version',
cyan(PUPPETEER_CHROMIUM_REVISION)
);
}
},
});
}
return browserFetcher;
return computeExecutablePath({
cacheDir,
browser: BrowserEnum.CHROMIUM,
buildId: PUPPETEER_CHROMIUM_REVISION,
});
}

module.exports = {
PUPPETEER_CHROMIUM_REVISION,
launchBrowser,
loadBrowserFetcher,
fetchBrowserExecutablePath,
newPage,
resetPage,
};
4 changes: 3 additions & 1 deletion build-system/tasks/visual-diff/dev-mode.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const path = require('path');
const puppeteer = require('puppeteer'); // eslint-disable-line @typescript-eslint/no-unused-vars
const {
verifySelectorsInvisible,
verifySelectorsVisible,
Expand All @@ -16,6 +15,9 @@ const {WebpageDef} = require('./types');

const ROOT_DIR = path.resolve(__dirname, '../../../');

/** @typedef {import('puppeteer-core')} puppeteer */
/** @typedef {import('puppeteer-core').Browser} puppeteer.Browser */

/**
* Runs a development mode.
*
Expand Down
48 changes: 20 additions & 28 deletions build-system/tasks/visual-diff/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ const fs = require('fs');
const JSON5 = require('json5');
const os = require('os');
const path = require('path');
const PercyImportPromise = import('@percy/core');
const PercyModulePromise = import('@percy/core');
const percySnapshot = require('@percy/puppeteer');
const puppeteer = require('puppeteer'); // eslint-disable-line @typescript-eslint/no-unused-vars
const {
createCtrlcHandler,
exitCtrlcHandler,
Expand All @@ -20,9 +19,8 @@ const {
shortSha,
} = require('../../common/git');
const {
PUPPETEER_CHROMIUM_REVISION,
fetchBrowserExecutablePath,
launchBrowser,
loadBrowserFetcher,
newPage,
resetPage,
} = require('./browser');
Expand All @@ -41,6 +39,11 @@ const {isPercyEnabled} = require('@percy/sdk-utils');
const {startServer, stopServer} = require('../serve');
const {TestErrorDef, WebpageDef} = require('./types');

/** @typedef {import('@percy/core').default} Percy */
/** @typedef {import('puppeteer-core')} puppeteer */
/** @typedef {import('puppeteer-core').Browser} puppeteer.Browser */
/** @typedef {import('puppeteer-core').ConsoleMessage} puppeteer.ConsoleMessage */

// CSS injected in every page tested.
// Normally, as in https://docs.percy.io/docs/percy-specific-css
// Otherwise, as a <style> in an iframe, see snippets/iframe-wrapper.js
Expand Down Expand Up @@ -135,28 +138,25 @@ function setPercyTargetCommit() {
}

/**
* Launches a @percy/cli instance.
* Launches a Percy agent instance.
*
* @param {!puppeteer.BrowserFetcher} browserFetcher Puppeteer browser binaries
* manager.
* @return {Promise<Percy|undefined>} percy agent instance.
* @param {string} executablePath browser executable path.
* @return {!Promise<Percy|undefined>} percy agent instance.
*/
async function launchPercyAgent(browserFetcher) {
async function launchPercyAgent(executablePath) {
if (argv.percy_disabled) {
return;
}

// @ts-ignore Type mismatch in library
const {Percy} = await PercyImportPromise;
const percy = await Percy.start({
const {default: PercyModule} = await PercyModulePromise;
const percy = await PercyModule.start({
token: process.env.PERCY_TOKEN,
loglevel: argv.percy_agent_debug ? 'debug' : 'info',
port: PERCY_AGENT_PORT,
config: path.join(__dirname, '.percy.yaml'),
discovery: {
launchOptions: {
executable: browserFetcher.revisionInfo(PUPPETEER_CHROMIUM_REVISION)
.executablePath,
executable: executablePath,
},
},
});
Expand Down Expand Up @@ -592,15 +592,8 @@ async function createEmptyBuild(browser) {
* @return {Promise<void>}
*/
async function visualDiff() {
if (!PUPPETEER_CHROMIUM_REVISION) {
throw new Error(
`${cyan('amp visual-diff')} is only available on Linux, Mac, and Windows`
);
}

const handlerProcess = createCtrlcHandler('visual-diff');
await ensureOrBuildAmpRuntimeInTestMode_();
const browserFetcher = await loadBrowserFetcher();
decodePercyTokenForCi();
maybeOverridePercyEnvironmentVariables();
setPercyBranch();
Expand All @@ -618,11 +611,11 @@ async function visualDiff() {
log('fatal', 'Could not find', cyan('PERCY_TOKEN'), 'environment variable');
}

const percy = await launchPercyAgent(browserFetcher);
const executablePath = await fetchBrowserExecutablePath();
const percy = await launchPercyAgent(executablePath);
try {
await performVisualTests(browserFetcher);
await performVisualTests(executablePath);
} finally {
// @ts-ignore Type mismatch in library
await percy?.stop();
}
exitCtrlcHandler(handlerProcess);
Expand All @@ -631,14 +624,13 @@ async function visualDiff() {
/**
* Runs the AMP visual diff tests.
*
* @param {!puppeteer.BrowserFetcher} browserFetcher Puppeteer browser binaries
* manager.
* @param {string} executablePath browser executable path.
* @return {Promise<void>}
*/
async function performVisualTests(browserFetcher) {
async function performVisualTests(executablePath) {
setDebuggingLevel();

const browser = await launchBrowser(browserFetcher);
const browser = await launchBrowser(executablePath);
const handlerProcess = createCtrlcHandler(
'visual-diff:browser',
browser.process()?.pid
Expand Down
4 changes: 3 additions & 1 deletion build-system/tasks/visual-diff/log.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use strict';

const argv = require('minimist')(process.argv.slice(2));
const puppeteer = require('puppeteer'); // eslint-disable-line @typescript-eslint/no-unused-vars
const {green, red, yellow} = require('kleur/colors');
const {log: logBase} = require('../../common/logging');

/** @typedef {import('puppeteer-core')} puppeteer */
/** @typedef {import('puppeteer-core').Page} puppeteer.Page */

/**
* Logs a message to the console.
*
Expand Down

0 comments on commit 320404e

Please sign in to comment.