docxtemplater
Version:
Generate docx, pptx, and xlsx from templates (Word, Powerpoint and Excel documents), from Node.js, the Browser and the command line
352 lines (331 loc) • 8.63 kB
JavaScript
/* eslint-disable no-process-env */
/* eslint-disable no-console */
const {
BROWSER = "CHROME",
browserName,
version,
platform,
TRAVIS_JOB_NUMBER,
TRAVIS_BUILD_NUMBER,
SAUCE_USERNAME,
SAUCE_ACCESS_KEY,
} = process.env;
function exit(message) {
console.log(message);
/* eslint-disable-next-line no-process-exit */
process.exit(1);
}
let fullBrowserName = null;
import chalk from "chalk";
import url from "url";
import finalhandler from "finalhandler";
import { remote } from "webdriverio";
import { expect } from "chai";
import serveStatic from "serve-static";
const port = 9000;
import http from "http";
import { dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
async function sleep(ms) {
return new Promise(function (resolve) {
setTimeout(() => resolve(), ms);
});
}
// These options are the modern, W3C ones
const sauceLabsW3COptions = {
browserName,
browserVersion: version,
platformName: platform,
"sauce:options": {
name: "docxtemplater mocha",
build: TRAVIS_BUILD_NUMBER,
tags: ["docxtemplater"],
tunnelIdentifier: TRAVIS_JOB_NUMBER,
public: true,
},
};
// These options are the legacy, JWP ones
const saucelabsJWPOptions = {
browserName,
version,
platform,
tags: ["docxtemplater"],
name: "docxtemplater mocha",
"tunnel-identifier": TRAVIS_JOB_NUMBER,
tunnelIdentifier: TRAVIS_JOB_NUMBER,
build: TRAVIS_BUILD_NUMBER,
captureHtml: true,
public: true,
};
// USE JWP instead of W3C for chrome < 75 only
const useJWP = browserName === "chrome" && +version < 75;
const browserCapability = {
CHROME: {
browserName: "chrome",
"goog:chromeOptions": {
args: [
"--headless",
// Use --disable-gpu to avoid an error from a missing Mesa
// library, as per
// https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
"--disable-gpu",
],
},
},
FIREFOX: {
browserName: "firefox",
"moz:firefoxOptions": {
args: ["-headless"],
},
},
SAUCELABS: useJWP ? saucelabsJWPOptions : sauceLabsW3COptions,
};
const desiredCapabilities = browserCapability[BROWSER];
fullBrowserName = BROWSER + " (local)";
if (!desiredCapabilities) {
exit("Unknown browser :" + BROWSER);
}
const second = 1000;
const commonOptions = {
automationProtocol: "webdriver",
logLevel: "warn",
connectionRetryTimeout: 10 * second,
};
let options;
if (BROWSER === "SAUCELABS") {
fullBrowserName = `${browserName} ${version} ${platform} (SAUCELABS)`;
options = {
...commonOptions,
tunnelIdentifier: TRAVIS_JOB_NUMBER,
"tunnel-identifier": TRAVIS_JOB_NUMBER,
build: TRAVIS_BUILD_NUMBER,
user: SAUCE_USERNAME,
key: SAUCE_ACCESS_KEY,
};
} else {
options = {
...commonOptions,
path: "/wd/hub/",
};
}
options.capabilities = desiredCapabilities;
console.log("Running test on " + fullBrowserName);
const serve = serveStatic(__dirname);
const server = http.createServer(function onRequest(req, res) {
serve(req, res, finalhandler(req, res));
});
let logPostRequest = false;
function logE(arg) {
if (logPostRequest) {
console.log(arg);
console.log("Stacktrace is : ");
console.log(new Error());
}
}
const timeoutConnection = 180;
const failuresRegex = /.*failures: ([0-9]+).*/;
const passesRegex = /.*passes: ([0-9]+).*/;
const startTime = +new Date();
server.listen(port, async function () {
let client;
try {
client = await remote(options);
} catch (e) {
exit(e);
}
async function mockConsole() {
await client.execute(() => {
if (window.myLogs) {
return;
}
window.myLogs = [];
console.log = function () {
const myLog = [];
for (let i = 0, len = arguments.length; i < len; i++) {
myLog.push(arguments[i]);
}
window.myLogs.push(myLog);
};
console.error = function () {
const myLog = [];
for (let i = 0, len = arguments.length; i < len; i++) {
myLog.push(arguments[i]);
}
window.myLogs.push(myLog);
};
console.warn = function () {
const myLog = [];
for (let i = 0, len = arguments.length; i < len; i++) {
myLog.push(arguments[i]);
}
window.myLogs.push(myLog);
};
});
}
async function getConsole() {
return await client.execute(() => {
if (!window.myLogs) {
return "[]";
}
return JSON.stringify(window.myLogs);
});
}
await mockConsole();
let logIndex = 0;
const int2 = setInterval(async function () {
if (logPostRequest) {
clearInterval(int2);
return;
}
await mockConsole();
const logOutput = await getConsole();
const logs = JSON.parse(logOutput);
logs.slice(logIndex).forEach(function (log) {
console.log("BROWSERLOG:", log.join(" , "));
});
logIndex = logs.length;
}, 100);
async function waitForText(selector, timeout) {
return await client.waitUntil(
async function getText() {
logE("client.selector" + selector);
const el = await client.$(selector);
logE("el.isExisting" + selector);
if (!(await el.isExisting())) {
return false;
}
logE("el.getText" + selector);
const text = await el.getText();
if (text.length > 0) {
return true;
}
},
{
timeout,
timeoutMsg: `Expected to find text in ${selector} but did not find it`,
}
);
}
async function waitForExist(selector, timeout) {
return await client.waitUntil(
async function exists() {
logE("waitForExist.selector" + selector);
const el = await client.$(selector);
logE("waitForExist.selector" + selector);
if (await el.isExisting()) {
return true;
}
},
{
timeout,
timeoutMsg: `Expected to find ${selector} but did not find it`,
}
);
}
async function test() {
let interval;
try {
if (+new Date() - startTime > timeoutConnection * second) {
exit(
`Aborting connection to webdriver after ${timeoutConnection} seconds`
);
}
const mochaUrl = url.parse(
`http://localhost:${port}/test/mocha.html`,
true
);
delete mochaUrl.search;
if (process.env.filter) {
mochaUrl.query.grep = process.env.filter;
mochaUrl.query.invert = "true";
}
mochaUrl.query.browser = fullBrowserName;
await client.url(url.format(mochaUrl));
await waitForExist("li.test", 120000);
let index = 0;
let running = false;
interval = setInterval(async function () {
if (interval === null || running) {
return;
}
running = true;
logE("get h1,h2");
const texts = await client.$$("li h1, li h2");
if (index === texts.length) {
return;
}
for (let i = index, len = texts.length; i < len; i++) {
if (interval === null) {
return;
}
logE("get text[i]" + i);
const text = await texts[i].getText();
console.log(
text
.replace(/^(.*)\n(.*)$/g, "$2 $1")
.replace(/^(.*[^0-9])([0-9]+ms)$/g, "$1 $2")
);
}
index = texts.length;
running = false;
}, 100);
await waitForText("#status", 120000);
await client.pause(5000);
await waitForExist("li.failures a", 5000);
const text = await (await client.$("#mocha-stats")).getText();
clearInterval(interval);
setTimeout(function () {
interval = null;
}, 1000);
const passes = parseInt(text.replace(passesRegex, "$1"), 10);
const failures = parseInt(text.replace(failuresRegex, "$1"), 10);
if (failures > 0) {
await sleep(1000);
const failedSuites = await client.$$("li.test.fail");
for (let i = 0, len = failedSuites.length; i < len; i++) {
const titleElement = await await failedSuites[i].$("h2");
const title = await client.execute((parent) => {
let child = parent.firstChild;
let ret = "";
while (child) {
if (child.nodeType === Node.TEXT_NODE) {
ret += child.textContent;
}
child = child.nextSibling;
}
return ret;
}, titleElement);
const error = await (await failedSuites[i].$("pre.error")).getText();
console.log(title.replace(/./g, "="));
console.log(title);
console.log(title.replace(/./g, "="));
console.log(error);
console.log();
}
throw new Error(`${failures} failures happened on ${fullBrowserName}`);
}
expect(passes).to.be.above(0);
await sleep(1000);
console.log(
chalk.green(
`browser tests successful (${passes} passes) on ${fullBrowserName}`
)
);
server.close();
logPostRequest = true;
setTimeout(function () {
client.deleteSession();
}, 5000);
} catch (e) {
clearInterval(interval);
interval = null;
logPostRequest = true;
if (e.message.indexOf("ECONNREFUSED") !== -1) {
return test();
}
exit(e);
}
}
test();
});