diff --git a/.eslintrc.js b/.eslintrc.js index 1dc3939..65cae79 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,47 +2,46 @@ module.exports = { root: true, parserOptions: { ecmaVersion: 2017, - sourceType: 'module' + sourceType: "module" }, - plugins: [ - 'ember' - ], - extends: [ - 'eslint:recommended', - 'plugin:ember/recommended' - ], + plugins: ["ember"], + extends: ["eslint:recommended", "plugin:ember/recommended"], env: { browser: true }, - rules: { - }, + rules: {}, overrides: [ // node files { files: [ - 'index.js', - 'testem.js', - 'ember-cli-build.js', - 'config/**/*.js', - 'tests/dummy/config/**/*.js' + "index.js", + "testem.js", + "ember-cli-build.js", + "config/**/*.js", + "tests/dummy/config/**/*.js" ], excludedFiles: [ - 'app/**', - 'addon/**', - 'tests/dummy/app/**' + "app/**", + "addon/**", + "tests/dummy/app/**", + "addon-test-support/**" ], parserOptions: { - sourceType: 'script', + sourceType: "script", ecmaVersion: 2015 }, env: { browser: false, node: true }, - plugins: ['node'], - rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { - // add your custom rules and overrides for node files here - }) + plugins: ["node"], + rules: Object.assign( + {}, + require("eslint-plugin-node").configs.recommended.rules, + { + // add your custom rules and overrides for node files here + } + ) } ] }; diff --git a/.travis.yml b/.travis.yml index c9db7e0..2e658e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: node_js node_js: # we recommend testing addons with the same minimum supported node version as Ember CLI # so that your addon works for all apps - - "4" + - "6" sudo: false dist: trusty @@ -35,11 +35,6 @@ matrix: allow_failures: - env: EMBER_TRY_SCENARIO=ember-canary -before_install: - - npm config set spin false - - npm install -g npm@4 - - npm --version - script: - npm run lint:js # Usually, it's ok to finish the test scenario without reverting diff --git a/README.md b/README.md index dd31b12..17dec6e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,28 @@ Then import as follows: import td from 'testdouble'; ``` -License ------------------------------------------------------------------------------- +## Test Helpers + +### `stubService` + +Included is a function that can replace a service with one stubbed using [`td.object()`][td-object]. + +```js +import td from "testdouble"; +import { stubService } from "ember-cli-testdouble/test-support"; + +test("verifying a method invocation", function() { + // `stubService` returns a reference to the servive + let someStubbedService = stubService("some-service"); + + // Do something that would call `method()` on the service + + td.verify(someService.method()); // Passes! +}); +``` + +## License This project is licensed under the [MIT License](LICENSE.md). + +[td-object]: https://github.com/testdouble/testdouble.js/blob/master/docs/4-creating-test-doubles.md#tdobject diff --git a/addon-test-support/index.js b/addon-test-support/index.js new file mode 100644 index 0000000..6d261a0 --- /dev/null +++ b/addon-test-support/index.js @@ -0,0 +1 @@ +export { default as stubService } from "./stub-service"; diff --git a/addon-test-support/stub-service.js b/addon-test-support/stub-service.js new file mode 100644 index 0000000..13efb18 --- /dev/null +++ b/addon-test-support/stub-service.js @@ -0,0 +1,42 @@ +import { getContext } from "@ember/test-helpers"; +import { run } from "@ember/runloop"; +import td from "testdouble"; + +function replace(owner, name) { + const serviceName = `service:${name}`; + const original = owner.lookup(serviceName); + const replacement = td.object(original); + + run(() => { + owner.unregister(serviceName); + owner.register(serviceName, replacement, { instantiate: false }); + }); + + return replacement; +} + +/** + * Replace a service with a stubbed version of itself + * + * Each method on the service will be replaced with a TestDouble function + */ +export default function stubService() { + if (arguments.length === 2) { + let [hooks, name] = arguments; + + hooks.beforeEach(function() { + replace(this.owner, name); + }); + + hooks.afterEach(function() { + td.reset(); + }); + } else if (arguments.length === 1) { + let [name] = arguments; + let { owner } = getContext(); + + return replace(owner, name); + } else { + throw new Error("Unexpected number of arguments"); + } +} diff --git a/config/ember-try.js b/config/ember-try.js index 08e0dd9..20105ab 100644 --- a/config/ember-try.js +++ b/config/ember-try.js @@ -1,65 +1,66 @@ -'use strict'; +"use strict"; -const getChannelURL = require('ember-source-channel-url'); +const getChannelURL = require("ember-source-channel-url"); module.exports = function() { return Promise.all([ - getChannelURL('release'), - getChannelURL('beta'), - getChannelURL('canary'), - ]).then((urls) => { + getChannelURL("release"), + getChannelURL("beta"), + getChannelURL("canary") + ]).then(urls => { return { + useYarn: true, scenarios: [ { - name: 'ember-lts-2.12', + name: "ember-lts-2.12", npm: { devDependencies: { - 'ember-source': '~2.12.0' + "ember-source": "~2.12.0" } } }, { - name: 'ember-lts-2.16', + name: "ember-lts-2.16", npm: { devDependencies: { - 'ember-source': '~2.16.0' + "ember-source": "~2.16.0" } } }, { - name: 'ember-lts-2.18', + name: "ember-lts-2.18", npm: { devDependencies: { - 'ember-source': '~2.18.0' + "ember-source": "~2.18.0" } } }, { - name: 'ember-release', + name: "ember-release", npm: { devDependencies: { - 'ember-source': urls[0] + "ember-source": urls[0] } } }, { - name: 'ember-beta', + name: "ember-beta", npm: { devDependencies: { - 'ember-source': urls[1] + "ember-source": urls[1] } } }, { - name: 'ember-canary', + name: "ember-canary", npm: { devDependencies: { - 'ember-source': urls[2] + "ember-source": urls[2] } } }, { - name: 'ember-default', + name: "ember-default", npm: { devDependencies: {} } diff --git a/package.json b/package.json index 29e8925..95b82ce 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "ember-cli-qunit": "^4.1.1", "ember-cli-shims": "^1.2.0", "ember-cli-sri": "^2.1.0", + "ember-cli-testdouble-qunit": "^2.0.2", "ember-cli-uglify": "^2.0.0", "ember-disable-prototype-extensions": "^1.1.2", "ember-export-application-global": "^2.0.0", diff --git a/tests/dummy/app/services/to-stub.js b/tests/dummy/app/services/to-stub.js new file mode 100644 index 0000000..f00f2ce --- /dev/null +++ b/tests/dummy/app/services/to-stub.js @@ -0,0 +1,7 @@ +import Service from "@ember/service"; + +export default Service.extend({ + method() { + // This will be stubbed in a test + } +}); diff --git a/tests/test-helper.js b/tests/test-helper.js index 0382a84..b8a6fb0 100644 --- a/tests/test-helper.js +++ b/tests/test-helper.js @@ -1,7 +1,9 @@ -import Application from '../app'; -import config from '../config/environment'; -import { setApplication } from '@ember/test-helpers'; -import { start } from 'ember-qunit'; +import Application from "../app"; +import config from "../config/environment"; +import { setApplication } from "@ember/test-helpers"; +import { start } from "ember-qunit"; + +import "ember-cli-testdouble-qunit"; setApplication(Application.create(config.APP)); diff --git a/tests/unit/test-support/stub-service-test.js b/tests/unit/test-support/stub-service-test.js new file mode 100644 index 0000000..fea8e89 --- /dev/null +++ b/tests/unit/test-support/stub-service-test.js @@ -0,0 +1,36 @@ +import { module, test } from "qunit"; +import { setupTest } from "ember-qunit"; +import { stubService } from "ember-cli-testdouble/test-support"; + +module("Test Helpers | stub-service", function(hooks) { + setupTest(hooks); + + module("as a `hooks` reciever", function(hooks) { + stubService(hooks, "to-stub"); + + test("it can replace a service", function(assert) { + let service = this.owner.lookup("service:to-stub"); + service.method(); + + assert.verify(service.method()); + }); + }); + + module("invoking without `hooks`", function(hooks) { + hooks.beforeEach(function() { + this.service = stubService("to-stub"); + }); + + test("it can replace a service", function(assert) { + let service = this.owner.lookup("service:to-stub"); + service.method(); + + assert.verify(service.method()); + assert.equal( + service, + this.service, + "Returns a references to the stubbed service" + ); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 3bed22b..29a0838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2071,6 +2071,14 @@ ember-cli-test-loader@^2.2.0: dependencies: ember-cli-babel "^6.8.1" +ember-cli-testdouble-qunit@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ember-cli-testdouble-qunit/-/ember-cli-testdouble-qunit-2.0.2.tgz#4edbe71e71d1abff101cb36e780a24586e8ba829" + dependencies: + broccoli-funnel "^2.0.1" + ember-cli-babel "^6.6.0" + testdouble-qunit "^2.0.2" + ember-cli-uglify@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/ember-cli-uglify/-/ember-cli-uglify-2.0.2.tgz#12becdaf1a2e6f0cdbd386b1d5f5a2d0540347d3" @@ -5896,6 +5904,10 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" +testdouble-qunit@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/testdouble-qunit/-/testdouble-qunit-2.0.2.tgz#740393c867901b9a814882b50f4c94a9e343fab9" + testdouble@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/testdouble/-/testdouble-3.5.2.tgz#7ac91d08be05bac3b2acba57c430f6c62d0ad8af"