Files
fx_cast/test/driver.js
2019-01-12 06:31:02 +00:00

201 lines
5.8 KiB
JavaScript

"use strict";
const path = require("path");
const fs = require("fs");
const EventEmitter = require("events");
const glob = require("glob");
const WebSocket = require("ws");
const JasmineConsoleReporter = require("jasmine-console-reporter");
const webdriver = require("selenium-webdriver");
const firefox = require("selenium-webdriver/firefox");
const chrome = require("selenium-webdriver/chrome");
// Webdriver shorthands
const { By, until } = webdriver;
const { __extensionName
, __extensionVersion } = require("../ext/package.json");
const extensionArchivePath = path.join(
path.join(__dirname, "../dist/ext/")
, `${__extensionName}-${__extensionVersion}.xpi`);
if (!fs.existsSync(extensionArchivePath)) {
console.error("Extension archive not found.");
process.exit(1);
}
const TEST_PAGE_URL = `file:///${__dirname}/SpecRunner.html`;
const POLLING_PAGE_URL = `file:///${__dirname}/polling.html`;
const firefoxOptions = new firefox.Options()
.setBinary(firefox.Channel.NIGHTLY)
.addExtensions(extensionArchivePath)
.setPreference("xpinstall.signatures.required", false);
const chromeOptions = new chrome.Options()
.excludeSwitches([ "disable-background-networking"
, "disable-default-apps"]);
/**
* Chrome doesn't load the media router extension immediately
* and there doesn't seem to be a consistent way of
* determining when it has loaded.
* Workaround is to poll every 100ms, refresh the page, and
* check whether the chrome.cast API objects are defined.
*/
function waitUntilDefined (
driver
, pollingTimeout = 10000
, pollingFrequency = 100) {
return new Promise(async (resolve, reject) => {
let elapsedTime = pollingFrequency;
// Navigate to polling page
await driver.get(POLLING_PAGE_URL);
const interval = setInterval(async () => {
await driver.navigate().refresh();
const isDefined = await driver.executeScript(() => {
return window.chrome.cast !== undefined;
});
elapsedTime += pollingFrequency;
if (isDefined) {
clearInterval(interval);
resolve();
} else if (elapsedTime >= pollingTimeout) {
clearInterval(interval);
reject("Timed out");
}
}, pollingFrequency);
});
}
/**
* Jasmine runs in browser context, but reporters for terminal
* output run in node context. Need to forward reporter output
* from Jasmine in the browser context to node context to use
* reporters here.
*
* Creates a WebSocket server. messageProxy.js creates a client
* socket to this server and facilitates a communication
* channel.
*/
class MessageProxy extends EventEmitter {
constructor () {
super();
const wss = new WebSocket.Server({
port: 8080
});
wss.on("connection", socket => {
socket.on("message", message => {
const messageContent = JSON.parse(message);
this.emit(messageContent.subject, messageContent.data);
});
})
}
}
/**
* Ensures cast API is loaded before finding and injecting spec
* file scripts into the test page.
*/
async function injectSpecs (driver) {
const [ loaded, errorInfo ] = await driver.executeAsyncScript(() => {
const callback = arguments[arguments.length - 1];
// If already loaded, return immediately
if (chrome.cast !== undefined) {
callback([ true ]);
}
// Set Cast API callback
window.__onGCastApiAvailable = function (...args) {
callback(args);
};
});
// Return error if API unavailable
if (!loaded) {
console.error("Failed to load cast API", errorInfo);
return;
}
// Get spec files
const specFiles = glob.sync("spec/**/*.spec.js", {
cwd: __dirname
});
// Inject spec files
for (const specFile of specFiles) {
await driver.executeScript(`
const scriptElement = document.createElement("script");
scriptElement.src = "${specFile}";
document.head.appendChild(scriptElement);
`);
}
// Trigger Jasmine spec execution
await driver.executeScript(() => {
jasmine.getEnv().execute();
});
}
(async () => {
const driver = new webdriver.Builder()
.forBrowser("firefox")
.setFirefoxOptions(firefoxOptions)
.setChromeOptions(chromeOptions)
.build();
const capabilties = await driver.getCapabilities();
const browserName = capabilties.get("browserName");
// Need to wait for cast extension on Chrome
if (browserName === "chrome") {
console.log("Waiting for cast extension...");
await waitUntilDefined(driver);
console.log("Cast extension loaded!");
}
// Create console reporter and message proxy.
const reporter = new JasmineConsoleReporter();
const messageProxy = new MessageProxy();
// Inject specs when ready
messageProxy.on("injectSpecs", () => {
injectSpecs(driver);
});
/**
* Forward events from Jasmine standlone via message proxy to
* console reporter.
*/
messageProxy.on("jasmineDone" , result => reporter.jasmineDone(result));
messageProxy.on("jasmineStarted" , result => reporter.jasmineStarted(result));
messageProxy.on("specDone" , result => reporter.specDone(result));
messageProxy.on("specStarted" , result => reporter.specStarted(result));
messageProxy.on("suiteDone" , result => reporter.suiteDone(result));
messageProxy.on("suiteStarted" , result => reporter.suiteStarted(result));
// Load Jasmine test page
driver.get(TEST_PAGE_URL);
})();
// Keep process alive
process.stdin.resume();