added basic selenium test suite, move the config page to its own tab

This commit is contained in:
Peter Snyder 2017-10-21 20:39:17 -04:00
parent fbdba9fca7
commit 7cd3aedca4
9 changed files with 2126 additions and 487 deletions

View file

@ -36,7 +36,7 @@
function (allHosts) {
if (rootObject.runtime.lastError && !allHosts) {
rootObject.browserAction.disable();
// rootObject.browserAction.disable();
rootObject.browserAction.setBadgeText({text: "-"});
return;
}

View file

@ -38,7 +38,7 @@
</section>
<script src="../lib/init.js"></script>
<script src="../lib/defaults.js"></script>
<script src="../data/standards.js"></script>
<script src="../lib/standards.js"></script>
<script src="../lib/storage.js"></script>
<script src="js/lib/vue.js"></script>
<script src="js/state.js"></script>

View file

@ -61,6 +61,7 @@
]
},
"options_ui": {
"page": "config/index.html"
"page": "config/index.html",
"open_in_tab": true
}
}

2300
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,10 +4,7 @@
"description": "Tools to generate Web API managing browser extensions for Firefox and Chrome.",
"author": "Peter Snyder <psnyde2@uic.edu> (https://www.cs.uic.edu/~psnyder/)",
"license": "GPL-3.0",
"dependencies": {
"gulp": "^3.9.1",
"web-ext": "^2.2.2"
},
"dependencies": {},
"homepage": "https://github.com/snyderp/web-api-manager",
"bugs": {
"url": "https://github.com/snyderp/web-api-manager/issues",
@ -24,7 +21,15 @@
],
"contributors": [],
"scripts": {
"bundle": "web-ext -s add-on -a dist build --overwrite-dest",
"firefox": "web-ext -s add-on run"
"bundle": "gulp && web-ext -s add-on -a dist build --overwrite-dest",
"firefox": "web-ext -s add-on run",
"test": "rm dist/* && npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && node_modules/mocha/bin/mocha test/functional/*.js"
},
"devDependencies": {
"geckodriver": "^1.9.0",
"gulp": "^3.9.1",
"mocha": "^4.0.1",
"selenium-webdriver": "^3.6.0",
"web-ext": "^2.2.2"
}
}

89
test/functional/block.js Normal file
View file

@ -0,0 +1,89 @@
"use strict";
const assert = require("assert");
const http = require("http");
const utils = require("./lib/utils");
const injected = require("./lib/injected");
describe("Basic", function () {
const testPort = 8989;
const testUrl = `http://localhost:${testPort}`;
const svgTestScript = injected.testSVGTestScript();
let httpServer;
this.timeout = function () {
return 10000;
};
beforeEach(function () {
httpServer = http.createServer(function (req, res) {
const staticResponse = `<!DOCTYPE "html">
<html>
<head>
<title>Test Page</title>
</head>
<body>
<p>Test Content</p>
</body>
</html>
`;
res.writeHead(200, {"Content-Type": "text/html"});
res.write(staticResponse);
res.end();
});
httpServer.listen(8989);
});
afterEach(function () {
httpServer.close();
});
describe("Blocking", function () {
it("SVG Not Blocking", function (done) {
const standardsToBlock = [];
let driverReference;
utils.promiseGetDriver()
.then(function (driver, addonId) {
driverReference = driver;
return utils.promiseSetBlockingRules(driver, standardsToBlock);
})
.then(function (result) {
return driverReference.get(testUrl);
})
.then(function () {
return driverReference.executeAsyncScript(svgTestScript);
})
.catch(function () {
// Since we're not blocking the SVG API, then the sample
// SVG code should throw an exception.
driverReference.quit();
done();
});
});
it("SVG Blocking", function (done) {
const standardsToBlock = ["Scalable Vector Graphics (SVG) 1.1 (Second Edition)"];
let driverReference;
utils.promiseGetDriver()
.then(function (driver, addonId) {
driverReference = driver;
return utils.promiseSetBlockingRules(driver, standardsToBlock);
})
.then(function (result) {
return driverReference.get(testUrl);
})
.then(function () {
return driverReference.executeAsyncScript(svgTestScript);
})
.then(function () {
driverReference.quit();
done();
});
});
});
});

View file

@ -0,0 +1,91 @@
// This module contains functions that *are not* executed as part of the
// node process, but injected into the browser during tests (using
// Function.prototype.toString).
"use strict";
// Returns the contents of the provided function definition source.
//
// Ex "function () { return 1; }" -> "return 1";
const stripFuncFromSource = function (source) {
const parts = source.split("\n").filter(x => !!x.trim());
parts.splice(0, 1);
parts.splice(parts.length - 1, 1);
const recreatedScript = parts.join("\n");
return recreatedScript;
};
module.exports.temporaryAddOnInstallScript = (function () {
const funcToInject = function () {
let fileUtils = Components.utils.import('resource://gre/modules/FileUtils.jsm');
let FileUtils = fileUtils.FileUtils;
let callback = arguments[arguments.length - 1];
Components.utils.import('resource://gre/modules/AddonManager.jsm');
let listener = {
onInstallEnded: function(install, addon) {
callback([addon.id, 0]);
},
onInstallFailed: function(install) {
callback([null, install.error]);
},
onInstalled: function(addon) {
AddonManager.removeAddonListener(listener);
callback([addon.id, 0]);
}
};
let file = new FileUtils.File(arguments[0]);
AddonManager.addAddonListener(listener);
AddonManager.installTemporaryAddon(file).catch(error => {
Components.utils.reportError(error);
callback([null, error]);
});
};
const funcSource = stripFuncFromSource(funcToInject.toString());
return function () {
return funcSource;
};
}());
module.exports.setStandardsAsBlockedScript = (function () {
const funcToInject = function () {
const doc = window.document;
const callback = arguments[arguments.length - 1];
const standardsToBlockArray = "###REPLACE###";
standardsToBlockArray.forEach(function (aStandardName) {
const input = doc.querySelector(`input[value='${aStandardName}']`);
input.click();
});
callback();
};
const funcSource = stripFuncFromSource(funcToInject.toString());
return function (standardsToBlock) {
return funcSource.replace('"###REPLACE###"', JSON.stringify(standardsToBlock));
};
}());
module.exports.testSVGTestScript = (function () {
const funcToInject = function () {
const callback = arguments[arguments.length - 1];
callback(HTMLEmbedElement.prototype.getSVGDocument.art.bart.fart());
};
const funcSource = stripFuncFromSource(funcToInject.toString());
return function (standardsToBlock) {
return funcSource;
};
}());

View file

@ -0,0 +1,79 @@
"use strict";
// The geckodriver package downloads and installs geckodriver for us.
// We use it by requiring it.
require("geckodriver");
const firefox = require("selenium-webdriver/firefox");
const webdriver = require("selenium-webdriver");
const FxRunnerUtils = require("fx-runner/lib/utils");
const injectedScripts = require("./injected");
const fs = require("fs");
const By = webdriver.By;
const Context = firefox.Context;
const until = webdriver.until;
const path = require("path");
module.exports.promiseAddonButton = function (driver) {
driver.setContext(Context.CHROME);
return driver.wait(until.elementLocated(
By.css("[tooltiptext='WebAPI Manager']")
), 2000);
};
module.exports.promiseExtensionConfigPage = function (driver) {
const extensionIdPattern = /url\("moz-extension:\/\/(.*?)\/images/;
return this.promiseAddonButton(driver)
.then(button => button.getAttribute("style"))
.then(function (buttonStyle) {
const match = extensionIdPattern.exec(buttonStyle);
const extensionId = match[1];
driver.setContext(Context.CONTENT);
return driver.get(`moz-extension://${extensionId}/config/index.html`);
})
};
module.exports.promiseAddonConfigButton = function (driver) {
driver.setContext(Context.CHROME);
return driver.wait(until.elementLocated(
By.id("config-page-link")
), 2000);
};
module.exports.promiseSetBlockingRules = function (driver, standardsToBlock) {
const setStandardsScript = injectedScripts.setStandardsAsBlockedScript(standardsToBlock);
driver.setContext(Context.CONTENT);
return this.promiseExtensionConfigPage(driver)
.then(driver.executeAsyncScript(setStandardsScript));
};
module.exports.promiseGetDriver = function (callback) {
let driver = new webdriver.Builder()
.forBrowser('firefox')
.build();
driver.setContext(Context.CHROME);
let fileLocation = path.join(process.cwd(), "dist", "webapi_manager.zip");
// This manually installs the add-on as a temporary add-on.
// Hopefully selenium/geckodriver will get a way to do this soon:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1298025
let installAddOnPromise = driver.executeAsyncScript(
injectedScripts.temporaryAddOnInstallScript(),
fileLocation
);
return installAddOnPromise
.then(function (result) {
if (!result[0] && result[1]) {
return driver.quit().then(() => {
throw new Error(`Failed to install add-on: ${result[1]}`);
});
}
return Promise.resolve(driver, result[0]);
});
};

30
test/functional/logins.js Normal file
View file

@ -0,0 +1,30 @@
"use strict";
let assert = require("assert");
let utils = require("./lib/utils");
describe("Test logging into popular sites", function () {
this.timeout = function () {
return 10000;
};
describe("GitHub", function () {
it("Log into site", function (done) {
const standardsToBlock = ["Beacon"];
let driver;
utils.promiseGetDriver()
.then(function (driver, addonId) {
driver = driver;
return utils.promiseSetBlockingRules(driver, standardsToBlock);
});
// .then(_ => utils.promiseAddonConfigButton(driver))
// .then(function (configButton) {
// configButton.click();
// });
});
});
});