Permalink
Browse files

Add tests for the client-side part of pywb using Karma

 * Add a Karma configuration for unit/integration tests
   for the client-side pywb code.

 * Add an integration test suite which creates an <iframe> loads
   the client-side rewriting code (wombat.js) in it and
   then executes a test script.

   Since wombat.js monkey-patches the DOM and the exact behavior
   of DOM objects varies between browsers, which we want to test,
   the suite does not mock the DOM but instead runs
   a set of tests in an isolated environment against
   the DOM.

 * Add Travis config to run the Karma tests
  • Loading branch information...
1 parent 6035a9d commit f4a43a14f5d89bd2b2c6f294d68c984451cb0c19 @robertknight robertknight committed Nov 25, 2015
Showing with 256 additions and 1 deletion.
  1. +3 −0 .gitignore
  2. +3 −1 .travis.yml
  3. +4 −0 karma-tests/Makefile
  4. +68 −0 karma-tests/karma.conf.js
  5. +145 −0 karma-tests/wombat.spec.js
  6. +33 −0 package.json
View
@@ -41,3 +41,6 @@ nosetests.xml
.pydevproject
.vagrant
+
+# Node
+node_modules/
View
@@ -15,9 +15,11 @@ install:
- pip install boto certauth
- python setup.py -q install
- pip install coverage pytest-cov coveralls --use-mirrors
+ - npm install
script:
- python setup.py test
+ - python setup.py test
+ - cd karma-tests && make test
after_success:
coveralls
@@ -0,0 +1,4 @@
+NODE_BIN_DIR=../node_modules/.bin
+
+test:
+ $(NODE_BIN_DIR)/karma start --single-run
@@ -0,0 +1,68 @@
+var WOMBAT_JS_PATH = 'pywb/static/wombat.js';
+
+module.exports = function(config) {
+ config.set({
+
+ // base path that will be used to resolve all patterns (eg. files, exclude)
+ basePath: '../',
+
+
+ // frameworks to use
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+ frameworks: ['mocha', 'chai'],
+
+
+ // list of files / patterns to load in the browser
+ files: [
+ {
+ pattern: WOMBAT_JS_PATH,
+ watched: true,
+ included: false,
+ served: true,
+ },
+ 'karma-tests/*.spec.js',
+ ],
+
+
+ // preprocess matching files before serving them to the browser
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+ preprocessors: {},
+
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress'
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+ reporters: ['progress'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // start these browsers
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
+ browsers: ['Chrome'],
+
+
+ // Continuous Integration mode
+ // if true, Karma captures browsers, runs the tests and exits
+ singleRun: false,
+
+ // Concurrency level
+ // how many browser should be started simultanous
+ concurrency: Infinity
+ })
+}
@@ -0,0 +1,145 @@
+var WOMBAT_SRC = '../pywb/static/wombat.js';
+
+// creates a new document in an <iframe> and runs
+// a WombatJS test case in it.
+//
+// A new <iframe> is used for each test so that each
+// case is run with fresh Document and Window objects,
+// since Wombat monkey-patches many Document and Window
+// functions
+//
+function runWombatTest(testCase, done) {
+ // create an <iframe>
+ var testFrame = document.createElement('iframe');
+ testFrame.src = '/dummy.html';
+ document.body.appendChild(testFrame);
+
+ testFrame.contentWindow.addEventListener('load', function () {
+ var testDocument = testFrame.contentDocument;
+
+ function runFunctionInIFrame(func) {
+ testFrame.contentWindow.eval('(' + func.toString() + ')()');
+ }
+
+ // expose an error reporting function to the <iframe>
+ window.reportError = function(ex) {
+ done(new Error(ex));
+ };
+
+ // expose chai assertions to the <iframe>
+ window.assert = assert;
+
+ runFunctionInIFrame(function () {
+ // re-assign the iframe's console object to the parent window's
+ // console so that messages are intercepted by Karma
+ // and output to wherever it is configured to send
+ // console logs (typically stdout)
+ console = window.parent.console;
+ window.onerror = function (message, url, line, col, error) {
+ if (error) {
+ console.log(error.stack);
+ }
+ reportError(new Error(message));
+ };
+
+ // expose chai's assertion testing API to the test script
+ assert = window.parent.assert;
+ reportError = window.parent.reportError;
+ });
+
+ try {
+ runFunctionInIFrame(testCase.initScript);
+ } catch (e) {
+ throw new Error('Configuring Wombat failed: ' + e.toString());
+ }
+
+ try {
+ testFrame.contentWindow.eval(testCase.wombatScript);
+ runFunctionInIFrame(function () {
+ new window._WBWombat(wbinfo);
+ });
+ } catch (e) {
+ throw new Error('Initializing WombatJS failed: ' + e.toString());
+ }
+
+ if (testCase.html) {
+ testDocument.body.innerHTML = testCase.html;
+ }
+
+ if (testCase.testScript) {
+ try {
+ runFunctionInIFrame(testCase.testScript);
+ } catch (e) {
+ throw new Error('Test script failed: ' + e.toString());
+ }
+ }
+
+ testFrame.remove();
+ done();
+ });
+}
+
+describe('WombatJS', function () {
+ var wombatScript;
+
+ before(function (done) {
+ // load the source of the WombatJS content
+ // rewriting script
+ var req = new XMLHttpRequest();
+ req.open('GET', '/base/pywb/static/wombat.js');
+ req.onload = function () {
+ wombatScript = req.responseText;
+ done();
+ };
+ req.send();
+ });
+
+ it('should load', function (done) {
+ runWombatTest({
+ initScript: function () {
+ wbinfo = {
+ wombat_opts: {},
+ };
+ },
+ wombatScript: wombatScript,
+ }, done);
+ });
+
+ it('should rewrite document.baseURI', function (done) {
+ runWombatTest({
+ initScript: function () {
+ wbinfo = {
+ wombat_opts: {},
+ prefix: window.location.origin,
+ wombat_ts: '',
+ };
+ },
+ wombatScript: wombatScript,
+ testScript: function () {
+ var baseURI = document.baseURI;
+ if (typeof baseURI !== 'string') {
+ throw new Error('baseURI is not a string');
+ }
+ assert.equal(baseURI, 'http:///dummy.html');
+ },
+ }, done);
+ });
+
+ it('should rewrite links in dynamically injected <a> tags', function (done) {
+ runWombatTest({
+ initScript: function () {
+ wbinfo = {
+ wombat_opts: {},
+ prefix: window.location.origin,
+ wombat_ts: '',
+ };
+ },
+ wombatScript: wombatScript,
+ html: '<a href="foobar.html" id="link">A link</a>',
+ testScript: function () {
+ var link = document.getElementById('link');
+ assert.equal(link.href, 'http:///foobar.html');
+ },
+ }, done);
+ });
+});
View
@@ -0,0 +1,33 @@
+{
+ "name": "pywb",
+ "version": "1.0.0",
+ "description": "Web archival replay tools",
+ "main": "index.js",
+ "directories": {
+ "doc": "doc",
+ "test": "tests"
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ikreymer/pywb.git"
+ },
+ "author": "",
+ "license": "GPL-3.0",
+ "bugs": {
+ "url": "https://github.com/ikreymer/pywb/issues"
+ },
+ "homepage": "https://github.com/ikreymer/pywb#readme",
+ "devDependencies": {
+ "chai": "^3.4.1",
+ "karma": "^0.13.15",
+ "karma-chai": "^0.1.0",
+ "karma-chrome-launcher": "^0.2.1",
+ "karma-firefox-launcher": "^0.1.7",
+ "karma-html2js-preprocessor": "^0.1.0",
+ "karma-mocha": "^0.2.1",
+ "mocha": "^2.3.4"
+ }
+}

0 comments on commit f4a43a1

Please sign in to comment.