├── test ├── fixtures │ ├── paths │ │ ├── bar │ │ │ ├── a.js │ │ │ ├── index.js │ │ │ └── index.html │ │ ├── baz │ │ │ ├── b.js │ │ │ ├── a.html │ │ │ ├── b │ │ │ │ ├── one.js │ │ │ │ ├── index.html │ │ │ │ └── deep │ │ │ │ │ ├── index.html │ │ │ │ │ ├── stuff.html │ │ │ │ │ └── stuff.js │ │ │ └── a │ │ │ │ └── fizz.html │ │ ├── foo.html │ │ ├── foo.js │ │ └── foo │ │ │ ├── one.js │ │ │ ├── three.css │ │ │ └── two.html │ ├── cli │ │ ├── conf │ │ │ ├── test │ │ │ │ └── foo.js │ │ │ ├── branch │ │ │ │ └── leaf │ │ │ │ │ └── thing.js │ │ │ ├── wct.conf.js │ │ │ ├── json │ │ │ │ ├── wct.conf.json │ │ │ │ └── wct.conf.js │ │ │ └── rooted │ │ │ │ └── wct.conf.js │ │ └── standard │ │ │ ├── test │ │ │ ├── a.html │ │ │ └── b.js │ │ │ └── x-foo.html │ ├── integration │ │ ├── nested │ │ │ ├── test │ │ │ │ ├── leaf.js │ │ │ │ ├── leaf.html │ │ │ │ ├── one │ │ │ │ │ ├── index.html │ │ │ │ │ └── tests.html │ │ │ │ ├── two │ │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── components_dir │ │ │ ├── bower_components │ │ │ │ └── foo-element │ │ │ │ │ └── foo-element.js │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ └── index.html │ │ ├── multiple-component_dirs │ │ │ ├── bower_components-bar │ │ │ │ └── package │ │ │ │ │ └── index.js │ │ │ ├── bower_components-foo │ │ │ │ └── package │ │ │ │ │ └── index.js │ │ │ ├── bower_components │ │ │ │ └── package │ │ │ │ │ └── index.js │ │ │ ├── test │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── missing │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ └── missing.html │ │ ├── no-tests │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ └── index.html │ │ ├── failing │ │ │ ├── test │ │ │ │ ├── tests.js │ │ │ │ ├── tests.html │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── hybrid │ │ │ ├── test │ │ │ │ ├── tests.js │ │ │ │ ├── tests.html │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── one-js │ │ │ ├── test │ │ │ │ ├── tests.js │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── many-js │ │ │ ├── test │ │ │ │ ├── one.js │ │ │ │ ├── three.js │ │ │ │ ├── two.js │ │ │ │ └── index.html │ │ │ └── golden.json │ │ ├── compilation │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ └── index.html │ │ ├── inline-js │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ └── index.html │ │ ├── one-html │ │ │ ├── golden.json │ │ │ └── test │ │ │ │ ├── index.html │ │ │ │ └── tests.html │ │ ├── define-webserver-hook │ │ │ ├── test │ │ │ │ ├── index.html │ │ │ │ └── tests.html │ │ │ └── golden.json │ │ ├── many-html │ │ │ ├── test │ │ │ │ ├── index.html │ │ │ │ ├── one.html │ │ │ │ ├── two.html │ │ │ │ └── three.html │ │ │ └── golden.json │ │ ├── multiple-replace │ │ │ ├── projection-element-2.html │ │ │ ├── projection-element-3.html │ │ │ ├── projection-element-4.html │ │ │ ├── exception-element.html │ │ │ ├── test │ │ │ │ ├── index.html │ │ │ │ └── tests.html │ │ │ ├── normal-element.html │ │ │ ├── exception-fixture.html │ │ │ ├── projection-element.html │ │ │ ├── dom-if-element.html │ │ │ ├── golden.json │ │ │ └── dom-repeat-fixture.html │ │ └── query-string │ │ │ ├── test │ │ │ ├── tests.html │ │ │ ├── index.html │ │ │ └── tests.js │ │ │ └── golden.json │ └── early-failure │ │ ├── bower_components │ │ └── web-component-tester │ │ │ └── package.json │ │ └── test │ │ └── index.html ├── unit │ ├── config.ts │ ├── gulp.ts │ ├── paths.ts │ ├── grunt.ts │ └── context.ts └── integration │ └── setup_test_dir.ts ├── a11ySuiteExample.png ├── .npmignore ├── .clang-format ├── data ├── a11ySuite-npm-header.txt ├── index.html └── a11ySuite.js ├── custom_typings ├── findup-sync.d.ts ├── server-destroy.d.ts ├── promisify-node.d.ts ├── send.d.ts ├── stacky.d.ts └── wd.d.ts ├── .github └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── browser ├── more-declarations.ts ├── tsconfig.json ├── mocha │ ├── fixture.ts │ ├── stub.ts │ ├── extend.ts │ └── replace.ts ├── environment │ ├── errors.ts │ ├── compatability.ts │ └── helpers.ts ├── declarations.ts ├── reporters │ ├── html.ts │ ├── title.ts │ └── console.ts ├── reporters.ts ├── environment.ts ├── mocha.ts ├── index.ts ├── config.ts ├── clisocket.ts ├── childrunner.ts └── suites.ts ├── .travis.yml ├── browser-js-header.txt ├── .vscode └── settings.json ├── runner.js ├── tsconfig.json ├── tasks └── test.js ├── bin ├── wct └── wct-st ├── bower.json ├── wct-browser-legacy ├── package.json └── a11ySuite.js ├── LICENSE ├── runner ├── gulp.ts ├── port-scanner.ts ├── test.ts ├── paths.ts ├── httpbin.ts ├── plugin.ts ├── cli.ts ├── steps.ts └── context.ts ├── tslint.json ├── package.json └── gulpfile.js /test/fixtures/paths/bar/a.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/foo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/foo.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/bar/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/a.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b/one.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/foo/one.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/foo/three.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/foo/two.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/cli/conf/test/foo.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/cli/standard/test/a.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/cli/standard/test/b.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/cli/standard/x-foo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/bar/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/a/fizz.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/cli/conf/branch/leaf/thing.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b/deep/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b/deep/stuff.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/paths/baz/b/deep/stuff.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/leaf.js: -------------------------------------------------------------------------------- 1 | test('js test', function() {}); 2 | -------------------------------------------------------------------------------- /a11ySuiteExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/comcast/web-component-tester/HEAD/a11ySuiteExample.png -------------------------------------------------------------------------------- /test/fixtures/early-failure/bower_components/web-component-tester/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/integration/components_dir/bower_components/foo-element/foo-element.js: -------------------------------------------------------------------------------- 1 | window.fooElementLoaded = 'yes'; -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-component_dirs/bower_components-bar/package/index.js: -------------------------------------------------------------------------------- 1 | window.nameOfThing = 'bar'; -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-component_dirs/bower_components-foo/package/index.js: -------------------------------------------------------------------------------- 1 | window.nameOfThing = 'foo'; -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-component_dirs/bower_components/package/index.js: -------------------------------------------------------------------------------- 1 | window.nameOfThing = 'mainline'; -------------------------------------------------------------------------------- /test/fixtures/integration/missing/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 0, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "error" 6 | } -------------------------------------------------------------------------------- /test/fixtures/integration/no-tests/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 0, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete" 6 | } -------------------------------------------------------------------------------- /test/fixtures/cli/conf/wct.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | sauce: { 4 | username: 'abc123', 5 | }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/cli/conf/json/wct.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "..", 3 | "plugins": { 4 | "sauce": { 5 | "username": "jsonconf" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/integration/failing/test/tests.js: -------------------------------------------------------------------------------- 1 | test('passing test', function() {}); 2 | test('failing test', function() { 3 | assert.isTrue(false); 4 | }); 5 | -------------------------------------------------------------------------------- /test/fixtures/integration/hybrid/test/tests.js: -------------------------------------------------------------------------------- 1 | suite('suite', function() { 2 | test('nested test', function() {}); 3 | }); 4 | test('test', function() {}); 5 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-js/test/tests.js: -------------------------------------------------------------------------------- 1 | suite('suite', function() { 2 | test('nested test', function() {}); 3 | }); 4 | test('test', function() {}); 5 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-js/test/one.js: -------------------------------------------------------------------------------- 1 | suite('suite 1', function() { 2 | test('nested test 1', function() {}); 3 | }); 4 | test('test 1', function() {}); 5 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-js/test/three.js: -------------------------------------------------------------------------------- 1 | suite('suite 3', function() { 2 | test('nested test 3', function() {}); 3 | }); 4 | test('test 3', function() {}); 5 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-js/test/two.js: -------------------------------------------------------------------------------- 1 | suite('suite 2', function() { 2 | test('nested test 2', function() {}); 3 | }); 4 | test('test 2', function() {}); 5 | -------------------------------------------------------------------------------- /test/fixtures/cli/conf/rooted/wct.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | root: path.resolve(__dirname, '../../..'), 5 | suites: ['cli/conf/test'], 6 | }; 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Update .gitignore whenever you update this file! 2 | 3 | .todo 4 | bower_components 5 | node_modules 6 | npm-debug.log 7 | typings/ 8 | 9 | # Don't ignore runner/*.js 10 | -------------------------------------------------------------------------------- /test/fixtures/cli/conf/json/wct.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | root: path.resolve(__dirname, '..'), 5 | plugins: { 6 | sauce: { 7 | username: 'jsconf', 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AllowShortBlocksOnASingleLine: false 3 | AllowShortCaseLabelsOnASingleLine: false 4 | AllowShortFunctionsOnASingleLine: None 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortLoopsOnASingleLine: false 7 | -------------------------------------------------------------------------------- /test/fixtures/integration/components_dir/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 1, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "inline passing test": {"state": "passing"} 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /test/fixtures/integration/compilation/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 1, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "ES6 works": { 9 | "state": "passing" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-js/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 2, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "suite": {"nested test": {"state": "passing"}}, 9 | "test": {"state": "passing"} 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/fixtures/integration/inline-js/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 2, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "suite": {"nested test": {"state": "passing"}}, 9 | "test": {"state": "passing"} 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /data/a11ySuite-npm-header.txt: -------------------------------------------------------------------------------- 1 | import * as polymerDom from '../@polymer/polymer/lib/legacy/polymer.dom.js'; 2 | const Polymer = { dom: polymerDom }; 3 | export {a11ySuiteExport as a11ySuite}; 4 | 5 | // wct-browser-legacy/a11ySuite.js is a generated file. Source is in web-component-tester/data/a11ySuite.js 6 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-html/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 2, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/tests.html": { 8 | "suite": {"nested test": {"state": "passing"}}, 9 | "test": {"state": "passing"} 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/fixtures/integration/no-tests/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /custom_typings/findup-sync.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'findup-sync' { 2 | import * as minimatch from 'minimatch'; 3 | 4 | interface IOptions extends minimatch.IOptions { 5 | cwd?: string; 6 | } 7 | 8 | function mod(pattern: string[]|string, opts?: IOptions): string; 9 | namespace mod {} 10 | export = mod; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/leaf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-html/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-js/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/one/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/one/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/two/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/missing/test/missing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/define-webserver-hook/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-js/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-html/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/projection-element-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/projection-element-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/projection-element-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/early-failure/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/define-webserver-hook/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 2, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/tests.html": { 8 | "suite": { 9 | "nested test": { 10 | "state": "passing" 11 | } 12 | }, 13 | "test": { 14 | "state": "passing" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/integration/nested/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 4, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": {"js test": {"state": "passing"}}, 8 | "test/one/tests.html": {"test": {"state": "passing"}}, 9 | "test/two/": {"inline test": {"state": "passing"}}, 10 | "test/leaf.html": {"test": {"state": "passing"}} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/exception-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/integration/query-string/test/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/failing/test/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/hybrid/test/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/inline-js/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/one-html/test/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-html/test/one.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-html/test/two.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-html/test/three.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/define-webserver-hook/test/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/fixtures/integration/components_dir/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/fixtures/integration/failing/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/normal-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-component_dirs/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/integration/hybrid/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-js/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 6, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "suite 1": {"nested test 1": {"state": "passing"}}, 9 | "test 1": {"state": "passing"}, 10 | "suite 2": {"nested test 2": {"state": "passing"}}, 11 | "test 2": {"state": "passing"}, 12 | "suite 3": {"nested test 3": {"state": "passing"}}, 13 | "test 3": {"state": "passing"} 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 15 | 16 | - [ ] CHANGELOG.md has been updated 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/query-string/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 3, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/tests.html": { 8 | "preserves query strings": { 9 | "state": "passing" 10 | } 11 | }, 12 | "test/": { 13 | "preserves query strings (?fizz=buzz&foo=bar)": { 14 | "state": "passing" 15 | }, 16 | "preserves query strings (?fizz=buzz)": { 17 | "state": "passing" 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Update .npmignore whenever you update this file! 2 | 3 | .todo 4 | /bower_components 5 | /node_modules 6 | package-lock.json 7 | npm-debug.log 8 | typings/ 9 | runner/*.js 10 | runner/*.d.ts 11 | runner/*.js.map 12 | test/unit/*.js 13 | test/unit/*.d.ts 14 | test/unit/*.js.map 15 | test/integration/*.js 16 | test/integration/*.d.ts 17 | test/integration/*.js.map 18 | test/fixtures/integration/temp 19 | 20 | browser/*.js 21 | browser/*.js.map 22 | browser/environment/*.js* 23 | browser/mocha/*.js* 24 | browser/reporters/*.js* 25 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/exception-fixture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/hybrid/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 6, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "inline suite": {"inline nested test": {"state": "passing"}}, 9 | "inline test": {"state": "passing"}, 10 | "suite": {"nested test": {"state": "passing"}}, 11 | "test": {"state": "passing"} 12 | }, 13 | "test/tests.html": { 14 | "suite": {"nested test": {"state": "passing"}}, 15 | "test": {"state": "passing"} 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /test/fixtures/integration/query-string/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/fixtures/integration/many-html/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 6, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/one.html": { 8 | "suite 1": {"nested test 1": {"state": "passing"}}, 9 | "test 1": {"state": "passing"} 10 | }, 11 | "test/two.html": { 12 | "suite 2": {"nested test 2": {"state": "passing"}}, 13 | "test 2": {"state": "passing"} 14 | }, 15 | "test/three.html": { 16 | "suite 3": {"nested test 3": {"state": "passing"}}, 17 | "test 3": {"state": "passing"} 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /test/fixtures/integration/compilation/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /custom_typings/server-destroy.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'server-destroy' { 2 | import * as http from 'http'; 3 | 4 | /** 5 | * Monkey-patches the destroy() method onto the given server. 6 | * 7 | * It only accepts DestroyableServers as parameters to remind the user 8 | * to update their type annotations elsewhere, as we can't express the 9 | * mutation in the type system directly. 10 | */ 11 | function enableDestroy(server: enableDestroy.DestroyableServer): void; 12 | namespace enableDestroy { 13 | interface DestroyableServer extends http.Server { 14 | destroy(): void; 15 | } 16 | } 17 | export = enableDestroy; 18 | } 19 | -------------------------------------------------------------------------------- /data/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 12 | 13 | <% extraScripts.forEach(function(script) { %> 14 | 15 | <% }); %> 16 | <% if (typeof npm === 'undefined' || !npm) { %> 17 | 18 | <% } %> 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /custom_typings/promisify-node.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'promisify-node' { 2 | interface NodeCallback { 3 | (err: any, value: T): void; 4 | } 5 | function promisify(f: (cb: NodeCallback) => void): () => Promise; 6 | function promisify(f: (a: A1, cb: NodeCallback) => void): (a: A1) => 7 | Promise; 8 | function promisify( 9 | f: (a: A1, a2: A2, cb: NodeCallback) => void): (a: A1, a2: A2) => 10 | Promise; 11 | function promisify( 12 | f: (a: A1, a2: A2, a3: A3, cb: NodeCallback) => 13 | void): (a: A1, a2: A2, a3: A3) => Promise; 14 | namespace promisify {} 15 | export = promisify; 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/projection-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /browser/more-declarations.ts: -------------------------------------------------------------------------------- 1 | declare namespace Mocha { 2 | interface UtilsStatic { 3 | highlightTags(somethingSomething: string): void; 4 | } 5 | let utils: UtilsStatic; 6 | interface IRunner extends NodeJS.EventEmitter { 7 | name?: string; 8 | total: number; 9 | } 10 | 11 | interface IRunnable { 12 | parent: ISuite; 13 | root: boolean; 14 | state: 'passed'|'failed'|undefined; 15 | pending: boolean; 16 | } 17 | 18 | interface ISuite { 19 | root: boolean; 20 | } 21 | 22 | let Runner: {prototype: IRunner; immediately(callback: () => void): void}; 23 | } 24 | 25 | declare namespace SocketIO { 26 | interface Server { 27 | off(): void; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | addons: 4 | firefox: latest 5 | apt: 6 | sources: 7 | - google-chrome 8 | packages: 9 | - google-chrome-stable 10 | sauce_connect: true 11 | language: node_js 12 | node_js: 13 | - node 14 | - '6' 15 | script: 16 | - xvfb-run npm test 17 | env: 18 | global: 19 | - secure: erPJ5uPudGD1E05tCYCWk/sdSFYL5lK/7rzUPgijC27XtuTF0tvvLKJkId2sCzPb40yCBHhEv0UIVEnYzY4feCpx5kUqm+Iu6DHWDn4xJP+gRnCNMCJp0QcUFU3JDNGK07FG2mKUkxD8EHxYjATL7tnN9HduNxaornVapkgRu70= 20 | - secure: WkrrR4HpJr1jD86mOegdumhLqI5M1skh2j1iX0AYF41oPym4CIRHvFA8REY1f12OlPXfXCVZL8+7nyuoM6NEyddEDzZZXmgAJVFkFbP++veVjXGVAWsr2++n5lzcUfvhIuYTxTqSTpnA6E3DhoJdXBxt0FBTFxYA3yXSIfJsu2k= 21 | -------------------------------------------------------------------------------- /test/fixtures/integration/query-string/test/tests.js: -------------------------------------------------------------------------------- 1 | // TODO(usergenic): Figure out a reasonable solution to get URL() and 2 | // document.currentScript to work in IE11 and then put these tests back 3 | // in commission. 4 | // 5 | // See https://github.com/PolymerElements/iron-location/blob/3ef6d758514d7cb80a3297f8ef5208774d486e88/iron-location.html#L65 6 | // as a possible solution. 7 | /* 8 | var url = new URL(document.currentScript.src); 9 | 10 | test('preserves query strings (' + url.search + ')', function () { 11 | expect(url.search).to.match(/\?fizz=buzz/); 12 | }); 13 | */ 14 | test('preserves query strings (?fizz=buzz&foo=bar)', function () { }); 15 | test('preserves query strings (?fizz=buzz)', function () { }); 16 | -------------------------------------------------------------------------------- /browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "isolatedModules": false, 7 | "noImplicitAny": true, 8 | "noUnusedLocals": false, 9 | "noUnusedParameters": true, 10 | "noImplicitThis": false, 11 | "strictNullChecks": false, 12 | "removeComments": false, 13 | "preserveConstEnums": true, 14 | "suppressImplicitAnyIndexErrors": true, 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "sourceMap": true, 20 | "pretty": true 21 | }, 22 | "exclude": [ 23 | "node_modules" 24 | ], 25 | "include": [ 26 | "*.ts", 27 | "**/*.ts", 28 | "../custom_typings/*.d.ts" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /browser-js-header.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 | * Code distributed by Google as part of the polymer project is also 8 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 | */ 10 | 11 | /** 12 | * THIS FILE IS AUTOMATICALLY GENERATED! 13 | * To make changes to browser.js, please edit the source files in the repo's `browser/` directory! 14 | */ 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "clang-format.style": "file", 4 | "editor.formatOnSave": true, 5 | "editor.formatOnType": true, 6 | "files.exclude": { 7 | "runner/*.js": true, 8 | "runner/*.d.ts": true, 9 | "runner/*.js.map": true, 10 | "test/unit/*.js": true, 11 | "test/unit/*.d.ts": true, 12 | "test/unit/*.js.map": true, 13 | "test/integration/*.js": true, 14 | "test/integration/*.d.ts": true, 15 | "test/integration/*.js.map": true, 16 | "browser.js": true, 17 | "browser.js.map": true, 18 | "browser/**/*.js": true, 19 | "browser/**/*.js.map": true 20 | }, 21 | "typescript.tsdk": "./node_modules/typescript/lib" 22 | } 23 | -------------------------------------------------------------------------------- /custom_typings/send.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'send' { 2 | import * as http from 'http'; 3 | import * as events from 'events'; 4 | 5 | 6 | function send(req: http.IncomingMessage, path: string, options?: send.Options): send.SendStream; 7 | namespace send { 8 | export interface SendStream extends events.EventEmitter { 9 | pipe(res: http.ServerResponse): void; 10 | } 11 | 12 | export interface Options { 13 | dotfiles?: 'allow' | 'deny' | 'ignore'; 14 | end?: number; 15 | etag?: boolean; 16 | extensions?: string[]; 17 | index?: boolean|string|string[]; 18 | lastModified?: boolean; 19 | maxAge?: number; 20 | root?: string; 21 | start?: number; 22 | } 23 | } 24 | export = send; 25 | } 26 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/dom-if-element.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 27 | 28 | -------------------------------------------------------------------------------- /runner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 | * Code distributed by Google as part of the polymer project is also 8 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 | */ 10 | module.exports = { 11 | cli: require('./runner/cli'), 12 | config: require('./runner/config'), 13 | gulp: require('./runner/gulp'), 14 | steps: require('./runner/steps'), 15 | test: require('./runner/test'), 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 10, 3 | "pending": 0, 4 | "failing": 0, 5 | "status": "complete", 6 | "tests": { 7 | "test/tests.html": { 8 | "testing projected element replacement": { 9 | "projection replace test": {"state": "passing"} 10 | }, 11 | "testing standard multiple replace": { 12 | "double replace test": {"state": "passing"} 13 | }, 14 | "testing template bindings": { 15 | "dom-repeat bindings exist": {"state": "passing"} 16 | }, 17 | "testing template reset after test": { 18 | "checking template is reset after replace test": {"state": "passing"} 19 | }, 20 | "testing templatized multiple replace": { 21 | "dom-if test": {"state": "passing"} 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "isolatedModules": false, 7 | "noImplicitAny": true, 8 | "noUnusedLocals": false, 9 | "noUnusedParameters": true, 10 | "noImplicitThis": false, 11 | "strictNullChecks": false, 12 | "removeComments": false, 13 | "preserveConstEnums": true, 14 | "suppressImplicitAnyIndexErrors": true, 15 | "lib": ["es2017"], 16 | "declaration": true, 17 | "sourceMap": true, 18 | "pretty": true 19 | }, 20 | "exclude": [ 21 | "node_modules" 22 | ], 23 | "include": [ 24 | "runner/*.ts", 25 | "test/**/*.ts", 26 | "custom_typings/*.d.ts", 27 | "node_modules/@types/mocha/index.d.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /browser/mocha/fixture.ts: -------------------------------------------------------------------------------- 1 | import {extendInterfaces} from './extend.js'; 2 | 3 | interface TestFixture extends HTMLElement { 4 | create(model: object): HTMLElement; 5 | restore(): void; 6 | } 7 | 8 | extendInterfaces('fixture', function(context, teardown) { 9 | 10 | // Return context.fixture if it is already a thing, for backwards 11 | // compatibility with `test-fixture-mocha.js`: 12 | return context.fixture || function fixture(fixtureId: string, model: object) { 13 | 14 | // Automatically register a teardown callback that will restore the 15 | // test-fixture: 16 | teardown(function() { 17 | (document.getElementById(fixtureId) as TestFixture).restore(); 18 | }); 19 | 20 | // Find the test-fixture with the provided ID and create it, returning 21 | // the results: 22 | return (document.getElementById(fixtureId) as TestFixture).create(model); 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /custom_typings/stacky.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'stacky' { 2 | interface ParsedStackFrame { 3 | method: string; 4 | location: string; 5 | line: number; 6 | column: number; 7 | } 8 | type StyleFunction = (part: string) => string; 9 | interface Options { 10 | maxMethodPadding?: number; 11 | indent?: string; 12 | methodPlaceholder?: string; 13 | locationStrip?: (string|RegExp)[]; 14 | unimportantLocation?: (string|RegExp)[]; 15 | filter?: (line: ParsedStackFrame) => boolean; 16 | styles?: { 17 | method?: StyleFunction; 18 | location?: StyleFunction; 19 | line?: StyleFunction; 20 | column?: StyleFunction; 21 | unimportant?: StyleFunction; 22 | }; 23 | } 24 | export function clean(lines: ParsedStackFrame[], options: Options): void; 25 | export function pretty( 26 | errorStack: string|ParsedStackFrame[], options: Options): string; 27 | 28 | export function normalize(error: Error, config: Options): Error; 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-replace/dom-repeat-fixture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 18 | 19 | 20 | 21 | 26 | 41 | 42 | -------------------------------------------------------------------------------- /tasks/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 | * Code distributed by Google as part of the polymer project is also 8 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 | */ 10 | var chalk = require('chalk'); 11 | 12 | var test = require('../runner/test'); 13 | 14 | module.exports = function(grunt) { 15 | grunt.registerMultiTask('wct-test', 'Runs tests via web-component-tester', function() { 16 | var done = this.async(); 17 | test(this.options()).then(() => done(), (error) => { 18 | console.log(chalk.red(error)); 19 | // Grunt only errors on `false` and instances of `Error`. 20 | done(new Error(error)); 21 | }); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /bin/wct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @license 4 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 5 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 7 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 8 | * Code distributed by Google as part of the polymer project is also 9 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | var resolve = require('resolve'); 12 | 13 | process.title = 'wct'; 14 | 15 | resolve('web-component-tester', {basedir: process.cwd()}, function(error, path) { 16 | var wct = path ? require(path) : require('..'); 17 | var promise = wct.cli.run(process.env, process.argv.slice(2), process.stdout, function (error) { 18 | process.exit(error ? 1 : 0); 19 | }); 20 | if (promise) { 21 | promise.then(() => process.exit(0), () => process.exit(1)); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /bin/wct-st: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @license 4 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 5 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 7 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 8 | * Code distributed by Google as part of the polymer project is also 9 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | var resolve = require('resolve'); 12 | 13 | process.title = 'wct-st'; 14 | 15 | resolve('web-component-tester', {basedir: process.cwd()}, function(error, path) { 16 | var wct = path ? require(path) : require('..'); 17 | var promise = wct.cli.runSauceTunnel(process.env, process.argv.slice(2), process.stdout, function (error) { 18 | process.exit(error ? 1 : 0); 19 | }); 20 | if (promise) { 21 | promise.then(() => process.exit(0), () => process.exit(1)); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-component-tester", 3 | "description": "web-component-tester makes testing your web components a breeze!", 4 | "version": "6.0.0", 5 | "main": [ 6 | "browser.js" 7 | ], 8 | "license": "http://polymer.github.io/LICENSE.txt", 9 | "ignore": [ 10 | "*", 11 | "!/data/*", 12 | "!/browser.js", 13 | "!/browser.js.map", 14 | "!/package.json", 15 | "!/bower.json" 16 | ], 17 | "keywords": [ 18 | "browser", 19 | "grunt", 20 | "gruntplugin", 21 | "gulp", 22 | "polymer", 23 | "test", 24 | "testing", 25 | "web component", 26 | "web" 27 | ], 28 | "dependencies": { 29 | "accessibility-developer-tools": "^2.10.0", 30 | "async": "^1.5.0", 31 | "chai": "^3.2.0", 32 | "lodash": "^3.7.0", 33 | "mocha": "^3.1.2", 34 | "sinon-chai": "^2.7.0", 35 | "sinonjs": "^1.14.1", 36 | "stacky": "^1.3.0", 37 | "test-fixture": "^3.0.0" 38 | }, 39 | "devDependencies": { 40 | "polymer": "Polymer/polymer#^1.5.0", 41 | "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.22" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/fixtures/integration/failing/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "passing": 3, 3 | "pending": 0, 4 | "failing": 3, 5 | "status": "complete", 6 | "tests": { 7 | "test/": { 8 | "failing test": { 9 | "state": "failing" 10 | }, 11 | "inline failing test": { 12 | "state": "failing" 13 | }, 14 | "inline passing test": { 15 | "state": "passing" 16 | }, 17 | "passing test": { 18 | "state": "passing" 19 | } 20 | }, 21 | "test/tests.html": { 22 | "failing test": { 23 | "state": "failing" 24 | }, 25 | "passing test": { 26 | "state": "passing" 27 | } 28 | } 29 | }, 30 | "errors": { 31 | "test/": { 32 | "inline failing test": [ 33 | "expected false to be true", 34 | "at index\\.html:(12|15)(:|$)" 35 | ], 36 | "failing test": [ 37 | "expected false to be true", 38 | "at tests\\.js:3(:|$)" 39 | ] 40 | }, 41 | "test/tests.html": { 42 | "failing test": [ 43 | "expected false to be true", 44 | "at tests\\.html:(10|13)(:|$)" 45 | ] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /wct-browser-legacy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wct-browser-legacy", 3 | "version": "0.0.1-pre.11", 4 | "description": "Client-side dependencies for web-component-tester tests installed via npm.", 5 | "main": "browser.js", 6 | "license": "http://polymer.github.io/LICENSE.txt", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/polymer/web-component-tester.git" 13 | }, 14 | "keywords": [ 15 | "browser", 16 | "gulp", 17 | "polymer", 18 | "test", 19 | "testing", 20 | "web", 21 | "web component" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/polymer/web-component-tester/issues" 25 | }, 26 | "homepage": "https://github.com/polymer/web-component-tester#readme", 27 | "dependencies": { 28 | "@polymer/polymer": "^3.0.0-pre.1", 29 | "@polymer/sinonjs": "^1.14.1", 30 | "@polymer/test-fixture": "^3.0.0-pre.1", 31 | "@webcomponents/webcomponentsjs": "^1.0.7", 32 | "accessibility-developer-tools": "^2.12.0", 33 | "async": "^1.5.2", 34 | "chai": "^3.5.0", 35 | "lodash": "^3.10.1", 36 | "mocha": "^3.4.2", 37 | "sinon": "^1.17.1", 38 | "sinon-chai": "^2.10.0", 39 | "stacky": "^1.3.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /browser/environment/errors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import * as config from '../config.js'; 12 | 13 | // We may encounter errors during initialization (for example, syntax errors in 14 | // a test file). Hang onto those (and more) until we are ready to report them. 15 | export let globalErrors: any[] = []; 16 | 17 | /** 18 | * Hook the environment to pick up on global errors. 19 | */ 20 | export function listenForErrors() { 21 | window.addEventListener('error', function(event) { 22 | globalErrors.push(event.error); 23 | }); 24 | 25 | // Also, we treat `console.error` as a test failure. Unless you prefer not. 26 | const origConsole = console; 27 | const origError = console.error; 28 | console.error = function wctShimmedError() { 29 | origError.apply(origConsole, arguments); 30 | if (config.get('trackConsoleError')) { 31 | throw 'console.error: ' + Array.prototype.join.call(arguments, ' '); 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /test/fixtures/integration/multiple-component_dirs/golden.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "passing": 1, 5 | "pending": 0, 6 | "failing": 0, 7 | "status": "complete", 8 | "tests": { 9 | "test/": { 10 | "only works with mainline components": { 11 | "state": "passing" 12 | } 13 | } 14 | } 15 | }, 16 | "foo": { 17 | "passing": 0, 18 | "pending": 0, 19 | "failing": 1, 20 | "status": "complete", 21 | "tests": { 22 | "test/": { 23 | "only works with mainline components": { 24 | "state": "failing" 25 | } 26 | } 27 | }, 28 | "errors": { 29 | "test/": { 30 | "only works with mainline components": [ 31 | "expected 'foo' to equal 'mainline'", 32 | "at index\\.html:(10|13)" 33 | ] 34 | } 35 | } 36 | }, 37 | "bar": { 38 | "passing": 0, 39 | "pending": 0, 40 | "failing": 1, 41 | "status": "complete", 42 | "tests": { 43 | "test/": { 44 | "only works with mainline components": { 45 | "state": "failing" 46 | } 47 | } 48 | }, 49 | "errors": { 50 | "test/": { 51 | "only works with mainline components": [ 52 | "expected 'bar' to equal 'mainline'", 53 | "at index\\.html:(10|13)" 54 | ] 55 | } 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /browser/mocha/stub.ts: -------------------------------------------------------------------------------- 1 | import {extendInterfaces} from './extend.js'; 2 | 3 | /** 4 | * stub 5 | * 6 | * The stub addon allows the tester to partially replace the implementation of 7 | * an element with some custom implementation. Usage example: 8 | * 9 | * beforeEach(function() { 10 | * stub('x-foo', { 11 | * attached: function() { 12 | * // Custom implementation of the `attached` method of element `x-foo`.. 13 | * }, 14 | * otherMethod: function() { 15 | * // More custom implementation.. 16 | * }, 17 | * getterSetterProperty: { 18 | * get: function() { 19 | * // Custom getter implementation.. 20 | * }, 21 | * set: function() { 22 | * // Custom setter implementation.. 23 | * } 24 | * }, 25 | * // etc.. 26 | * }); 27 | * }); 28 | */ 29 | extendInterfaces('stub', function(_context, teardown) { 30 | 31 | return function stub(tagName: string, implementation: object) { 32 | // Find the prototype of the element being stubbed: 33 | const proto = document.createElement(tagName).constructor.prototype; 34 | 35 | // For all keys in the implementation to stub with.. 36 | const stubs = Object.keys(implementation).map(function(key) { 37 | // Stub the method on the element prototype with Sinon: 38 | return sinon.stub(proto, key, implementation[key]); 39 | }); 40 | 41 | // After all tests.. 42 | teardown(function() { 43 | stubs.forEach(function(stub) { 44 | stub.restore(); 45 | }); 46 | }); 47 | }; 48 | }); 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Polymer Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /runner/gulp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | import * as chalk from 'chalk'; 16 | import {Gulp} from 'gulp'; 17 | 18 | import {Config} from './config'; 19 | import {test} from './test'; 20 | 21 | 22 | export function init(gulp: Gulp, dependencies?: string[]): void { 23 | if (!dependencies) { 24 | dependencies = []; 25 | } 26 | 27 | // TODO(nevir): Migrate fully to wct:local/etc. 28 | gulp.task('test', ['wct:local']); 29 | gulp.task('test:local', ['wct:local']); 30 | gulp.task('test:remote', ['wct:sauce']); 31 | 32 | gulp.task('wct', ['wct:local']); 33 | 34 | gulp.task('wct:local', dependencies, () => { 35 | return test({plugins: {local: {}, sauce: false}}).catch(cleanError); 36 | }); 37 | 38 | gulp.task('wct:sauce', dependencies, () => { 39 | return test({plugins: {local: false, sauce: {}}}).catch(cleanError); 40 | }); 41 | } 42 | 43 | // Utility 44 | 45 | function cleanError(error: any) { 46 | // Pretty error for gulp. 47 | error = new Error(chalk.red(error.message || error)); 48 | error.showStack = false; 49 | throw error; 50 | } 51 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "arrow-parens": true, 4 | "class-name": true, 5 | "indent": [ 6 | true, 7 | "spaces" 8 | ], 9 | "prefer-const": true, 10 | "no-duplicate-variable": true, 11 | "no-eval": true, 12 | "no-internal-module": true, 13 | "no-trailing-whitespace": true, 14 | "no-var-keyword": true, 15 | "one-line": [ 16 | true, 17 | "check-open-brace", 18 | "check-whitespace" 19 | ], 20 | "quotemark": [ 21 | true, 22 | "single", 23 | "avoid-escape" 24 | ], 25 | "semicolon": [ 26 | true, 27 | "always" 28 | ], 29 | "trailing-comma": [ 30 | true, 31 | "multiline" 32 | ], 33 | "triple-equals": [ 34 | true, 35 | "allow-null-check" 36 | ], 37 | "typedef-whitespace": [ 38 | true, 39 | { 40 | "call-signature": "nospace", 41 | "index-signature": "nospace", 42 | "parameter": "nospace", 43 | "property-declaration": "nospace", 44 | "variable-declaration": "nospace" 45 | } 46 | ], 47 | "variable-name": [ 48 | true, 49 | "ban-keywords" 50 | ], 51 | "whitespace": [ 52 | true, 53 | "check-branch", 54 | "check-decl", 55 | "check-operator", 56 | "check-separator", 57 | "check-type" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /browser/environment/compatability.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import ChildRunner from '../childrunner.js'; 12 | 13 | // polymer-test-tools (and Polymer/tools) support HTML tests where each file is 14 | // expected to call `done()`, which posts a message to the parent window. 15 | window.addEventListener('message', function(event) { 16 | if (!event.data || (event.data !== 'ok' && !event.data.error)) { 17 | return; 18 | } 19 | const childRunner = ChildRunner.get(event.source); 20 | if (!childRunner) { 21 | return; 22 | } 23 | 24 | childRunner.ready(); 25 | // The name of the suite as exposed to the user. 26 | const reporter = childRunner.parentScope.WCT._reporter; 27 | const title = reporter.suiteTitle(event.source.location); 28 | reporter.emitOutOfBandTest( 29 | 'page-wide tests via global done()', event.data.error, title, true); 30 | 31 | childRunner.done(); 32 | }); 33 | 34 | // Attempt to ensure that we complete a test suite if it is interrupted by a 35 | // document unload. 36 | window.addEventListener('unload', function(_event: BeforeUnloadEvent) { 37 | // Mocha's hook queue is asynchronous; but we want synchronous behavior if 38 | // we've gotten to the point of unloading the document. 39 | Mocha.Runner.immediately = function(callback: () => void) { 40 | callback(); 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /browser/declarations.ts: -------------------------------------------------------------------------------- 1 | import * as ChaiStatic from 'chai'; 2 | import * as SinonStatic from 'sinon'; 3 | import * as SocketIOStatic from 'socket.io'; 4 | import * as StackyStatic from 'stacky'; 5 | 6 | import {default as ChildRunner, SharedState} from './childrunner.js'; 7 | import {Config} from './config.js'; 8 | import MultiReporter from './reporters/multi.js'; 9 | import * as suites from './suites.js'; 10 | 11 | type loadSuitesType = (typeof suites.loadSuites); 12 | 13 | declare global { 14 | interface Window { 15 | __wctUseNpm?: boolean; 16 | WebComponents?: WebComponentsStatic; 17 | Platform?: PlatformStatic; 18 | Polymer?: PolymerStatic; 19 | WCT: { 20 | readonly _ChildRunner: typeof ChildRunner; // 21 | readonly share: SharedState; // 22 | readonly _config: Config; // 23 | readonly loadSuites: loadSuitesType; 24 | _reporter: MultiReporter; 25 | }; 26 | mocha: typeof mocha; 27 | Mocha: typeof Mocha; 28 | __generatedByWct?: boolean; 29 | 30 | chai: typeof ChaiStatic; 31 | assert: typeof ChaiStatic.assert; 32 | expect: typeof ChaiStatic.expect; 33 | } 34 | interface WebComponentsStatic { 35 | ready?(): void; 36 | flush?(): void; 37 | } 38 | interface PlatformStatic { 39 | performMicrotaskCheckpoint(): void; 40 | } 41 | interface PolymerElement { 42 | _stampTemplate?(): void; 43 | } 44 | interface PolymerElementConstructor { 45 | prototype: PolymerElement; 46 | } 47 | interface PolymerStatic { 48 | flush(): void; 49 | dom: {flush(): void}; 50 | Element: PolymerElementConstructor; 51 | } 52 | 53 | interface Element { 54 | isConnected?: boolean; 55 | } 56 | 57 | interface Mocha { 58 | suite: Mocha.ISuite; 59 | } 60 | 61 | var Stacky: typeof StackyStatic; 62 | var io: typeof SocketIOStatic; 63 | var Platform: PlatformStatic; 64 | var sinon: typeof SinonStatic; 65 | } 66 | -------------------------------------------------------------------------------- /custom_typings/wd.d.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | declare module 'wd' { 4 | interface NodeCB { 5 | (err: any, value: T): void; 6 | } 7 | export interface Browser { 8 | configureHttp(options: { 9 | retries: number 10 | }): void; 11 | attach(sessionId: string, callback: NodeCB): void; 12 | init(capabilities: Capabilities, callback: NodeCB): void; 13 | 14 | get(url: string, callback: NodeCB): void; 15 | quit(callback: NodeCB): void; 16 | 17 | on(eventName: string, handler: Function): void; 18 | } 19 | export interface Capabilities { 20 | /** The name of the browser being used */ 21 | browserName: 'android'|'chrome'|'firefox'|'htmlunit'|'internet explorer'|'iPhone'|'iPad'|'opera'|'safari'; 22 | /** The browser version, or the empty string if unknown. */ 23 | version: string; 24 | /** A key specifying which platform the browser should be running on. */ 25 | platform: 'WINDOWS'|'XP'|'VISTA'|'MAC'|'LINUX'|'UNIX'|'ANDROID'|'ANY'; 26 | 27 | /** Whether the session can interact with modal popups, 28 | * such as window.alert and window.confirm. */ 29 | handlesAlerts: boolean; 30 | /** Whether the session supports CSS selectors when searching for elements. */ 31 | cssSelectorsEnabled: boolean; 32 | 33 | webdriver: { 34 | remote: { 35 | quietExceptions: boolean; 36 | } 37 | }; 38 | 39 | selenium: { 40 | server: { 41 | url: string; 42 | } 43 | }; 44 | } 45 | 46 | export function remote( 47 | hostnameOrUrl: string, port?: number, 48 | username?: string, password?: string): Browser; 49 | export function remote( 50 | options: {hostname: string, port?: number, 51 | auth?: string, path?: string, } 52 | ): Browser; 53 | export function remote( 54 | options: {host: string, port?: number, 55 | username?: string, accesskey?: string, path?: string, } 56 | ): Browser; 57 | } 58 | -------------------------------------------------------------------------------- /runner/port-scanner.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | import * as net from 'net'; 16 | 17 | function checkPort(port: number): Promise { 18 | return new Promise(function(resolve) { 19 | const server = net.createServer(); 20 | let hasPort = false; 21 | 22 | // if server is listening, we have the port! 23 | server.on('listening', function(_err: any) { 24 | hasPort = true; 25 | server.close(); 26 | }); 27 | 28 | // callback on server close to free up the port before report it can be used 29 | server.on('close', function(_err: any) { 30 | resolve(hasPort); 31 | }); 32 | 33 | // our port is busy, ignore it 34 | server.on('error', function(_err: any) { 35 | // docs say the server should close, this doesn't seem to be the case :( 36 | server.close(); 37 | }); 38 | 39 | server.listen(port); 40 | }); 41 | } 42 | 43 | interface PromiseGetter { 44 | (val: T): Promise; 45 | } 46 | 47 | async function detectSeries( 48 | values: T[], promiseGetter: PromiseGetter): Promise { 49 | for (const value of values) { 50 | if (await promiseGetter(value)) { 51 | return value; 52 | } 53 | } 54 | throw new Error('Couldn\'t find a good value in detectSeries'); 55 | } 56 | 57 | export async function findPort(ports: number[]): Promise { 58 | try { 59 | return await detectSeries(ports, checkPort); 60 | } catch (error) { 61 | throw new Error('no port found!'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /browser/reporters/html.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | 12 | /** 13 | * WCT-specific behavior on top of Mocha's default HTML reporter. 14 | * 15 | * @param {!Mocha.Runner} runner The runner that is being reported on. 16 | */ 17 | export default function HTML(runner: Mocha.IRunner) { 18 | const output = document.createElement('div'); 19 | output.id = 'mocha'; 20 | document.body.appendChild(output); 21 | 22 | runner.on('suite', function(_test: any) { 23 | this.total = runner.total; 24 | }.bind(this)); 25 | 26 | Mocha.reporters.HTML.call(this, runner); 27 | } 28 | 29 | // Woo! What a hack. This just saves us from adding a bunch of complexity around 30 | // style loading. 31 | const style = document.createElement('style'); 32 | style.textContent = ` 33 | html, body { 34 | position: relative; 35 | height: 100%; 36 | width: 100%; 37 | min-width: 900px; 38 | } 39 | #mocha, #subsuites { 40 | height: 100%; 41 | position: absolute; 42 | top: 0; 43 | } 44 | #mocha { 45 | box-sizing: border-box; 46 | margin: 0 !important; 47 | padding: 60px 20px; 48 | right: 0; 49 | left: 500px; 50 | } 51 | #subsuites { 52 | -ms-flex-direction: column; 53 | -webkit-flex-direction: column; 54 | display: -ms-flexbox; 55 | display: -webkit-flex; 56 | display: flex; 57 | flex-direction: column; 58 | left: 0; 59 | width: 500px; 60 | } 61 | #subsuites .subsuite { 62 | border: 0; 63 | width: 100%; 64 | height: 100%; 65 | } 66 | #mocha .test.pass .duration { 67 | color: #555 !important; 68 | } 69 | `; 70 | document.head.appendChild(style); 71 | -------------------------------------------------------------------------------- /browser/reporters.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import CLISocket from './clisocket.js'; 12 | import ConsoleReporter from './reporters/console.js'; 13 | import HTMLReporter from './reporters/html.js'; 14 | import MultiReporter, {ReporterFactory} from './reporters/multi.js'; 15 | import TitleReporter from './reporters/title.js'; 16 | import * as suites from './suites.js'; 17 | 18 | export let htmlSuites: Array = []; 19 | export let jsSuites: Array = []; 20 | 21 | /** 22 | * @param {CLISocket} socket The CLI socket, if present. 23 | * @param {MultiReporter} parent The parent reporter, if present. 24 | * @return {!Array. = [TitleReporter, ConsoleReporter]; 35 | 36 | if (socket) { 37 | reporters.push(function(runner: MultiReporter) { 38 | socket.observe(runner); 39 | } as any); 40 | } 41 | 42 | if (suites.htmlSuites.length > 0 || suites.jsSuites.length > 0) { 43 | reporters.push(HTMLReporter as any); 44 | } 45 | 46 | return reporters; 47 | } 48 | 49 | export type MochaStatic = typeof Mocha; 50 | /** 51 | * Yeah, hideous, but this allows us to be loaded before Mocha, which is handy. 52 | */ 53 | export function injectMocha(Mocha: MochaStatic) { 54 | _injectPrototype(ConsoleReporter, Mocha.reporters.Base.prototype); 55 | _injectPrototype(HTMLReporter, Mocha.reporters.HTML.prototype); 56 | // Mocha doesn't expose its `EventEmitter` shim directly, so: 57 | _injectPrototype( 58 | MultiReporter, Object.getPrototypeOf(Mocha.Runner.prototype)); 59 | } 60 | 61 | function _injectPrototype(klass: any, prototype: any) { 62 | const newPrototype = Object.create(prototype); 63 | // Only support 64 | Object.keys(klass.prototype).forEach(function(key) { 65 | newPrototype[key] = klass.prototype[key]; 66 | }); 67 | 68 | klass.prototype = newPrototype; 69 | } 70 | -------------------------------------------------------------------------------- /browser/mocha/extend.ts: -------------------------------------------------------------------------------- 1 | 2 | const interfaceExtensions: Array<() => void> = []; 3 | 4 | /** 5 | * Registers an extension that extends the global `Mocha` implementation 6 | * with new helper methods. These helper methods will be added to the `window` 7 | * when tests run for both BDD and TDD interfaces. 8 | */ 9 | export function extendInterfaces( 10 | helperName: string, 11 | helperFactory: ( 12 | context: any, teardown: (cb: () => void) => void, 13 | interfaceName: 'tdd'|'bdd') => void) { 14 | interfaceExtensions.push(function() { 15 | const Mocha = window.Mocha; 16 | // For all Mocha interfaces (probably just TDD and BDD): 17 | Object.keys((Mocha as any).interfaces) 18 | .forEach(function(interfaceName: 'tdd'|'bdd') { 19 | // This is the original callback that defines the interface (TDD or 20 | // BDD): 21 | const originalInterface = (Mocha as any).interfaces[interfaceName]; 22 | // This is the name of the "teardown" or "afterEach" property for the 23 | // current interface: 24 | const teardownProperty = 25 | interfaceName === 'tdd' ? 'teardown' : 'afterEach'; 26 | // The original callback is monkey patched with a new one that appends 27 | // to the global context however we want it to: 28 | (Mocha as any).interfaces[interfaceName] = function(suite: any) { 29 | // Call back to the original callback so that we get the base 30 | // interface: 31 | originalInterface.apply(this, arguments); 32 | // Register a listener so that we can further extend the base 33 | // interface: 34 | suite.on( 35 | 'pre-require', 36 | function(context: any, _file: string, _mocha: any) { 37 | // Capture a bound reference to the teardown function as a 38 | // convenience: 39 | const teardown = context[teardownProperty].bind(context); 40 | // Add our new helper to the testing context. The helper is 41 | // generated by a factory method that receives the context, 42 | // the teardown function and the interface name and returns 43 | // the new method to be added to that context: 44 | context[helperName] = 45 | helperFactory(context, teardown, interfaceName); 46 | }); 47 | }; 48 | }); 49 | }); 50 | } 51 | 52 | /** 53 | * Applies any registered interface extensions. The extensions will be applied 54 | * as many times as this function is called, so don't call it more than once. 55 | */ 56 | export function applyExtensions() { 57 | interfaceExtensions.forEach(function(applyExtension) { 58 | applyExtension(); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /runner/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | import * as cleankill from 'cleankill'; 15 | 16 | import {CliReporter} from './clireporter'; 17 | import {Config} from './config'; 18 | import {Context} from './context'; 19 | import * as steps from './steps'; 20 | 21 | /** 22 | * Runs a suite of web component tests. 23 | * 24 | * The returned Context (a kind of EventEmitter) fires various events to allow 25 | * you to track the progress of the tests: 26 | * 27 | * Lifecycle Events: 28 | * 29 | * `run-start` 30 | * WCT is ready to begin spinning up browsers. 31 | * 32 | * `browser-init` {browser} {stats} 33 | * WCT is ready to begin spinning up browsers. 34 | * 35 | * `browser-start` {browser} {metadata} {stats} 36 | * The browser has begun running tests. May fire multiple times (i.e. when 37 | * manually refreshing the tests). 38 | * 39 | * `sub-suite-start` {browser} {sharedState} {stats} 40 | * A suite file has begun running. 41 | * 42 | * `test-start` {browser} {test} {stats} 43 | * A test has begun. 44 | * 45 | * `test-end` {browser} {test} {stats} 46 | * A test has ended. 47 | * 48 | * `sub-suite-end` {browser} {sharedState} {stats} 49 | * A suite file has finished running all of its tests. 50 | * 51 | * `browser-end` {browser} {error} {stats} 52 | * The browser has completed, and it shutting down. 53 | * 54 | * `run-end` {error} 55 | * WCT has run all browsers, and is shutting down. 56 | * 57 | * Generic Events: 58 | * 59 | * * log:debug 60 | * * log:info 61 | * * log:warn 62 | * * log:error 63 | * 64 | * @param {!Config|!Context} options The configuration or an already formed 65 | * `Context` object. 66 | */ 67 | export async function test(options: Config|Context): Promise { 68 | const context = (options instanceof Context) ? options : new Context(options); 69 | 70 | // We assume that any options related to logging are passed in via the initial 71 | // `options`. 72 | if (context.options.output) { 73 | new CliReporter(context, context.options.output, context.options); 74 | } 75 | 76 | try { 77 | await steps.setupOverrides(context); 78 | await steps.loadPlugins(context); 79 | await steps.configure(context); 80 | await steps.prepare(context); 81 | await steps.runTests(context); 82 | } finally { 83 | if (!context.options.skipCleanup) { 84 | await cleankill.close(); 85 | } 86 | } 87 | } 88 | 89 | // HACK 90 | test['test'] = test; 91 | 92 | module.exports = test; 93 | -------------------------------------------------------------------------------- /runner/paths.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | import * as fs from 'fs'; 16 | import * as glob from 'glob'; 17 | import * as _ from 'lodash'; 18 | import * as path from 'path'; 19 | import * as promisify from 'promisify-node'; 20 | 21 | /** 22 | * Expands a series of path patterns (globs, files, directories) into a set of 23 | * files that represent those patterns. 24 | * 25 | * @param baseDir The directory that patterns are relative to. 26 | * @param patterns The patterns to expand. 27 | * @returns The expanded paths. 28 | */ 29 | export async function expand( 30 | baseDir: string, patterns: string[]): Promise { 31 | return expandDirectories(baseDir, await unglob(baseDir, patterns)); 32 | } 33 | 34 | /** 35 | * Expands any glob expressions in `patterns`. 36 | */ 37 | async function unglob(baseDir: string, patterns: string[]): Promise { 38 | const strs: string[][] = []; 39 | const pGlob: any = promisify(glob); 40 | for (const pattern of patterns) { 41 | strs.push(await pGlob(String(pattern), {cwd: baseDir, root: baseDir})); 42 | } 43 | 44 | // for non-POSIX support, replacing path separators 45 | return _.union(_.flatten(strs)).map((str) => str.replace(/\//g, path.sep)); 46 | } 47 | 48 | /** 49 | * Expands any directories in `patterns`, following logic similar to a web 50 | * server. 51 | * 52 | * If a pattern resolves to a directory, that directory is expanded. If the 53 | * directory contains an `index.html`, it is expanded to that. Otheriwse, the 54 | * it expands into its children (recursively). 55 | */ 56 | async function expandDirectories(baseDir: string, paths: string[]) { 57 | const listsOfPaths: string[][] = []; 58 | for (const aPath of paths) { 59 | listsOfPaths.push(await expandDirectory(baseDir, aPath)); 60 | } 61 | 62 | const files = _.union(_.flatten(listsOfPaths)); 63 | return files.filter((file) => /\.(js|html)$/.test(file)); 64 | } 65 | 66 | async function expandDirectory( 67 | baseDir: string, aPath: string): Promise { 68 | const stat = await promisify(fs.stat)(path.resolve(baseDir, aPath)); 69 | if (!stat.isDirectory()) { 70 | return [aPath]; 71 | } 72 | const files = await promisify(fs.readdir)(path.resolve(baseDir, aPath)); 73 | // We have an index; defer to that. 74 | if (_.includes(files, 'index.html')) { 75 | return [path.join(aPath, 'index.html')]; 76 | } 77 | const children = await expandDirectories(path.join(baseDir, aPath), files); 78 | return children.map((child) => path.join(aPath, child)); 79 | } 80 | -------------------------------------------------------------------------------- /test/unit/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | import * as chai from 'chai'; 15 | 16 | import * as config from '../../runner/config'; 17 | import {Context} from '../../runner/context'; 18 | const expect = chai.expect; 19 | 20 | describe('config', function() { 21 | 22 | describe('.merge', function() { 23 | 24 | it('avoids modifying the input', function() { 25 | const one = {foo: 1}; 26 | const two = {foo: 2}; 27 | const merged = config.merge(one, two); 28 | 29 | expect(one.foo).to.eq(1); 30 | expect(two.foo).to.eq(2); 31 | expect(merged.foo).to.eq(2); 32 | expect(merged).to.not.equal(two); 33 | }); 34 | 35 | it('honors false as an explicit blacklisting', function() { 36 | const merged = config.merge( 37 | {plugins: {foo: {}}}, {plugins: {foo: false}}, 38 | {plugins: {foo: {}, bar: {}}}); 39 | 40 | expect(merged).to.deep.equal({plugins: {foo: false, bar: {}}}); 41 | }); 42 | 43 | }); 44 | 45 | describe('.expand', function() { 46 | 47 | describe('deprecated options', function() { 48 | 49 | it('expands local string browsers', function() { 50 | const context = new Context({browsers: ['chrome']}); 51 | return config.expand(context).then(() => { 52 | expect(context.options.plugins['local'].browsers).to.have.members([ 53 | 'chrome' 54 | ]); 55 | }); 56 | }); 57 | 58 | it('expands sauce string browsers', function() { 59 | const context = new Context({browsers: ['linux/firefox']}); 60 | return config.expand(context).then(() => { 61 | expect(context.options.plugins['sauce'].browsers).to.have.members([ 62 | 'linux/firefox' 63 | ]); 64 | }); 65 | }); 66 | 67 | it('expands local object browsers', function() { 68 | const context = 69 | new Context({browsers: [{browserName: 'firefox'}]}); 70 | return config.expand(context).then(() => { 71 | expect(context.options.plugins['local'].browsers) 72 | .to.deep['have'] 73 | .members([{browserName: 'firefox'}]); 74 | }); 75 | }); 76 | 77 | it('expands sauce object browsers', function() { 78 | const context = new Context( 79 | {browsers: [{browserName: 'safari', platform: 'OS X'}]}); 80 | return config.expand(context).then(() => { 81 | expect(context.options.plugins['sauce'].browsers) 82 | .to.deep['have'] 83 | .members([{browserName: 'safari', platform: 'OS X'}]); 84 | }); 85 | }); 86 | }); 87 | 88 | }); 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /test/unit/gulp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | import * as chai from 'chai'; 15 | import * as gulp from 'gulp'; 16 | import * as path from 'path'; 17 | import * as sinon from 'sinon'; 18 | 19 | import {Config} from '../../runner/config'; 20 | import {Context} from '../../runner/context'; 21 | import * as wctGulp from '../../runner/gulp'; 22 | import {Plugin} from '../../runner/plugin'; 23 | import * as steps from '../../runner/steps'; 24 | 25 | const expect = chai.expect; 26 | chai.use(require('sinon-chai')); 27 | 28 | const FIXTURES = path.resolve(__dirname, '../fixtures/cli'); 29 | 30 | describe('gulp', function() { 31 | 32 | let pluginsCalled: string[]; 33 | let sandbox: sinon.SinonSandbox; 34 | let orch: gulp.Gulp; 35 | let options: Config; 36 | beforeEach(function() { 37 | orch = new gulp['Gulp'](); 38 | wctGulp.init(orch); 39 | 40 | sandbox = sinon.sandbox.create(); 41 | sandbox.stub( 42 | steps, 'prepare').callsFake(async(_context: Context): Promise => undefined); 43 | sandbox.stub(steps, 'runTests').callsFake(async (context: Context) => { 44 | options = context.options; 45 | }); 46 | 47 | pluginsCalled = []; 48 | sandbox.stub(Plugin.prototype, 'execute').callsFake(async function(context: Context) { 49 | pluginsCalled.push(this.name); 50 | context.options.activeBrowsers.push( 51 | {browserName: 'fake for ' + this.name}); 52 | }); 53 | }); 54 | 55 | afterEach(function() { 56 | sandbox.restore(); 57 | }); 58 | 59 | async function runGulpTask(name: string) { 60 | await new Promise((resolve, reject) => { 61 | orch.start(name, (error) => error ? reject(error) : resolve()); 62 | }); 63 | } 64 | 65 | it('honors wcf.conf.js', async () => { 66 | process.chdir(path.join(FIXTURES, 'conf')); 67 | await runGulpTask('wct:sauce'); 68 | expect(options.plugins['sauce'].username).to.eq('abc123'); 69 | }); 70 | 71 | it('prefers wcf.conf.json', async () => { 72 | process.chdir(path.join(FIXTURES, 'conf', 'json')); 73 | await runGulpTask('wct:sauce'); 74 | expect(options.plugins['sauce'].username).to.eq('jsonconf'); 75 | }); 76 | 77 | describe('wct:local', function() { 78 | 79 | it('kicks off local tests', async () => { 80 | await runGulpTask('wct:local'); 81 | expect(steps.runTests).to.have.been.calledOnce; 82 | expect(pluginsCalled).to.have.members(['local']); 83 | }); 84 | 85 | }); 86 | 87 | describe('wct:sauce', function() { 88 | 89 | it('kicks off sauce tests', async () => { 90 | await runGulpTask('wct:sauce'); 91 | expect(steps.runTests).to.have.been.calledOnce; 92 | expect(pluginsCalled).to.have.members(['sauce']); 93 | }); 94 | 95 | }); 96 | 97 | }); 98 | -------------------------------------------------------------------------------- /browser/reporters/title.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import * as util from '../util.js'; 12 | 13 | const ARC_OFFSET = 0; // start at the right. 14 | const ARC_WIDTH = 6; 15 | 16 | /** 17 | * A Mocha reporter that updates the document's title and favicon with 18 | * at-a-glance stats. 19 | * 20 | * @param {!Mocha.Runner} runner The runner that is being reported on. 21 | */ 22 | export default class Title { 23 | runner: Mocha.IRunner; 24 | constructor(runner: Mocha.IRunner) { 25 | Mocha.reporters.Base.call(this, runner); 26 | 27 | runner.on('test end', this.report.bind(this)); 28 | } 29 | 30 | /** Reports current stats via the page title and favicon. */ 31 | report() { 32 | this.updateTitle(); 33 | this.updateFavicon(); 34 | } 35 | 36 | /** Updates the document title with a summary of current stats. */ 37 | updateTitle() { 38 | if (this.stats.failures > 0) { 39 | document.title = util.pluralizedStat(this.stats.failures, 'failing'); 40 | } else { 41 | document.title = util.pluralizedStat(this.stats.passes, 'passing'); 42 | } 43 | } 44 | 45 | /** Updates the document's favicon w/ a summary of current stats. */ 46 | updateFavicon() { 47 | const canvas = document.createElement('canvas'); 48 | canvas.height = canvas.width = 32; 49 | const context = canvas.getContext('2d'); 50 | 51 | const passing = this.stats.passes; 52 | const pending = this.stats.pending; 53 | const failing = this.stats.failures; 54 | const total = Math.max(this.runner.total, passing + pending + failing); 55 | drawFaviconArc(context, total, 0, passing, '#0e9c57'); 56 | drawFaviconArc(context, total, passing, pending, '#f3b300'); 57 | drawFaviconArc(context, total, pending + passing, failing, '#ff5621'); 58 | 59 | this.setFavicon(canvas.toDataURL()); 60 | } 61 | 62 | /** Sets the current favicon by URL. */ 63 | setFavicon(url: string) { 64 | const current = document.head.querySelector('link[rel="icon"]'); 65 | if (current) { 66 | document.head.removeChild(current); 67 | } 68 | 69 | const link = document.createElement('link'); 70 | link.rel = 'icon'; 71 | link.type = 'image/x-icon'; 72 | link.href = url; 73 | link.setAttribute('sizes', '32x32'); 74 | document.head.appendChild(link); 75 | } 76 | } 77 | 78 | /** 79 | * Draws an arc for the favicon status, relative to the total number of tests. 80 | */ 81 | function drawFaviconArc( 82 | context: CanvasRenderingContext2D, total: number, start: number, 83 | length: number, color: string) { 84 | const arcStart = ARC_OFFSET + Math.PI * 2 * (start / total); 85 | const arcEnd = ARC_OFFSET + Math.PI * 2 * ((start + length) / total); 86 | 87 | context.beginPath(); 88 | context.strokeStyle = color; 89 | context.lineWidth = ARC_WIDTH; 90 | context.arc(16, 16, 16 - ARC_WIDTH / 2, arcStart, arcEnd); 91 | context.stroke(); 92 | } 93 | 94 | export default interface Title extends Mocha.reporters.Base {} 95 | -------------------------------------------------------------------------------- /browser/environment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import * as config from './config.js'; 12 | import * as reporters from './reporters.js'; 13 | import * as util from './util.js'; 14 | 15 | /** 16 | * Loads all environment scripts ...synchronously ...after us. 17 | */ 18 | export function loadSync() { 19 | util.debug('Loading environment scripts:'); 20 | const a11ySuiteScriptPath = 'web-component-tester/data/a11ySuite.js'; 21 | const scripts = config.get('environmentScripts'); 22 | const a11ySuiteWillBeLoaded = 23 | window.__generatedByWct || scripts.indexOf(a11ySuiteScriptPath) > -1; 24 | 25 | // We can't inject a11ySuite when running the npm version because it is a 26 | // module-based script that needs `'); // jshint ignore:line 39 | }); 40 | util.debug('Environment scripts loaded'); 41 | 42 | const imports = config.get('environmentImports'); 43 | imports.forEach(function(path) { 44 | const url = util.expandUrl(path, config.get('root')); 45 | util.debug('Loading environment import:', url); 46 | // Synchronous load. 47 | document.write( 48 | ''); // jshint ignore:line 50 | }); 51 | util.debug('Environment imports loaded'); 52 | } 53 | 54 | /** 55 | * We have some hard dependencies on things that should be loaded via 56 | * `environmentScripts`, so we assert that they're present here; and do any 57 | * post-facto setup. 58 | */ 59 | export function ensureDependenciesPresent() { 60 | _ensureMocha(); 61 | _checkChai(); 62 | } 63 | 64 | function _ensureMocha() { 65 | const Mocha = window.Mocha; 66 | if (!Mocha) { 67 | throw new Error( 68 | 'WCT requires Mocha. Please ensure that it is present in WCT.environmentScripts, or that you load it before loading web-component-tester/browser.js'); 69 | } 70 | reporters.injectMocha(Mocha); 71 | // Magic loading of mocha's stylesheet 72 | const mochaPrefix = util.scriptPrefix('mocha.js'); 73 | // only load mocha stylesheet for the test runner output 74 | // Not the end of the world, if it doesn't load. 75 | if (mochaPrefix && window.top === window.self) { 76 | util.loadStyle(mochaPrefix + 'mocha.css'); 77 | } 78 | } 79 | 80 | function _checkChai() { 81 | if (!window.chai) { 82 | util.debug('Chai not present; not registering shorthands'); 83 | return; 84 | } 85 | 86 | window.assert = window.chai.assert; 87 | window.expect = window.chai.expect; 88 | } 89 | -------------------------------------------------------------------------------- /browser/mocha.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import './mocha/fixture.js'; 12 | import './mocha/stub.js'; 13 | import './mocha/replace.js'; 14 | 15 | import * as config from './config.js'; 16 | import {applyExtensions} from './mocha/extend.js'; 17 | 18 | // Mocha global helpers, broken out by testing method. 19 | // 20 | // Keys are the method for a particular interface; values are their analog in 21 | // the opposite interface. 22 | const MOCHA_EXPORTS = { 23 | // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/tdd.js 24 | tdd: { 25 | 'setup': '"before"', 26 | 'teardown': '"after"', 27 | 'suiteSetup': '"beforeEach"', 28 | 'suiteTeardown': '"afterEach"', 29 | 'suite': '"describe" or "context"', 30 | 'test': '"it" or "specify"', 31 | }, 32 | // https://github.com/visionmedia/mocha/blob/master/lib/interfaces/bdd.js 33 | bdd: { 34 | 'before': '"setup"', 35 | 'after': '"teardown"', 36 | 'beforeEach': '"suiteSetup"', 37 | 'afterEach': '"suiteTeardown"', 38 | 'describe': '"suite"', 39 | 'context': '"suite"', 40 | 'xdescribe': '"suite.skip"', 41 | 'xcontext': '"suite.skip"', 42 | 'it': '"test"', 43 | 'xit': '"test.skip"', 44 | 'specify': '"test"', 45 | 'xspecify': '"test.skip"', 46 | }, 47 | }; 48 | 49 | /** 50 | * Exposes all Mocha methods up front, configuring and running mocha 51 | * automatically when you call them. 52 | * 53 | * The assumption is that it is a one-off (sub-)suite of tests being run. 54 | */ 55 | export function stubInterfaces() { 56 | const keys = Object.keys(MOCHA_EXPORTS) as Array; 57 | keys.forEach(function(ui) { 58 | Object.keys(MOCHA_EXPORTS[ui]).forEach(function(key) { 59 | window[key] = function wrappedMochaFunction() { 60 | _setupMocha(ui, key, MOCHA_EXPORTS[ui][key]); 61 | if (!window[key] || window[key] === wrappedMochaFunction) { 62 | throw new Error('Expected mocha.setup to define ' + key); 63 | } 64 | window[key].apply(window, arguments); 65 | }; 66 | }); 67 | }); 68 | } 69 | 70 | // Whether we've called `mocha.setup` 71 | const _mochaIsSetup = false; 72 | 73 | /** 74 | * @param {string} ui Sets up mocha to run `ui`-style tests. 75 | * @param {string} key The method called that triggered this. 76 | * @param {string} alternate The matching method in the opposite interface. 77 | */ 78 | function _setupMocha(ui: 'tdd'|'bdd', key: string, alternate: 'string') { 79 | const mochaOptions = config.get('mochaOptions'); 80 | if (mochaOptions.ui && mochaOptions.ui !== ui) { 81 | const message = 'Mixing ' + mochaOptions.ui + ' and ' + ui + 82 | ' Mocha styles is not supported. ' + 83 | 'You called "' + key + '". Did you mean ' + alternate + '?'; 84 | throw new Error(message); 85 | } 86 | if (_mochaIsSetup) { 87 | return; 88 | } 89 | 90 | applyExtensions(); 91 | mochaOptions.ui = ui; 92 | mocha.setup(mochaOptions); // Note that the reporter is configured in run.js. 93 | } 94 | -------------------------------------------------------------------------------- /runner/httpbin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | 'use strict'; 16 | 17 | import * as bodyParser from 'body-parser'; 18 | import * as cleankill from 'cleankill'; 19 | import * as express from 'express'; 20 | import * as http from 'http'; 21 | import * as multer from 'multer'; 22 | import * as serverDestroy from 'server-destroy'; 23 | 24 | import {findPort} from './port-scanner'; 25 | import {Router} from 'express'; 26 | export {Router} from 'express'; 27 | 28 | export const httpbin: Router = express.Router(); 29 | 30 | function capWords(s: string) { 31 | return s.split('-') 32 | .map((word) => word[0].toUpperCase() + word.slice(1)) 33 | .join('-'); 34 | } 35 | 36 | function formatRequest(req: express.Request) { 37 | const headers = {}; 38 | for (const key in req.headers) { 39 | headers[capWords(key)] = req.headers[key]; 40 | } 41 | const formatted = { 42 | headers: headers, 43 | url: req.originalUrl, 44 | data: req.body, 45 | files: (req).files, 46 | form: {}, 47 | json: {}, 48 | }; 49 | const contentType = 50 | (headers['Content-Type'] || '').toLowerCase().split(';')[0]; 51 | const field = { 52 | 'application/json': 'json', 53 | 'application/x-www-form-urlencoded': 'form', 54 | 'multipart/form-data': 'form' 55 | }[contentType]; 56 | if (field) { 57 | formatted[field] = req.body; 58 | } 59 | return formatted; 60 | } 61 | 62 | httpbin.use(bodyParser.urlencoded({extended: false})); 63 | httpbin.use(bodyParser.json()); 64 | const storage = multer.memoryStorage(); 65 | const upload = multer({storage: storage}); 66 | httpbin.use(upload.any()); 67 | httpbin.use(bodyParser.text()); 68 | httpbin.use(bodyParser.text({type: 'html'})); 69 | httpbin.use(bodyParser.text({type: 'xml'})); 70 | 71 | httpbin.get('/delay/:seconds', function(req, res) { 72 | setTimeout(function() { 73 | res.json(formatRequest(req)); 74 | }, (req.params.seconds || 0) * 1000); 75 | }); 76 | 77 | httpbin.post('/post', function(req, res) { 78 | res.json(formatRequest(req)); 79 | }); 80 | 81 | // Running this script directly with `node httpbin.js` will start up a server 82 | // that just serves out /httpbin/... 83 | // Useful for debugging only the httpbin functionality without the rest of 84 | // wct. 85 | async function main() { 86 | const app = express(); 87 | const server = http.createServer(app) as serverDestroy.DestroyableServer; 88 | 89 | app.use('/httpbin', httpbin); 90 | 91 | 92 | const port = await findPort([7777, 7000, 8000, 8080, 8888]); 93 | 94 | server.listen(port); 95 | (server).port = port; 96 | serverDestroy(server); 97 | cleankill.onInterrupt(() => { 98 | return new Promise((resolve) => { 99 | server.destroy(); 100 | server.on('close', resolve); 101 | }); 102 | }); 103 | 104 | console.log('Server running at http://localhost:' + port + '/httpbin/'); 105 | } 106 | 107 | if (require.main === module) { 108 | main().catch((err) => { 109 | console.error(err); 110 | process.exit(1); 111 | }); 112 | } 113 | -------------------------------------------------------------------------------- /test/integration/setup_test_dir.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as rimraf from 'rimraf'; 4 | 5 | const baseDir = path.join(__dirname, '..', 'fixtures', 'integration'); 6 | 7 | /** 8 | * Sets up the given integration fixture with proper bower components. 9 | * 10 | * For wct to work it needs to be installed in the bower_components directory 11 | * (or, with variants, in each variant directory). So this copies the given 12 | * integration test fixture, then sets up symlinks from 13 | * bower_components/web-component-tester/browser.js to the browser.js of this 14 | * repo. It also makes symlinks for each of wct's bower dependencies into the 15 | * integration tests' bower_components dir. 16 | * 17 | * @param dirname The basename of an integration fixture directory. 18 | * @return A fully resolved path to a copy of the fixture directory with 19 | * a proper bower_components directory. 20 | */ 21 | export async function makeProperTestDir(dirname: string) { 22 | const startingDir = path.join(baseDir, dirname); 23 | const tempDir = path.join(baseDir, 'temp'); 24 | if (await exists(tempDir)) { 25 | await new Promise((resolve, reject) => { 26 | rimraf(tempDir, (err) => err ? reject(err) : resolve()); 27 | }); 28 | } 29 | fs.mkdirSync(tempDir); 30 | 31 | // copy dir 32 | const pathToTestDir = await copyDir(startingDir, tempDir); 33 | 34 | fs.mkdirSync(path.join(pathToTestDir, 'node_modules')); 35 | fs.mkdirSync( 36 | path.join(pathToTestDir, 'node_modules', 'web-component-tester')); 37 | 38 | // set up symlinks into component dirs for browser.js, data/, and wct's 39 | // dependencies (like mocha, sinon, etc) 40 | const componentsDirs = new Set(['bower_components']); 41 | for (const baseFile of fs.readdirSync(startingDir)) { 42 | if (/^bower_components(-|$)/.test(baseFile)) { 43 | componentsDirs.add(baseFile); 44 | } 45 | } 46 | 47 | for (const baseComponentsDir of componentsDirs) { 48 | const componentsDir = path.join(pathToTestDir, baseComponentsDir); 49 | if (!await exists(componentsDir)) { 50 | fs.mkdirSync(componentsDir); 51 | } 52 | // all of wct's bower deps should be present in the project under tests' 53 | // components dir 54 | const bowerDeps = 55 | fs.readdirSync(path.join(__dirname, '../../bower_components')); 56 | for (const baseFile of bowerDeps) { 57 | fs.symlinkSync( 58 | path.join('../../../../../../bower_components', baseFile), 59 | path.join(componentsDir, baseFile)); 60 | } 61 | // Also set up a web-component-tester dir with symlinks into our own 62 | // client-side files. 63 | const wctDir = path.join(componentsDir, 'web-component-tester'); 64 | fs.mkdirSync(wctDir); 65 | fs.symlinkSync( 66 | '../../../../../../../browser.js', path.join(wctDir, 'browser.js'), 67 | 'file'); 68 | fs.symlinkSync( 69 | '../../../../../../../package.json', path.join(wctDir, 'package.json'), 70 | 'file'); 71 | fs.symlinkSync( 72 | '../../../../../../../data', path.join(wctDir, 'data'), 'dir'); 73 | } 74 | 75 | return pathToTestDir; 76 | } 77 | 78 | async function copyDir(from: string, to: string) { 79 | const newDir = path.join(to, path.basename(from)); 80 | fs.mkdirSync(newDir); 81 | for (const baseFile of fs.readdirSync(from)) { 82 | const file = path.join(from, baseFile); 83 | if (fs.statSync(file).isDirectory()) { 84 | await copyDir(file, newDir); 85 | } else { 86 | const newFile = path.join(newDir, baseFile); 87 | fs.writeFileSync(newFile, fs.readFileSync(file)); 88 | } 89 | } 90 | return newDir; 91 | } 92 | 93 | async function exists(fn: string) { 94 | return new Promise((resolve) => fs.stat(fn, (err) => resolve(!err))); 95 | } 96 | -------------------------------------------------------------------------------- /test/unit/paths.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | import {expect} from 'chai'; 15 | import * as path from 'path'; 16 | 17 | import * as paths from '../../runner/paths'; 18 | 19 | describe('paths', function() { 20 | 21 | describe('.expand', function() { 22 | const baseDir = path.resolve(__dirname, '../fixtures/paths'); 23 | 24 | async function expectExpands(patterns: string[], expected: string[]) { 25 | const actual = await paths.expand(baseDir, patterns); 26 | 27 | // for non-POSIX support 28 | expected = expected.map((str) => str.replace(/\//g, path.sep)); 29 | expect(actual).to.have.members(expected); 30 | } 31 | 32 | it('is ok with an empty list', async () => { 33 | await expectExpands([], []); 34 | }); 35 | 36 | it('ignores explicit files that are missing', async () => { 37 | await expectExpands(['404.js'], []); 38 | await expectExpands(['404.js', 'foo.html'], ['foo.html']); 39 | }); 40 | 41 | it('does not expand explicit files', async () => { 42 | await expectExpands(['foo.js'], ['foo.js']); 43 | await expectExpands(['foo.html'], ['foo.html']); 44 | await expectExpands(['foo.js', 'foo.html'], ['foo.js', 'foo.html']); 45 | }); 46 | 47 | it('expands directories into their files', async () => { 48 | await expectExpands(['foo'], ['foo/one.js', 'foo/two.html']); 49 | await expectExpands(['foo/'], ['foo/one.js', 'foo/two.html']); 50 | }); 51 | 52 | it('expands directories into index.html when present', async () => { 53 | await expectExpands(['bar'], ['bar/index.html']); 54 | await expectExpands(['bar/'], ['bar/index.html']); 55 | }); 56 | 57 | it('expands directories recursively, honoring all rules', async () => { 58 | await expectExpands(['baz'], [ 59 | 'baz/a/fizz.html', 60 | 'baz/b/index.html', 61 | 'baz/a.html', 62 | 'baz/b.js', 63 | ]); 64 | }); 65 | 66 | it('accepts globs for explicit file matches', async () => { 67 | await expectExpands(['baz/*.js'], ['baz/b.js']); 68 | await expectExpands(['baz/*.html'], ['baz/a.html']); 69 | await expectExpands(['baz/**/*.js'], [ 70 | 'baz/b/deep/stuff.js', 71 | 'baz/b/one.js', 72 | 'baz/b.js', 73 | ]); 74 | await expectExpands(['baz/**/*.html'], [ 75 | 'baz/a/fizz.html', 76 | 'baz/b/deep/index.html', 77 | 'baz/b/deep/stuff.html', 78 | 'baz/b/index.html', 79 | 'baz/a.html', 80 | ]); 81 | }); 82 | 83 | it('accepts globs for directories, honoring directory behavior', 84 | async () => { 85 | await expectExpands(['*'], [ 86 | 'bar/index.html', 87 | 'baz/a/fizz.html', 88 | 'baz/b/index.html', 89 | 'baz/a.html', 90 | 'baz/b.js', 91 | 'foo/one.js', 92 | 'foo/two.html', 93 | 'foo.html', 94 | 'foo.js', 95 | ]); 96 | await expectExpands(['baz/*'], [ 97 | 'baz/a/fizz.html', 98 | 'baz/b/index.html', 99 | 'baz/a.html', 100 | 'baz/b.js', 101 | ]); 102 | }); 103 | 104 | it('deduplicates', async () => { 105 | await expectExpands(['bar/a.js', 'bar/*.js', 'bar', 'bar/*.html'], [ 106 | 'bar/a.js', 107 | 'bar/index.html', 108 | 'bar/index.js', 109 | ]); 110 | }); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-component-tester", 3 | "version": "6.5.0", 4 | "--private-wct--": { 5 | "client-side-version-range": "4 - 6 || ^6.0.0-prerelease.1", 6 | "wct-browser-legacy-version-range": "0.0.1-pre.1 || ^1.0.0" 7 | }, 8 | "description": "web-component-tester makes testing your web components a breeze!", 9 | "keywords": [ 10 | "browser", 11 | "grunt", 12 | "gruntplugin", 13 | "gulp", 14 | "polymer", 15 | "test", 16 | "testing", 17 | "web component", 18 | "web" 19 | ], 20 | "homepage": "https://github.com/Polymer/web-component-tester", 21 | "bugs": "https://github.com/Polymer/web-component-tester/issues", 22 | "license": "BSD-3-Clause", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/Polymer/web-component-tester.git" 26 | }, 27 | "main": "runner.js", 28 | "bin": { 29 | "wct": "./bin/wct", 30 | "wct-st": "./bin/wct-st" 31 | }, 32 | "files": [ 33 | "bin/", 34 | "data/", 35 | "runner/", 36 | "scripts/", 37 | "tasks/", 38 | ".bowerrc", 39 | "bower.json", 40 | "browser.js", 41 | "browser.js.map", 42 | "package.json", 43 | "LICENSE", 44 | "README.md", 45 | "runner.js" 46 | ], 47 | "scripts": { 48 | "lint": "gulp lint", 49 | "build": "tsc && gulp build", 50 | "test": "tsc && gulp test", 51 | "prepublishOnly": "gulp prepublish", 52 | "test:watch": "watch 'gulp test:unit' runner/ browser/ bin/ test/ tasks/", 53 | "format": "find runner test | grep '\\.js$\\|\\.ts$' | xargs ./node_modules/.bin/clang-format --style=file -i" 54 | }, 55 | "dependencies": { 56 | "@polymer/sinonjs": "^1.14.1", 57 | "@polymer/test-fixture": "^0.0.3", 58 | "@webcomponents/webcomponentsjs": "^1.0.7", 59 | "accessibility-developer-tools": "^2.12.0", 60 | "async": "^2.4.1", 61 | "body-parser": "^1.17.2", 62 | "chai": "^4.0.2", 63 | "chalk": "^1.1.3", 64 | "cleankill": "^2.0.0", 65 | "express": "^4.15.3", 66 | "findup-sync": "^1.0.0", 67 | "glob": "^7.1.2", 68 | "lodash": "^3.10.1", 69 | "mocha": "^3.4.2", 70 | "multer": "^1.3.0", 71 | "nomnom": "^1.8.1", 72 | "polyserve": "^0.23.0", 73 | "promisify-node": "^0.4.0", 74 | "resolve": "^1.3.3", 75 | "semver": "^5.3.0", 76 | "send": "^0.11.1", 77 | "server-destroy": "^1.0.1", 78 | "sinon": "^2.3.5", 79 | "sinon-chai": "^2.10.0", 80 | "socket.io": "^2.0.3", 81 | "stacky": "^1.3.1", 82 | "wd": "^1.2.0" 83 | }, 84 | "optionalDependencies": { 85 | "update-notifier": "^2.2.0", 86 | "wct-local": "^2.1.0", 87 | "wct-sauce": "^2.0.0" 88 | }, 89 | "devDependencies": { 90 | "@types/body-parser": "0.0.33", 91 | "@types/chai": "^3.4.34", 92 | "@types/chalk": "^0.4.31", 93 | "@types/express": "^4.0.33", 94 | "@types/express-serve-static-core": "^4.0.39", 95 | "@types/glob": "^5.0.30", 96 | "@types/grunt": "^0.4.20", 97 | "@types/gulp": "^3.8.8", 98 | "@types/lodash": "^4.14.38", 99 | "@types/mime": "0.0.29", 100 | "@types/minimatch": "^2.0.29", 101 | "@types/mocha": "^2.2.32", 102 | "@types/multer": "0.0.32", 103 | "@types/node": "^8.0.0", 104 | "@types/nomnom": "0.0.28", 105 | "@types/rimraf": "0.0.28", 106 | "@types/semver": "^5.3.30", 107 | "@types/sinon": "^1.16.31", 108 | "@types/sinon-chai": "^2.7.27", 109 | "@types/socket.io": "^1.4.27", 110 | "bower": "^1.7.9", 111 | "clang-format": "^1.0.43", 112 | "depcheck": "^0.6.3", 113 | "grunt": "^0.4.5", 114 | "gulp": "^3.8.8", 115 | "gulp-bower": "0.0.13", 116 | "gulp-concat": "^2.6.1", 117 | "gulp-spawn-mocha": "^3.1.0", 118 | "gulp-tslint": "^8.1.2", 119 | "gulp-typescript": "^3.1.2", 120 | "lazypipe": "^1.0.1", 121 | "rimraf": "^2.5.4", 122 | "rollup": "^0.25.1", 123 | "run-sequence": "^1.0.1", 124 | "tslint": "^5.7.0", 125 | "typescript": "^2.1.4", 126 | "watch": "^0.18.0" 127 | }, 128 | "engines": { 129 | "node": ">= 6.0" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /browser/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | /** 12 | * This file is the entry point into `web-component-tester`'s browser client. 13 | */ 14 | // Registers a bunch of globals: 15 | import './environment/helpers.js'; 16 | 17 | import ChildRunner from './childrunner.js'; 18 | import CLISocket from './clisocket.js'; 19 | import * as config from './config.js'; 20 | import * as environment from './environment.js'; 21 | import * as errors from './environment/errors.js'; 22 | import * as mocha from './mocha.js'; 23 | import * as reporters from './reporters.js'; 24 | import MultiReporter from './reporters/multi.js'; 25 | import * as suites from './suites.js'; 26 | import * as util from './util.js'; 27 | 28 | // You can configure WCT before it has loaded by assigning your custom 29 | // configuration to the global `WCT`. 30 | config.setup(window.WCT as any as config.Config); 31 | 32 | // Maybe some day we'll expose WCT as a module to whatever module registry you 33 | // are using (aka the UMD approach), or as an es6 module. 34 | const WCT = window.WCT = { 35 | // A generic place to hang data about the current suite. This object is 36 | // reported 37 | // back via the `sub-suite-start` and `sub-suite-end` events. 38 | share: {}, 39 | // Until then, we get to rely on it to expose parent runners to their 40 | // children. 41 | _ChildRunner: ChildRunner, 42 | _reporter: undefined as any, // assigned below 43 | _config: config._config, 44 | 45 | // Public API 46 | 47 | /** 48 | * Loads suites of tests, supporting both `.js` and `.html` files. 49 | * 50 | * @param {!Array.} files The files to load. 51 | */ 52 | loadSuites: suites.loadSuites, 53 | }; 54 | 55 | // Load Process 56 | 57 | errors.listenForErrors(); 58 | mocha.stubInterfaces(); 59 | environment.loadSync(); 60 | 61 | // Give any scripts on the page a chance to declare tests and muck with things. 62 | document.addEventListener('DOMContentLoaded', function() { 63 | util.debug('DOMContentLoaded'); 64 | 65 | environment.ensureDependenciesPresent(); 66 | 67 | // We need the socket built prior to building its reporter. 68 | CLISocket.init(function(error, socket) { 69 | if (error) 70 | throw error; 71 | 72 | // Are we a child of another run? 73 | const current = ChildRunner.current(); 74 | const parent = current && current.parentScope.WCT._reporter; 75 | util.debug('parentReporter:', parent); 76 | 77 | const childSuites = suites.activeChildSuites(); 78 | const reportersToUse = reporters.determineReporters(socket, parent); 79 | // +1 for any local tests. 80 | const reporter = 81 | new MultiReporter(childSuites.length + 1, reportersToUse, parent); 82 | WCT._reporter = reporter; // For environment/compatibility.js 83 | 84 | // We need the reporter so that we can report errors during load. 85 | suites.loadJsSuites(reporter, function(error) { 86 | // Let our parent know that we're about to start the tests. 87 | if (current) 88 | current.ready(error); 89 | if (error) 90 | throw error; 91 | 92 | // Emit any errors we've encountered up til now 93 | errors.globalErrors.forEach(function onError(error) { 94 | reporter.emitOutOfBandTest('Test Suite Initialization', error); 95 | }); 96 | 97 | suites.runSuites(reporter, childSuites, function(error) { 98 | // Make sure to let our parent know that we're done. 99 | if (current) 100 | current.done(); 101 | if (error) 102 | throw error; 103 | }); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /test/unit/grunt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | import * as chai from 'chai'; 15 | import * as grunt from 'grunt'; 16 | import * as _ from 'lodash'; 17 | import * as path from 'path'; 18 | import * as sinon from 'sinon'; 19 | 20 | import {Context} from '../../runner/context'; 21 | import * as steps from '../../runner/steps'; 22 | 23 | 24 | const wctLocalBrowsers = require('wct-local/lib/browsers'); 25 | const expect = chai.expect; 26 | chai.use(require('sinon-chai')); 27 | 28 | const LOCAL_BROWSERS = { 29 | aurora: {browserName: 'aurora', version: '1'}, 30 | canary: {browserName: 'canary', version: '2'}, 31 | chrome: {browserName: 'chrome', version: '3'}, 32 | firefox: {browserName: 'firefox', version: '4'}, 33 | }; 34 | 35 | describe('grunt', function() { 36 | 37 | // Sinon doesn't stub process.env very well. 38 | let origEnv: any, origArgv: any; 39 | beforeEach(function() { 40 | origEnv = _.clone(process.env); 41 | origArgv = process.argv; 42 | }); 43 | afterEach(function() { 44 | _.assign(process.env, origEnv); 45 | _.difference(_.keys(process.env), _.keys(origEnv)).forEach(function(key) { 46 | delete process.env[key]; 47 | }); 48 | process.argv = origArgv; 49 | }); 50 | 51 | before(function() { 52 | grunt.initConfig({ 53 | 'wct-test': { 54 | 'passthrough': { 55 | options: {foo: 1, bar: 'asdf'}, 56 | }, 57 | 'override': { 58 | options: {sauce: {username: '--real-sauce--'}}, 59 | }, 60 | }, 61 | }); 62 | grunt.loadTasks(path.resolve(__dirname, '../../tasks')); 63 | }); 64 | 65 | async function runTask(task: string) { 66 | await new Promise((resolve, reject) => { 67 | grunt.task['options']({error: reject, done: resolve}); 68 | grunt.task.run('wct-test:' + task)['start'](); 69 | }); 70 | // We shouldn't error before hitting it. 71 | expect(steps.runTests).to.have.been.calledOnce; 72 | return <{args: [Context]}>steps.runTests['getCall'](0); 73 | } 74 | 75 | describe('wct-test', function() { 76 | 77 | let sandbox: sinon.SinonSandbox; 78 | beforeEach(function() { 79 | sandbox = sinon.sandbox.create(); 80 | sandbox.stub(steps, 'prepare') 81 | .callsFake( 82 | async(_context: Context): Promise => undefined); 83 | 84 | sandbox.stub(wctLocalBrowsers, 'detect').callsFake(async () => LOCAL_BROWSERS); 85 | sandbox.stub(wctLocalBrowsers, 'supported').callsFake(() => _.keys(LOCAL_BROWSERS)); 86 | 87 | process.chdir(path.resolve(__dirname, '../fixtures/cli/standard')); 88 | }); 89 | 90 | afterEach(function() { 91 | sandbox.restore(); 92 | }); 93 | 94 | describe('with a passing suite', function() { 95 | 96 | beforeEach(function() { 97 | sandbox.stub(steps, 'runTests').callsFake(async(): Promise => undefined); 98 | }); 99 | 100 | it('passes configuration through', async () => { 101 | const call = await runTask('passthrough'); 102 | expect(call.args[0].options).to.include({foo: 1, bar: 'asdf'}); 103 | }); 104 | }); 105 | 106 | describe('with a failing suite', function() { 107 | beforeEach(function() { 108 | sandbox.stub(steps, 'runTests').callsFake(async () => { 109 | throw 'failures'; 110 | }); 111 | }); 112 | 113 | it('passes errors out', async () => { 114 | try { 115 | await runTask('passthrough'); 116 | } catch (error) { 117 | return; // All's well! 118 | } 119 | throw new Error('Expected runTask to fail!'); 120 | }); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /runner/plugin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | import * as _ from 'lodash'; 16 | import * as path from 'path'; 17 | 18 | import {Config} from './config'; 19 | import {Context} from './context'; 20 | 21 | // Plugin module names can be prefixed by the following: 22 | const PREFIXES = [ 23 | 'web-component-tester-', 24 | 'wct-', 25 | ]; 26 | 27 | export interface Metadata {} 28 | 29 | /** 30 | * A WCT plugin. This constructor is private. Plugins can be retrieved via 31 | * `Plugin.get`. 32 | */ 33 | export class Plugin { 34 | name: string; 35 | cliConfig: Config; 36 | packageName: string; 37 | metadata: Metadata; 38 | constructor(packageName: string, metadata: Metadata) { 39 | this.packageName = packageName; 40 | this.metadata = metadata; 41 | this.name = Plugin.shortName(packageName); 42 | 43 | this.cliConfig = this.metadata['cli-options'] || {}; 44 | } 45 | 46 | /** 47 | * @param {!Context} context The context that this plugin should be evaluated 48 | * within. 49 | */ 50 | async execute(context: Context): Promise { 51 | try { 52 | const plugin = require(this.packageName); 53 | plugin(context, context.pluginOptions(this.name), this); 54 | } catch (error) { 55 | throw `Failed to load plugin "${this.name}": ${error}`; 56 | } 57 | } 58 | 59 | /** 60 | * Retrieves a plugin by shorthand or module name (loading it as necessary). 61 | * 62 | * @param {string} name 63 | */ 64 | static async get(name: string): Promise { 65 | const shortName = Plugin.shortName(name); 66 | if (_loadedPlugins[shortName]) { 67 | return _loadedPlugins[shortName]; 68 | } 69 | 70 | const names = [shortName].concat(PREFIXES.map((p) => p + shortName)); 71 | const loaded = _.compact(names.map(_tryLoadPluginPackage)); 72 | if (loaded.length > 1) { 73 | const prettyNames = loaded.map((p) => p.packageName).join(' '); 74 | throw `Loaded conflicting WCT plugin packages: ${prettyNames}`; 75 | } 76 | if (loaded.length < 1) { 77 | throw `Could not find WCT plugin named "${name}"`; 78 | } 79 | 80 | return loaded[0]; 81 | } 82 | 83 | /** 84 | * @param {string} name 85 | * @return {string} The short form of `name`. 86 | */ 87 | static shortName(name: string) { 88 | for (const prefix of PREFIXES) { 89 | if (name.indexOf(prefix) === 0) { 90 | return name.substr(prefix.length); 91 | } 92 | } 93 | return name; 94 | } 95 | 96 | // HACK(rictic): Makes es6 style imports happy, so that we can do, e.g. 97 | // import {Plugin} from './plugin'; 98 | static Plugin = Plugin; 99 | } 100 | 101 | // Plugin Loading 102 | 103 | // We maintain an identity map of plugins, keyed by short name. 104 | const _loadedPlugins: {[name: string]: Plugin} = {}; 105 | 106 | /** 107 | * @param {string} packageName Attempts to load a package as a WCT plugin. 108 | * @return {Plugin} 109 | */ 110 | function _tryLoadPluginPackage(packageName: string) { 111 | let packageInfo: Object; 112 | try { 113 | packageInfo = require(path.join(packageName, 'package.json')); 114 | } catch (error) { 115 | if (error.code !== 'MODULE_NOT_FOUND') { 116 | console.log(error); 117 | } 118 | return null; 119 | } 120 | 121 | // Plugins must have a (truthy) wct-plugin field. 122 | if (!packageInfo['wct-plugin']) { 123 | return null; 124 | } 125 | // Allow {"wct-plugin": true} as a shorthand. 126 | const metadata = 127 | _.isObject(packageInfo['wct-plugin']) ? packageInfo['wct-plugin'] : {}; 128 | 129 | return new Plugin(packageName, metadata); 130 | } 131 | 132 | 133 | module.exports = Plugin; 134 | -------------------------------------------------------------------------------- /browser/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import ChildRunner from './childrunner.js'; 12 | import * as util from './util.js'; 13 | 14 | export interface Config { 15 | /** 16 | * `.js` scripts to be loaded (synchronously) before WCT starts in earnest. 17 | * 18 | * Paths are relative to `scriptPrefix`. 19 | */ 20 | environmentScripts: string[]; 21 | environmentImports: string[]; 22 | /** Absolute root for client scripts. Detected in `setup()` if not set. */ 23 | root: null|string; 24 | /** By default, we wait for any web component frameworks to load. */ 25 | waitForFrameworks: boolean; 26 | /** 27 | * Alternate callback for waiting for tests. 28 | * `this` for the callback will be the window currently running tests. 29 | */ 30 | waitFor: null|Function; 31 | /** How many `.html` suites that can be concurrently loaded & run. */ 32 | numConcurrentSuites: number; 33 | /** Whether `console.error` should be treated as a test failure. */ 34 | trackConsoleError: boolean; 35 | /** Configuration passed to mocha.setup. */ 36 | mochaOptions: MochaSetupOptions; 37 | /** Whether WCT should emit (extremely verbose) debugging log messages. */ 38 | verbose: boolean; 39 | } 40 | 41 | /** 42 | * The global configuration state for WCT's browser client. 43 | */ 44 | export let _config: Config = { 45 | environmentScripts: !!window.__wctUseNpm ? 46 | [ 47 | 'stacky/browser.js', 'async/lib/async.js', 'lodash/index.js', 48 | 'mocha/mocha.js', 'chai/chai.js', '@polymer/sinonjs/sinon.js', 49 | 'sinon-chai/lib/sinon-chai.js', 50 | 'accessibility-developer-tools/dist/js/axs_testing.js', 51 | '@polymer/test-fixture/test-fixture.js' 52 | ] : 53 | [ 54 | 'stacky/browser.js', 'async/lib/async.js', 'lodash/lodash.js', 55 | 'mocha/mocha.js', 'chai/chai.js', 'sinonjs/sinon.js', 56 | 'sinon-chai/lib/sinon-chai.js', 57 | 'accessibility-developer-tools/dist/js/axs_testing.js' 58 | ], 59 | 60 | environmentImports: !!window.__wctUseNpm ? [] : 61 | ['test-fixture/test-fixture.html'], 62 | root: null as null | string, 63 | waitForFrameworks: true, 64 | waitFor: null as null | Function, 65 | numConcurrentSuites: 1, 66 | trackConsoleError: true, 67 | mochaOptions: {timeout: 10 * 1000}, 68 | verbose: false, 69 | }; 70 | 71 | /** 72 | * Merges initial `options` into WCT's global configuration. 73 | * 74 | * @param {Object} options The options to merge. See `browser/config.js` for a 75 | * reference. 76 | */ 77 | export function setup(options: Config) { 78 | const childRunner = ChildRunner.current(); 79 | if (childRunner) { 80 | _deepMerge(_config, childRunner.parentScope.WCT._config); 81 | // But do not force the mocha UI 82 | delete _config.mochaOptions.ui; 83 | } 84 | 85 | if (options && typeof options === 'object') { 86 | _deepMerge(_config, options); 87 | } 88 | 89 | if (!_config.root) { 90 | // Sibling dependencies. 91 | const root = util.scriptPrefix('browser.js'); 92 | _config.root = util.basePath(root.substr(0, root.length - 1)); 93 | if (!_config.root) { 94 | throw new Error( 95 | 'Unable to detect root URL for WCT sources. Please set WCT.root before including browser.js'); 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * Retrieves a configuration value. 102 | */ 103 | export function get(key: K): Config[K] { 104 | return _config[key]; 105 | } 106 | 107 | // Internal 108 | function _deepMerge(target: Partial, source: Config) { 109 | Object.keys(source).forEach(function(key) { 110 | if (target[key] !== null && typeof target[key] === 'object' && 111 | !Array.isArray(target[key])) { 112 | _deepMerge(target[key], source[key]); 113 | } else { 114 | target[key] = source[key]; 115 | } 116 | }); 117 | } 118 | -------------------------------------------------------------------------------- /browser/mocha/replace.ts: -------------------------------------------------------------------------------- 1 | import {extendInterfaces} from './extend.js'; 2 | 3 | // replacement map stores what should be 4 | let replacements = {}; 5 | let replaceTeardownAttached = false; 6 | 7 | /** 8 | * replace 9 | * 10 | * The replace addon allows the tester to replace all usages of one element with 11 | * another element within all Polymer elements created within the time span of 12 | * the test. Usage example: 13 | * 14 | * beforeEach(function() { 15 | * replace('x-foo').with('x-fake-foo'); 16 | * }); 17 | * 18 | * All annotations and attributes will be set on the placement element the way 19 | * they were set for the original element. 20 | */ 21 | extendInterfaces('replace', function(_context, teardown) { 22 | return function replace(oldTagName: string) { 23 | return { 24 | with: function(tagName: string) { 25 | // Standardizes our replacements map 26 | oldTagName = oldTagName.toLowerCase(); 27 | tagName = tagName.toLowerCase(); 28 | 29 | replacements[oldTagName] = tagName; 30 | 31 | // If the function is already a stub, restore it to original 32 | if ((document.importNode as any).isSinonProxy) { 33 | return; 34 | } 35 | 36 | if (!window.Polymer.Element) { 37 | window.Polymer.Element = function() {}; 38 | window.Polymer.Element.prototype._stampTemplate = function() {}; 39 | } 40 | 41 | // Keep a reference to the original `document.importNode` 42 | // implementation for later: 43 | const originalImportNode = document.importNode; 44 | 45 | // Use Sinon to stub `document.ImportNode`: 46 | sinon.stub( 47 | document, 'importNode', function(origContent: any, deep: boolean) { 48 | const templateClone = document.createElement('template'); 49 | const content = templateClone.content; 50 | const inertDoc = content.ownerDocument; 51 | 52 | // imports node from inertDoc which holds inert nodes. 53 | templateClone.content.appendChild( 54 | inertDoc.importNode(origContent, true)); 55 | 56 | // optional arguments are not optional on IE. 57 | const nodeIterator = document.createNodeIterator( 58 | content, NodeFilter.SHOW_ELEMENT, null, true); 59 | let node; 60 | 61 | // Traverses the tree. A recently-replaced node will be put next, 62 | // so if a node is replaced, it will be checked if it needs to be 63 | // replaced again. 64 | while (node = nodeIterator.nextNode() as Element) { 65 | let currentTagName = node.tagName.toLowerCase(); 66 | 67 | if (replacements.hasOwnProperty(currentTagName)) { 68 | currentTagName = replacements[currentTagName]; 69 | 70 | // find the final tag name. 71 | while (replacements[currentTagName]) { 72 | currentTagName = replacements[currentTagName]; 73 | } 74 | 75 | // Create a replacement: 76 | const replacement = document.createElement(currentTagName); 77 | 78 | // For all attributes in the original node.. 79 | for (let index = 0; index < node.attributes.length; ++index) { 80 | // Set that attribute on the replacement: 81 | replacement.setAttribute( 82 | node.attributes[index].name, 83 | node.attributes[index].value); 84 | } 85 | 86 | // Replace the original node with the replacement node: 87 | node.parentNode.replaceChild(replacement, node); 88 | } 89 | } 90 | 91 | return originalImportNode.call(this, content, deep); 92 | }); 93 | 94 | if (!replaceTeardownAttached) { 95 | // After each test... 96 | teardown(function() { 97 | replaceTeardownAttached = true; 98 | // Restore the stubbed version of `document.importNode`: 99 | const documentImportNode = document.importNode as any; 100 | if (documentImportNode.isSinonProxy) { 101 | documentImportNode.restore(); 102 | } 103 | 104 | // Empty the replacement map 105 | replacements = {}; 106 | }); 107 | } 108 | } 109 | }; 110 | }; 111 | }); 112 | -------------------------------------------------------------------------------- /browser/reporters/console.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt The complete set of authors may be found 6 | * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may 7 | * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by 8 | * Google as part of the polymer project is also subject to an additional IP 9 | * rights grant found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import * as util from '../util.js'; 12 | 13 | // We capture console events when running tests; so make sure we have a 14 | // reference to the original one. 15 | const console = window.console; 16 | 17 | const FONT = 18 | ';font: normal 13px "Roboto", "Helvetica Neue", "Helvetica", sans-serif;'; 19 | const STYLES = { 20 | plain: FONT, 21 | suite: 'color: #5c6bc0' + FONT, 22 | test: FONT, 23 | passing: 'color: #259b24' + FONT, 24 | pending: 'color: #e65100' + FONT, 25 | failing: 'color: #c41411' + FONT, 26 | stack: 'color: #c41411', 27 | results: FONT + 'font-size: 16px', 28 | }; 29 | 30 | // I don't think we can feature detect this one... 31 | const userAgent = navigator.userAgent.toLowerCase(); 32 | const CAN_STYLE_LOG = userAgent.match('firefox') || userAgent.match('webkit'); 33 | const CAN_STYLE_GROUP = userAgent.match('webkit'); 34 | // Track the indent for faked `console.group` 35 | let logIndent = ''; 36 | 37 | function log(text: string, style?: keyof typeof STYLES) { 38 | text = text.split('\n') 39 | .map(function(l) { 40 | return logIndent + l; 41 | }) 42 | .join('\n'); 43 | if (CAN_STYLE_LOG) { 44 | console.log('%c' + text, STYLES[style] || STYLES.plain); 45 | } else { 46 | console.log(text); 47 | } 48 | } 49 | 50 | function logGroup(text: string, style?: keyof typeof STYLES) { 51 | if (CAN_STYLE_GROUP) { 52 | console.group('%c' + text, STYLES[style] || STYLES.plain); 53 | } else if (console.group) { 54 | console.group(text); 55 | } else { 56 | logIndent = logIndent + ' '; 57 | log(text, style); 58 | } 59 | } 60 | 61 | function logGroupEnd() { 62 | if (console.groupEnd) { 63 | console.groupEnd(); 64 | } else { 65 | logIndent = logIndent.substr(0, logIndent.length - 2); 66 | } 67 | } 68 | 69 | function logException(error: Error) { 70 | log(error.stack || error.message || (error + ''), 'stack'); 71 | } 72 | 73 | /** 74 | * A Mocha reporter that logs results out to the web `console`. 75 | */ 76 | export default class Console { 77 | /** 78 | * @param runner The runner that is being reported on. 79 | */ 80 | constructor(runner: Mocha.IRunner) { 81 | Mocha.reporters.Base.call(this, runner); 82 | 83 | runner.on('suite', function(suite: Mocha.ISuite) { 84 | if (suite.root) { 85 | return; 86 | } 87 | logGroup(suite.title, 'suite'); 88 | }.bind(this)); 89 | 90 | runner.on('suite end', function(suite: Mocha.ISuite) { 91 | if (suite.root) { 92 | return; 93 | } 94 | logGroupEnd(); 95 | }.bind(this)); 96 | 97 | runner.on('test', function(test: Mocha.ITest) { 98 | logGroup(test.title, 'test'); 99 | }.bind(this)); 100 | 101 | runner.on('pending', function(test: Mocha.ITest) { 102 | logGroup(test.title, 'pending'); 103 | }.bind(this)); 104 | 105 | runner.on('fail', function(_test: Mocha.ITest, error: any) { 106 | logException(error); 107 | }.bind(this)); 108 | 109 | runner.on('test end', function(_test: Mocha.ITest) { 110 | logGroupEnd(); 111 | }.bind(this)); 112 | 113 | runner.on('end', this.logSummary.bind(this)); 114 | } 115 | 116 | /** Prints out a final summary of test results. */ 117 | logSummary() { 118 | logGroup('Test Results', 'results'); 119 | 120 | if (this.stats.failures > 0) { 121 | log(util.pluralizedStat(this.stats.failures, 'failing'), 'failing'); 122 | } 123 | if (this.stats.pending > 0) { 124 | log(util.pluralizedStat(this.stats.pending, 'pending'), 'pending'); 125 | } 126 | log(util.pluralizedStat(this.stats.passes, 'passing')); 127 | 128 | if (!this.stats.failures) { 129 | log('test suite passed', 'passing'); 130 | } 131 | log('Evaluated ' + this.stats.tests + ' tests in ' + 132 | (this.stats as any).duration + 'ms.'); 133 | logGroupEnd(); 134 | } 135 | } 136 | 137 | export default interface Console extends Mocha.reporters.Base {} 138 | -------------------------------------------------------------------------------- /runner/cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at 5 | * http://polymer.github.io/LICENSE.txt 6 | * The complete set of authors may be found at 7 | * http://polymer.github.io/AUTHORS.txt 8 | * The complete set of contributors may be found at 9 | * http://polymer.github.io/CONTRIBUTORS.txt 10 | * Code distributed by Google as part of the polymer project is also 11 | * subject to an additional IP rights grant found at 12 | * http://polymer.github.io/PATENTS.txt 13 | */ 14 | 15 | import * as chalk from 'chalk'; 16 | import * as events from 'events'; 17 | import * as _ from 'lodash'; 18 | 19 | import {CliReporter} from './clireporter'; 20 | import * as config from './config'; 21 | import {Context} from './context'; 22 | import {Plugin} from './plugin'; 23 | import {test} from './test'; 24 | 25 | const PACKAGE_INFO = require('../package.json'); 26 | const noopNotifier = { 27 | notify: () => {} 28 | }; 29 | let updateNotifier = noopNotifier; 30 | 31 | (function() { 32 | try { 33 | updateNotifier = require('update-notifier')({pkg: PACKAGE_INFO}); 34 | } catch (error) { 35 | // S'ok if we don't have update-notifier. It's optional. 36 | } 37 | })(); 38 | 39 | export async function run( 40 | _env: any, args: string[], output: NodeJS.WritableStream): Promise { 41 | await wrapResult(output, _run(args, output)); 42 | } 43 | 44 | async function _run(args: string[], output: NodeJS.WritableStream) { 45 | // If the "--version" or "-V" flag is ever present, just print 46 | // the current version. Useful for globally installed CLIs. 47 | if (args.includes('--version') || args.includes('-V')) { 48 | output.write(`${PACKAGE_INFO.version}\n`); 49 | return Promise.resolve(); 50 | } 51 | 52 | // Options parsing is a two phase affair. First, we need an initial set of 53 | // configuration so that we know which plugins to load, etc: 54 | let options = config.preparseArgs(args) as config.Config; 55 | // Depends on values from the initial merge: 56 | options = config.merge(options, { 57 | output: output, 58 | ttyOutput: !process.env.CI && output['isTTY'] && !options.simpleOutput, 59 | }); 60 | const context = new Context(options); 61 | 62 | if (options.skipUpdateCheck) { 63 | updateNotifier = noopNotifier; 64 | } 65 | 66 | // `parseArgs` merges any new configuration into `context.options`. 67 | await config.parseArgs(context, args); 68 | await test(context); 69 | } 70 | 71 | // Note that we're cheating horribly here. Ideally all of this logic is within 72 | // wct-sauce. The trouble is that we also want WCT's configuration lookup logic, 73 | // and that's not (yet) cleanly exposed. 74 | export async function runSauceTunnel( 75 | _env: any, args: string[], output: NodeJS.WritableStream): Promise { 76 | await wrapResult(output, _runSauceTunnel(args, output)); 77 | } 78 | 79 | async function _runSauceTunnel(args: string[], output: NodeJS.WritableStream) { 80 | const cmdOptions = config.preparseArgs(args) as config.Config; 81 | const context = new Context(cmdOptions); 82 | 83 | const diskOptions = context.options; 84 | const baseOptions: config.Config = 85 | (diskOptions.plugins && diskOptions.plugins['sauce']) || 86 | diskOptions.sauce || {}; 87 | 88 | const plugin = await Plugin.get('sauce'); 89 | const parser = require('nomnom'); 90 | parser.script('wct-st'); 91 | parser.options(_.omit(plugin.cliConfig, 'browsers', 'tunnelId')); 92 | const options = _.merge(baseOptions, parser.parse(args)); 93 | 94 | const wctSauce = require('wct-sauce'); 95 | wctSauce.expandOptions(options); 96 | 97 | const emitter = new events.EventEmitter(); 98 | new CliReporter(emitter, output, {}); 99 | const tunnelId = await new Promise((resolve, reject) => { 100 | wctSauce.startTunnel( 101 | options, emitter, 102 | (error: any, tunnelId: string) => 103 | error ? reject(error) : resolve(tunnelId)); 104 | }); 105 | 106 | output.write('\n'); 107 | output.write( 108 | 'The tunnel will remain active while this process is running.\n'); 109 | output.write( 110 | 'To use this tunnel for other WCT runs, export the following:\n'); 111 | output.write('\n'); 112 | output.write(chalk.cyan('export SAUCE_TUNNEL_ID=' + tunnelId) + '\n'); 113 | } 114 | 115 | async function wrapResult( 116 | output: NodeJS.WritableStream, promise: Promise) { 117 | let error: any; 118 | try { 119 | await promise; 120 | } catch (e) { 121 | error = e; 122 | } 123 | 124 | if (!process.env.CI) { 125 | updateNotifier.notify(); 126 | } 127 | 128 | if (error) { 129 | output.write('\n'); 130 | output.write(chalk.red(error) + '\n'); 131 | output.write('\n'); 132 | throw error; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /data/a11ySuite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4 | * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 | * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 | * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 | * Code distributed by Google as part of the polymer project is also 8 | * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 | */ 10 | 11 | var a11ySuiteExport; 12 | 13 | (function(Mocha, chai, axs) { 14 | 15 | Object.keys(Mocha.interfaces).forEach(function(iface) { 16 | var orig = Mocha.interfaces[iface]; 17 | 18 | Mocha.interfaces[iface] = function(suite) { 19 | orig.apply(this, arguments); 20 | 21 | var Suite = Mocha.Suite; 22 | var Test = Mocha.Test; 23 | 24 | suite.on('pre-require', function(context, file, mocha) { 25 | 26 | /** 27 | * Runs the Chrome Accessibility Developer Tools Audit against a test-fixture 28 | * 29 | * @param {String} fixtureId ID of the fixture element in the document to use 30 | * @param {Array?} ignoredRules Array of rules to ignore for this suite 31 | * @param {Function?} beforeEach Function to be called before each test to ensure proper setup 32 | */ 33 | a11ySuiteExport = context.a11ySuite = function(fixtureId, ignoredRules, beforeEach) { 34 | // capture a reference to the fixture element early 35 | var fixtureElement = document.getElementById(fixtureId); 36 | if (!fixtureElement) { 37 | return; 38 | } 39 | 40 | // build an audit config to disable certain ignorable tests 41 | var axsConfig = new axs.AuditConfiguration(); 42 | axsConfig.scope = document.body; 43 | axsConfig.showUnsupportedRulesWarning = false; 44 | axsConfig.auditRulesToIgnore = ignoredRules; 45 | 46 | // build mocha suite 47 | var a11ySuite = Suite.create(suite, 'A11y Audit - Fixture: ' + fixtureId); 48 | 49 | // override the `eachTest` function to hackily create the tests 50 | // 51 | // eachTest is called right before test runs to calculate the total 52 | // number of tests 53 | a11ySuite.eachTest = function() { 54 | // instantiate fixture 55 | fixtureElement.create(); 56 | 57 | // Make sure lazy-loaded dom is ready (eg