/** * @license * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ /** * THIS FILE IS AUTOMATICALLY GENERATED! * To make changes to browser.js, please edit the source files in the repo's `browser/` directory! */ (function () { 'use strict'; window.__wctUseNpm = false; /** * @license * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt The complete set of authors may be found * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by * Google as part of the polymer project is also subject to an additional IP * rights grant found at http://polymer.github.io/PATENTS.txt */ // Make sure that we use native timers, in case they're being stubbed out. var nativeSetInterval = window.setInterval; var nativeSetTimeout = window.setTimeout; var nativeRequestAnimationFrame = window.requestAnimationFrame; /** * Runs `stepFn`, catching any error and passing it to `callback` (Node-style). * Otherwise, calls `callback` with no arguments on success. * * @param {function()} callback * @param {function()} stepFn */ function safeStep(callback, stepFn) { var err; try { stepFn(); } catch (error) { err = error; } callback(err); } /** * Runs your test at declaration time (before Mocha has begun tests). Handy for * when you need to test document initialization. * * Be aware that any errors thrown asynchronously cannot be tied to your test. * You may want to catch them and pass them to the done event, instead. See * `safeStep`. * * @param {string} name The name of the test. * @param {function(?function())} testFn The test function. If an argument is * accepted, the test will be treated as async, just like Mocha tests. */ function testImmediate(name, testFn) { if (testFn.length > 0) { return testImmediateAsync(name, testFn); } var err; try { testFn(); } catch (error) { err = error; } test(name, function (done) { done(err); }); } /** * An async-only variant of `testImmediate`. * * @param {string} name * @param {function(?function())} testFn */ function testImmediateAsync(name, testFn) { var testComplete = false; var err; test(name, function (done) { var intervalId = nativeSetInterval(function () { if (!testComplete) return; clearInterval(intervalId); done(err); }, 10); }); try { testFn(function (error) { if (error) err = error; testComplete = true; }); } catch (error) { err = error; testComplete = true; } } /** * Triggers a flush of any pending events, observations, etc and calls you back * after they have been processed. * * @param {function()} callback */ function flush(callback) { // Ideally, this function would be a call to Polymer.dom.flush, but that // doesn't support a callback yet // (https://github.com/Polymer/polymer-dev/issues/851), // ...and there's cross-browser flakiness to deal with. // Make sure that we're invoking the callback with no arguments so that the // caller can pass Mocha callbacks, etc. var done = function done() { callback(); }; // Because endOfMicrotask is flaky for IE, we perform microtask checkpoints // ourselves (https://github.com/Polymer/polymer-dev/issues/114): var isIE = navigator.appName === 'Microsoft Internet Explorer'; if (isIE && window.Platform && window.Platform.performMicrotaskCheckpoint) { var reallyDone_1 = done; done = function doneIE() { Platform.performMicrotaskCheckpoint(); nativeSetTimeout(reallyDone_1, 0); }; } // Everyone else gets a regular flush. var scope; if (window.Polymer && window.Polymer.dom && window.Polymer.dom.flush) { scope = window.Polymer.dom; } else if (window.Polymer && window.Polymer.flush) { scope = window.Polymer; } else if (window.WebComponents && window.WebComponents.flush) { scope = window.WebComponents; } if (scope) { scope.flush(); } // Ensure that we are creating a new _task_ to allow all active microtasks to // finish (the code you're testing may be using endOfMicrotask, too). nativeSetTimeout(done, 0); } /** * Advances a single animation frame. * * Calls `flush`, `requestAnimationFrame`, `flush`, and `callback` sequentially * @param {function()} callback */ function animationFrameFlush(callback) { flush(function () { nativeRequestAnimationFrame(function () { flush(callback); }); }); } /** * DEPRECATED: Use `flush`. * @param {function} callback */ function asyncPlatformFlush(callback) { console.warn('asyncPlatformFlush is deprecated in favor of the more terse flush()'); return window.flush(callback); } /** * */ function waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime) { timeoutTime = timeoutTime || Date.now() + (timeout || 1000); intervalOrMutationEl = intervalOrMutationEl || 32; try { fn(); } catch (e) { if (Date.now() > timeoutTime) { throw e; } else { if (typeof intervalOrMutationEl !== 'number') { intervalOrMutationEl.onMutation(intervalOrMutationEl, function () { waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime); }); } else { nativeSetTimeout(function () { waitFor(fn, next, intervalOrMutationEl, timeout, timeoutTime); }, intervalOrMutationEl); } return; } } next(); } window.safeStep = safeStep; window.testImmediate = testImmediate; window.testImmediateAsync = testImmediateAsync; window.flush = flush; window.animationFrameFlush = animationFrameFlush; window.asyncPlatformFlush = asyncPlatformFlush; window.waitFor = waitFor; /** * The global configuration state for WCT's browser client. */ var _config = { environmentScripts: !!window.__wctUseNpm ? [ 'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js', 'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js', 'sinon-chai/lib/sinon-chai.js', 'accessibility-developer-tools/dist/js/axs_testing.js', '@polymer/test-fixture/test-fixture.js' ] : [ 'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js', 'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js', 'sinon-chai/lib/sinon-chai.js', 'accessibility-developer-tools/dist/js/axs_testing.js' ], environmentImports: !!window.__wctUseNpm ? [] : ['test-fixture/test-fixture.html'], root: null, waitForFrameworks: true, waitFor: null, numConcurrentSuites: 1, trackConsoleError: true, mochaOptions: { timeout: 10 * 1000 }, verbose: false, }; /** * Merges initial `options` into WCT's global configuration. * * @param {Object} options The options to merge. See `browser/config.js` for a * reference. */ function setup(options) { var childRunner = ChildRunner.current(); if (childRunner) { _deepMerge(_config, childRunner.parentScope.WCT._config); // But do not force the mocha UI delete _config.mochaOptions.ui; } if (options && typeof options === 'object') { _deepMerge(_config, options); } if (!_config.root) { // Sibling dependencies. var root = scriptPrefix('browser.js'); _config.root = basePath(root.substr(0, root.length - 1)); if (!_config.root) { throw new Error('Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js'); } } } /** * Retrieves a configuration value. */ function get(key) { return _config[key]; } // Internal function _deepMerge(target, source) { Object.keys(source).forEach(function (key) { if (target[key] !== null && typeof target[key] === 'object' && !Array.isArray(target[key])) { _deepMerge(target[key], source[key]); } else { target[key] = source[key]; } }); } /** * @param {function()} callback A function to call when the active web component * frameworks have loaded. */ function whenFrameworksReady(callback) { debug('whenFrameworksReady'); var done = function () { debug('whenFrameworksReady done'); callback(); }; // If webcomponents script is in the document, wait for WebComponentsReady. if (window.WebComponents && !window.WebComponents.ready) { debug('WebComponentsReady?'); window.addEventListener('WebComponentsReady', function wcReady() { window.removeEventListener('WebComponentsReady', wcReady); debug('WebComponentsReady'); done(); }); } else { done(); } } /** * @return {string} ' tests' or ' test'. */ function pluralizedStat(count, kind) { if (count === 1) { return count + ' ' + kind + ' test'; } else { return count + ' ' + kind + ' tests'; } } /** * @param {string} path The URI of the script to load. * @param {function} done */ function loadScript(path, done) { var script = document.createElement('script'); script.src = path; if (done) { script.onload = done.bind(null, null); script.onerror = done.bind(null, 'Failed to load script ' + script.src); } document.head.appendChild(script); } /** * @param {string} path The URI of the stylesheet to load. * @param {function} done */ function loadStyle(path, done) { var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = path; if (done) { link.onload = done.bind(null, null); link.onerror = done.bind(null, 'Failed to load stylesheet ' + link.href); } document.head.appendChild(link); } /** * @param {...*} var_args Logs values to the console when the `debug` * configuration option is true. */ function debug() { var var_args = []; for (var _i = 0; _i < arguments.length; _i++) { var_args[_i] = arguments[_i]; } if (!get('verbose')) { return; } var args = [window.location.pathname].concat(var_args); (console.debug || console.log).apply(console, args); } // URL Processing /** * @param {string} url * @return {{base: string, params: string}} */ function parseUrl(url) { var parts = url.match(/^(.*?)(?:\?(.*))?$/); return { base: parts[1], params: getParams(parts[2] || ''), }; } /** * Expands a URL that may or may not be relative to `base`. * * @param {string} url * @param {string} base * @return {string} */ function expandUrl(url, base) { if (!base) return url; if (url.match(/^(\/|https?:\/\/)/)) return url; if (base.substr(base.length - 1) !== '/') { base = base + '/'; } return base + url; } /** * @param {string=} opt_query A query string to parse. * @return {!Object>} All params on the URL's query. */ function getParams(query) { query = typeof query === 'string' ? query : window.location.search; if (query.substring(0, 1) === '?') { query = query.substring(1); } // python's SimpleHTTPServer tacks a `/` on the end of query strings :( if (query.slice(-1) === '/') { query = query.substring(0, query.length - 1); } if (query === '') return {}; var result = {}; query.split('&').forEach(function (part) { var pair = part.split('='); if (pair.length !== 2) { console.warn('Invalid URL query part:', part); return; } var key = decodeURIComponent(pair[0]); var value = decodeURIComponent(pair[1]); if (!result[key]) { result[key] = []; } result[key].push(value); }); return result; } /** * Merges params from `source` into `target` (mutating `target`). * * @param {!Object>} target * @param {!Object>} source */ function mergeParams(target, source) { Object.keys(source).forEach(function (key) { if (!(key in target)) { target[key] = []; } target[key] = target[key].concat(source[key]); }); } /** * @param {string} param The param to return a value for. * @return {?string} The first value for `param`, if found. */ function getParam(param) { var params = getParams(); return params[param] ? params[param][0] : null; } /** * @param {!Object>} params * @return {string} `params` encoded as a URI query. */ function paramsToQuery(params) { var pairs = []; Object.keys(params).forEach(function (key) { params[key].forEach(function (value) { pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); }); }); return (pairs.length > 0) ? ('?' + pairs.join('&')) : ''; } function getPathName(location) { return typeof location === 'string' ? location : location.pathname; } function basePath(location) { return getPathName(location).match(/^.*\//)[0]; } function relativeLocation(location, basePath) { var path = getPathName(location); if (path.indexOf(basePath) === 0) { path = path.substring(basePath.length); } return path; } function cleanLocation(location) { var path = getPathName(location); if (path.slice(-11) === '/index.html') { path = path.slice(0, path.length - 10); } return path; } function parallel(runners, maybeLimit, done) { var limit; if (typeof maybeLimit !== 'number') { done = maybeLimit; limit = 0; } else { limit = maybeLimit; } if (!runners.length) { return done(); } var called = false; var total = runners.length; var numActive = 0; var numDone = 0; function runnerDone(error) { if (called) { return; } numDone = numDone + 1; numActive = numActive - 1; if (error || numDone >= total) { called = true; done(error); } else { runOne(); } } function runOne() { if (limit && numActive >= limit) { return; } if (!runners.length) { return; } numActive = numActive + 1; runners.shift()(runnerDone); } runners.forEach(runOne); } /** * Finds the directory that a loaded script is hosted on. * * @param {string} filename * @return {string?} */ function scriptPrefix(filename) { var scripts = document.querySelectorAll('script[src*="' + filename + '"]'); if (scripts.length !== 1) { return null; } var script = scripts[0].src; return script.substring(0, script.indexOf(filename)); } var util = Object.freeze({ whenFrameworksReady: whenFrameworksReady, pluralizedStat: pluralizedStat, loadScript: loadScript, loadStyle: loadStyle, debug: debug, parseUrl: parseUrl, expandUrl: expandUrl, getParams: getParams, mergeParams: mergeParams, getParam: getParam, paramsToQuery: paramsToQuery, basePath: basePath, relativeLocation: relativeLocation, cleanLocation: cleanLocation, parallel: parallel, scriptPrefix: scriptPrefix }); /** * A Mocha suite (or suites) run within a child iframe, but reported as if they * are part of the current context. */ var ChildRunner = /** @class */ (function () { function ChildRunner(url, parentScope) { var urlBits = parseUrl(url); mergeParams(urlBits.params, getParams(parentScope.location.search)); delete urlBits.params.cli_browser_id; this.url = urlBits.base + paramsToQuery(urlBits.params); this.parentScope = parentScope; this.state = 'initializing'; } /** * @return {ChildRunner} The `ChildRunner` that was registered for this * window. */ ChildRunner.current = function () { return ChildRunner.get(window); }; /** * @param {!Window} target A window to find the ChildRunner of. * @param {boolean} traversal Whether this is a traversal from a child window. * @return {ChildRunner} The `ChildRunner` that was registered for `target`. */ ChildRunner.get = function (target, traversal) { var childRunner = ChildRunner._byUrl[target.location.href]; if (childRunner) { return childRunner; } if (window.parent === window) { if (traversal) { console.warn('Subsuite loaded but was never registered. This most likely is due to wonky history behavior. Reloading...'); window.location.reload(); } return null; } // Otherwise, traverse. return window.parent.WCT._ChildRunner.get(target, true); }; /** * Loads and runs the subsuite. * * @param {function} done Node-style callback. */ ChildRunner.prototype.run = function (done) { debug('ChildRunner#run', this.url); this.state = 'loading'; this.onRunComplete = done; this.iframe = document.createElement('iframe'); this.iframe.src = this.url; this.iframe.classList.add('subsuite'); var container = document.getElementById('subsuites'); if (!container) { container = document.createElement('div'); container.id = 'subsuites'; document.body.appendChild(container); } container.appendChild(this.iframe); // let the iframe expand the URL for us. this.url = this.iframe.src; ChildRunner._byUrl[this.url] = this; this.timeoutId = setTimeout(this.loaded.bind(this, new Error('Timed out loading ' + this.url)), ChildRunner.loadTimeout); this.iframe.addEventListener('error', this.loaded.bind(this, new Error('Failed to load document ' + this.url))); this.iframe.contentWindow.addEventListener('DOMContentLoaded', this.loaded.bind(this, null)); }; /** * Called when the sub suite's iframe has loaded (or errored during load). * * @param {*} error The error that occured, if any. */ ChildRunner.prototype.loaded = function (error) { debug('ChildRunner#loaded', this.url, error); // Not all targets have WCT loaded (compatiblity mode) if (this.iframe.contentWindow.WCT) { this.share = this.iframe.contentWindow.WCT.share; } if (error) { this.signalRunComplete(error); this.done(); } }; /** * Called in mocha/run.js when all dependencies have loaded, and the child is * ready to start running tests * * @param {*} error The error that occured, if any. */ ChildRunner.prototype.ready = function (error) { debug('ChildRunner#ready', this.url, error); if (this.timeoutId) { clearTimeout(this.timeoutId); } if (error) { this.signalRunComplete(error); this.done(); } }; /** * Called when the sub suite's tests are complete, so that it can clean up. */ ChildRunner.prototype.done = function () { debug('ChildRunner#done', this.url, arguments); // make sure to clear that timeout this.ready(); this.signalRunComplete(); if (!this.iframe) return; // Be safe and avoid potential browser crashes when logic attempts to // interact with the removed iframe. setTimeout(function () { this.iframe.parentNode.removeChild(this.iframe); this.iframe = null; }.bind(this), 1); }; ChildRunner.prototype.signalRunComplete = function (error) { if (!this.onRunComplete) return; this.state = 'complete'; this.onRunComplete(error); this.onRunComplete = null; }; // ChildRunners get a pretty generous load timeout by default. ChildRunner.loadTimeout = 60000; // We can't maintain properties on iframe elements in Firefox/Safari/???, so // we track childRunners by URL. ChildRunner._byUrl = {}; return ChildRunner; }()); var SOCKETIO_ENDPOINT = window.location.protocol + '//' + window.location.host; var SOCKETIO_LIBRARY = SOCKETIO_ENDPOINT + '/socket.io/socket.io.js'; /** * A socket for communication between the CLI and browser runners. * * @param {string} browserId An ID generated by the CLI runner. * @param {!io.Socket} socket The socket.io `Socket` to communicate over. */ var CLISocket = /** @class */ (function () { function CLISocket(browserId, socket) { this.browserId = browserId; this.socket = socket; } /** * @param {!Mocha.Runner} runner The Mocha `Runner` to observe, reporting * interesting events back to the CLI runner. */ CLISocket.prototype.observe = function (runner) { var _this = this; this.emitEvent('browser-start', { url: window.location.toString(), }); // We only emit a subset of events that we care about, and follow a more // general event format that is hopefully applicable to test runners beyond // mocha. // // For all possible mocha events, see: // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36 runner.on('test', function (test) { _this.emitEvent('test-start', { test: getTitles(test) }); }); runner.on('test end', function (test) { _this.emitEvent('test-end', { state: getState(test), test: getTitles(test), duration: test.duration, error: test.err, }); }); runner.on('fail', function (test, err) { // fail the test run if we catch errors outside of a test function if (test.type !== 'test') { _this.emitEvent('browser-fail', 'Error thrown outside of test function: ' + err.stack); } }); runner.on('childRunner start', function (childRunner) { _this.emitEvent('sub-suite-start', childRunner.share); }); runner.on('childRunner end', function (childRunner) { _this.emitEvent('sub-suite-end', childRunner.share); }); runner.on('end', function () { _this.emitEvent('browser-end'); }); }; /** * @param {string} event The name of the event to fire. * @param {*} data Additional data to pass with the event. */ CLISocket.prototype.emitEvent = function (event, data) { this.socket.emit('client-event', { browserId: this.browserId, event: event, data: data, }); }; /** * Builds a `CLISocket` if we are within a CLI-run environment; short-circuits * otherwise. * * @param {function(*, CLISocket)} done Node-style callback. */ CLISocket.init = function (done) { var browserId = getParam('cli_browser_id'); if (!browserId) return done(); // Only fire up the socket for root runners. if (ChildRunner.current()) return done(); loadScript(SOCKETIO_LIBRARY, function (error) { if (error) return done(error); var socket = io(SOCKETIO_ENDPOINT); socket.on('error', function (error) { socket.off(); done(error); }); socket.on('connect', function () { socket.off(); done(null, new CLISocket(browserId, socket)); }); }); }; return CLISocket; }()); // Misc Utility /** * @param {!Mocha.Runnable} runnable The test or suite to extract titles from. * @return {!Array.} The titles of the runnable and its parents. */ function getTitles(runnable) { var titles = []; while (runnable && !runnable.root && runnable.title) { titles.unshift(runnable.title); runnable = runnable.parent; } return titles; } /** * @param {!Mocha.Runnable} runnable * @return {string} */ function getState(runnable) { if (runnable.state === 'passed') { return 'passing'; } else if (runnable.state === 'failed') { return 'failing'; } else if (runnable.pending) { return 'pending'; } else { return 'unknown'; } } // We capture console events when running tests; so make sure we have a // reference to the original one. var console$1 = window.console; var FONT = ';font: normal 13px "Roboto", "Helvetica Neue", "Helvetica", sans-serif;'; var STYLES = { plain: FONT, suite: 'color: #5c6bc0' + FONT, test: FONT, passing: 'color: #259b24' + FONT, pending: 'color: #e65100' + FONT, failing: 'color: #c41411' + FONT, stack: 'color: #c41411', results: FONT + 'font-size: 16px', }; // I don't think we can feature detect this one... var userAgent = navigator.userAgent.toLowerCase(); var CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit'); var CAN_STYLE_GROUP = userAgent.match('webkit'); // Track the indent for faked `console.group` var logIndent = ''; function log(text, style) { text = text.split('\n') .map(function (l) { return logIndent + l; }) .join('\n'); if (CAN_STYLE_LOG) { console$1.log('%c' + text, STYLES[style] || STYLES.plain); } else { console$1.log(text); } } function logGroup(text, style) { if (CAN_STYLE_GROUP) { console$1.group('%c' + text, STYLES[style] || STYLES.plain); } else if (console$1.group) { console$1.group(text); } else { logIndent = logIndent + ' '; log(text, style); } } function logGroupEnd() { if (console$1.groupEnd) { console$1.groupEnd(); } else { logIndent = logIndent.substr(0, logIndent.length - 2); } } function logException(error) { log(error.stack || error.message || (error + ''), 'stack'); } /** * A Mocha reporter that logs results out to the web `console`. */ var Console = /** @class */ (function () { /** * @param runner The runner that is being reported on. */ function Console(runner) { Mocha.reporters.Base.call(this, runner); runner.on('suite', function (suite) { if (suite.root) { return; } logGroup(suite.title, 'suite'); }.bind(this)); runner.on('suite end', function (suite) { if (suite.root) { return; } logGroupEnd(); }.bind(this)); runner.on('test', function (test) { logGroup(test.title, 'test'); }.bind(this)); runner.on('pending', function (test) { logGroup(test.title, 'pending'); }.bind(this)); runner.on('fail', function (_test, error) { logException(error); }.bind(this)); runner.on('test end', function (_test) { logGroupEnd(); }.bind(this)); runner.on('end', this.logSummary.bind(this)); } /** Prints out a final summary of test results. */ Console.prototype.logSummary = function () { logGroup('Test Results', 'results'); if (this.stats.failures > 0) { log(pluralizedStat(this.stats.failures, 'failing'), 'failing'); } if (this.stats.pending > 0) { log(pluralizedStat(this.stats.pending, 'pending'), 'pending'); } log(pluralizedStat(this.stats.passes, 'passing')); if (!this.stats.failures) { log('test suite passed', 'passing'); } log('Evaluated ' + this.stats.tests + ' tests in ' + this.stats.duration + 'ms.'); logGroupEnd(); }; return Console; }()); /** * @license * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt The complete set of authors may be found * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by * Google as part of the polymer project is also subject to an additional IP * rights grant found at http://polymer.github.io/PATENTS.txt */ /** * WCT-specific behavior on top of Mocha's default HTML reporter. * * @param {!Mocha.Runner} runner The runner that is being reported on. */ function HTML(runner) { var output = document.createElement('div'); output.id = 'mocha'; document.body.appendChild(output); runner.on('suite', function (_test) { this.total = runner.total; }.bind(this)); Mocha.reporters.HTML.call(this, runner); } // Woo! What a hack. This just saves us from adding a bunch of complexity around // style loading. var style = document.createElement('style'); style.textContent = "\n html, body {\n position: relative;\n height: 100%;\n width: 100%;\n min-width: 900px;\n }\n #mocha, #subsuites {\n height: 100%;\n position: absolute;\n top: 0;\n }\n #mocha {\n box-sizing: border-box;\n margin: 0 !important;\n padding: 60px 20px;\n right: 0;\n left: 500px;\n }\n #subsuites {\n -ms-flex-direction: column;\n -webkit-flex-direction: column;\n display: -ms-flexbox;\n display: -webkit-flex;\n display: flex;\n flex-direction: column;\n left: 0;\n width: 500px;\n }\n #subsuites .subsuite {\n border: 0;\n width: 100%;\n height: 100%;\n }\n #mocha .test.pass .duration {\n color: #555 !important;\n }\n"; document.head.appendChild(style); var STACKY_CONFIG = { indent: ' ', locationStrip: [ /^https?:\/\/[^\/]+/, /\?.*$/, ], filter: function (line) { return !!line.location.match(/\/web-component-tester\/[^\/]+(\?.*)?$/); }, }; // https://github.com/visionmedia/mocha/blob/master/lib/runner.js#L36-46 var MOCHA_EVENTS = [ 'start', 'end', 'suite', 'suite end', 'test', 'test end', 'hook', 'hook end', 'pass', 'fail', 'pending', 'childRunner end' ]; // Until a suite has loaded, we assume this many tests in it. var ESTIMATED_TESTS_PER_SUITE = 3; /** * A Mocha-like reporter that combines the output of multiple Mocha suites. */ var MultiReporter = /** @class */ (function () { /** * @param numSuites The number of suites that will be run, in order to * estimate the total number of tests that will be performed. * @param reporters The set of reporters that * should receive the unified event stream. * @param parent The parent reporter, if present. */ function MultiReporter(numSuites, reporters, parent) { var _this = this; this.reporters = reporters.map(function (reporter) { return new reporter(_this); }); this.parent = parent; this.basePath = parent && parent.basePath || basePath(window.location); this.total = numSuites * ESTIMATED_TESTS_PER_SUITE; // Mocha reporters assume a stream of events, so we have to be careful to // only report on one runner at a time... this.currentRunner = null; // ...while we buffer events for any other active runners. this.pendingEvents = []; this.emit('start'); } /** * @param location The location this reporter represents. * @return A reporter-like "class" for each child suite * that should be passed to `mocha.run`. */ MultiReporter.prototype.childReporter = function (location) { var name = this.suiteTitle(location); // The reporter is used as a constructor, so we can't depend on `this` being // properly bound. var self = this; return _a = /** @class */ (function () { function ChildReporter(runner) { runner.name = window.name; self.bindChildRunner(runner); } return ChildReporter; }()), _a.title = window.name, _a; var _a; }; /** Must be called once all runners have finished. */ MultiReporter.prototype.done = function () { this.complete = true; this.flushPendingEvents(); this.emit('end'); }; /** * Emit a top level test that is not part of any suite managed by this * reporter. * * Helpful for reporting on global errors, loading issues, etc. * * @param title The title of the test. * @param error An error associated with this test. If falsy, test is * considered to be passing. * @param suiteTitle Title for the suite that's wrapping the test. * @param estimated If this test was included in the original * estimate of `numSuites`. */ MultiReporter.prototype.emitOutOfBandTest = function (title, error, suiteTitle, estimated) { debug('MultiReporter#emitOutOfBandTest(', arguments, ')'); var root = new Mocha.Suite(suiteTitle || ''); var test = new Mocha.Test(title, function () { }); test.parent = root; test.state = error ? 'failed' : 'passed'; test.err = error; if (!estimated) { this.total = this.total + ESTIMATED_TESTS_PER_SUITE; } var runner = { total: 1 }; this.proxyEvent('start', runner); this.proxyEvent('suite', runner, root); this.proxyEvent('test', runner, test); if (error) { this.proxyEvent('fail', runner, test, error); } else { this.proxyEvent('pass', runner, test); } this.proxyEvent('test end', runner, test); this.proxyEvent('suite end', runner, root); this.proxyEvent('end', runner); }; /** * @param {!Location|string} location * @return {string} */ MultiReporter.prototype.suiteTitle = function (location) { var path = relativeLocation(location, this.basePath); path = cleanLocation(path); return path; }; // Internal Interface /** @param {!Mocha.runners.Base} runner The runner to listen to events for. */ MultiReporter.prototype.bindChildRunner = function (runner) { var _this = this; MOCHA_EVENTS.forEach(function (eventName) { runner.on(eventName, _this.proxyEvent.bind(_this, eventName, runner)); }); }; /** * Evaluates an event fired by `runner`, proxying it forward or buffering it. * * @param {string} eventName * @param {!Mocha.runners.Base} runner The runner that emitted this event. * @param {...*} var_args Any additional data passed as part of the event. */ MultiReporter.prototype.proxyEvent = function (eventName, runner) { var _args = []; for (var _i = 2; _i < arguments.length; _i++) { _args[_i - 2] = arguments[_i]; } var extraArgs = Array.prototype.slice.call(arguments, 2); if (this.complete) { console.warn('out of order Mocha event for ' + runner.name + ':', eventName, extraArgs); return; } if (this.currentRunner && runner !== this.currentRunner) { this.pendingEvents.push(Array.prototype.slice.call(arguments)); return; } debug('MultiReporter#proxyEvent(', arguments, ')'); // This appears to be a Mocha bug: Tests failed by passing an error to their // done function don't set `err` properly. // // TODO(nevir): Track down. if (eventName === 'fail' && !extraArgs[0].err) { extraArgs[0].err = extraArgs[1]; } if (eventName === 'start') { this.onRunnerStart(runner); } else if (eventName === 'end') { this.onRunnerEnd(runner); } else { this.cleanEvent(eventName, runner, extraArgs); this.emit.apply(this, [eventName].concat(extraArgs)); } }; /** * Cleans or modifies an event if needed. * * @param eventName * @param runner The runner that emitted this event. * @param extraArgs */ MultiReporter.prototype.cleanEvent = function (eventName, _runner, extraArgs) { // Suite hierarchy if (extraArgs[0]) { extraArgs[0] = this.showRootSuite(extraArgs[0]); } // Normalize errors if (eventName === 'fail') { extraArgs[1] = Stacky.normalize(extraArgs[1], STACKY_CONFIG); } if (extraArgs[0] && extraArgs[0].err) { extraArgs[0].err = Stacky.normalize(extraArgs[0].err, STACKY_CONFIG); } }; /** * We like to show the root suite's title, which requires a little bit of * trickery in the suite hierarchy. * * @param {!Mocha.Runnable} node */ MultiReporter.prototype.showRootSuite = function (node) { var leaf = node = Object.create(node); while (node && node.parent) { var wrappedParent = Object.create(node.parent); node.parent = wrappedParent; node = wrappedParent; } node.root = false; return leaf; }; /** @param {!Mocha.runners.Base} runner */ MultiReporter.prototype.onRunnerStart = function (runner) { debug('MultiReporter#onRunnerStart:', runner.name); this.total = this.total - ESTIMATED_TESTS_PER_SUITE + runner.total; this.currentRunner = runner; }; /** @param {!Mocha.runners.Base} runner */ MultiReporter.prototype.onRunnerEnd = function (runner) { debug('MultiReporter#onRunnerEnd:', runner.name); this.currentRunner = null; this.flushPendingEvents(); }; /** * Flushes any buffered events and runs them through `proxyEvent`. This will * loop until all buffered runners are complete, or we have run out of * buffered events. */ MultiReporter.prototype.flushPendingEvents = function () { var _this = this; var events = this.pendingEvents; this.pendingEvents = []; events.forEach(function (eventArgs) { _this.proxyEvent.apply(_this, eventArgs); }); }; return MultiReporter; }()); var ARC_OFFSET = 0; // start at the right. var ARC_WIDTH = 6; /** * A Mocha reporter that updates the document's title and favicon with * at-a-glance stats. * * @param {!Mocha.Runner} runner The runner that is being reported on. */ var Title = /** @class */ (function () { function Title(runner) { Mocha.reporters.Base.call(this, runner); runner.on('test end', this.report.bind(this)); } /** Reports current stats via the page title and favicon. */ Title.prototype.report = function () { this.updateTitle(); this.updateFavicon(); }; /** Updates the document title with a summary of current stats. */ Title.prototype.updateTitle = function () { if (this.stats.failures > 0) { document.title = pluralizedStat(this.stats.failures, 'failing'); } else { document.title = pluralizedStat(this.stats.passes, 'passing'); } }; /** Updates the document's favicon w/ a summary of current stats. */ Title.prototype.updateFavicon = function () { var canvas = document.createElement('canvas'); canvas.height = canvas.width = 32; var context = canvas.getContext('2d'); var passing = this.stats.passes; var pending = this.stats.pending; var failing = this.stats.failures; var total = Math.max(this.runner.total, passing + pending + failing); drawFaviconArc(context, total, 0, passing, '#0e9c57'); drawFaviconArc(context, total, passing, pending, '#f3b300'); drawFaviconArc(context, total, pending + passing, failing, '#ff5621'); this.setFavicon(canvas.toDataURL()); }; /** Sets the current favicon by URL. */ Title.prototype.setFavicon = function (url) { var current = document.head.querySelector('link[rel="icon"]'); if (current) { document.head.removeChild(current); } var link = document.createElement('link'); link.rel = 'icon'; link.type = 'image/x-icon'; link.href = url; link.setAttribute('sizes', '32x32'); document.head.appendChild(link); }; return Title; }()); /** * Draws an arc for the favicon status, relative to the total number of tests. */ function drawFaviconArc(context, total, start, length, color) { var arcStart = ARC_OFFSET + Math.PI * 2 * (start / total); var arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total); context.beginPath(); context.strokeStyle = color; context.lineWidth = ARC_WIDTH; context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd); context.stroke(); } var htmlSuites$1 = []; var jsSuites$1 = []; // We process grep ourselves to avoid loading suites that will be filtered. var GREP = getParam('grep'); // work around mocha bug (https://github.com/mochajs/mocha/issues/2070) if (GREP) { GREP = GREP.replace(/\\\./g, '.'); } /** * Loads suites of tests, supporting both `.js` and `.html` files. * * @param files The files to load. */ function loadSuites(files) { files.forEach(function (file) { if (/\.js(\?.*)?$/.test(file)) { jsSuites$1.push(file); } else if (/\.html(\?.*)?$/.test(file)) { htmlSuites$1.push(file); } else { throw new Error('Unknown resource type: ' + file); } }); } /** * @return The child suites that should be loaded, ignoring * those that would not match `GREP`. */ function activeChildSuites() { var subsuites = htmlSuites$1; if (GREP) { var cleanSubsuites = []; for (var i = 0, subsuite = void 0; subsuite = subsuites[i]; i++) { if (GREP.indexOf(cleanLocation(subsuite)) !== -1) { cleanSubsuites.push(subsuite); } } subsuites = cleanSubsuites; } return subsuites; } /** * Loads all `.js` sources requested by the current suite. */ function loadJsSuites(_reporter, done) { debug('loadJsSuites', jsSuites$1); var loaders = jsSuites$1.map(function (file) { // We only support `.js` dependencies for now. return loadScript.bind(util, file); }); parallel(loaders, done); } function runSuites(reporter, childSuites, done) { debug('runSuites'); var suiteRunners = [ // Run the local tests (if any) first, not stopping on error; _runMocha.bind(null, reporter), ]; // As well as any sub suites. Again, don't stop on error. childSuites.forEach(function (file) { suiteRunners.push(function (next) { var childRunner = new ChildRunner(file, window); reporter.emit('childRunner start', childRunner); childRunner.run(function (error) { reporter.emit('childRunner end', childRunner); if (error) reporter.emitOutOfBandTest(file, error); next(); }); }); }); parallel(suiteRunners, get('numConcurrentSuites'), function (error) { reporter.done(); done(error); }); } /** * Kicks off a mocha run, waiting for frameworks to load if necessary. * * @param {!MultiReporter} reporter Where to send Mocha's events. * @param {function} done A callback fired, _no error is passed_. */ function _runMocha(reporter, done, waited) { if (get('waitForFrameworks') && !waited) { var waitFor = (get('waitFor') || whenFrameworksReady).bind(window); waitFor(function () { _fixCustomElements(); _runMocha(reporter, done, true); }); return; } debug('_runMocha'); var mocha = window.mocha; var Mocha = window.Mocha; mocha.reporter(reporter.childReporter(window.location)); mocha.suite.title = reporter.suiteTitle(window.location); mocha.grep(GREP); // We can't use `mocha.run` because it bashes over grep, invert, and friends. // See https://github.com/visionmedia/mocha/blob/master/support/tail.js#L137 var runner = Mocha.prototype.run.call(mocha, function (_error) { if (document.getElementById('mocha')) { Mocha.utils.highlightTags('code'); } done(); // We ignore the Mocha failure count. }); // Mocha's default `onerror` handling strips the stack (to support really old // browsers). We upgrade this to get better stacks for async errors. // // TODO(nevir): Can we expand support to other browsers? if (navigator.userAgent.match(/chrome/i)) { window.onerror = null; window.addEventListener('error', function (event) { if (!event.error) return; if (event.error.ignore) return; runner.uncaught(event.error); }); } } /** * In Chrome57 custom elements in the document might not get upgraded when * there is a high GC * https://bugs.chromium.org/p/chromium/issues/detail?id=701601 We clone and * replace the ones that weren't upgraded. */ function _fixCustomElements() { // Bail out if it is not Chrome 57. var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); var isM57 = raw && raw[2] === '57'; if (!isM57) return; var elements = document.body.querySelectorAll('*:not(script):not(style)'); var constructors = {}; for (var i = 0; i < elements.length; i++) { var el = elements[i]; // This child has already been cloned and replaced by its parent, skip it! if (!el.isConnected) continue; var tag = el.localName; // Not a custom element! if (tag.indexOf('-') === -1) continue; // Memoize correct constructors. constructors[tag] = constructors[tag] || document.createElement(tag).constructor; // This one was correctly upgraded. if (el instanceof constructors[tag]) continue; debug('_fixCustomElements: found non-upgraded custom element ' + el); var clone = document.importNode(el, true); el.parentNode.replaceChild(clone, el); } } /** * @param {CLISocket} socket The CLI socket, if present. * @param {MultiReporter} parent The parent reporter, if present. * @return {!Array. 0 || jsSuites$1.length > 0) { reporters.push(HTML); } return reporters; } /** * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy. */ function injectMocha(Mocha) { _injectPrototype(Console, Mocha.reporters.Base.prototype); _injectPrototype(HTML, Mocha.reporters.HTML.prototype); // Mocha doesn't expose its `EventEmitter` shim directly, so: _injectPrototype(MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype)); } function _injectPrototype(klass, prototype) { var newPrototype = Object.create(prototype); // Only support Object.keys(klass.prototype).forEach(function (key) { newPrototype[key] = klass.prototype[key]; }); klass.prototype = newPrototype; } /** * Loads all environment scripts ...synchronously ...after us. */ function loadSync() { debug('Loading environment scripts:'); var a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js'; var scripts = get('environmentScripts'); var a11ySuiteWillBeLoaded = window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1; // We can't inject a11ySuite when running the npm version because it is a // module-based script that needs `'); // jshint ignore:line }); debug('Environment scripts loaded'); var imports = get('environmentImports'); imports.forEach(function (path) { var url = expandUrl(path, get('root')); debug('Loading environment import:', url); // Synchronous load. document.write(''); // jshint ignore:line }); debug('Environment imports loaded'); } /** * We have some hard dependencies on things that should be loaded via * `environmentScripts`, so we assert that they're present here; and do any * post-facto setup. */ function ensureDependenciesPresent() { _ensureMocha(); _checkChai(); } function _ensureMocha() { var Mocha = window.Mocha; if (!Mocha) { throw new Error('WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js'); } injectMocha(Mocha); // Magic loading of mocha's stylesheet var mochaPrefix = scriptPrefix('mocha.js'); // only load mocha stylesheet for the test runner output // Not the end of the world, if it doesn't load. if (mochaPrefix && window.top === window.self) { loadStyle(mochaPrefix + 'mocha.css'); } } function _checkChai() { if (!window.chai) { debug('Chai not present; not registering shorthands'); return; } window.assert = window.chai.assert; window.expect = window.chai.expect; } // We may encounter errors during initialization (for example, syntax errors in // a test file). Hang onto those (and more) until we are ready to report them. var globalErrors = []; /** * Hook the environment to pick up on global errors. */ function listenForErrors() { window.addEventListener('error', function (event) { globalErrors.push(event.error); }); // Also, we treat `console.error` as a test failure. Unless you prefer not. var origConsole = console; var origError = console.error; console.error = function wctShimmedError() { origError.apply(origConsole, arguments); if (get('trackConsoleError')) { throw 'console.error: ' + Array.prototype.join.call(arguments, ' '); } }; } var interfaceExtensions = []; /** * Registers an extension that extends the global `Mocha` implementation * with new helper methods. These helper methods will be added to the `window` * when tests run for both BDD and TDD interfaces. */ function extendInterfaces(helperName, helperFactory) { interfaceExtensions.push(function () { var Mocha = window.Mocha; // For all Mocha interfaces (probably just TDD and BDD): Object.keys(Mocha.interfaces) .forEach(function (interfaceName) { // This is the original callback that defines the interface (TDD or // BDD): var originalInterface = Mocha.interfaces[interfaceName]; // This is the name of the "teardown" or "afterEach" property for the // current interface: var teardownProperty = interfaceName === 'tdd' ? 'teardown' : 'afterEach'; // The original callback is monkey patched with a new one that appends // to the global context however we want it to: Mocha.interfaces[interfaceName] = function (suite) { // Call back to the original callback so that we get the base // interface: originalInterface.apply(this, arguments); // Register a listener so that we can further extend the base // interface: suite.on('pre-require', function (context, _file, _mocha) { // Capture a bound reference to the teardown function as a // convenience: var teardown = context[teardownProperty].bind(context); // Add our new helper to the testing context. The helper is // generated by a factory method that receives the context, // the teardown function and the interface name and returns // the new method to be added to that context: context[helperName] = helperFactory(context, teardown, interfaceName); }); }; }); }); } /** * Applies any registered interface extensions. The extensions will be applied * as many times as this function is called, so don't call it more than once. */ function applyExtensions() { interfaceExtensions.forEach(function (applyExtension) { applyExtension(); }); } extendInterfaces('fixture', function (context, teardown) { // Return context.fixture if it is already a thing, for backwards // compatibility with `test-fixture-mocha.js`: return context.fixture || function fixture(fixtureId, model) { // Automatically register a teardown callback that will restore the // test-fixture: teardown(function () { document.getElementById(fixtureId).restore(); }); // Find the test-fixture with the provided ID and create it, returning // the results: return document.getElementById(fixtureId).create(model); }; }); /** * stub * * The stub addon allows the tester to partially replace the implementation of * an element with some custom implementation. Usage example: * * beforeEach(function() { * stub('x-foo', { * attached: function() { * // Custom implementation of the `attached` method of element `x-foo`.. * }, * otherMethod: function() { * // More custom implementation.. * }, * getterSetterProperty: { * get: function() { * // Custom getter implementation.. * }, * set: function() { * // Custom setter implementation.. * } * }, * // etc.. * }); * }); */ extendInterfaces('stub', function (_context, teardown) { return function stub(tagName, implementation) { // Find the prototype of the element being stubbed: var proto = document.createElement(tagName).constructor.prototype; // For all keys in the implementation to stub with.. var stubs = Object.keys(implementation).map(function (key) { // Stub the method on the element prototype with Sinon: return sinon.stub(proto, key, implementation[key]); }); // After all tests.. teardown(function () { stubs.forEach(function (stub) { stub.restore(); }); }); }; }); // replacement map stores what should be var replacements = {}; var replaceTeardownAttached = false; /** * replace * * The replace addon allows the tester to replace all usages of one element with * another element within all Polymer elements created within the time span of * the test. Usage example: * * beforeEach(function() { * replace('x-foo').with('x-fake-foo'); * }); * * All annotations and attributes will be set on the placement element the way * they were set for the original element. */ extendInterfaces('replace', function (_context, teardown) { return function replace(oldTagName) { return { with: function (tagName) { // Standardizes our replacements map oldTagName = oldTagName.toLowerCase(); tagName = tagName.toLowerCase(); replacements[oldTagName] = tagName; // If the function is already a stub, restore it to original if (document.importNode.isSinonProxy) { return; } if (!window.Polymer.Element) { window.Polymer.Element = function () { }; window.Polymer.Element.prototype._stampTemplate = function () { }; } // Keep a reference to the original `document.importNode` // implementation for later: var originalImportNode = document.importNode; // Use Sinon to stub `document.ImportNode`: sinon.stub(document, 'importNode', function (origContent, deep) { var templateClone = document.createElement('template'); var content = templateClone.content; var inertDoc = content.ownerDocument; // imports node from inertDoc which holds inert nodes. templateClone.content.appendChild(inertDoc.importNode(origContent, true)); // optional arguments are not optional on IE. var nodeIterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, true); var node; // Traverses the tree. A recently-replaced node will be put next, // so if a node is replaced, it will be checked if it needs to be // replaced again. while (node = nodeIterator.nextNode()) { var currentTagName = node.tagName.toLowerCase(); if (replacements.hasOwnProperty(currentTagName)) { currentTagName = replacements[currentTagName]; // find the final tag name. while (replacements[currentTagName]) { currentTagName = replacements[currentTagName]; } // Create a replacement: var replacement = document.createElement(currentTagName); // For all attributes in the original node.. for (var index = 0; index < node.attributes.length; ++index) { // Set that attribute on the replacement: replacement.setAttribute(node.attributes[index].name, node.attributes[index].value); } // Replace the original node with the replacement node: node.parentNode.replaceChild(replacement, node); } } return originalImportNode.call(this, content, deep); }); if (!replaceTeardownAttached) { // After each test... teardown(function () { replaceTeardownAttached = true; // Restore the stubbed version of `document.importNode`: var documentImportNode = document.importNode; if (documentImportNode.isSinonProxy) { documentImportNode.restore(); } // Empty the replacement map replacements = {}; }); } } }; }; }); // Mocha global helpers, broken out by testing method. // // Keys are the method for a particular interface; values are their analog in // the opposite interface. var MOCHA_EXPORTS = { // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js tdd: { 'setup': '"before"', 'teardown': '"after"', 'suiteSetup': '"beforeEach"', 'suiteTeardown': '"afterEach"', 'suite': '"describe" or "context"', 'test': '"it" or "specify"', }, // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js bdd: { 'before': '"setup"', 'after': '"teardown"', 'beforeEach': '"suiteSetup"', 'afterEach': '"suiteTeardown"', 'describe': '"suite"', 'context': '"suite"', 'xdescribe': '"suite.skip"', 'xcontext': '"suite.skip"', 'it': '"test"', 'xit': '"test.skip"', 'specify': '"test"', 'xspecify': '"test.skip"', }, }; /** * Exposes all Mocha methods up front, configuring and running mocha * automatically when you call them. * * The assumption is that it is a one-off (sub-)suite of tests being run. */ function stubInterfaces() { var keys = Object.keys(MOCHA_EXPORTS); keys.forEach(function (ui) { Object.keys(MOCHA_EXPORTS[ui]).forEach(function (key) { window[key] = function wrappedMochaFunction() { _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]); if (!window[key] || window[key] === wrappedMochaFunction) { throw new Error('Expected mocha.setup to define ' + key); } window[key].apply(window, arguments); }; }); }); } // Whether we've called `mocha.setup` var _mochaIsSetup = false; /** * @param {string} ui Sets up mocha to run `ui`-style tests. * @param {string} key The method called that triggered this. * @param {string} alternate The matching method in the opposite interface. */ function _setupMocha(ui, key, alternate) { var mochaOptions = get('mochaOptions'); if (mochaOptions.ui && mochaOptions.ui !== ui) { var message = 'Mixing ' + mochaOptions.ui + ' and ' + ui + ' Mocha styles is not supported. ' + 'You called "' + key + '". Did you mean ' + alternate + '?'; throw new Error(message); } if (_mochaIsSetup) { return; } applyExtensions(); mochaOptions.ui = ui; mocha.setup(mochaOptions); // Note that the reporter is configured in run.js. } // You can configure WCT before it has loaded by assigning your custom // configuration to the global `WCT`. setup(window.WCT); // Maybe some day we'll expose WCT as a module to whatever module registry you // are using (aka the UMD approach), or as an es6 module. var WCT = window.WCT = { // A generic place to hang data about the current suite. This object is // reported // back via the `sub-suite-start` and `sub-suite-end` events. share: {}, // Until then, we get to rely on it to expose parent runners to their // children. _ChildRunner: ChildRunner, _reporter: undefined, _config: _config, // Public API /** * Loads suites of tests, supporting both `.js` and `.html` files. * * @param {!Array.} files The files to load. */ loadSuites: loadSuites, }; // Load Process listenForErrors(); stubInterfaces(); loadSync(); // Give any scripts on the page a chance to declare tests and muck with things. document.addEventListener('DOMContentLoaded', function () { debug('DOMContentLoaded'); ensureDependenciesPresent(); // We need the socket built prior to building its reporter. CLISocket.init(function (error, socket) { if (error) throw error; // Are we a child of another run? var current = ChildRunner.current(); var parent = current && current.parentScope.WCT._reporter; debug('parentReporter:', parent); var childSuites = activeChildSuites(); var reportersToUse = determineReporters(socket, parent); // +1 for any local tests. var reporter = new MultiReporter(childSuites.length + 1, reportersToUse, parent); WCT._reporter = reporter; // For environment/compatibility.js // We need the reporter so that we can report errors during load. loadJsSuites(reporter, function (error) { // Let our parent know that we're about to start the tests. if (current) current.ready(error); if (error) throw error; // Emit any errors we've encountered up til now globalErrors.forEach(function onError(error) { reporter.emitOutOfBandTest('Test Suite Initialization', error); }); runSuites(reporter, childSuites, function (error) { // Make sure to let our parent know that we're done. if (current) current.done(); if (error) throw error; }); }); }); }); }()); //# sourceMappingURL=browser.js.map