2018-05-10 21:55:05 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const fs = require("fs");
|
2017-10-23 17:42:01 +00:00
|
|
|
const http = require("http");
|
2018-05-10 21:55:05 +00:00
|
|
|
const path = require("path");
|
|
|
|
|
|
|
|
const browserify = require("browserify");
|
|
|
|
const finalhandler = require("finalhandler");
|
2017-10-23 17:42:01 +00:00
|
|
|
const serveStatic = require("serve-static");
|
2018-05-10 21:55:05 +00:00
|
|
|
const {Builder, By, until} = require("selenium-webdriver");
|
|
|
|
|
|
|
|
const test = require("tape-async");
|
|
|
|
const tmp = require("tmp");
|
|
|
|
const {cp} = require("shelljs");
|
|
|
|
|
|
|
|
const TEST_TIMEOUT = 5000;
|
|
|
|
|
|
|
|
const launchBrowser = async (launchOptions) => {
|
2019-01-28 17:49:25 +00:00
|
|
|
const browser = launchOptions.browser;
|
2018-05-10 21:55:05 +00:00
|
|
|
const extensionPath = launchOptions.extensionPath;
|
2019-02-01 07:38:35 +00:00
|
|
|
const openDevTools = launchOptions.openDevTools;
|
2018-05-10 21:55:05 +00:00
|
|
|
|
|
|
|
let driver;
|
|
|
|
|
|
|
|
if (browser === "chrome") {
|
|
|
|
const chrome = require("selenium-webdriver/chrome");
|
|
|
|
const chromedriver = require("chromedriver");
|
|
|
|
|
|
|
|
if (process.env.HEADLESS === "1") {
|
|
|
|
console.warn("WARN: Chrome doesn't currently support extensions in headless mode. " +
|
|
|
|
"Falling back to non-headless mode");
|
|
|
|
}
|
2017-10-23 17:42:01 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
const options = new chrome.Options();
|
|
|
|
options.addArguments([
|
|
|
|
`--load-extension=${extensionPath}`,
|
2021-01-21 12:20:11 +00:00
|
|
|
// See issue #85 for a rationale.
|
2018-05-10 21:55:05 +00:00
|
|
|
"--no-sandbox",
|
|
|
|
]);
|
|
|
|
|
2019-02-01 07:38:35 +00:00
|
|
|
if (openDevTools) {
|
|
|
|
options.addArguments(["-auto-open-devtools-for-tabs"]);
|
|
|
|
}
|
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
if (process.env.TEST_NATIVE_CRX_BINDINGS === "1") {
|
|
|
|
console.warn("NOTE: Running tests on a Chrome instance with NativeCrxBindings enabled.");
|
|
|
|
options.addArguments([
|
|
|
|
"--enable-features=NativeCrxBindings",
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2021-01-21 12:20:11 +00:00
|
|
|
const service = new chrome.ServiceBuilder(chromedriver.path);
|
|
|
|
|
|
|
|
if (process.env.CHROMEDRIVER_VERBOSE_LOGFILE) {
|
|
|
|
// Prevent intermittent failures due to limited resources while running
|
|
|
|
// in small VMs / docker containers (See #256 for a rationale).
|
|
|
|
const logsPath = process.env.CHROMEDRIVER_VERBOSE_LOGFILE;
|
|
|
|
console.warn(`NOTE: Verbose chromedriver logs: ${logsPath}`);
|
|
|
|
service.loggingTo(logsPath);
|
|
|
|
service.enableVerboseLogging();
|
|
|
|
}
|
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
driver = await new Builder()
|
|
|
|
.forBrowser("chrome")
|
|
|
|
.setChromeOptions(options)
|
2021-01-21 12:20:11 +00:00
|
|
|
.setChromeService(service)
|
2018-05-10 21:55:05 +00:00
|
|
|
.build();
|
|
|
|
} else if (browser === "firefox") {
|
|
|
|
const firefox = require("selenium-webdriver/firefox");
|
|
|
|
const geckodriver = require("geckodriver");
|
|
|
|
const {Command} = require("selenium-webdriver/lib/command");
|
|
|
|
|
|
|
|
const options = new firefox.Options();
|
|
|
|
|
|
|
|
if (process.env.HEADLESS === "1") {
|
|
|
|
options.headless();
|
|
|
|
}
|
|
|
|
|
2019-02-01 07:38:35 +00:00
|
|
|
if (openDevTools) {
|
|
|
|
options.addArguments("-devtools");
|
|
|
|
}
|
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
driver = await new Builder()
|
|
|
|
.forBrowser("firefox")
|
|
|
|
.setFirefoxOptions(options)
|
|
|
|
.setFirefoxService(new firefox.ServiceBuilder(geckodriver.path))
|
|
|
|
.build();
|
|
|
|
|
|
|
|
const command = new Command("install addon")
|
|
|
|
.setParameter("path", extensionPath)
|
|
|
|
.setParameter("temporary", true);
|
|
|
|
|
|
|
|
await driver.execute(command);
|
|
|
|
} else {
|
|
|
|
const errorHelpMsg = (
|
|
|
|
"Set a supported browser (firefox or chrome) " +
|
|
|
|
"using the TEST_BROWSER_TYPE environment var.");
|
|
|
|
throw new Error(`Target browser not supported yet: ${browser}. ${errorHelpMsg}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return driver;
|
|
|
|
};
|
2017-10-23 17:42:01 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
const createHTTPServer = async (path) => {
|
|
|
|
const serve = serveStatic(path);
|
|
|
|
const server = http.createServer((req, res) => {
|
2017-10-23 17:42:01 +00:00
|
|
|
serve(req, res, finalhandler(req, res));
|
|
|
|
});
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
server.listen((err) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
} else {
|
|
|
|
resolve(server);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
2018-01-09 16:49:56 +00:00
|
|
|
|
2019-01-28 17:49:25 +00:00
|
|
|
async function runExtensionTest(t, server, driver, extensionDirName, browser) {
|
2018-05-10 21:55:05 +00:00
|
|
|
try {
|
|
|
|
const url = `http://localhost:${server.address().port}`;
|
|
|
|
const userAgent = await driver.executeScript(() => window.navigator.userAgent);
|
|
|
|
|
2019-01-28 17:49:25 +00:00
|
|
|
t.pass(`Connected to ${browser} browser: ${userAgent}"`);
|
2018-01-09 16:49:56 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
await driver.get(url);
|
2018-01-09 16:49:56 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
// Merge tap results from the connected browser.
|
|
|
|
const el = await driver.wait(until.elementLocated(By.id("test-results")), 10000);
|
|
|
|
const testResults = await el.getAttribute("textContent");
|
|
|
|
console.log(testResults);
|
|
|
|
} catch (err) {
|
|
|
|
t.fail(err);
|
2018-01-09 16:49:56 +00:00
|
|
|
}
|
2018-05-10 21:55:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const awaitStreamEnd = (stream) => {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
stream.on("end", resolve);
|
|
|
|
stream.on("error", reject);
|
|
|
|
});
|
|
|
|
};
|
2018-01-09 16:49:56 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
const bundleTapeStandalone = async (destDir) => {
|
|
|
|
const bundleFileName = path.join(destDir, "tape.js");
|
|
|
|
const b = browserify();
|
|
|
|
b.add(path.join(__dirname, "..", "fixtures", "tape-standalone.js"));
|
2018-01-09 16:49:56 +00:00
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
// Inject setImmediate (used internally by tape).
|
|
|
|
b.transform("global-replaceify", {
|
|
|
|
global: true,
|
|
|
|
replacements: {
|
|
|
|
setImmediate: "require('timers').setImmediate",
|
|
|
|
},
|
2018-01-09 16:49:56 +00:00
|
|
|
});
|
2018-05-10 21:55:05 +00:00
|
|
|
|
|
|
|
const stream = b.bundle();
|
|
|
|
const onceStreamEnd = awaitStreamEnd(stream);
|
2019-01-28 17:49:25 +00:00
|
|
|
const destFileStream = fs.createWriteStream(bundleFileName);
|
|
|
|
const onceWritten = new Promise(resolve => {
|
|
|
|
destFileStream.on("close", resolve);
|
|
|
|
});
|
|
|
|
stream.pipe(destFileStream);
|
2018-05-10 21:55:05 +00:00
|
|
|
|
2019-01-28 17:49:25 +00:00
|
|
|
await Promise.all([onceStreamEnd, onceWritten]);
|
2018-05-10 21:55:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
test.onFailure(() => {
|
|
|
|
process.exit(1);
|
|
|
|
});
|
|
|
|
|
2019-01-28 17:49:25 +00:00
|
|
|
/**
|
|
|
|
* @param {object} parameters
|
|
|
|
* @param {string} parameters.description
|
|
|
|
* @param {string[]} parameters.extensions
|
|
|
|
* @param {boolean|string|string[]} [parameters.skip]
|
2019-03-20 11:27:09 +00:00
|
|
|
* @param {boolean} [parameters.openDevTools]
|
2019-01-28 17:49:25 +00:00
|
|
|
*/
|
2019-02-01 07:38:35 +00:00
|
|
|
const defineExtensionTests = ({description, extensions, skip, openDevTools}) => {
|
2018-05-10 21:55:05 +00:00
|
|
|
for (const extensionDirName of extensions) {
|
|
|
|
test(`${description} (test extension: ${extensionDirName})`, async (tt) => {
|
|
|
|
let timeout;
|
|
|
|
let driver;
|
|
|
|
let server;
|
|
|
|
let tempDir;
|
|
|
|
|
2019-01-28 17:49:25 +00:00
|
|
|
const browser = process.env.TEST_BROWSER_TYPE;
|
|
|
|
|
|
|
|
if (skip) {
|
|
|
|
if (skip === true) {
|
|
|
|
tt.skip("Test extension skipped");
|
|
|
|
return;
|
|
|
|
} else if (skip instanceof Array ? skip.includes(browser) : skip === browser) {
|
|
|
|
tt.skip("Test extension skipped on: " + browser);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.log(`Skip condition ignored: '${skip}' != '${browser}'`);
|
|
|
|
}
|
|
|
|
|
2018-05-10 21:55:05 +00:00
|
|
|
try {
|
|
|
|
const srcExtensionPath = path.resolve(
|
|
|
|
path.join(__dirname, "..", "fixtures", extensionDirName));
|
|
|
|
const srcPolyfill = path.join(__dirname, "..", "..", "dist", "browser-polyfill.js");
|
|
|
|
|
2019-03-20 11:27:09 +00:00
|
|
|
tempDir = tmp.dirSync({unsafeCleanup: true});
|
|
|
|
const extensionPath = path.join(tempDir.name, extensionDirName);
|
2018-05-10 21:55:05 +00:00
|
|
|
|
|
|
|
cp("-rf", srcExtensionPath, extensionPath);
|
|
|
|
cp("-f", srcPolyfill, extensionPath);
|
|
|
|
cp("-f", `${srcPolyfill}.map`, extensionPath);
|
|
|
|
await bundleTapeStandalone(extensionPath);
|
|
|
|
|
|
|
|
server = await createHTTPServer(path.join(__dirname, "..", "fixtures"));
|
2019-02-01 07:38:35 +00:00
|
|
|
driver = await launchBrowser({extensionPath, browser, openDevTools});
|
2018-05-10 21:55:05 +00:00
|
|
|
await Promise.race([
|
2019-01-28 17:49:25 +00:00
|
|
|
runExtensionTest(tt, server, driver, extensionDirName, browser),
|
2018-05-10 21:55:05 +00:00
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
timeout = setTimeout(() => reject(new Error(`test timeout after ${TEST_TIMEOUT}`)), TEST_TIMEOUT);
|
|
|
|
}),
|
|
|
|
]);
|
|
|
|
} finally {
|
|
|
|
clearTimeout(timeout);
|
|
|
|
if (driver) {
|
|
|
|
await driver.quit();
|
|
|
|
driver = null;
|
|
|
|
}
|
|
|
|
if (server) {
|
|
|
|
server.close();
|
|
|
|
server = null;
|
|
|
|
}
|
|
|
|
if (tempDir) {
|
|
|
|
tempDir.removeCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
launchBrowser,
|
|
|
|
createHTTPServer,
|
|
|
|
defineExtensionTests,
|
2018-01-09 16:49:56 +00:00
|
|
|
};
|