From 651978726aa1342ddc3c7ff423d872878c08b141 Mon Sep 17 00:00:00 2001 From: Michael Kelly Date: Mon, 23 Jul 2018 02:05:53 -0700 Subject: [PATCH 1/2] Fix #28: Add Firefox/Marionette/Tape-based test suite. Adds an automated test suite that runs JS tests using Tape. The tests are run in Firefox using the remote code execution capabilities of Marionette. Marionette's only well-maintained client is in Python, so we also have to add Python dependencies, including a Pipfile. bin/run_tests.py contains most of the plumbing for getting the test JS running, including bundling it with Webpack, launching Firefox, and formatting the output. --- .circleci/config.yml | 11 +- .gitignore | 1 + Pipfile | 18 + Pipfile.lock | 251 +++++++++++++ README.md | 31 +- bin/run_tests.py | 78 ++++ package-lock.json | 347 +++++++++++++++++- package.json | 13 +- src/tests/.eslintrc.json | 9 + src/tests/index.jsx | 37 ++ .../sidebar/components/test_Accordion.jsx | 16 + webpack.config.js | 9 + webpack.config.test.js | 54 +++ 13 files changed, 870 insertions(+), 5 deletions(-) create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100755 bin/run_tests.py create mode 100644 src/tests/.eslintrc.json create mode 100644 src/tests/index.jsx create mode 100644 src/tests/sidebar/components/test_Accordion.jsx create mode 100644 webpack.config.test.js diff --git a/.circleci/config.yml b/.circleci/config.yml index ec972b1..d9cb6ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,13 +2,16 @@ version: 2 jobs: build: docker: - - image: circleci/node:10-browsers + - image: circleci/python:2.7-node-browsers steps: - checkout - run: name: Install dependencies command: | + pipenv install npm install + pipenv run mozdownload --version latest --destination $HOME/firefox.tar.bz2 + pipenv run mozinstall --destination $HOME $HOME/firefox.tar.bz2 - run: name: Build extension command: | @@ -17,9 +20,15 @@ jobs: name: Run linting checks command: | npm run lint + - run: + name: Run automated tests + command: | + pipenv run test -- --firefox_bin $HOME/firefox/firefox - run: name: Package extension as XPI command: | npm run package - store_artifacts: path: web-ext-artifacts + - store_artifacts: + path: gecko.log diff --git a/.gitignore b/.gitignore index ea9fc82..a26de97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules web-ext-artifacts build +gecko.log diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..2622af5 --- /dev/null +++ b/Pipfile @@ -0,0 +1,18 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[dev-packages] + +[packages] +click = "*" +marionette-client = "*" +mozdownload = "*" +mozinstall = "*" + +[requires] +python_version = "2.7" + +[scripts] +test = "npm test" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..454e8d3 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,251 @@ +{ + "_meta": { + "hash": { + "sha256": "5b6ba8e19281de5fdd31b2ced1fd9ece5591aacea4e432fe021c473d60657f61" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "2.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "blessings": { + "hashes": [ + "sha256:98e5854d805f50a5b58ac2333411b0482516a8210f23f43308baeb58d77c157d", + "sha256:b1fdd7e7a675295630f9ae71527a8ebc10bfefa236b3d6aa4932ee4462c17ba3", + "sha256:caad5211e7ba5afe04367cdd4cfc68fa886e2e08f6f35e76b7387d2109ccea6e" + ], + "version": "==1.7" + }, + "browsermob-proxy": { + "hashes": [ + "sha256:5f0e72767938d268999f1b56b0e8ff01cecd051bb868637ff550e25495cc840b", + "sha256:fb345bc2207fccdb8a584694c8d02d01c2cfc539c9d43bbed38f0c54e1abbbaf" + ], + "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", + "version": "==0.8.0" + }, + "certifi": { + "hashes": [ + "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", + "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" + ], + "version": "==2018.4.16" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "index": "pypi", + "version": "==6.7" + }, + "idna": { + "hashes": [ + "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", + "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + ], + "version": "==2.7" + }, + "manifestparser": { + "hashes": [ + "sha256:adb0c58b8811711fc2220cb23620b7cd1fb7f61124cc61e2e54c592f90919619" + ], + "version": "==1.1" + }, + "marionette-client": { + "hashes": [ + "sha256:6bf104cfe75c1b3a8dfcfb6dfdd35427e96b3c31213eae6b34e5a670e695624e", + "sha256:d4ee1a2848215a42a12e99f5994a6a6da1aef6460146c142e22dfd3deb7f0291" + ], + "index": "pypi", + "version": "==3.3.0" + }, + "marionette-driver": { + "hashes": [ + "sha256:15c77ba548847dc05ce1b663a22c3324623f217dce5a859c3aaced31fd16707b", + "sha256:4b550e719f3d8245a51330bb4039a6f1972db9308a5707cac82a28266cdb3459" + ], + "version": "==2.7.0" + }, + "mohawk": { + "hashes": [ + "sha256:b3f85ffa93a5c7d2f9cc591246ef9f8ac4a9fa716bfd5bae0377699a2d89d78c", + "sha256:e98b331d9fa9ece7b8be26094cbe2d57613ae882133cc755167268a984bc0ab3" + ], + "version": "==0.3.4" + }, + "mozcrash": { + "hashes": [ + "sha256:3eb6568674056f58029bdc1dbfaa1ed53c3608b09d904466e6d697bbc1f8bc72", + "sha256:63c61f720beb045cd3ac668152cddfe96b25827cafd6bc98d9144bb1cd0e2008" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==1.0" + }, + "mozdevice": { + "hashes": [ + "sha256:c3c50219127e36c171f84b2427e8fcf3a85457f336559aead9770b2a27fbc4b6" + ], + "version": "==1.0.0" + }, + "mozdownload": { + "hashes": [ + "sha256:5ca97e858e6fc0a37188ff72d0029e8b372e4b8d41181833281f718fa62c1ea9", + "sha256:efdcbc7e84b4a292c855c2753c659b19d018e3e2669b1f538e09b9c6f9a80f9b" + ], + "index": "pypi", + "version": "==1.23" + }, + "mozfile": { + "hashes": [ + "sha256:d3b00f336c6a89449bd78dd3ae65d74eb98497438d1ccfec07af0a736d20e957" + ], + "version": "==1.2" + }, + "mozinfo": { + "hashes": [ + "sha256:dcd53a1b1793340418e1ae42bf300e3e56d8f12047972378c6f9318b220b1023" + ], + "version": "==0.10" + }, + "mozinstall": { + "hashes": [ + "sha256:034e1e21cf5504324c74cab62944b25cedc7d2650054ec94f537055d6f581d7d", + "sha256:8a27c03a98f88f3998d5ba910b61b7f44f7aa43113f60e5b76bf4a860548cb89" + ], + "index": "pypi", + "version": "==1.16.0" + }, + "mozlog": { + "hashes": [ + "sha256:3c4386e6d07beca9f64ac176728205d69e822b3f19acccec8c52531ee1f9d957", + "sha256:a05718e9eeff693bc0509f6c66bd1fc68a5f14744bc7b2a67a06f0d8cc47a203" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==3.8" + }, + "moznetwork": { + "hashes": [ + "sha256:5ac0d56cc46d7a080e4ff0742238360f72f43eb52191319df1b4021779e9d727" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==0.27" + }, + "mozprocess": { + "hashes": [ + "sha256:9f3d533439f7721c90547bee0ecdf421c838e5976673430f6ea37db7a789b363", + "sha256:9f471c45bee9ff14e936c6ee216a6cc4941223659c01fa626bce628001d8485c" + ], + "version": "==0.26" + }, + "mozprofile": { + "hashes": [ + "sha256:25ffe505b9839ec8d044ddf7d829c21ad8618d1ae0e1e3a7e1278b3c5d1f1bb2", + "sha256:aa7fe7248719a224dd63cdc0498c9971d07cfc62fee7a69f51d593316b6bc1d8" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==1.1.0" + }, + "mozrunner": { + "hashes": [ + "sha256:a4e3d532d1d7eb34b6332e0568612ebac3f56da849894005b3dbe68968e5d292" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==7.0.1" + }, + "mozterm": { + "hashes": [ + "sha256:b1e91acec188de07c704dbb7b0100a7be5c1e06567b3beb67f6ea11d00a483a4", + "sha256:f5eafa25c23d391e2a2bb1dd45ee928fc9e3c811977a3856b5a5a0778011053c" + ], + "version": "==1.0.0" + }, + "moztest": { + "hashes": [ + "sha256:5ab589a30f24f608cd3ea31471bc315b3cf5037ad82f2cc324c1da4e30bee9dd", + "sha256:d4d154fa2fb7864ec27cf8026534c6c3df4bb218cd105f6382d63c2870368077" + ], + "version": "==0.8" + }, + "mozversion": { + "hashes": [ + "sha256:e4a8aad0a4748c8b4f1990ec52837d79be8564f30d097a10242d5a08c6b10206", + "sha256:e9b11e4a46bf7a4a11469ea4589c75f3ba50b34b7801e7edf1a09147af8bf70f" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==1.5" + }, + "progressbar": { + "hashes": [ + "sha256:b2d38a729785149e65323381d2e6fca0a5e9615a6d8bcf10bfa8adedfc481254" + ], + "version": "==2.3" + }, + "redo": { + "hashes": [ + "sha256:69ea97e4d934806475fe86f93e4f74da2994acab20c2e3cfe0d3ca6380e3f907" + ], + "version": "==1.6" + }, + "requests": { + "hashes": [ + "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", + "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + ], + "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", + "version": "==2.19.1" + }, + "requests-hawk": { + "hashes": [ + "sha256:aef0dff8053dcae2057774516386bed0a3bc03fabea9e18f3aa98f02672ea5d0", + "sha256:c2626ab31ebef0c81b97781c44c2275bfcc6d8e8520fc4ced495f0f386f8fe26" + ], + "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", + "version": "==1.0.0" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "version": "==1.11.0" + }, + "treeherder-client": { + "hashes": [ + "sha256:84690096902891bef2d9b555352f791510f9a280a76c8cf76a2730deccf8dc4b", + "sha256:e4221b867621afedcd88d076d8652435a8b62d5b0d1c0149afddce4516c2f0c4" + ], + "version": "==4.0.0" + }, + "urllib3": { + "hashes": [ + "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", + "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + ], + "markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'", + "version": "==1.23" + }, + "wptserve": { + "hashes": [ + "sha256:ef6aa5ca7c7cc802129eefab773172fac49281e50b34352a311d06426f237866" + ], + "version": "==1.4.0" + } + }, + "develop": {} +} diff --git a/README.md b/README.md index e6ebe8e..0ee9492 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,35 @@ Prerequisites: npm start ``` -## NPM Scripts +## Running Tests + +Automated tests are run in a Firefox browser instance using [Marionette][]. We use the Python client for Marionette since there is no up-to-date JavaScript client. + +To set up your environment for running the tests, you must have: + +- A Firefox binary. On MacOS, this can be found within the `.app` folder at `Firefox.app/Contents/MacOS/firefox`. +- Python 2.7 +- [Pipenv][] + +With these installed, you can set up the test suite: + +1. Install Python dependencies: + + ```sh + pipenv install + ``` +2. Save the path to your Firefox binary with `npm`: + + ```sh + npm config set webext-commerce:firefox_bin + ``` + +After this, you can run `pipenv run test` to run the automated test suite. + +[Marionette]: https://firefox-source-docs.mozilla.org/testing/marionette/marionette/index.html +[Pipenv]: https://docs.pipenv.org/ + +## Scripts | Command | Description | | --- | --- | @@ -39,6 +67,7 @@ Prerequisites: | `npm run build` | Compile source files with Webpack | | `npm run watch` | Watch for changes and rebuild | | `npm run package` | Package the extension into an XPI file | +| `pipenv run test` | Run test suite (See "Running Tests" for setup) | ## License diff --git a/bin/run_tests.py b/bin/run_tests.py new file mode 100755 index 0000000..aadfd15 --- /dev/null +++ b/bin/run_tests.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +""" +Runs automated JavaScript tests by launching Firefox in headless mode +and using the Marionette protocol to run the test code in Firefox. + +This script relies on path modifications from npm and should be run +using the ``npm run test`` command. +""" + +import os +import sys +from subprocess import check_call, PIPE, Popen, STDOUT +from tempfile import mkstemp + +import click +from marionette_driver.marionette import Marionette + + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + + +@click.command() +@click.option( + '--firefox_bin', + required=True, + envvar='npm_package_config_firefox_bin', + help='Path to Firefox binary', +) +def main(firefox_bin): + click.echo('== Building test bundle with Webpack') + bundle_handle, bundle_path = mkstemp() + try: + webpack_config_path = os.path.join(ROOT, 'webpack.config.test.js') + check_call([ + 'webpack', + '--bail', + '--config', webpack_config_path, + '--output', bundle_path, + ]) + with open(bundle_path) as f: + test_code = f.read() + finally: + os.remove(bundle_path) + + click.echo('== Running tests') + client = None + try: + client = Marionette(bin=firefox_bin, headless=True) + client.start_session() + results = client.execute_async_script(test_code) + finally: + if client: + client.cleanup() + + # Pipe output through formatter to make it readable. + reporter_env = os.environ.copy() + reporter_env.setdefault('TAP_COLORS', '1') # Support color outpput + reporter = Popen( + ['tap-mocha-reporter', 'spec'], + stdout=PIPE, + stderr=STDOUT, + stdin=PIPE, + env=reporter_env, + ) + formatted_output, stderr = reporter.communicate(results['output']) + click.echo(formatted_output) + + # Exit with an error code if there were any failures + if results['failures'] > 0: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/package-lock.json b/package-lock.json index 1b8aeda..37e2da2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "moz-commerce", + "name": "webext-commerce", "version": "0.1.0", "lockfileVersion": 1, "requires": true, @@ -2492,6 +2492,12 @@ "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, "colors": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", @@ -3077,6 +3083,12 @@ } } }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, "degenerator": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", @@ -3164,6 +3176,12 @@ "repeating": "^2.0.0" } }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -3185,6 +3203,12 @@ "path-type": "^3.0.0" } }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", + "dev": true + }, "dispensary": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/dispensary/-/dispensary-0.18.0.tgz", @@ -3473,6 +3497,55 @@ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", "dev": true }, + "enzyme": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.3.0.tgz", + "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==", + "dev": true, + "requires": { + "cheerio": "^1.0.0-rc.2", + "function.prototype.name": "^1.0.3", + "has": "^1.0.1", + "is-boolean-object": "^1.0.0", + "is-callable": "^1.1.3", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-subset": "^0.1.1", + "lodash": "^4.17.4", + "object-inspect": "^1.5.0", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4", + "object.values": "^1.0.4", + "raf": "^3.4.0", + "rst-selector-parser": "^2.2.3" + } + }, + "enzyme-adapter-react-16": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz", + "integrity": "sha512-kC8pAtU2Jk3OJ0EG8Y2813dg9Ol0TXi7UNxHzHiWs30Jo/hj7alc//G1YpKUsPP1oKl9X+Lkx+WlGJpPYA+nvw==", + "dev": true, + "requires": { + "enzyme-adapter-utils": "^1.3.0", + "lodash": "^4.17.4", + "object.assign": "^4.0.4", + "object.values": "^1.0.4", + "prop-types": "^15.6.0", + "react-reconciler": "^0.7.0", + "react-test-renderer": "^16.0.0-0" + } + }, + "enzyme-adapter-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.4.0.tgz", + "integrity": "sha512-ajvyXQYbmCoKCX/FaraNzBgXDXJBltCd0GdXfKc0DdRPYgCLaZfS6Ts576IFt8aX2GU9ajZv2g5jfcJ+Nttejw==", + "dev": true, + "requires": { + "object.assign": "^4.1.0", + "prop-types": "^15.6.0" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -4248,6 +4321,12 @@ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, + "events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", + "dev": true + }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -4755,6 +4834,15 @@ "readable-stream": "^2.0.4" } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -5418,6 +5506,17 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", + "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "is-callable": "^1.1.3" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -6263,6 +6362,12 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz", + "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -6459,6 +6564,12 @@ } } }, + "is-number-object": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz", + "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", + "dev": true + }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -6572,6 +6683,18 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", + "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", + "dev": true + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, "is-supported-regexp-flag": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz", @@ -7005,6 +7128,12 @@ "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", "dev": true }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -7673,6 +7802,12 @@ "dev": true, "optional": true }, + "moo": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", + "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -7880,6 +8015,37 @@ "dev": true, "optional": true }, + "nearley": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.15.0.tgz", + "integrity": "sha512-ZjzdO+yBtMrRrBbr+BJ35ECla6PGCAb/6hqpBQe7bmhEJabQ4rpVdj4sadP1Z1jQGyaDmm1GciQWsGVxIZ3uJA==", + "dev": true, + "requires": { + "moo": "^0.4.3", + "nomnom": "~1.6.2", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6", + "semver": "^5.4.1" + }, + "dependencies": { + "nomnom": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", + "dev": true, + "requires": { + "colors": "0.5.x", + "underscore": "~1.4.4" + } + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", + "dev": true + } + } + }, "needle": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", @@ -8059,6 +8225,12 @@ "boolbase": "~1.0.0" } }, + "null-loader": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-0.1.1.tgz", + "integrity": "sha1-F76av80/8OFRL2/Er8sfUDk3j64=", + "dev": true + }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -8113,6 +8285,18 @@ } } }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "dev": true + }, "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", @@ -9225,6 +9409,31 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "raf": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz", + "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", + "dev": true, + "requires": { + "performance-now": "^2.1.0" + } + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", + "dev": true + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "randomatic": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", @@ -9317,6 +9526,36 @@ "prop-types": "^15.6.0" } }, + "react-is": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz", + "integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==", + "dev": true + }, + "react-reconciler": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.7.0.tgz", + "integrity": "sha512-50JwZ3yNyMS8fchN+jjWEJOH3Oze7UmhxeoJLn2j6f3NjpfCRbcmih83XTWmzqtar/ivd5f7tvQhvvhism2fgg==", + "dev": true, + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" + } + }, + "react-test-renderer": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.1.tgz", + "integrity": "sha512-wyyiPxRZOTpKnNIgUBOB6xPLTpIzwcQMIURhZvzUqZzezvHjaGNsDPBhMac5fIY3Jf5NuKxoGvV64zDSOECPPQ==", + "dev": true, + "requires": { + "fbjs": "^0.8.16", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0", + "react-is": "^16.4.1" + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -9727,6 +9966,15 @@ "signal-exit": "^3.0.2" } }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "dev": true, + "requires": { + "through": "~2.3.4" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -9752,6 +10000,16 @@ "inherits": "^2.0.1" } }, + "rst-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", + "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", + "dev": true, + "requires": { + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" + } + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -10951,6 +11209,17 @@ } } }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -11248,12 +11517,78 @@ } } }, + "tap-mocha-reporter": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-3.0.7.tgz", + "integrity": "sha512-GHVXJ38C3oPRpM3YUc43JlGdpVZYiKeT1fmAd3HH2+J+ZWwsNAUFvRRdoGsXLw9+gU9o+zXpBqhS/oXyRQYwlA==", + "dev": true, + "requires": { + "color-support": "^1.1.0", + "debug": "^2.1.3", + "diff": "^1.3.2", + "escape-string-regexp": "^1.0.3", + "glob": "^7.0.5", + "js-yaml": "^3.3.1", + "readable-stream": "^2.1.5", + "tap-parser": "^5.1.0", + "unicode-length": "^1.0.0" + } + }, + "tap-parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-5.4.0.tgz", + "integrity": "sha512-BIsIaGqv7uTQgTW1KLTMNPSEQf4zDDPgYOBRdgOfuB+JFOLRBfEu6cLa/KvMvmqggu1FKXDfitjLwsq4827RvA==", + "dev": true, + "requires": { + "events-to-array": "^1.0.1", + "js-yaml": "^3.2.7", + "readable-stream": "^2" + } + }, "tapable": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, + "tape": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz", + "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==", + "dev": true, + "requires": { + "deep-equal": "~1.0.1", + "defined": "~1.0.0", + "for-each": "~0.3.3", + "function-bind": "~1.1.1", + "glob": "~7.1.2", + "has": "~1.0.3", + "inherits": "~2.0.3", + "minimist": "~1.2.0", + "object-inspect": "~1.6.0", + "resolve": "~1.7.1", + "resumer": "~0.0.0", + "string.prototype.trim": "~1.1.2", + "through": "~2.3.8" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + } + } + }, "tar-stream": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", @@ -11640,6 +11975,16 @@ "xtend": "^4.0.1" } }, + "unicode-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-1.0.3.tgz", + "integrity": "sha1-Wtp6f+1RhBpBijKM8UlHisg1irs=", + "dev": true, + "requires": { + "punycode": "^1.3.2", + "strip-ansi": "^3.0.1" + } + }, "unified": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", diff --git a/package.json b/package.json index 0295f4b..9158b76 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "moz-commerce", + "name": "webext-commerce", "version": "0.1.0", "description": "", "private": "true", @@ -10,7 +10,11 @@ "build": "webpack --mode=development", "watch": "webpack --watch --mode=development", "package": "web-ext build --source-dir build --overwrite-dest", - "lint": "bin/run_lints.sh" + "lint": "bin/run_lints.sh", + "test": "bin/run_tests.py" + }, + "config": { + "firefox_bin": "" }, "devDependencies": { "babel-core": "6.26.3", @@ -21,15 +25,20 @@ "babel-preset-react": "6.24.1", "copy-webpack-plugin": "4.5.2", "css-loader": "1.0.0", + "enzyme": "3.3.0", + "enzyme-adapter-react-16": "1.1.1", "eslint": "4.19.1", "eslint-config-airbnb": "17.0.0", "eslint-import-resolver-webpack": "0.10.1", "eslint-plugin-import": "2.13.0", "eslint-plugin-jsx-a11y": "6.0.3", "eslint-plugin-react": "7.10.0", + "null-loader": "0.1.1", "style-loader": "0.21.0", "stylelint": "9.3.0", "stylelint-config-standard": "18.2.0", + "tap-mocha-reporter": "3.0.7", + "tape": "4.9.1", "web-ext": "2.7.0", "webpack": "4.15.1", "webpack-command": "0.3.1" diff --git a/src/tests/.eslintrc.json b/src/tests/.eslintrc.json new file mode 100644 index 0000000..56edc86 --- /dev/null +++ b/src/tests/.eslintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "import/no-extraneous-dependencies": ["error", { + "devDependencies": true, + "optionalDependencies": false, + "peerDependencies": false + }] + } +} diff --git a/src/tests/index.jsx b/src/tests/index.jsx new file mode 100644 index 0000000..3b1b623 --- /dev/null +++ b/src/tests/index.jsx @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Entry point for the automated test suite. This script is run inside a + * content-scoped sandbox in Firefox by Marionette. See bin/run_tests.py for + * more info. + */ + +// marionetteScriptFinished is a wrapper added during the webpack build that +// returns a value to run_tests.py and ends execution. +/* global marionetteScriptFinished */ + +import Enzyme from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import test from 'tape'; + +// Configure testing libraries +Enzyme.configure({ adapter: new Adapter() }); + +// Collect formatted output and count failures. +let output = ''; +let failures = 0; +test.createStream() + .on('data', (data) => { + output += data; + }).on('end', () => { + // Send the results back to run_tests.py + marionetteScriptFinished({output, failures}); + }); +test.onFailure(() => failures++); + +// Import all files within the tests directory that have the word "test" +// in their filename. +const requireTest = require.context('.', true, /test.*\.jsx?$/); +requireTest.keys().forEach(requireTest); diff --git a/src/tests/sidebar/components/test_Accordion.jsx b/src/tests/sidebar/components/test_Accordion.jsx new file mode 100644 index 0000000..131bae1 --- /dev/null +++ b/src/tests/sidebar/components/test_Accordion.jsx @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import React from 'react'; +import {shallow} from 'enzyme'; + +import test from 'tape'; + +import Accordion from 'commerce/sidebar/components/Accordion'; + +test(' should apply the className prop to the returned container element', (t) => { + const wrapper = shallow(); + t.ok(wrapper.is('.foobar.accordion'), 'Accordion container has extra class'); + t.end(); +}); diff --git a/webpack.config.js b/webpack.config.js index 0412f2f..9dd7c9f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Webpack configuration for building the webextension. + */ + /* eslint-env node */ + const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); diff --git a/webpack.config.test.js b/webpack.config.test.js new file mode 100644 index 0000000..8bc5750 --- /dev/null +++ b/webpack.config.test.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Webpack configuration for the automated test suite. + */ + +/* eslint-env node */ + +const path = require('path'); +const webpack = require('webpack'); + +module.exports = { + mode: 'development', + devtool: 'eval-source-map', + target: 'web', + entry: { + tests: './src/tests/index', + }, + module: { + rules: [ + { + test: /\.jsx$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.css$/, + use: { + loader: 'null-loader', + }, + }, + ], + }, + node: { + fs: 'empty', + }, + plugins: [ + new webpack.BannerPlugin({ + banner: 'const marionetteScriptFinished = arguments[0];', + raw: true, + entryOnly: true, + }), + ], + resolve: { + extensions: ['.js', '.jsx'], + alias: { + commerce: path.resolve(__dirname, 'src'), + }, + }, +}; From 5e806405674ded0fdaa6407b60518f73e19116c3 Mon Sep 17 00:00:00 2001 From: Michael Kelly Date: Tue, 24 Jul 2018 15:11:20 -0700 Subject: [PATCH 2/2] Improve error message when Firefox binary path is misconfigured. --- bin/run_tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/run_tests.py b/bin/run_tests.py index aadfd15..1770779 100755 --- a/bin/run_tests.py +++ b/bin/run_tests.py @@ -31,6 +31,13 @@ help='Path to Firefox binary', ) def main(firefox_bin): + if not firefox_bin: + raise click.BadParameter( + 'No Firefox binary found; configure the path to Firefox with `npm config`.' + ) + elif not os.path.exists(firefox_bin): + raise click.BadParameter('Path to Firefox binary does not exist.') + click.echo('== Building test bundle with Webpack') bundle_handle, bundle_path = mkstemp() try: