Add ability to add domains to an "allow all" list from the browser action, issue #6
This commit is contained in:
parent
6b0b35cdf5
commit
95480a5449
19 changed files with 507 additions and 66 deletions
|
@ -23,6 +23,7 @@
|
|||
"valid-jsdoc": "error",
|
||||
"prefer-const": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-unused-expressions": "error"
|
||||
"no-unused-expressions": "error",
|
||||
"no-trailing-spaces": "error"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,14 +54,14 @@
|
|||
rootObject.tabs.onUpdated.addListener(updateBrowserActionBadge);
|
||||
rootObject.tabs.onActivated.addListener(updateBrowserActionBadge);
|
||||
|
||||
window.setInterval(function () {
|
||||
rootObject.tabs.getCurrent(function (currentTab) {
|
||||
if (currentTab === undefined) {
|
||||
return;
|
||||
}
|
||||
updateBrowserActionBadge({tabId: currentTab.id});
|
||||
});
|
||||
}, 1000);
|
||||
// window.setInterval(function () {
|
||||
// rootObject.tabs.getCurrent(function (currentTab) {
|
||||
// if (currentTab === undefined) {
|
||||
// return;
|
||||
// }
|
||||
// updateBrowserActionBadge({tabId: currentTab.id});
|
||||
// });
|
||||
// }, 1000);
|
||||
|
||||
// Listen for updates to the domain rules from the config page.
|
||||
// The two types of messages that are sent to the background page are
|
||||
|
@ -73,26 +73,56 @@
|
|||
rootObject.runtime.onMessage.addListener(function (request, ignore, sendResponse) {
|
||||
|
||||
const [label, data] = request;
|
||||
|
||||
// Sent from the config page, when the "which APIs should be
|
||||
// blocked for which domains" has been changed on the config page.
|
||||
if (label === "stateUpdate") {
|
||||
domainRules = data.domainRules;
|
||||
shouldLog = data.shouldLog;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sent from the popup / browser action, asking for infromation about
|
||||
// which blocking rules are being applied to which domains in the
|
||||
// tab.
|
||||
if (label === "rulesForDomains") {
|
||||
|
||||
const matchHostName = domainMatcherLib.matchHostName;
|
||||
const matchHostNameBound = matchHostName.bind(undefined, Object.keys(domainRules));
|
||||
const rulesForDomains = data.map(matchHostNameBound);
|
||||
const domainToRuleMapping = {};
|
||||
|
||||
data.forEach(function (aHostName, index) {
|
||||
domainToRuleMapping[aHostName] = rulesForDomains[index] || defaultKey;
|
||||
data.forEach(function (aHostName) {
|
||||
const ruleNameForHost = matchHostNameBound(aHostName) || defaultKey;
|
||||
domainToRuleMapping[aHostName] = {
|
||||
"ruleName": ruleNameForHost,
|
||||
"numRules": domainRules[ruleNameForHost].length
|
||||
};
|
||||
});
|
||||
|
||||
sendResponse(domainToRuleMapping);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sent from the popup / browser action, saying that a given
|
||||
// host name should have the default blocking rule applied
|
||||
// (action === "block", or all APIs allowed
|
||||
// (action === "allow").
|
||||
if (label === "toggleBlocking") {
|
||||
|
||||
const {action, hostName} = data;
|
||||
if (action === "block") {
|
||||
delete domainRules[hostName];
|
||||
sendResponse(["toggleBlockingResponse", domainRules[defaultKey].length]);
|
||||
} else if (action === "allow") {
|
||||
domainRules[hostName] = [];
|
||||
sendResponse(["toggleBlockingResponse", 0]);
|
||||
}
|
||||
|
||||
storageLib.set({
|
||||
domainRules,
|
||||
shouldLog
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const requestFilter = {
|
||||
|
|
|
@ -3,6 +3,75 @@
|
|||
|
||||
const defaultDomain = "(default)";
|
||||
|
||||
/**
|
||||
* Checks if two arrays contain the same values, regardless of order.
|
||||
*
|
||||
* @param {array} arrayOne
|
||||
* One array to test.
|
||||
* @param {array} arrayTwo
|
||||
* The second array to test against.
|
||||
*
|
||||
* @return {bool}
|
||||
* Returns true if the two arrays contain all of the same values,
|
||||
* an otherwise false.
|
||||
*/
|
||||
const areArrayValuesIdentical = function (arrayOne, arrayTwo) {
|
||||
if (arrayOne.length !== arrayTwo.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const arrayOneSorted = arrayOne.sort();
|
||||
const arrayTwoSorted = arrayTwo.sort();
|
||||
|
||||
const areAllValuesEqual = arrayOneSorted.every(function (value, index) {
|
||||
return value === arrayTwoSorted[index];
|
||||
});
|
||||
|
||||
return areAllValuesEqual;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if two domain rule sets describe the standard blocking same policy.
|
||||
*
|
||||
* This check is independent of the ordering of the domain matching rules
|
||||
* (the keys of the rule sets), or the standards that should be blocked
|
||||
* (the values in the rule sets).
|
||||
*
|
||||
* @param {object} firstRuleSet
|
||||
* The first rule set to compare.
|
||||
* @param {object} secondRuleSet
|
||||
* The second rule set to compare against.
|
||||
*
|
||||
* @return {bool}
|
||||
* Returns true if the two objects describe identical policies
|
||||
* (ie would block the same standards on the same domains), and
|
||||
* otherwise false.
|
||||
*/
|
||||
const areRuleSetsIdentical = function (firstRuleSet, secondRuleSet) {
|
||||
|
||||
const firstRuleSetDomains = Object.keys(firstRuleSet).sort();
|
||||
const secondRuleSetDomains = Object.keys(secondRuleSet).sort();
|
||||
|
||||
// First check if both rule sets have the same matching patterns
|
||||
// defined. If not, then no need to consider further.
|
||||
const haveSameMatchPatterns = areArrayValuesIdentical(firstRuleSet, secondRuleSetDomains);
|
||||
|
||||
if (haveSameMatchPatterns === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Next, now that we know both rule sets have rules describing the
|
||||
// same domains, check that standards blocked for all domains are
|
||||
// the same.
|
||||
return firstRuleSetDomains.every(function (value) {
|
||||
if (secondRuleSet[value] === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return areArrayValuesIdentical(firstRuleSet[value], secondRuleSet[value]);
|
||||
});
|
||||
};
|
||||
|
||||
const generateStateObject = function (initialDomain, standards) {
|
||||
|
||||
const state = {
|
||||
|
@ -20,15 +89,45 @@
|
|||
};
|
||||
},
|
||||
|
||||
domainsBlockingNoStandards: function () {
|
||||
return this.domainNames
|
||||
.filter(domain => this.domainRules[domain].length === 0)
|
||||
.sort();
|
||||
},
|
||||
|
||||
domainsBlockingStandards: function () {
|
||||
return this.domainNames
|
||||
.filter(domain => this.domainRules[domain].length > 0)
|
||||
.sort();
|
||||
},
|
||||
|
||||
populateFromStorage: function (storedValues) {
|
||||
this.setDomainRules(storedValues.domainRules);
|
||||
this.setShouldLog(storedValues.shouldLog);
|
||||
},
|
||||
|
||||
setDomainRules: function (newDomainRules) {
|
||||
|
||||
const isRuleSetMatchingCurrentRules = areRuleSetsIdentical(
|
||||
newDomainRules,
|
||||
this.domainRules
|
||||
);
|
||||
|
||||
// If the "new" domain rule set is identical to the existing
|
||||
// one, then don't set any propreties (to avoid unnecessarily
|
||||
// triggering storage and Vue.js callbacks).
|
||||
if (isRuleSetMatchingCurrentRules === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.domainRules = newDomainRules;
|
||||
this.domainNames = Object.keys(newDomainRules);
|
||||
this.selectedStandards = this.domainRules[this.selectedDomain];
|
||||
|
||||
if (this.domainRules[this.selectedDomain] === undefined) {
|
||||
this.selectedStandards = this.domainRules[this.defaultDomain];
|
||||
} else {
|
||||
this.selectedStandards = this.domainRules[this.selectedDomain];
|
||||
}
|
||||
},
|
||||
|
||||
setSelectedDomain: function (newDomain) {
|
||||
|
@ -77,6 +176,8 @@
|
|||
};
|
||||
|
||||
window.WEB_API_MANAGER.stateLib = {
|
||||
generateStateObject
|
||||
generateStateObject,
|
||||
areRuleSetsIdentical,
|
||||
areArrayValuesIdentical
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
const rootObject = (window.browser || window.chrome);
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const doc = window.document;
|
||||
const standards = window.WEB_API_MANAGER.standards;
|
||||
const {storageLib, stateLib} = window.WEB_API_MANAGER;
|
||||
|
@ -10,12 +10,14 @@
|
|||
|
||||
const state = stateLib.generateStateObject(defaultDomain, standards);
|
||||
|
||||
let globalVmInstance;
|
||||
|
||||
const onSettingsLoaded = function (storedSettings) {
|
||||
|
||||
state.populateFromStorage(storedSettings);
|
||||
state.activeTab = "domain-rules";
|
||||
|
||||
const vm = new Vue({
|
||||
globalVmInstance = new Vue({
|
||||
el: doc.querySelector("#config-root"),
|
||||
render: window.WEB_API_MANAGER.vueComponents["config-root"].render,
|
||||
staticRenderFns: window.WEB_API_MANAGER.vueComponents["config-root"].staticRenderFns,
|
||||
|
@ -36,12 +38,15 @@
|
|||
});
|
||||
};
|
||||
|
||||
vm.$watch("selectedStandards", updateStoredSettings);
|
||||
vm.$watch("domainNames", updateStoredSettings);
|
||||
vm.$watch("shouldLog", updateStoredSettings);
|
||||
globalVmInstance.$watch("selectedStandards", updateStoredSettings);
|
||||
globalVmInstance.$watch("domainNames", updateStoredSettings);
|
||||
globalVmInstance.$watch("shouldLog", updateStoredSettings);
|
||||
};
|
||||
|
||||
window.onload = function () {
|
||||
storageLib.get(onSettingsLoaded);
|
||||
storageLib.onChange(function (newStoredValues) {
|
||||
globalVmInstance.$data.setDomainRules(newStoredValues.domainRules);
|
||||
});
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
blockingRules: function () {
|
||||
return this.$root.$data.domainsBlockingStandards();
|
||||
},
|
||||
allowingRules: function () {
|
||||
return this.$root.$data.domainsBlockingNoStandards();
|
||||
},
|
||||
newDomainSubmitted: function () {
|
||||
|
||||
const state = this.$root.$data;
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
const logMessages = newDomainRules.map(function (newDomainRule) {
|
||||
const {pattern, standards} = newDomainRule;
|
||||
if (currentDomainRules[pattern] !== undefined && shouldOverwrite === false) {
|
||||
return ` ! ${pattern}: Skipped. Set to not override.\n`;
|
||||
return ` ! ${pattern}: Skipped. Set to not override.\n`;
|
||||
}
|
||||
|
||||
stateObject.setStandardsForDomain(pattern, standards);
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
/**
|
||||
* Returns a new CSP instruction, with source with the given hash
|
||||
* whitelisted.
|
||||
*
|
||||
*
|
||||
* If the CSP instruction has a "script-src" rule, then the hash-value
|
||||
* will be inserted there. Otherwise, it will be inserted in the
|
||||
* default-src section.
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
/**
|
||||
* Encodes a buffer (such as a Uint8Array) to a base64 encoded string.
|
||||
*
|
||||
*
|
||||
* @param {ArrayBuffer} buf
|
||||
* A buffer of binary data.
|
||||
*
|
||||
|
|
|
@ -34,8 +34,39 @@
|
|||
storageObject.sync.set(valueToStore, callback);
|
||||
};
|
||||
|
||||
const onChange = (function () {
|
||||
|
||||
const queue = [];
|
||||
|
||||
storageObject.onChanged.addListener(function (changes) {
|
||||
|
||||
if (changes[webApiManagerKeySettingsKey] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {newValue, oldValue} = changes[webApiManagerKeySettingsKey];
|
||||
|
||||
if (JSON.stringify(newValue) === JSON.stringify(oldValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue.forEach(function (callback) {
|
||||
try {
|
||||
callback(newValue);
|
||||
} catch (e) {
|
||||
// Intentionally left blank...
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return function (callback) {
|
||||
queue.push(callback);
|
||||
};
|
||||
}());
|
||||
|
||||
window.WEB_API_MANAGER.storageLib = {
|
||||
get,
|
||||
set
|
||||
set,
|
||||
onChange
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
body, html {
|
||||
min-width: 320px;
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
section .row {
|
||||
|
@ -12,4 +13,4 @@ section .row {
|
|||
|
||||
.loaded .loading-section {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,31 +5,131 @@
|
|||
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const doc = window.document;
|
||||
|
||||
const configureButton = doc.getElementById("config-page-link");
|
||||
const listGroupElm = doc.querySelector("ul.list-group");
|
||||
const domainRuleTableBody = doc.querySelector("#domain-rule-table tbody");
|
||||
const defaultDomainRule = "(default)";
|
||||
|
||||
const addRuleToList = function (hostToRuleMapping, listElm, aHostName) {
|
||||
/**
|
||||
* Returns a function for use as the "onclick" handler for a toggle button.
|
||||
*
|
||||
* @param {string} hostName
|
||||
* The name of the host to change the blocking settings for.
|
||||
* @param {string} action
|
||||
* Either "allow" (indicating that all APIs should be allowed for this
|
||||
* host) or "block" (indicating that all APIs should be blocked for
|
||||
* this host).
|
||||
*
|
||||
* @return {function}
|
||||
* A function that takes a single event object as an argument. For
|
||||
* use as an event handler callback.
|
||||
*/
|
||||
const createOnToggleHandler = function (hostName, action) {
|
||||
const onClickHandler = function (event) {
|
||||
const message = ["toggleBlocking", {
|
||||
"action": action,
|
||||
"hostName": hostName
|
||||
}];
|
||||
const button = event.target;
|
||||
const containingRowElm = button.parentNode.parentNode;
|
||||
|
||||
const domainRule = hostToRuleMapping[aHostName];
|
||||
const appliedRuleTd = containingRowElm.querySelector("td:nth-child(2)");
|
||||
const numApisBlockedTd = containingRowElm.querySelector("td:nth-child(3)");
|
||||
|
||||
const liElm = doc.createElement("li");
|
||||
liElm.className = "list-group-item";
|
||||
button.className += " disabled";
|
||||
button.innerHtml = "setting…";
|
||||
|
||||
if (domainRule !== "(default)") {
|
||||
liElm.className += " list-group-item-success";
|
||||
rootObject.runtime.sendMessage(message, function (responseMessage) {
|
||||
|
||||
const [messageType, numAPIsBlocked] = responseMessage;
|
||||
|
||||
if (messageType === "toggleBlockingResponse") {
|
||||
|
||||
numApisBlockedTd.innerText = numAPIsBlocked;
|
||||
|
||||
if (action === "block") {
|
||||
appliedRuleTd.innerText = defaultDomainRule;
|
||||
} else if (action === "allow") {
|
||||
appliedRuleTd.innerText = hostName;
|
||||
}
|
||||
|
||||
button.innerText = "👍";
|
||||
}
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
return onClickHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a TR element based on a domain's blocking status
|
||||
*
|
||||
* @param {string} domainName
|
||||
* The name of a domain of a frame on the current tab
|
||||
* @param {string} appliedRuleName
|
||||
* The pattern matching rule for the rule set applied (or,
|
||||
* if no matching rule "(default)").
|
||||
* @param {number} numAPIsBlocked
|
||||
* The number of APIs blocked for this domain.
|
||||
*
|
||||
* @return {Node}
|
||||
* a HTMLTRElement object.
|
||||
*/
|
||||
const ruleToTr = function (domainName, appliedRuleName, numAPIsBlocked) {
|
||||
|
||||
const trElm = doc.createElement("tr");
|
||||
|
||||
const domainTd = doc.createElement("td");
|
||||
const domainTdText = doc.createTextNode(domainName);
|
||||
domainTd.appendChild(domainTdText);
|
||||
trElm.appendChild(domainTd);
|
||||
|
||||
const ruleTd = doc.createElement("td");
|
||||
ruleTd.appendChild(doc.createTextNode(appliedRuleName));
|
||||
trElm.appendChild(ruleTd);
|
||||
|
||||
const numBlockedTd = doc.createElement("td");
|
||||
numBlockedTd.appendChild(doc.createTextNode(numAPIsBlocked));
|
||||
trElm.appendChild(numBlockedTd);
|
||||
|
||||
const actionsTd = doc.createElement("td");
|
||||
const toggleButton = doc.createElement("button");
|
||||
toggleButton.className = "btn btn-default btn-xs block-toggle";
|
||||
|
||||
const isAllowingAll = numAPIsBlocked === 0;
|
||||
|
||||
let toggleButtonText;
|
||||
let toggleAction;
|
||||
|
||||
// If the domain is using the default rule, and the default rule is
|
||||
// allowing all API's then do nothing, since there is no sensible
|
||||
// option to "toggle" to.
|
||||
if (isAllowingAll === false) {
|
||||
toggleButtonText = "allow all";
|
||||
toggleButton.className += " success";
|
||||
toggleAction = "allow";
|
||||
} else {
|
||||
toggleButtonText = "remove grant";
|
||||
toggleButton.className += " warn";
|
||||
toggleAction = "block";
|
||||
}
|
||||
|
||||
const spanElm = doc.createElement("span");
|
||||
spanElm.className = "badge";
|
||||
if (toggleButtonText !== undefined) {
|
||||
const toggleButtonTextElm = doc.createTextNode(toggleButtonText);
|
||||
toggleButton.appendChild(toggleButtonTextElm);
|
||||
|
||||
const badgeText = doc.createTextNode(domainRule);
|
||||
spanElm.appendChild(badgeText);
|
||||
liElm.appendChild(spanElm);
|
||||
if (toggleAction !== undefined) {
|
||||
const onClickToggleButton = createOnToggleHandler(domainName, toggleAction);
|
||||
toggleButton.addEventListener("click", onClickToggleButton, false);
|
||||
}
|
||||
}
|
||||
|
||||
const textElm = doc.createTextNode(aHostName);
|
||||
liElm.appendChild(textElm);
|
||||
listElm.appendChild(liElm);
|
||||
actionsTd.appendChild(toggleButton);
|
||||
trElm.appendChild(actionsTd);
|
||||
|
||||
return trElm;
|
||||
};
|
||||
|
||||
configureButton.addEventListener("click", function (event) {
|
||||
|
@ -52,10 +152,12 @@
|
|||
|
||||
doc.body.className = "loaded";
|
||||
|
||||
const domainNames = Object.keys(response);
|
||||
const addRuleToListBound = addRuleToList.bind(undefined, response, listGroupElm);
|
||||
|
||||
domainNames.forEach(addRuleToListBound);
|
||||
const currentDomains = Object.keys(response);
|
||||
currentDomains.forEach(function (aDomain) {
|
||||
const {ruleName, numRules} = response[aDomain];
|
||||
const rowElm = ruleToTr(aDomain, ruleName, numRules);
|
||||
domainRuleTableBody.appendChild(rowElm);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -27,8 +27,19 @@
|
|||
|
||||
<div class="loaded-section">
|
||||
<p>The following origins are executing code on this page.</p>
|
||||
<ul class="list-group">
|
||||
</ul>
|
||||
<table class="table table-striped" id="domain-rule-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Rule</th>
|
||||
<th># APIs Blocked</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="button" id="config-page-link" class="btn btn-default btn-block">
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
"lint:fix": "node_modules/eslint/bin/eslint.js --fix .",
|
||||
"test": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && node_modules/cross-env/dist/bin/cross-env.js node_modules/mocha/bin/mocha test/unit/*.js test/functional/*.js --only-local-tests",
|
||||
"test:watch": "node_modules/cross-env/dist/bin/cross-env.js npm test --watch",
|
||||
"test:all": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && node_modules/cross-env/dist/bin/cross-env.js node_modules/mocha/bin/mocha test/unit/*.js test/functional/*.js"
|
||||
"test:all": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && node_modules/cross-env/dist/bin/cross-env.js node_modules/mocha/bin/mocha test/unit/*.js test/functional/*.js",
|
||||
"test:unit": "npm run clean; npm run bundle && ln -s `ls dist/` dist/webapi_manager.zip && node_modules/cross-env/dist/bin/cross-env.js node_modules/mocha/bin/mocha test/unit/*.js"
|
||||
},
|
||||
"pre-push": {
|
||||
"run": [
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<div class="domain-rules-container well">
|
||||
<div class="radio" v-for="aDomain in domainNames">
|
||||
<h3>Domains</h3>
|
||||
<div class="radio" v-for="aDomain in blockingRules()">
|
||||
<label>
|
||||
<input type="radio"
|
||||
:value="aDomain"
|
||||
|
@ -13,16 +14,31 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<h3>Whitelisted Domains</h3>
|
||||
<div class="radio" v-for="aDomain in allowingRules()">
|
||||
<label>
|
||||
<input type="radio"
|
||||
:value="aDomain"
|
||||
v-model="selectedDomain"
|
||||
@change="onRadioChange">
|
||||
{{ aDomain }}
|
||||
<span class="glyphicon glyphicon-remove"
|
||||
v-if="!isDefault(aDomain)"
|
||||
:data-domain="aDomain"
|
||||
@click="onRemoveClick"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-danger" role="alert" v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-bind:class="{ 'has-error': errorMessage }">
|
||||
<label for="newDomainName">Add New Domain Rule</label>
|
||||
<input
|
||||
class="form-control"
|
||||
v-model.trim="newDomain"
|
||||
placeholder="*.example.org">
|
||||
<input class="form-control"
|
||||
v-model.trim="newDomain"
|
||||
placeholder="*.example.org">
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
|
|
|
@ -6,6 +6,7 @@ const testServer = require("./lib/server");
|
|||
const webdriver = require("selenium-webdriver");
|
||||
const by = webdriver.By;
|
||||
const until = webdriver.until;
|
||||
const Context = require("selenium-webdriver/firefox").Context;
|
||||
|
||||
describe("Basic Functionality", function () {
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ const promiseOpenImportExportTab = function (driver) {
|
|||
|
||||
describe("Import / Export", function () {
|
||||
|
||||
this.timeout = () => 10000;
|
||||
this.timeout = () => 20000;
|
||||
|
||||
describe("Exporting", function () {
|
||||
|
||||
|
@ -35,10 +35,14 @@ describe("Import / Export", function () {
|
|||
.then(() => driverReference.findElement(by.css(".export-section select option:nth-child(1)")).click())
|
||||
.then(() => driverReference.findElement(by.css(".export-section textarea")).getAttribute("value"))
|
||||
.then(function (exportValue) {
|
||||
driverReference.quit();
|
||||
assert.equal(exportValue.trim(), emptyRuleSet, "Exported ruleset does not match expected value.");
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
.catch(function (e) {
|
||||
driverReference.quit();
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
it("Exporting SVG and Beacon blocking rules", function (done) {
|
||||
|
@ -55,9 +59,13 @@ describe("Import / Export", function () {
|
|||
.then(() => driverReference.findElement(by.css(".export-section textarea")).getAttribute("value"))
|
||||
.then(function (exportValue) {
|
||||
assert.equal(exportValue.trim(), blockingSVGandBeacon, "Exported ruleset does not match expected value.");
|
||||
driverReference.quit();
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
.catch(function (e) {
|
||||
driverReference.quit();
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -89,10 +97,13 @@ describe("Import / Export", function () {
|
|||
})
|
||||
.then(function (secondCheckboxValue) {
|
||||
assert.equal(secondCheckboxValue, utils.constants.svgBlockRule[0], "The second blocked standard should be the SVG standard.");
|
||||
driverReference.close();
|
||||
driverReference.quit();
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
.catch(function (e) {
|
||||
driverReference.quit();
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
it("Importing rules for new domain", function (done) {
|
||||
|
@ -112,7 +123,7 @@ describe("Import / Export", function () {
|
|||
.then(() => driverReference.findElements(by.css("#domain-rules input[type='radio']")))
|
||||
.then(function (radioElms) {
|
||||
assert.equal(radioElms.length, 2, "There should be two domain rules in place.");
|
||||
return radioElms[1].click();
|
||||
return radioElms[0].click();
|
||||
})
|
||||
.then(() => driverReference.findElements(by.css("#domain-rules input[type='checkbox']:checked")))
|
||||
.then(function (checkboxElms) {
|
||||
|
@ -126,10 +137,13 @@ describe("Import / Export", function () {
|
|||
})
|
||||
.then(function (secondCheckboxValue) {
|
||||
assert.equal(secondCheckboxValue, "WebGL Specification", "The second blocked standard should be 'WebGL Specification'.");
|
||||
driverReference.close();
|
||||
driverReference.quit();
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
.catch(function (e) {
|
||||
driverReference.quit();
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -66,11 +66,6 @@ module.exports.promiseExtensionConfigPage = function (driver) {
|
|||
});
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Tests to ensure that the pattern matching code (what determins which
|
||||
* standard blocking rules should be applied to which domain) is correct.
|
||||
*
|
||||
*
|
||||
* The code being tested here mostly lives in (from the project root)
|
||||
* add-on/lib/domainmatcher.js
|
||||
*/
|
||||
|
|
126
test/unit/state-management.js
Normal file
126
test/unit/state-management.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* Tests to ensure that the tools that are used to manage the state
|
||||
* of the extension perform as expected..
|
||||
*
|
||||
* The code being tested here mostly lives in (from the project root)
|
||||
* add-on/config/js/state.js
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const addonLibPath = path.join(__dirname, "..", "..", "add-on", "lib");
|
||||
const addonConfigLibPath = path.join(__dirname, "..", "..", "add-on", "config", "js");
|
||||
|
||||
// These will end up not returing anything, but will instead populate
|
||||
// window.WEB_API_MANAGER
|
||||
require(path.join(addonLibPath, "init.js"));
|
||||
require(path.join(addonConfigLibPath, "state.js"));
|
||||
const stateLib = window.WEB_API_MANAGER.stateLib;
|
||||
|
||||
describe("Extension State Management", function () {
|
||||
|
||||
describe("Array comparisons", function () {
|
||||
|
||||
it("Identical values in same order", function (done) {
|
||||
|
||||
const firstArray = [1, 2, 3, "A", "B", "C"];
|
||||
const secondArray = [1, 2, 3, "A", "B", "C"];
|
||||
const areEqual = stateLib.areArrayValuesIdentical(firstArray, secondArray);
|
||||
|
||||
assert.equal(areEqual, true, "Arrays contain identical values, so should evaluate to identical.");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Identical values in different order", function (done) {
|
||||
|
||||
const firstArray = [1, 2, 3, "A", "B", "C"];
|
||||
const secondArray = [1, "B", 2, "A", 3, "C"];
|
||||
const areEqual = stateLib.areArrayValuesIdentical(firstArray, secondArray);
|
||||
|
||||
assert.equal(areEqual, true, "Arrays contain identical values, so should evaluate to identical.");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Different values", function (done) {
|
||||
|
||||
const firstArray = [1, 2, 3, "A", "B", "C"];
|
||||
const secondArray = ["Totally", "different", "values"];
|
||||
const areEqual = stateLib.areArrayValuesIdentical(firstArray, secondArray);
|
||||
|
||||
assert.equal(areEqual, false, "Arrays contain different values, so should evaluate to not identical.");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Domain rule comparisons", function () {
|
||||
|
||||
it("Identical rule sets: same order", function (done) {
|
||||
|
||||
const firstRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const secondRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const areEqual = stateLib.areRuleSetsIdentical(firstRuleSet, secondRuleSet);
|
||||
assert.equal(areEqual, false, "Both rule sets block the same standards on the same domains.");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Identical rule sets: different orders", function (done) {
|
||||
|
||||
const firstRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const secondRuleSet = {
|
||||
"www.example.com": ["Vibrate API", "Beacon"],
|
||||
"(default)": []
|
||||
};
|
||||
|
||||
const areEqual = stateLib.areRuleSetsIdentical(firstRuleSet, secondRuleSet);
|
||||
assert.equal(areEqual, false, "Both rule sets block the same standards on the same domains.");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Different rule sets: different domains", function (done) {
|
||||
|
||||
const firstRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const secondRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.net": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const areEqual = stateLib.areRuleSetsIdentical(firstRuleSet, secondRuleSet);
|
||||
assert.equal(areEqual, false, "The domains being described by these rule sets are different.");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Different rule sets: different standards", function (done) {
|
||||
|
||||
const firstRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Vibrate API"]
|
||||
};
|
||||
|
||||
const secondRuleSet = {
|
||||
"(default)": [],
|
||||
"www.example.com": ["Beacon", "Gamepad API"]
|
||||
};
|
||||
|
||||
const areEqual = stateLib.areRuleSetsIdentical(firstRuleSet, secondRuleSet);
|
||||
assert.equal(areEqual, false, "The standards blocked by the rule sets are different.");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue