var DEFAULT_TIMEOUT = 20000;

// 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 = '/base/karma-tests/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 utility methods for assertion testing in tests.
        // (We used to expose chai asserts here but Karma's default
        //  error reporter replaces URLs in exception messages with
        //  the corresponding file paths, which is unhelpful for us
        //  since assert.equal() will often be called with URLs in our tests)
        window.assert = {
            equal: function (a, b) {
                if (a !== b) {
                    console.error('Mismatch between', a, 'and', b);
                    throw new Error('AssertionError');
                }
            }
        };

        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
            window.assert = window.parent.assert;
            window.reportError = window.parent.reportError;

            // helpers which check whether DOM property overrides are supported
            // in the current browser
            window.domTests = {
                areDOMPropertiesConfigurable: function () {
                    var descriptor = Object.getOwnPropertyDescriptor(Node.prototype, 'baseURI');
                    if (descriptor && !descriptor.configurable) {
                        return false;
                    } else {
                        return true;
                    }
                }
            };
        });

        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(window, wbinfo);
            });
        } catch (e) {
            console.error(e.stack);
            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 () {
    this.timeout(DEFAULT_TIMEOUT);

    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: {},
                    wombat_ts: '',
                };
            },
            wombatScript: wombatScript,
        }, done);
    });

    describe('anchor rewriting', function () {
        var config;
        beforeEach(function () {
            config = {
                initScript: function () {
                    wbinfo = {
                        wombat_opts: {},
                        prefix: window.location.origin,
                        wombat_ts: '',
                    };
                },
                wombatScript: wombatScript,
                html: '<a href="foobar.html" id="link">A link</a>',
            };
        });

        it('should rewrite links in dynamically injected <a> tags', function (done) {
            config.testScript = function () {
                if (domTests.areDOMPropertiesConfigurable()) {
                    var link = document.getElementById('link');
                    assert.equal(link.href, 'http:///base/karma-tests/foobar.html');
                }
            };

            runWombatTest(config, done);
        });

        it('toString() should return the rewritten URL', function (done) {
            config.testScript = function () {
                if (domTests.areDOMPropertiesConfigurable()) {
                    var link = document.getElementById('link');
                    assert.equal(link.href, link.toString());
                }
            };
            runWombatTest(config, done);
        });
    });

    describe('base URL overrides', function () {
        it('document.baseURI should return the original URL', 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');
                    }
                    if (domTests.areDOMPropertiesConfigurable()) {
                        assert.equal(baseURI, 'http:///base/karma-tests/dummy.html');
                    }
                },
            }, done);
        });

        it('should allow base.href to be assigned', function (done) {
            runWombatTest({
                initScript: function () {
                    wbinfo = {
                        wombat_opts: {},
                    };
                },
                wombatScript: wombatScript,
                testScript: function () {
                    'use strict';
                    var baseElement = document.createElement('base');
                    baseElement.href = 'http://foobar.com/base';
                    assert.equal(baseElement.href, 'http://foobar.com/base');
                },
            }, done);
        });
    });
});