mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-07 12:18:39 +00:00
chore(deps): update selenium-webdriver to 4 (#3205)
* chore(deps): update selenium-webdriver to 4 refactor: use async/await * fix(screenshotter): disable static watch
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const childProcess = require("child_process");
|
const childProcess = require("child_process");
|
||||||
|
const util = require("util");
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const jspngopt = require("jspngopt");
|
const jspngopt = require("jspngopt");
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
@@ -9,6 +10,9 @@ const os = require("os");
|
|||||||
const pako = require("pako");
|
const pako = require("pako");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const got = require("got");
|
const got = require("got");
|
||||||
|
const pRetry = require('p-retry');
|
||||||
|
|
||||||
|
const execFile = util.promisify(childProcess.execFile);
|
||||||
|
|
||||||
const selenium = require("selenium-webdriver");
|
const selenium = require("selenium-webdriver");
|
||||||
const firefox = require("selenium-webdriver/firefox");
|
const firefox = require("selenium-webdriver/firefox");
|
||||||
@@ -112,15 +116,6 @@ if (opts.browserstack) {
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Work out connection to selenium docker container
|
// Work out connection to selenium docker container
|
||||||
|
|
||||||
function check(err) {
|
|
||||||
if (!err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.error(err);
|
|
||||||
console.error(err.stack);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cmd() {
|
function cmd() {
|
||||||
const args = Array.prototype.slice.call(arguments);
|
const args = Array.prototype.slice.call(arguments);
|
||||||
const cmd = args.shift();
|
const cmd = args.shift();
|
||||||
@@ -184,8 +179,36 @@ if (seleniumURL) {
|
|||||||
console.log("Selenium driver in local session");
|
console.log("Selenium driver in local session");
|
||||||
}
|
}
|
||||||
|
|
||||||
process.nextTick(startServer);
|
(async() => {
|
||||||
let attempts = 0;
|
if (!(katexURL || katexPort)) {
|
||||||
|
await pRetry(startServer, {retries: 50, minTimeout: 100});
|
||||||
|
}
|
||||||
|
if (opts.seleniumProxy) {
|
||||||
|
driver = await getProxyDriver();
|
||||||
|
} else {
|
||||||
|
if (opts.browserstack) {
|
||||||
|
await startBrowserstackLocal();
|
||||||
|
}
|
||||||
|
if (seleniumIP) {
|
||||||
|
await pRetry(tryConnect, {retries: 50, minTimeout: 100});
|
||||||
|
}
|
||||||
|
driver = buildDriver();
|
||||||
|
}
|
||||||
|
await setupDriver();
|
||||||
|
await findHostIP();
|
||||||
|
await takeScreenshots();
|
||||||
|
|
||||||
|
await driver.quit();
|
||||||
|
await devServer.stop();
|
||||||
|
if (bsLocal) {
|
||||||
|
const bsLocalStop = util.promisify(bsLocal.stop);
|
||||||
|
await bsLocalStop();
|
||||||
|
}
|
||||||
|
process.exit(exitStatus);
|
||||||
|
})().catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Start up development server
|
// Start up development server
|
||||||
@@ -195,13 +218,8 @@ let coverageMap;
|
|||||||
const minPort = 32768;
|
const minPort = 32768;
|
||||||
const maxPort = 61000;
|
const maxPort = 61000;
|
||||||
|
|
||||||
function startServer() {
|
async function startServer() {
|
||||||
if (katexURL || katexPort) {
|
katexPort = Math.floor(Math.random() * (maxPort - minPort)) + minPort;
|
||||||
process.nextTick(tryConnect);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const port = Math.floor(Math.random() * (maxPort - minPort)) + minPort;
|
|
||||||
|
|
||||||
if (opts.coverage) {
|
if (opts.coverage) {
|
||||||
coverageMap = istanbulLibCoverage.createCoverageMap({});
|
coverageMap = istanbulLibCoverage.createCoverageMap({});
|
||||||
webpackConfig.module.rules[0].use = {
|
webpackConfig.module.rules[0].use = {
|
||||||
@@ -216,67 +234,50 @@ function startServer() {
|
|||||||
}
|
}
|
||||||
const config = {
|
const config = {
|
||||||
...webpackConfig.devServer,
|
...webpackConfig.devServer,
|
||||||
port,
|
static: [{directory: process.cwd(), watch: false}],
|
||||||
|
port: katexPort,
|
||||||
hot: false,
|
hot: false,
|
||||||
liveReload: false,
|
liveReload: false,
|
||||||
client: false,
|
client: false,
|
||||||
};
|
};
|
||||||
const compiler = webpack(webpackConfig);
|
const compiler = webpack(webpackConfig);
|
||||||
const wds = new WebpackDevServer(config, compiler);
|
devServer = new WebpackDevServer(config, compiler);
|
||||||
wds.start(port).then(() => {
|
await devServer.start(katexPort);
|
||||||
devServer = wds;
|
|
||||||
katexPort = port;
|
|
||||||
attempts = 0;
|
|
||||||
process.nextTick(opts.seleniumProxy ? getProxyDriver
|
|
||||||
: opts.browserstack ? startBrowserstackLocal : tryConnect);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (devServer !== null) { // error after we started listening
|
|
||||||
throw err;
|
|
||||||
} else if (++attempts > 50) {
|
|
||||||
throw new Error("Failed to start up dev server");
|
|
||||||
} else {
|
|
||||||
process.nextTick(startServer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Browserstack Local connection
|
// Start Browserstack Local connection
|
||||||
function startBrowserstackLocal() {
|
async function startBrowserstackLocal() {
|
||||||
// unique identifier for the session
|
// unique identifier for the session
|
||||||
const localIdentifier = process.env.CIRCLE_BUILD_NUM || "p" + katexPort;
|
const localIdentifier = process.env.CIRCLE_BUILD_NUM || "p" + katexPort;
|
||||||
opts.seleniumCapabilities["browserstack.localIdentifier"] = localIdentifier;
|
opts.seleniumCapabilities["browserstack.localIdentifier"] = localIdentifier;
|
||||||
|
|
||||||
bsLocal = new browserstack.Local();
|
bsLocal = new browserstack.Local();
|
||||||
bsLocal.start({localIdentifier}, function(err) {
|
await new Promise((resolve, reject) => {
|
||||||
if (err) {
|
bsLocal.start({localIdentifier}, err => {
|
||||||
throw err;
|
if (err) {
|
||||||
}
|
reject(err);
|
||||||
process.nextTick(tryConnect);
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Wait for container to become ready
|
// Wait for container to become ready
|
||||||
|
|
||||||
function tryConnect() {
|
async function tryConnect() {
|
||||||
if (!seleniumIP) {
|
return new Promise((resolve, reject) => {
|
||||||
process.nextTick(buildDriver);
|
const sock = net.connect({
|
||||||
return;
|
host: seleniumIP,
|
||||||
}
|
port: +seleniumPort,
|
||||||
const sock = net.connect({
|
});
|
||||||
host: seleniumIP,
|
sock.on("connect", function() {
|
||||||
port: +seleniumPort,
|
sock.end();
|
||||||
});
|
resolve();
|
||||||
sock.on("connect", function() {
|
}).on("error", function(err) {
|
||||||
sock.end();
|
reject(err);
|
||||||
attempts = 0;
|
});
|
||||||
process.nextTick(buildDriver);
|
|
||||||
}).on("error", function() {
|
|
||||||
if (++attempts > 50) {
|
|
||||||
throw new Error("Failed to connect selenium server.");
|
|
||||||
}
|
|
||||||
setTimeout(tryConnect, 200);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,11 +289,10 @@ let driverReady = false;
|
|||||||
function buildDriver() {
|
function buildDriver() {
|
||||||
const builder = new selenium.Builder().forBrowser(opts.browser);
|
const builder = new selenium.Builder().forBrowser(opts.browser);
|
||||||
if (opts.browser === "firefox") {
|
if (opts.browser === "firefox") {
|
||||||
const ffProfile = new firefox.Profile();
|
const ffOptions = new firefox.Options();
|
||||||
ffProfile.setPreference(
|
ffOptions.setPreference(
|
||||||
"browser.startup.homepage_override.mstone", "ignore");
|
"browser.startup.homepage_override.mstone", "ignore");
|
||||||
ffProfile.setPreference("browser.startup.page", 0);
|
ffOptions.setPreference("browser.startup.page", 0);
|
||||||
const ffOptions = new firefox.Options().setProfile(ffProfile);
|
|
||||||
builder.setFirefoxOptions(ffOptions);
|
builder.setFirefoxOptions(ffOptions);
|
||||||
} else if (opts.browser === "chrome") {
|
} else if (opts.browser === "chrome") {
|
||||||
// https://stackoverflow.com/questions/48450594/selenium-timed-out-receiving-message-from-renderer
|
// https://stackoverflow.com/questions/48450594/selenium-timed-out-receiving-message-from-renderer
|
||||||
@@ -305,39 +305,35 @@ function buildDriver() {
|
|||||||
if (opts.seleniumCapabilities) {
|
if (opts.seleniumCapabilities) {
|
||||||
builder.withCapabilities(opts.seleniumCapabilities);
|
builder.withCapabilities(opts.seleniumCapabilities);
|
||||||
}
|
}
|
||||||
driver = builder.build();
|
return builder.build();
|
||||||
setupDriver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProxyDriver() {
|
async function getProxyDriver() {
|
||||||
got.post(opts.seleniumProxy, {
|
const {body} = await got.post(opts.seleniumProxy, {
|
||||||
json: {
|
json: {
|
||||||
browserstack: opts.browserstack,
|
browserstack: opts.browserstack,
|
||||||
capabilities: opts.seleniumCapabilities,
|
capabilities: opts.seleniumCapabilities,
|
||||||
seleniumURL,
|
seleniumURL,
|
||||||
},
|
},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
}).then(({body}) => {
|
|
||||||
const session = new selenium.Session(body.id, body.capabilities);
|
|
||||||
const client = Promise.resolve(seleniumURL)
|
|
||||||
.then(url => new seleniumHttp.HttpClient(url));
|
|
||||||
const executor = new seleniumHttp.Executor(client);
|
|
||||||
driver = new selenium.WebDriver(session, executor);
|
|
||||||
setupDriver();
|
|
||||||
});
|
});
|
||||||
|
const session = new selenium.Session(body.id, body.capabilities);
|
||||||
|
const client = Promise.resolve(new seleniumHttp.HttpClient(seleniumURL));
|
||||||
|
const executor = new seleniumHttp.Executor(client);
|
||||||
|
return new selenium.WebDriver(session, executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupDriver() {
|
async function setupDriver() {
|
||||||
driver.manage().timeouts().setScriptTimeout(3000).then(function() {
|
await driver.manage().setTimeouts({script: 5000});
|
||||||
let html = '<!DOCTYPE html>' +
|
|
||||||
'<html><head><style type="text/css">html,body{' +
|
let html = '<!DOCTYPE html>' +
|
||||||
'width:100%;height:100%;margin:0;padding:0;overflow:hidden;' +
|
'<html><head><style type="text/css">html,body{' +
|
||||||
'}</style></head><body><p>Test</p></body></html>';
|
'width:100%;height:100%;margin:0;padding:0;overflow:hidden;' +
|
||||||
html = "data:text/html," + encodeURIComponent(html);
|
'}</style></head><body><p>Test</p></body></html>';
|
||||||
return driver.get(html);
|
html = "data:text/html," + encodeURIComponent(html);
|
||||||
}).then(function() {
|
await driver.get(html);
|
||||||
setSize(targetW, targetH);
|
|
||||||
});
|
await setSize(targetW, targetH);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@@ -345,22 +341,20 @@ function setupDriver() {
|
|||||||
|
|
||||||
const targetW = 1024;
|
const targetW = 1024;
|
||||||
const targetH = 768;
|
const targetH = 768;
|
||||||
function setSize(reqW, reqH) {
|
let attempts = 0;
|
||||||
return driver.manage().window().setSize(reqW, reqH).then(function() {
|
async function setSize(width, height) {
|
||||||
return driver.takeScreenshot();
|
await driver.manage().window().setRect({width, height});
|
||||||
}).then(function(img) {
|
let img = await driver.takeScreenshot();
|
||||||
img = imageDimensions(img);
|
img = imageDimensions(img);
|
||||||
const actualW = img.width;
|
const actualW = img.width;
|
||||||
const actualH = img.height;
|
const actualH = img.height;
|
||||||
if (actualW === targetW && actualH === targetH) {
|
if (actualW === targetW && actualH === targetH) {
|
||||||
findHostIP();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (++attempts > opts.attempts) {
|
||||||
if (++attempts > opts.attempts) {
|
throw new Error("Failed to set window size correctly.");
|
||||||
throw new Error("Failed to set window size correctly.");
|
}
|
||||||
}
|
return setSize(targetW + width - actualW, targetH + height - actualH);
|
||||||
return setSize(targetW + reqW - actualW, targetH + reqH - actualH);
|
|
||||||
}, check);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageDimensions(img) {
|
function imageDimensions(img) {
|
||||||
@@ -375,7 +369,7 @@ function imageDimensions(img) {
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Work out how to connect to host KaTeX server
|
// Work out how to connect to host KaTeX server
|
||||||
|
|
||||||
function findHostIP() {
|
async function findHostIP() {
|
||||||
if (!katexIP) {
|
if (!katexIP) {
|
||||||
katexIP = "localhost";
|
katexIP = "localhost";
|
||||||
}
|
}
|
||||||
@@ -384,21 +378,22 @@ function findHostIP() {
|
|||||||
katexURL = "http://" + katexIP + ":" + katexPort + "/";
|
katexURL = "http://" + katexIP + ":" + katexPort + "/";
|
||||||
console.log("KaTeX URL is " + katexURL);
|
console.log("KaTeX URL is " + katexURL);
|
||||||
}
|
}
|
||||||
process.nextTick(takeScreenshots);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we need to find an IP the container can connect to.
|
// Now we need to find an IP the container can connect to.
|
||||||
// First, install a server component to get notified of successful connects
|
// First, install a server component to get notified of successful connects
|
||||||
devServer.app.get("/ss-connect.js", function(req, res, next) {
|
const connect = new Promise((resolve) => {
|
||||||
if (!katexURL) {
|
devServer.app.get("/ss-connect.js", function(req, res, next) {
|
||||||
katexIP = req.query.ip;
|
if (!katexURL) {
|
||||||
katexURL = "http://" + katexIP + ":" + katexPort + "/";
|
katexIP = req.query.ip;
|
||||||
console.log("KaTeX URL is " + katexURL);
|
katexURL = "http://" + katexIP + ":" + katexPort + "/";
|
||||||
process.nextTick(takeScreenshots);
|
console.log("KaTeX URL is " + katexURL);
|
||||||
}
|
}
|
||||||
res.setHeader("Content-Type", "text/javascript");
|
res.setHeader("Content-Type", "text/javascript");
|
||||||
res.send("//OK");
|
res.send("//OK");
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Next, enumerate all network addresses
|
// Next, enumerate all network addresses
|
||||||
@@ -427,22 +422,41 @@ function findHostIP() {
|
|||||||
}).join("\n");
|
}).join("\n");
|
||||||
html += "\n</body></html>";
|
html += "\n</body></html>";
|
||||||
html = "data:text/html," + encodeURIComponent(html);
|
html = "data:text/html," + encodeURIComponent(html);
|
||||||
driver.get(html);
|
await driver.get(html);
|
||||||
|
await connect;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Take the screenshots
|
// Take the screenshots
|
||||||
|
|
||||||
let countdown = listOfCases.length;
|
|
||||||
|
|
||||||
let exitStatus = 0;
|
let exitStatus = 0;
|
||||||
const listOfFailed = [];
|
const listOfFailed = [];
|
||||||
|
|
||||||
function takeScreenshots() {
|
async function takeScreenshots() {
|
||||||
listOfCases.forEach(takeScreenshot);
|
for (const key of listOfCases) {
|
||||||
|
await takeScreenshot(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listOfFailed.length) {
|
||||||
|
console.error("Failed: " + listOfFailed.join(" "));
|
||||||
|
}
|
||||||
|
if (opts.diff) {
|
||||||
|
console.log("Diffs have been generated in: " + diffDir);
|
||||||
|
}
|
||||||
|
if (opts.new) {
|
||||||
|
console.log("New screenshots have been generated in: " + newDir);
|
||||||
|
}
|
||||||
|
if (opts.coverage) {
|
||||||
|
await collectCoverage();
|
||||||
|
const context = istanbulLibReport.createContext({coverageMap});
|
||||||
|
['json', 'text', 'lcov'].forEach(fmt => {
|
||||||
|
const report = istanbulReports.create(fmt);
|
||||||
|
report.execute(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function takeScreenshot(key) {
|
async function takeScreenshot(key) {
|
||||||
const itm = data[key];
|
const itm = data[key];
|
||||||
if (!itm) {
|
if (!itm) {
|
||||||
console.error("Test case " + key + " not known!");
|
console.error("Test case " + key + " not known!");
|
||||||
@@ -450,70 +464,43 @@ function takeScreenshot(key) {
|
|||||||
if (exitStatus === 0) {
|
if (exitStatus === 0) {
|
||||||
exitStatus = 1;
|
exitStatus = 1;
|
||||||
}
|
}
|
||||||
oneDone();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = path.join(dstDir, key + "-" + opts.browser + ".png");
|
let file = path.join(dstDir, key + "-" + opts.browser + ".png");
|
||||||
let retry = 0;
|
let retry = 0;
|
||||||
let loadExpected = null;
|
let expected = null;
|
||||||
if (opts.verify) {
|
if (opts.verify && await fs.pathExists(file)) {
|
||||||
loadExpected = fs.readFile(file);
|
expected = await fs.readFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = katexURL + "test/screenshotter/test.html?" + itm.query;
|
const url = katexURL + "test/screenshotter/test.html?" + itm.query;
|
||||||
driver.call(loadMath);
|
let buf;
|
||||||
|
|
||||||
function loadMath() {
|
while (++retry <= opts.attempts) {
|
||||||
if (!opts.reload && driverReady) {
|
if (!opts.reload && driverReady) {
|
||||||
driver.executeScript(
|
await driver.executeScript(
|
||||||
"handle_search_string(" +
|
"handle_search_string(" +
|
||||||
JSON.stringify("?" + itm.query) + ");")
|
JSON.stringify("?" + itm.query) + ");");
|
||||||
.then(waitThenScreenshot);
|
|
||||||
} else if (opts.coverage) {
|
|
||||||
// collect coverage before reloading
|
|
||||||
collectCoverage().then(function() {
|
|
||||||
return driver.get(url).then(loadFonts);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
driver.get(url).then(loadFonts);
|
if (opts.coverage) {
|
||||||
|
// collect coverage before reloading
|
||||||
|
await collectCoverage();
|
||||||
|
}
|
||||||
|
await driver.get(url);
|
||||||
|
await driver.executeAsyncScript(
|
||||||
|
"var callback = arguments[arguments.length - 1]; " +
|
||||||
|
"load_fonts_and_images(callback);");
|
||||||
|
driverReady = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function collectCoverage() {
|
|
||||||
return driver.executeScript('return window.__coverage__;')
|
|
||||||
.then(function(result) {
|
|
||||||
if (result) {
|
|
||||||
coverageMap.merge(result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFonts() {
|
|
||||||
driver.executeAsyncScript(
|
|
||||||
"var callback = arguments[arguments.length - 1]; " +
|
|
||||||
"load_fonts_and_images(callback);")
|
|
||||||
.then(waitThenScreenshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitThenScreenshot() {
|
|
||||||
driverReady = true;
|
|
||||||
if (opts.wait) {
|
if (opts.wait) {
|
||||||
browserSideWait(1000 * opts.wait);
|
await browserSideWait(1000 * opts.wait);
|
||||||
}
|
}
|
||||||
const promise = driver.takeScreenshot().then(haveScreenshot);
|
let img = await driver.takeScreenshot();
|
||||||
if (retry === 0) {
|
|
||||||
// The `oneDone` promise remains outstanding if we retry, so
|
|
||||||
// don't re-add it
|
|
||||||
promise.then(oneDone, check);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function haveScreenshot(img) {
|
|
||||||
img = imageDimensions(img);
|
img = imageDimensions(img);
|
||||||
if (img.width !== targetW || img.height !== targetH) {
|
if (img.width !== targetW || img.height !== targetH) {
|
||||||
throw new Error("Expected " + targetW + " x " + targetH +
|
console.error("Expected " + targetW + " x " + targetH +
|
||||||
", got " + img.width + "x" + img.height);
|
", got " + img.width + "x" + img.height);
|
||||||
|
await setSize(targetW, targetH);
|
||||||
}
|
}
|
||||||
if (key === "Lap" && opts.browser === "firefox" &&
|
if (key === "Lap" && opts.browser === "firefox" &&
|
||||||
img.buf[0x32] === 0xf8) {
|
img.buf[0x32] === 0xf8) {
|
||||||
@@ -525,141 +512,77 @@ function takeScreenshot(key) {
|
|||||||
*/
|
*/
|
||||||
key += "_alt";
|
key += "_alt";
|
||||||
file = path.join(dstDir, key + "-" + opts.browser + ".png");
|
file = path.join(dstDir, key + "-" + opts.browser + ".png");
|
||||||
if (loadExpected) {
|
if (expected) {
|
||||||
loadExpected = fs.readFile(file);
|
expected = await fs.readFile(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const opt = new jspngopt.Optimizer({
|
const opt = new jspngopt.Optimizer({
|
||||||
pako: pako,
|
pako: pako,
|
||||||
});
|
});
|
||||||
const buf = opt.bufferSync(img.buf);
|
buf = opt.bufferSync(img.buf);
|
||||||
if (loadExpected) {
|
if (expected) {
|
||||||
return loadExpected.then(function(expected) {
|
if (buf.equals(expected)) {
|
||||||
if (!buf.equals(expected)) {
|
console.log("* ok " + key);
|
||||||
if (++retry >= opts.attempts) {
|
return;
|
||||||
console.error("FAIL! " + key);
|
}
|
||||||
listOfFailed.push(key);
|
console.log("error " + key);
|
||||||
exitStatus = 3;
|
await browserSideWait(300 * retry);
|
||||||
if (opts.diff || opts.new) {
|
if (retry > 1) {
|
||||||
return saveFailedScreenshot(key, buf);
|
driverReady = false; // reload fully
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log("error " + key);
|
|
||||||
browserSideWait(300 * retry);
|
|
||||||
if (retry > 1) {
|
|
||||||
driverReady = false; // reload fully
|
|
||||||
}
|
|
||||||
return driver.call(loadMath);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("* ok " + key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return fs.writeFile(file, buf).then(function() {
|
await fs.writeFile(file, buf);
|
||||||
console.log(key);
|
console.log(key);
|
||||||
});
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFailedScreenshot(key, buf) {
|
console.error("FAIL! " + key);
|
||||||
|
listOfFailed.push(key);
|
||||||
|
exitStatus = 3;
|
||||||
|
if (opts.diff || opts.new) {
|
||||||
const filenamePrefix = key + "-" + opts.browser;
|
const filenamePrefix = key + "-" + opts.browser;
|
||||||
const outputDir = opts.new ? newDir : diffDir;
|
const outputDir = opts.new ? newDir : diffDir;
|
||||||
const baseFile = path.join(dstDir, filenamePrefix + ".png");
|
const baseFile = path.join(dstDir, filenamePrefix + ".png");
|
||||||
const diffFile = path.join(diffDir, filenamePrefix + "-diff.png");
|
const diffFile = path.join(diffDir, filenamePrefix + "-diff.png");
|
||||||
const bufFile = path.join(outputDir, filenamePrefix + ".png");
|
const bufFile = path.join(outputDir, filenamePrefix + ".png");
|
||||||
|
|
||||||
let promise = fs.ensureDir(outputDir)
|
await fs.ensureDir(outputDir);
|
||||||
.then(function() {
|
await fs.writeFile(bufFile, buf);
|
||||||
return fs.writeFile(bufFile, buf);
|
|
||||||
});
|
|
||||||
if (opts.diff) {
|
if (opts.diff) {
|
||||||
promise = promise.then(fs.ensureDir(diffDir))
|
await fs.ensureDir(diffDir);
|
||||||
.then(function() {
|
await execFile("convert", [
|
||||||
return execFile("convert", [
|
"-fill", "white",
|
||||||
"-fill", "white",
|
// First image: saved screenshot in red
|
||||||
// First image: saved screenshot in red
|
"(", baseFile, "-colorize", "100,0,0", ")",
|
||||||
"(", baseFile, "-colorize", "100,0,0", ")",
|
// Second image: new screenshot in green
|
||||||
// Second image: new screenshot in green
|
"(", bufFile, "-colorize", "0,80,0", ")",
|
||||||
"(", bufFile, "-colorize", "0,80,0", ")",
|
// Composite them
|
||||||
// Composite them
|
"-compose", "darken", "-composite",
|
||||||
"-compose", "darken", "-composite",
|
"-trim", // remove everything with the same color as
|
||||||
"-trim", // remove everything with the same color as
|
// the corners
|
||||||
// the corners
|
diffFile, // output file name
|
||||||
diffFile, // output file name
|
]);
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (!opts.new) {
|
if (!opts.new) {
|
||||||
promise = promise.then(function() {
|
await fs.unlink(bufFile);
|
||||||
return fs.unlink(bufFile);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function oneDone() {
|
|
||||||
if (--countdown === 0) {
|
|
||||||
if (listOfFailed.length) {
|
|
||||||
console.error("Failed: " + listOfFailed.join(" "));
|
|
||||||
}
|
|
||||||
if (opts.diff) {
|
|
||||||
console.log("Diffs have been generated in: " + diffDir);
|
|
||||||
}
|
|
||||||
if (opts.new) {
|
|
||||||
console.log("New screenshots have been generated in: " + newDir);
|
|
||||||
}
|
|
||||||
if (opts.coverage) {
|
|
||||||
collectCoverage().then(function() {
|
|
||||||
const context = istanbulLibReport.createContext({coverageMap});
|
|
||||||
['json', 'text', 'lcov'].forEach(fmt => {
|
|
||||||
const report = istanbulReports.create(fmt);
|
|
||||||
report.execute(context);
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function done() {
|
|
||||||
// devServer.close(cb) will take too long.
|
|
||||||
driver.quit().then(() => {
|
|
||||||
if (bsLocal) {
|
|
||||||
bsLocal.stop(() => {
|
|
||||||
process.exit(exitStatus);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
process.exit(exitStatus);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait using a timeout call in the browser, to ensure that the wait
|
// Wait using a timeout call in the browser, to ensure that the wait
|
||||||
// time doesn't start before the page has reportedly been loaded.
|
// time doesn't start before the page has reportedly been loaded.
|
||||||
function browserSideWait(milliseconds) {
|
async function browserSideWait(milliseconds) {
|
||||||
// The last argument (arguments[1] here) is the callback to selenium
|
// The last argument (arguments[1] here) is the callback to selenium
|
||||||
return driver.executeAsyncScript(
|
await driver.executeAsyncScript(
|
||||||
"window.setTimeout(arguments[1], arguments[0]);",
|
"window.setTimeout(arguments[1], arguments[0]);",
|
||||||
milliseconds);
|
milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a given command, and return a promise to its output.
|
async function collectCoverage() {
|
||||||
function execFile(cmd, args, opts) {
|
const result = await driver.executeScript('return window.__coverage__;');
|
||||||
return new Promise(function(resolve, reject) {
|
if (result) {
|
||||||
childProcess.execFile(cmd, args, opts, function(err, stdout, stderr) {
|
coverageMap.merge(result);
|
||||||
if (err) {
|
}
|
||||||
console.error("Error executing " + cmd + " " + args.join(" "));
|
|
||||||
console.error(stdout + stderr);
|
|
||||||
err.stdout = stdout;
|
|
||||||
err.stderr = stderr;
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(stdout);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@@ -65,6 +65,7 @@
|
|||||||
"less-loader": "^10.0.0",
|
"less-loader": "^10.0.0",
|
||||||
"mini-css-extract-plugin": "^2.0.0",
|
"mini-css-extract-plugin": "^2.0.0",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
|
"p-retry": "^4.6.1",
|
||||||
"pako": "^2.0.0",
|
"pako": "^2.0.0",
|
||||||
"postcss": "^8.0.0",
|
"postcss": "^8.0.0",
|
||||||
"postcss-loader": "^6.0.0",
|
"postcss-loader": "^6.0.0",
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
"query-string": "^7.0.0",
|
"query-string": "^7.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.21.0",
|
"rollup": "^2.21.0",
|
||||||
"selenium-webdriver": "^3.6.0",
|
"selenium-webdriver": "^4.0.0-beta.4",
|
||||||
"semantic-release": "^17.4.1",
|
"semantic-release": "^17.4.1",
|
||||||
"sri-toolbox": "^0.2.0",
|
"sri-toolbox": "^0.2.0",
|
||||||
"style-loader": "^3.0.0",
|
"style-loader": "^3.0.0",
|
||||||
|
90
yarn.lock
90
yarn.lock
@@ -8391,15 +8391,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"jszip@npm:^3.1.3":
|
"jszip@npm:^3.6.0":
|
||||||
version: 3.5.0
|
version: 3.7.1
|
||||||
resolution: "jszip@npm:3.5.0"
|
resolution: "jszip@npm:3.7.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
lie: ~3.3.0
|
lie: ~3.3.0
|
||||||
pako: ~1.0.2
|
pako: ~1.0.2
|
||||||
readable-stream: ~2.3.6
|
readable-stream: ~2.3.6
|
||||||
set-immediate-shim: ~1.0.1
|
set-immediate-shim: ~1.0.1
|
||||||
checksum: e4c555273063ac1284f387e440c9b0ccef99af3dd87f3af4a7fc1b4396f33cc45fa10afdb059105dd15a61763288c99e53a5e078dd0af9183a83e694e005d054
|
checksum: 67d737a82b294cc102e7451e32d5acbbab29860399be460cae598084327e6f2ea0c9bca2d3dad701da6a75ddf77f34c6a1dd7db0c3d5c0fec5998b7e56d6d59d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8455,6 +8455,7 @@ __metadata:
|
|||||||
less-loader: ^10.0.0
|
less-loader: ^10.0.0
|
||||||
mini-css-extract-plugin: ^2.0.0
|
mini-css-extract-plugin: ^2.0.0
|
||||||
mkdirp: ^1.0.4
|
mkdirp: ^1.0.4
|
||||||
|
p-retry: ^4.6.1
|
||||||
pako: ^2.0.0
|
pako: ^2.0.0
|
||||||
postcss: ^8.0.0
|
postcss: ^8.0.0
|
||||||
postcss-loader: ^6.0.0
|
postcss-loader: ^6.0.0
|
||||||
@@ -8463,7 +8464,7 @@ __metadata:
|
|||||||
query-string: ^7.0.0
|
query-string: ^7.0.0
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
rollup: ^2.21.0
|
rollup: ^2.21.0
|
||||||
selenium-webdriver: ^3.6.0
|
selenium-webdriver: ^4.0.0-beta.4
|
||||||
semantic-release: ^17.4.1
|
semantic-release: ^17.4.1
|
||||||
sri-toolbox: ^0.2.0
|
sri-toolbox: ^0.2.0
|
||||||
style-loader: ^3.0.0
|
style-loader: ^3.0.0
|
||||||
@@ -10483,7 +10484,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"os-tmpdir@npm:^1.0.0, os-tmpdir@npm:~1.0.1":
|
"os-tmpdir@npm:^1.0.0":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "os-tmpdir@npm:1.0.2"
|
resolution: "os-tmpdir@npm:1.0.2"
|
||||||
checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d
|
checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d
|
||||||
@@ -10639,7 +10640,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"p-retry@npm:^4.0.0, p-retry@npm:^4.5.0":
|
"p-retry@npm:^4.0.0, p-retry@npm:^4.5.0, p-retry@npm:^4.6.1":
|
||||||
version: 4.6.1
|
version: 4.6.1
|
||||||
resolution: "p-retry@npm:4.6.1"
|
resolution: "p-retry@npm:4.6.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -12785,7 +12786,7 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"sax@npm:>=0.6.0, sax@npm:^1.2.4":
|
"sax@npm:^1.2.4":
|
||||||
version: 1.2.4
|
version: 1.2.4
|
||||||
resolution: "sax@npm:1.2.4"
|
resolution: "sax@npm:1.2.4"
|
||||||
checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe
|
checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe
|
||||||
@@ -12830,15 +12831,15 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"selenium-webdriver@npm:^3.6.0":
|
"selenium-webdriver@npm:^4.0.0-beta.4":
|
||||||
version: 3.6.0
|
version: 4.0.0-beta.4
|
||||||
resolution: "selenium-webdriver@npm:3.6.0"
|
resolution: "selenium-webdriver@npm:4.0.0-beta.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
jszip: ^3.1.3
|
jszip: ^3.6.0
|
||||||
rimraf: ^2.5.4
|
rimraf: ^3.0.2
|
||||||
tmp: 0.0.30
|
tmp: ^0.2.1
|
||||||
xml2js: ^0.4.17
|
ws: ">=7.4.6"
|
||||||
checksum: 5bc1045d0205c5aed1f3e3cf8047d3bb677e370e96ae4a8acd172846c07aeb40c031bee5017a7c432bec36e46c5bbce82fe3b40086b7daa4cb31dcaf69daad55
|
checksum: c57ec5b41f8ff4a81280d548f95e899a01f6cf6b0b46fb4f7a92440a710836a2994368561c0be43abd301f76c293e29dca68d959a9ae6c15fb03f5a040dee240
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -14128,12 +14129,12 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tmp@npm:0.0.30":
|
"tmp@npm:^0.2.1":
|
||||||
version: 0.0.30
|
version: 0.2.1
|
||||||
resolution: "tmp@npm:0.0.30"
|
resolution: "tmp@npm:0.2.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
os-tmpdir: ~1.0.1
|
rimraf: ^3.0.0
|
||||||
checksum: d3e97e8e73b2d2dfff9916072004088b4737c67d11ea255d0ccc8584f252b253b60ecf04122b21848ec46ad5a92e31febc6d6a3068f6c8a20c9b0e23a802e78d
|
checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -15158,6 +15159,21 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ws@npm:>=7.4.6, ws@npm:^8.1.0":
|
||||||
|
version: 8.2.0
|
||||||
|
resolution: "ws@npm:8.2.0"
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: ^5.0.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
checksum: 7cd544312a48dafcb8158c9b4e5f20986cce980d516e0ef0602665911b0e95c5e0dea2846a4bb3153a1e2c839aa3d92fb7e69dd864fe432e881eee9d4e8cf70b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ws@npm:^7.3.1, ws@npm:^7.4.6":
|
"ws@npm:^7.3.1, ws@npm:^7.4.6":
|
||||||
version: 7.5.3
|
version: 7.5.3
|
||||||
resolution: "ws@npm:7.5.3"
|
resolution: "ws@npm:7.5.3"
|
||||||
@@ -15173,21 +15189,6 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"ws@npm:^8.1.0":
|
|
||||||
version: 8.2.0
|
|
||||||
resolution: "ws@npm:8.2.0"
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: ^5.0.2
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
checksum: 7cd544312a48dafcb8158c9b4e5f20986cce980d516e0ef0602665911b0e95c5e0dea2846a4bb3153a1e2c839aa3d92fb7e69dd864fe432e881eee9d4e8cf70b
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"xdg-basedir@npm:^3.0.0":
|
"xdg-basedir@npm:^3.0.0":
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
resolution: "xdg-basedir@npm:3.0.0"
|
resolution: "xdg-basedir@npm:3.0.0"
|
||||||
@@ -15202,23 +15203,6 @@ resolve@^2.0.0-next.3:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"xml2js@npm:^0.4.17":
|
|
||||||
version: 0.4.23
|
|
||||||
resolution: "xml2js@npm:0.4.23"
|
|
||||||
dependencies:
|
|
||||||
sax: ">=0.6.0"
|
|
||||||
xmlbuilder: ~11.0.0
|
|
||||||
checksum: ca0cf2dfbf6deeaae878a891c8fbc0db6fd04398087084edf143cdc83d0509ad0fe199b890f62f39c4415cf60268a27a6aed0d343f0658f8779bd7add690fa98
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"xmlbuilder@npm:~11.0.0":
|
|
||||||
version: 11.0.1
|
|
||||||
resolution: "xmlbuilder@npm:11.0.1"
|
|
||||||
checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"xmlchars@npm:^2.2.0":
|
"xmlchars@npm:^2.2.0":
|
||||||
version: 2.2.0
|
version: 2.2.0
|
||||||
resolution: "xmlchars@npm:2.2.0"
|
resolution: "xmlchars@npm:2.2.0"
|
||||||
|
Reference in New Issue
Block a user