From 0e4e6038a3f2c87458eee30cf04f56f2158195ab Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Tue, 23 Apr 2019 11:33:53 -0700 Subject: [PATCH 1/2] [CLEANUP] Make visit() fully async Makes the visit() test helper fully async. It now only resolves after the runloop has fully emptied and everything has settled. Also adds assert.throwsAsync and expectDeprecationAsync, which allow us to wrap asynchronous blocks and capture errors and deprecations within them. This makes this change much easier to deal with. --- .eslintrc.js | 1 + broccoli/test-polyfills.js | 8 +- broccoli/transforms/test-babel-plugins.js | 13 ++ ember-cli-build.js | 32 +-- package.json | 2 + .../integration/application/rendering-test.js | 10 +- .../link-to/query-params-curly-test.js | 8 +- .../link-to/rendering-angle-test.js | 11 +- .../link-to/rendering-curly-test.js | 9 +- .../components/link-to/routing-angle-test.js | 13 +- .../components/link-to/routing-curly-test.js | 11 +- .../glimmer/tests/integration/mount-test.js | 10 +- .../tests/component_registration_test.js | 11 +- .../tests/routing/decoupled_basic_test.js | 202 +++++++++--------- .../ember/tests/routing/query_params_test.js | 18 +- .../overlapping_query_params_test.js | 10 +- .../ember/tests/routing/substates_test.js | 103 ++++----- .../ember/tests/service_injection_test.js | 44 ++-- .../lib/ember-dev/debug.ts | 24 ++- .../lib/ember-dev/deprecation.ts | 29 +++ .../lib/ember-dev/setup-qunit.ts | 67 ++++++ .../lib/test-cases/abstract-application.js | 19 +- tests/index.html | 2 +- yarn.lock | 23 ++ 24 files changed, 423 insertions(+), 257 deletions(-) create mode 100644 broccoli/transforms/test-babel-plugins.js diff --git a/.eslintrc.js b/.eslintrc.js index acd91be167d..1e2dc70d39e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -109,6 +109,7 @@ module.exports = { globals: { 'expectAssertion': true, 'expectDeprecation': true, + 'expectDeprecationAsync': true, 'expectNoDeprecation': true, 'expectWarning': true, 'expectNoWarning': true, diff --git a/broccoli/test-polyfills.js b/broccoli/test-polyfills.js index 0c78c08ea33..5787ecd33aa 100644 --- a/broccoli/test-polyfills.js +++ b/broccoli/test-polyfills.js @@ -4,7 +4,13 @@ const resolve = require('rollup-plugin-node-resolve'); const commonjs = require('rollup-plugin-commonjs'); module.exports = function polyfills() { - let polyfillEntry = writeFile('polyfill-entry.js', 'require("core-js/modules/es6.symbol");'); + let polyfillEntry = writeFile( + 'polyfill-entry.js', + ` + require('core-js/modules/es6.symbol'); + require('regenerator-runtime/runtime') + ` + ); return new Rollup(polyfillEntry, { rollup: { diff --git a/broccoli/transforms/test-babel-plugins.js b/broccoli/transforms/test-babel-plugins.js new file mode 100644 index 00000000000..5dd6269bc36 --- /dev/null +++ b/broccoli/transforms/test-babel-plugins.js @@ -0,0 +1,13 @@ +const Babel = require('broccoli-babel-transpiler'); + +module.exports = function(tree) { + let options = { + sourceMaps: true, + plugins: [ + ['@babel/plugin-transform-async-to-generator'], + ['@babel/plugin-transform-regenerator'], + ], + }; + + return new Babel(tree, options); +}; diff --git a/ember-cli-build.js b/ember-cli-build.js index b87394020c0..d804442cecc 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -16,6 +16,7 @@ const stripForProd = require('./broccoli/strip-for-prod'); const debugMacros = require('./broccoli/debug-macros'); const minify = require('./broccoli/minify'); const rename = require('./broccoli/rename'); +const testBabelPluginsTransform = require('./broccoli/transforms/test-babel-plugins'); const { routerES, jquery, @@ -240,19 +241,24 @@ function buildBundles(packagesES, dependenciesES, templateCompilerDependenciesES bootstrapModule('sideeffects', 'ember'), ]); - emberTestsProdFiles = new MergeTrees([ - new Funnel(packagesProdES, { - include: [ - '@ember/-internals/*/tests/**' /* internal packages */, - 'internal-test-helpers/**', - '*/*/tests/**' /* scoped packages */, - '*/tests/**' /* packages */, - 'license.js', - ], - exclude: ['@ember/debug/tests/**', 'ember-testing/tests/**'], - }), - bootstrapModule('empty'), - ]); + emberTestsProdFiles = testBabelPluginsTransform( + MergeTrees([ + new Funnel(packagesProdES, { + include: [ + '@ember/-internals/*/tests/**' /* internal packages */, + 'internal-test-helpers/**', + '*/*/tests/**' /* scoped packages */, + '*/tests/**' /* packages */, + 'license.js', + ], + exclude: ['@ember/debug/tests/**', 'ember-testing/tests/**'], + }), + bootstrapModule('empty'), + ]) + ); + + // apply the babel transforms for legacy tests + emberTestsFiles = testBabelPluginsTransform(emberTestsFiles); // Note: // We have to build custom production template compiler diff --git a/package.json b/package.json index fa867639ce3..c5d980238c2 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "@babel/plugin-proposal-class-properties": "^7.4.0", "@babel/plugin-proposal-decorators": "^7.3.0", "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.4.0", "@babel/plugin-transform-block-scoping": "^7.4.0", "@babel/plugin-transform-classes": "^7.4.3", "@babel/plugin-transform-computed-properties": "^7.2.0", @@ -88,6 +89,7 @@ "@babel/plugin-transform-modules-amd": "^7.2.0", "@babel/plugin-transform-object-assign": "^7.2.0", "@babel/plugin-transform-parameters": "^7.4.3", + "@babel/plugin-transform-regenerator": "^7.4.3", "@babel/plugin-transform-shorthand-properties": "^7.2.0", "@babel/plugin-transform-spread": "^7.2.2", "@babel/plugin-transform-template-literals": "^7.2.0", diff --git a/packages/@ember/-internals/glimmer/tests/integration/application/rendering-test.js b/packages/@ember/-internals/glimmer/tests/integration/application/rendering-test.js index 8b2d22d0de1..1121c1c4449 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/application/rendering-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/application/rendering-test.js @@ -469,7 +469,7 @@ moduleFor( }); } - ['@test it emits a useful backtracking re-render assertion message']() { + async ['@test it emits a useful backtracking re-render assertion message'](assert) { this.router.map(function() { this.route('routeWithError'); }); @@ -497,11 +497,9 @@ moduleFor( let expectedBacktrackingMessage = /modified "model\.name" twice on \[object Object\] in a single render\. It was rendered in "template:my-app\/templates\/routeWithError.hbs" and modified in "component:x-foo"/; - return this.visit('/').then(() => { - expectAssertion(() => { - this.visit('/routeWithError'); - }, expectedBacktrackingMessage); - }); + await this.visit('/'); + + assert.rejectsAssertion(this.visit('/routeWithError'), expectedBacktrackingMessage); } ['@test route templates with {{{undefined}}} [GH#14924] [GH#16172]']() { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js index 6c3c002e381..ead477d711e 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js @@ -51,14 +51,16 @@ moduleFor( }); } - ['@feature(ember-glimmer-angle-bracket-built-ins) `(query-params)` must be used in conjunction with `{{link-to}}']() { + async ['@feature(ember-glimmer-angle-bracket-built-ins) `(query-params)` must be used in conjunction with `{{link-to}}']( + assert + ) { this.addTemplate( 'index', `{{#let (query-params foo='456' bar='NAW') as |qp|}}{{link-to 'Index' 'index' qp}}{{/let}}` ); - return expectAssertion( - () => this.visit('/'), + await assert.rejectsAssertion( + this.visit('/'), /The `\(query-params\)` helper can only be used when invoking the `{{link-to}}` component\./ ); } diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-angle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-angle-test.js index d24fd0076c2..ab642cac0d0 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-angle-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-angle-test.js @@ -9,14 +9,13 @@ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) { moduleFor( ' component (rendering tests)', class extends ApplicationTestCase { - [`@test throws a useful error if you invoke it wrong`](assert) { - assert.expect(1); - + async [`@test throws a useful error if you invoke it wrong`](assert) { this.addTemplate('application', `Index`); - expectAssertion(() => { - this.visit('/'); - }, /You must provide at least one of the `@route`, `@model`, `@models` or `@query` argument to ``/); + await assert.rejectsAssertion( + this.visit('/'), + /You must provide at least one of the `@route`, `@model`, `@models` or `@query` argument to ``/ + ); } ['@test should be able to be inserted in DOM when the router is not present']() { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-curly-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-curly-test.js index fdcf5a48b93..588904bccef 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-curly-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/rendering-curly-test.js @@ -17,16 +17,17 @@ moduleFor( }, /You must provide one or more parameters to the `{{link-to}}` component\. \('my-app\/templates\/application\.hbs' @ L1:C0\)/); } - [`@feature(!ember-glimmer-angle-bracket-built-ins) throws a useful error if you invoke it wrong`]( + async [`@feature(!ember-glimmer-angle-bracket-built-ins) throws a useful error if you invoke it wrong`]( assert ) { assert.expect(1); this.addTemplate('application', `{{#link-to id='the-link'}}Index{{/link-to}}`); - expectAssertion(() => { - this.visit('/'); - }, /You must provide one or more parameters to the link-to component/); + await assert.rejectsAssertion( + this.visit('/'), + /You must provide one or more parameters to the link-to component/ + ); } ['@test should be able to be inserted in DOM when the router is not present']() { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js index 19d6bab6f47..56dff76011e 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-angle-test.js @@ -1436,7 +1436,9 @@ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) { }); } - [`@test the component throws a useful error if you invoke it wrong`](assert) { + async [`@test the component throws a useful error if you invoke it wrong`]( + assert + ) { assert.expect(1); this.router.map(function() { @@ -1445,11 +1447,10 @@ if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) { this.addTemplate('application', `Post`); - assert.throws(() => { - this.visit('/'); - }, /(You attempted to generate a link for the "post" route, but did not pass the models required for generating its dynamic segments.|You must provide param `post_id` to `generate`)/); - - return runLoopSettled(); + await assert.rejects( + this.visit('/'), + /(You attempted to generate a link for the "post" route, but did not pass the models required for generating its dynamic segments.|You must provide param `post_id` to `generate`)/ + ); } [`@test the component does not throw an error if its route has exited`](assert) { diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js index 218d27aee29..269cc39b875 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/routing-curly-test.js @@ -1656,7 +1656,7 @@ moduleFor( }); } - [`@test the {{link-to}} component throws a useful error if you invoke it wrong`](assert) { + async [`@test the {{link-to}} component throws a useful error if you invoke it wrong`](assert) { assert.expect(1); this.router.map(function() { @@ -1665,11 +1665,10 @@ moduleFor( this.addTemplate('application', `{{#link-to 'post'}}Post{{/link-to}}`); - assert.throws(() => { - this.visit('/'); - }, /(You attempted to define a `\{\{link-to "post"\}\}` but did not pass the parameters required for generating its dynamic segments.|You must provide param `post_id` to `generate`)/); - - return runLoopSettled(); + await assert.rejects( + this.visit('/'), + /(You attempted to define a `\{\{link-to "post"\}\}` but did not pass the parameters required for generating its dynamic segments.|You must provide param `post_id` to `generate`)/ + ); } [`@test the {{link-to}} component does not throw an error if its route has exited`](assert) { diff --git a/packages/@ember/-internals/glimmer/tests/integration/mount-test.js b/packages/@ember/-internals/glimmer/tests/integration/mount-test.js index b7da18b2af2..5b851bfdec4 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/mount-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/mount-test.js @@ -101,7 +101,7 @@ moduleFor( }); } - ['@test it emits a useful backtracking re-render assertion message']() { + async ['@test it emits a useful backtracking re-render assertion message'](assert) { this.router.map(function() { this.route('route-with-mount'); }); @@ -132,11 +132,9 @@ moduleFor( let expectedBacktrackingMessage = /modified "person\.name" twice on \[object Object\] in a single render\. It was rendered in "template:my-app\/templates\/route-with-mount.hbs" \(in "engine:chat"\) and modified in "component:component-with-backtracking-set" \(in "engine:chat"\)/; - return this.visit('/').then(() => { - expectAssertion(() => { - this.visit('/route-with-mount'); - }, expectedBacktrackingMessage); - }); + await this.visit('/'); + + await assert.rejectsAssertion(this.visit('/route-with-mount'), expectedBacktrackingMessage); } ['@test it renders with a bound engine name']() { diff --git a/packages/ember/tests/component_registration_test.js b/packages/ember/tests/component_registration_test.js index a173f8aad88..d209887ae17 100644 --- a/packages/ember/tests/component_registration_test.js +++ b/packages/ember/tests/component_registration_test.js @@ -2,7 +2,7 @@ import Application from '@ember/application'; import Controller from '@ember/controller'; import { Component } from '@ember/-internals/glimmer'; import { compile } from 'ember-template-compiler'; -import { moduleFor, ApplicationTestCase, runLoopSettled } from 'internal-test-helpers'; +import { moduleFor, ApplicationTestCase } from 'internal-test-helpers'; import { ENV } from '@ember/-internals/environment'; moduleFor( @@ -238,15 +238,10 @@ moduleFor( }); } - ['@test Using name of component that does not exist']() { + async ['@test Using name of component that does not exist'](assert) { this.addTemplate('application', `
{{#no-good}} {{/no-good}}
`); - // TODO: Use the async form of expectAssertion here when it is available - expectAssertion(() => { - this.visit('/'); - }, /.* named "no-good" .*/); - - return runLoopSettled(); + await assert.rejectsAssertion(this.visit('/'), /.* named "no-good" .*/); } } ); diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 2a1c6be46ee..095a5220d84 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -90,12 +90,10 @@ moduleFor( }); } - ['@test warn on URLs not included in the route set']() { - return this.visit('/').then(() => { - expectAssertion(() => { - this.visit('/what-is-this-i-dont-even'); - }, /'\/what-is-this-i-dont-even' did not match any routes/); - }); + async ['@test warn on URLs not included in the route set'](assert) { + await this.visit('/'); + + await assert.rejects(this.visit('/what-is-this-i-dont-even'), /\/what-is-this-i-dont-even/); } ['@test The Homepage'](assert) { @@ -798,7 +796,7 @@ moduleFor( this.handleURLRejectsWith(this, assert, 'specials/1', 'Setup error'); - run(() => resolve(menuItem)); + resolve(menuItem); } ["@test ApplicationRoute's default error handler can be overridden"](assert) { @@ -848,7 +846,7 @@ moduleFor( this.handleURLRejectsWith(this, assert, '/specials/1', 'Setup error'); - run(() => resolve(menuItem)); + resolve(menuItem); } ['@test Moving from one page to another triggers the correct callbacks'](assert) { @@ -980,7 +978,7 @@ moduleFor( }); } - ['@test Events are triggered on the controller if a matching action name is implemented']( + async ['@test Events are triggered on the controller if a matching action name is implemented']( assert ) { let done = assert.async(); @@ -1021,15 +1019,17 @@ moduleFor( }) ); - this.visit('/').then(() => { - document - .getElementById('qunit-fixture') - .querySelector('a') - .click(); - }); + await this.visit('/'); + + document + .getElementById('qunit-fixture') + .querySelector('a') + .click(); } - ['@test Events are triggered on the current state when defined in `actions` object'](assert) { + async ['@test Events are triggered on the current state when defined in `actions` object']( + assert + ) { let done = assert.async(); this.router.map(function() { @@ -1058,15 +1058,15 @@ moduleFor( this.add('route:home', HomeRoute); this.addTemplate('home', '{{model.name}}'); - this.visit('/').then(() => { - document - .getElementById('qunit-fixture') - .querySelector('a') - .click(); - }); + await this.visit('/'); + + document + .getElementById('qunit-fixture') + .querySelector('a') + .click(); } - ['@test Events defined in `actions` object are triggered on the current state when routes are nested']( + async ['@test Events defined in `actions` object are triggered on the current state when routes are nested']( assert ) { let done = assert.async(); @@ -1104,12 +1104,12 @@ moduleFor( this.addTemplate('root.index', '{{model.name}}'); - this.visit('/').then(() => { - document - .getElementById('qunit-fixture') - .querySelector('a') - .click(); - }); + await this.visit('/'); + + document + .getElementById('qunit-fixture') + .querySelector('a') + .click(); } ['@test Events can be handled by inherited event handlers'](assert) { @@ -1162,7 +1162,7 @@ moduleFor( }); } - ['@test Actions are not triggered on the controller if a matching action name is implemented as a method']( + async ['@test Actions are not triggered on the controller if a matching action name is implemented as a method']( assert ) { let done = assert.async(); @@ -1203,15 +1203,15 @@ moduleFor( }) ); - this.visit('/').then(() => { - document - .getElementById('qunit-fixture') - .querySelector('a') - .click(); - }); + await this.visit('/'); + + document + .getElementById('qunit-fixture') + .querySelector('a') + .click(); } - ['@test actions can be triggered with multiple arguments'](assert) { + async ['@test actions can be triggered with multiple arguments'](assert) { let done = assert.async(); this.router.map(function() { this.route('root', { path: '/' }, function() { @@ -1253,12 +1253,12 @@ moduleFor( this.addTemplate('root.index', '{{model1.name}}'); - this.visit('/').then(() => { - document - .getElementById('qunit-fixture') - .querySelector('a') - .click(); - }); + await this.visit('/'); + + document + .getElementById('qunit-fixture') + .querySelector('a') + .click(); } ['@test transitioning multiple times in a single run loop only sets the URL once'](assert) { @@ -2822,7 +2822,7 @@ moduleFor( let deprecation = /You attempted to override the "willTransition" method which is deprecated\./; - return expectDeprecation(() => { + return expectDeprecationAsync(() => { return this.visit('/').then(() => { this.handleURLAborts(assert, '/nork', deprecation); this.handleURLAborts(assert, '/about', deprecation); @@ -2917,34 +2917,32 @@ moduleFor( }); } - ['@test `didTransition` event fires on the router'](assert) { + async ['@test `didTransition` event fires on the router'](assert) { assert.expect(3); this.router.map(function() { this.route('nork'); }); - return this.visit('/') - .then(() => { - let router = this.applicationInstance.lookup('router:main'); - router.one('didTransition', function() { - assert.ok(true, 'didTransition fired on initial routing'); - }); - this.visit('/'); - }) - .then(() => { - let router = this.applicationInstance.lookup('router:main'); - router.one('didTransition', function() { - assert.ok(true, 'didTransition fired on the router'); - assert.equal( - router.get('url'), - '/nork', - 'The url property is updated by the time didTransition fires' - ); - }); + await this.visit('/'); - return this.visit('/nork'); - }); + let router = this.applicationInstance.lookup('router:main'); + router.one('didTransition', function() { + assert.ok(true, 'didTransition fired on initial routing'); + }); + + await this.visit('/'); + + router.one('didTransition', function() { + assert.ok(true, 'didTransition fired on the router'); + assert.equal( + router.get('url'), + '/nork', + 'The url property is updated by the time didTransition fires' + ); + }); + + await this.visit('/nork'); } ['@test `activate` event fires on the route'](assert) { @@ -3281,7 +3279,7 @@ moduleFor( }); } - ['@test rejecting the model hooks promise with a non-error prints the `message` property']( + async ['@test rejecting the model hooks promise with a non-error prints the `message` property']( assert ) { assert.expect(5); @@ -3319,10 +3317,8 @@ moduleFor( }) ); - return assert.throws( - () => { - return this.visit('/'); - }, + await assert.rejects( + this.visit('/'), function(err) { assert.equal(err.message, rejectedMessage); return true; @@ -3331,7 +3327,7 @@ moduleFor( ); } - ['@test rejecting the model hooks promise with an error with `errorThrown` property prints `errorThrown.message` property']( + async ['@test rejecting the model hooks promise with an error with `errorThrown` property prints `errorThrown.message` property']( assert ) { assert.expect(5); @@ -3367,8 +3363,8 @@ moduleFor( }) ); - assert.throws( - () => this.visit('/'), + await assert.rejects( + this.visit('/'), function(err) { assert.equal(err.message, rejectedMessage); return true; @@ -3377,7 +3373,7 @@ moduleFor( ); } - ['@test rejecting the model hooks promise with no reason still logs error'](assert) { + async ['@test rejecting the model hooks promise with no reason still logs error'](assert) { assert.expect(2); this.router.map(function() { this.route('wowzers', { path: '/' }); @@ -3400,10 +3396,10 @@ moduleFor( }) ); - return assert.throws(() => this.visit('/')); + await assert.rejects(this.visit('/')); } - ['@test rejecting the model hooks promise with a string shows a good error'](assert) { + async ['@test rejecting the model hooks promise with a string shows a good error'](assert) { assert.expect(3); let rejectedMessage = 'Supercalifragilisticexpialidocious'; @@ -3433,7 +3429,7 @@ moduleFor( }) ); - assert.throws(() => this.visit('/'), new RegExp(rejectedMessage), 'expected an exception'); + await assert.rejects(this.visit('/'), new RegExp(rejectedMessage), 'expected an exception'); } ["@test willLeave, willChangeContext, willChangeModel actions don't fire unless feature flag enabled"]( @@ -3472,7 +3468,7 @@ moduleFor( return this.visit('/about'); } - ['@test Errors in transitionTo within redirect hook are logged'](assert) { + async ['@test Errors in transitionTo within redirect hook are logged'](assert) { assert.expect(4); let actual = []; @@ -3495,7 +3491,7 @@ moduleFor( actual.push(arguments); }; - assert.throws(() => this.visit('/'), /More context objects were passed/); + await assert.rejects(this.visit('/'), /More context objects were passed/); assert.equal(actual.length, 1, 'the error is only logged once'); assert.equal(actual[0][0], 'Error while processing route: yondo', 'source route is printed'); @@ -3585,7 +3581,7 @@ moduleFor( }); } - ['@test Exception during initialization of non-initial route is not swallowed'](assert) { + async ['@test Exception during initialization of non-initial route is not swallowed'](assert) { this.router.map(function() { this.route('boom'); }); @@ -3598,10 +3594,10 @@ moduleFor( }) ); - return assert.throws(() => this.visit('/boom'), /\bboom\b/); + await assert.rejects(this.visit('/boom'), /\bboom\b/); } - ['@test Exception during initialization of initial route is not swallowed'](assert) { + async ['@test Exception during initialization of initial route is not swallowed'](assert) { this.router.map(function() { this.route('boom', { path: '/' }); }); @@ -3613,7 +3609,8 @@ moduleFor( }, }) ); - return assert.throws(() => this.visit('/'), /\bboom\b/); + + await assert.rejects(this.visit('/'), /\bboom\b/); } ['@test {{outlet}} works when created after initial render'](assert) { @@ -4038,7 +4035,7 @@ moduleFor( }); } - ['@test Doesnt swallow exception thrown from willTransition'](assert) { + async ['@test Doesnt swallow exception thrown from willTransition'](assert) { assert.expect(1); this.addTemplate('application', '{{outlet}}'); this.addTemplate('index', 'index'); @@ -4060,15 +4057,12 @@ moduleFor( }) ); - return this.visit('/').then(() => { - return assert.throws( - () => { - return this.visit('/other'); - }, - /boom/, - 'expected an exception but none was thrown' - ); - }); + await this.visit('/'); + await assert.rejects( + this.visit('/other'), + /boom/, + 'expected an exception but none was thrown' + ); } ['@test Exception if outlet name is undefined in render and disconnectOutlet']() { @@ -4140,7 +4134,7 @@ moduleFor( }); } - ['@test Defining a Route#serialize method in an Engine throws an error'](assert) { + async ['@test Defining a Route#serialize method in an Engine throws an error'](assert) { assert.expect(1); // Register engine @@ -4157,16 +4151,20 @@ moduleFor( this.mount('blog'); }); - return this.visit('/').then(() => { - let router = this.applicationInstance.lookup('router:main'); - let PostRoute = Route.extend({ serialize() {} }); - this.applicationInstance.lookup('engine:blog').register('route:post', PostRoute); + await this.visit('/'); - assert.throws( - () => router.transitionTo('blog.post'), - /Defining a custom serialize method on an Engine route is not supported/ + let router = this.applicationInstance.lookup('router:main'); + let PostRoute = Route.extend({ serialize() {} }); + this.applicationInstance.lookup('engine:blog').register('route:post', PostRoute); + + try { + // TODO: for some reason this doesn't work with assert.reject + await router.transitionTo('blog.post'); + } catch (e) { + assert.ok( + e.message.match(/Defining a custom serialize method on an Engine route is not supported/) ); - }); + } } ['@test App.destroy does not leave undestroyed views after clearing engines'](assert) { diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index cf6efa4afff..9a913b9d9b6 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -311,7 +311,7 @@ moduleFor( return this.visitAndAssert('/boo?foo=baz'); } - ['@test error is thrown if dynamic segment and query param have same name'](assert) { + async ['@test error is thrown if dynamic segment and query param have same name'](assert) { assert.expect(1); this.router.map(function() { @@ -320,9 +320,10 @@ moduleFor( this.setSingleQPController('index'); - expectAssertion(() => { - this.visitAndAssert('/boo?foo=baz'); - }, `The route 'index' has both a dynamic segment and query param with name 'foo'. Please rename one to avoid collisions.`); + await assert.rejectsAssertion( + this.visitAndAssert('/boo?foo=baz'), + `The route 'index' has both a dynamic segment and query param with name 'foo'. Please rename one to avoid collisions.` + ); } ['@test query params have been set by the time setupController is called'](assert) { @@ -1535,7 +1536,7 @@ moduleFor( return this.refreshModelWhileLoadingTest(true); } - ["@test warn user that Route's queryParams configuration must be an Object, not an Array"]( + async ["@test warn user that Route's queryParams configuration must be an Object, not an Array"]( assert ) { assert.expect(1); @@ -1547,9 +1548,10 @@ moduleFor( }) ); - expectAssertion(() => { - this.visit('/'); - }, 'You passed in `[{"commitBy":{"replace":true}}]` as the value for `queryParams` but `queryParams` cannot be an Array'); + await assert.rejectsAssertion( + this.visit('/'), + 'You passed in `[{"commitBy":{"replace":true}}]` as the value for `queryParams` but `queryParams` cannot be an Array' + ); } ['@test handle route names that clash with Object.prototype properties'](assert) { diff --git a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js index 11239f354ba..3feb1f5739c 100644 --- a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js @@ -134,7 +134,8 @@ moduleFor( this.assertCurrentPath('/parent/child?page=2'); }); } - ['@test query params in the same route hierarchy with the same url key get auto-scoped']( + + async ['@test query params in the same route hierarchy with the same url key get auto-scoped']( assert ) { assert.expect(1); @@ -142,9 +143,10 @@ moduleFor( this.setMappedQPController('parent'); this.setMappedQPController('parent.child'); - expectAssertion(() => { - this.setupBase(); - }, "You're not allowed to have more than one controller property map to the same query param key, but both `parent:page` and `parent.child:page` map to `parentPage`. You can fix this by mapping one of the controller properties to a different query param key via the `as` config option, e.g. `page: { as: 'other-page' }`"); + await assert.rejectsAssertion( + this.setupBase(), + "You're not allowed to have more than one controller property map to the same query param key, but both `parent:page` and `parent.child:page` map to `parentPage`. You can fix this by mapping one of the controller properties to a different query param key via the `as` config option, e.g. `page: { as: 'other-page' }`" + ); } ['@test Support shared but overridable mixin pattern'](assert) { diff --git a/packages/ember/tests/routing/substates_test.js b/packages/ember/tests/routing/substates_test.js index 74a884e8b6f..43831b45672 100644 --- a/packages/ember/tests/routing/substates_test.js +++ b/packages/ember/tests/routing/substates_test.js @@ -1,6 +1,6 @@ import { RSVP } from '@ember/-internals/runtime'; import { Route } from '@ember/-internals/routing'; -import { moduleFor, ApplicationTestCase, runLoopSettled, runTask } from 'internal-test-helpers'; +import { moduleFor, ApplicationTestCase, runTask } from 'internal-test-helpers'; let counter; @@ -561,8 +561,6 @@ moduleFor( }); this.route('memere', { path: '/memere/:seg' }, function() {}); }); - - this.visit('/'); } getController(name) { @@ -577,7 +575,9 @@ moduleFor( return currentPath; } - ['@test ApplicationRoute#currentPath reflects loading state path'](assert) { + async ['@test ApplicationRoute#currentPath reflects loading state path'](assert) { + await this.visit('/'); + let momDeferred = RSVP.defer(); this.addTemplate('grandma.loading', 'GRANDMALOADING'); @@ -608,7 +608,9 @@ moduleFor( return promise; } - [`@test Loading actions bubble to root but don't enter substates above pivot `](assert) { + async [`@test Loading actions bubble to root but don't enter substates above pivot `](assert) { + await this.visit('/'); + let sallyDeferred = RSVP.defer(); let puppiesDeferred = RSVP.defer(); @@ -668,7 +670,9 @@ moduleFor( return promise; } - ['@test Default error event moves into nested route'](assert) { + async ['@test Default error event moves into nested route'](assert) { + await this.visit('/'); + this.addTemplate('grandma.error', 'ERROR: {{model.msg}}'); this.add( @@ -699,7 +703,9 @@ moduleFor( }); } - [`@test Non-bubbled errors that re-throw aren't swallowed`](assert) { + async [`@test Non-bubbled errors that re-throw aren't swallowed`](assert) { + await this.visit('/'); + this.add( 'route:mom.sally', Route.extend({ @@ -717,20 +723,18 @@ moduleFor( }) ); - assert.throws( - () => { - this.visit('/grandma/mom/sally'); - }, + await assert.rejects( + this.visit('/grandma/mom/sally'), function(err) { return err.msg === 'did it broke?'; }, 'it broke' ); - - return runLoopSettled(); } - [`@test Handled errors that re-throw aren't swallowed`](assert) { + async [`@test Handled errors that re-throw aren't swallowed`](assert) { + await this.visit('/'); + let handledError; this.add( @@ -764,20 +768,18 @@ moduleFor( }) ); - assert.throws( - () => { - this.visit('/grandma/mom/sally'); - }, + await assert.rejects( + this.visit('/grandma/mom/sally'), function(err) { return err.msg === 'did it broke?'; }, `it broke` ); - - return runLoopSettled(); } - ['@test errors that are bubbled are thrown at a higher level if not handled'](assert) { + async ['@test errors that are bubbled are thrown at a higher level if not handled'](assert) { + await this.visit('/'); + this.add( 'route:mom.sally', Route.extend({ @@ -796,20 +798,18 @@ moduleFor( }) ); - assert.throws( - () => { - this.visit('/grandma/mom/sally'); - }, + await assert.rejects( + this.visit('/grandma/mom/sally'), function(err) { return err.msg == 'did it broke?'; }, 'Correct error was thrown' ); - - return runLoopSettled(); } - [`@test Handled errors that are thrown through rejection aren't swallowed`](assert) { + async [`@test Handled errors that are thrown through rejection aren't swallowed`](assert) { + await this.visit('/'); + let handledError; this.add( @@ -843,22 +843,20 @@ moduleFor( }) ); - assert.throws( - () => { - this.visit('/grandma/mom/sally'); - }, + await assert.rejects( + this.visit('/grandma/mom/sally'), function(err) { return err.msg === 'did it broke?'; }, 'it broke' ); - - return runLoopSettled(); } - ['@test Default error events move into nested route, prioritizing more specifically named error routes - NEW']( + async ['@test Default error events move into nested route, prioritizing more specifically named error routes - NEW']( assert ) { + await this.visit('/'); + this.addTemplate('grandma.error', 'ERROR: {{model.msg}}'); this.addTemplate('mom_error', 'MOM ERROR: {{model.msg}}'); @@ -880,20 +878,22 @@ moduleFor( }) ); - return this.visit('/grandma/mom/sally').then(() => { - step(assert, 3, 'Application finished booting'); + await this.visit('/grandma/mom/sally'); - assert.equal( - this.$('#app').text(), - 'GRANDMA MOM ERROR: did it broke?', - 'the more specifically named mome error substate was entered over the other error route' - ); + step(assert, 3, 'Application finished booting'); - assert.equal(this.currentPath, 'grandma.mom_error', 'Initial route fully loaded'); - }); + assert.equal( + this.$('#app').text(), + 'GRANDMA MOM ERROR: did it broke?', + 'the more specifically named mome error substate was entered over the other error route' + ); + + assert.equal(this.currentPath, 'grandma.mom_error', 'Initial route fully loaded'); } - ['@test Slow promises waterfall on startup'](assert) { + async ['@test Slow promises waterfall on startup'](assert) { + await this.visit('/'); + let grandmaDeferred = RSVP.defer(); let sallyDeferred = RSVP.defer(); @@ -961,7 +961,10 @@ moduleFor( return promise; } - ['@test Enter child loading state of pivot route'](assert) { + + async ['@test Enter child loading state of pivot route'](assert) { + await this.visit('/'); + let deferred = RSVP.defer(); this.addTemplate('grandma.loading', 'GMONEYLOADING'); @@ -997,7 +1000,9 @@ moduleFor( }); } - [`@test Error events that aren't bubbled don't throw application assertions`](assert) { + async [`@test Error events that aren't bubbled don't throw application assertions`](assert) { + await this.visit('/'); + this.add( 'route:mom.sally', Route.extend({ @@ -1062,7 +1067,9 @@ moduleFor( return this.visit('/grandma/mom/sally'); } - ['@test Setting a query param during a slow transition should work'](assert) { + async ['@test Setting a query param during a slow transition should work'](assert) { + await this.visit('/'); + let deferred = RSVP.defer(); this.addTemplate('memere.loading', 'MMONEYLOADING'); diff --git a/packages/ember/tests/service_injection_test.js b/packages/ember/tests/service_injection_test.js index 17e9f81e119..c5f86500dde 100644 --- a/packages/ember/tests/service_injection_test.js +++ b/packages/ember/tests/service_injection_test.js @@ -9,7 +9,7 @@ import { EMBER_MODULE_UNIFICATION } from '@ember/canary-features'; moduleFor( 'Service Injection', class extends ApplicationTestCase { - ['@test Service can be injected and is resolved'](assert) { + async ['@test Service can be injected and is resolved'](assert) { this.add( 'controller:application', Controller.extend({ @@ -20,13 +20,13 @@ moduleFor( this.add('service:my-service', MyService); this.addTemplate('application', ''); - this.visit('/').then(() => { - let controller = this.applicationInstance.lookup('controller:application'); - assert.ok(controller.get('myService') instanceof MyService); - }); + await this.visit('/'); + + let controller = this.applicationInstance.lookup('controller:application'); + assert.ok(controller.get('myService') instanceof MyService); } - ['@test Service can be an object proxy and access owner in init GH#16484'](assert) { + async ['@test Service can be an object proxy and access owner in init GH#16484'](assert) { let serviceOwner; this.add( @@ -45,11 +45,11 @@ moduleFor( this.add('service:my-service', MyService); this.addTemplate('application', ''); - this.visit('/').then(instance => { - let controller = this.applicationInstance.lookup('controller:application'); - assert.ok(controller.get('myService') instanceof MyService); - assert.equal(serviceOwner, instance, 'should be able to `getOwner` in init'); - }); + let instance = await this.visit('/'); + + let controller = this.applicationInstance.lookup('controller:application'); + assert.ok(controller.get('myService') instanceof MyService); + assert.equal(serviceOwner, instance, 'should be able to `getOwner` in init'); } } ); @@ -57,7 +57,7 @@ moduleFor( moduleFor( 'Service Injection with ES5 Getters', class extends ApplicationTestCase { - ['@test Service can be injected and is resolved without calling `get`'](assert) { + async ['@test Service can be injected and is resolved without calling `get`'](assert) { this.add( 'controller:application', Controller.extend({ @@ -72,11 +72,11 @@ moduleFor( this.add('service:my-service', MyService); this.addTemplate('application', ''); - this.visit('/').then(() => { - let controller = this.applicationInstance.lookup('controller:application'); - assert.ok(controller.myService instanceof MyService); - assert.equal(controller.myService.name, 'The service name', 'service property accessible'); - }); + await this.visit('/'); + + let controller = this.applicationInstance.lookup('controller:application'); + assert.ok(controller.myService instanceof MyService); + assert.equal(controller.myService.name, 'The service name', 'service property accessible'); } } ); @@ -252,7 +252,7 @@ if (EMBER_MODULE_UNIFICATION) { }); } - ['@test Service with namespace can be injected and is resolved'](assert) { + async ['@test Service with namespace can be injected and is resolved'](assert) { this.add( 'controller:application', Controller.extend({ @@ -268,10 +268,10 @@ if (EMBER_MODULE_UNIFICATION) { MyService ); - this.visit('/').then(() => { - let controller = this.applicationInstance.lookup('controller:application'); - assert.ok(controller.get('myService') instanceof MyService); - }); + await this.visit('/'); + + let controller = this.applicationInstance.lookup('controller:application'); + assert.ok(controller.get('myService') instanceof MyService); } } ); diff --git a/packages/internal-test-helpers/lib/ember-dev/debug.ts b/packages/internal-test-helpers/lib/ember-dev/debug.ts index 7c2f334a60a..3be735f873f 100644 --- a/packages/internal-test-helpers/lib/ember-dev/debug.ts +++ b/packages/internal-test-helpers/lib/ember-dev/debug.ts @@ -34,7 +34,11 @@ class DebugAssert { // Run an expectation callback within the context of a new tracker, optionally // accepting a function to run, which asserts immediately - runExpectation(func: (() => void) | undefined, callback: (tracker: MethodCallTracker) => void) { + runExpectation( + func: (() => any) | undefined, + callback: (tracker: MethodCallTracker) => void, + async = false + ) { let originalTracker: MethodCallTracker | null = null; // When helpers are passed a callback, they get a new tracker context @@ -53,11 +57,21 @@ class DebugAssert { // Once the given callback is invoked, the pending assertions should be // flushed immediately if (func) { - func(); - this.assert(); - this.reset(); + let maybePromise = func(); - this.tracker = originalTracker; + if (async && typeof maybePromise.then === 'function') { + return maybePromise.then(() => { + this.assert(); + this.reset(); + + this.tracker = originalTracker; + }); + } else { + this.assert(); + this.reset(); + + this.tracker = originalTracker; + } } } } diff --git a/packages/internal-test-helpers/lib/ember-dev/deprecation.ts b/packages/internal-test-helpers/lib/ember-dev/deprecation.ts index c3d6fc6b34a..0731fa74af2 100644 --- a/packages/internal-test-helpers/lib/ember-dev/deprecation.ts +++ b/packages/internal-test-helpers/lib/ember-dev/deprecation.ts @@ -2,6 +2,10 @@ import DebugAssert from './debug'; import { callWithStub, DebugEnv, Message } from './utils'; type ExpectNoDeprecationFunc = (func?: () => void) => void; +type ExpectDeprecationAsyncFunc = ( + func: () => void | undefined | Message | Promise, + expectedMessage: Message +) => Promise; type ExpectDeprecationFunc = ( func: () => void | undefined | Message, expectedMessage: Message @@ -12,6 +16,7 @@ declare global { interface Window { expectNoDeprecation: ExpectNoDeprecationFunc | null; expectDeprecation: ExpectDeprecationFunc | null; + expectDeprecationAsync: ExpectDeprecationAsyncFunc | null; ignoreDeprecation: IgnoreDeprecationFunc | null; } } @@ -90,18 +95,42 @@ class DeprecationAssert extends DebugAssert { }); }; + let expectDeprecationAsync: ExpectDeprecationAsyncFunc = async (func, message) => { + let actualFunc: (() => void) | undefined; + if (typeof func !== 'function') { + message = func as Message; + actualFunc = undefined; + } else { + actualFunc = func; + } + + await this.runExpectation( + actualFunc, + tracker => { + if (tracker.isExpectingNoCalls()) { + throw new Error('expectDeprecation was called after expectNoDeprecation was called!'); + } + + tracker.expectCall(message, ['id', 'until']); + }, + true + ); + }; + let ignoreDeprecation: IgnoreDeprecationFunc = func => { callWithStub(this.env, 'deprecate', func); }; window.expectNoDeprecation = expectNoDeprecation; window.expectDeprecation = expectDeprecation; + window.expectDeprecationAsync = expectDeprecationAsync; window.ignoreDeprecation = ignoreDeprecation; } restore() { super.restore(); window.expectDeprecation = null; + window.expectDeprecationAsync = null; window.expectNoDeprecation = null; window.ignoreDeprecation = null; } diff --git a/packages/internal-test-helpers/lib/ember-dev/setup-qunit.ts b/packages/internal-test-helpers/lib/ember-dev/setup-qunit.ts index 91ae8068508..460a19bad7c 100644 --- a/packages/internal-test-helpers/lib/ember-dev/setup-qunit.ts +++ b/packages/internal-test-helpers/lib/ember-dev/setup-qunit.ts @@ -1,4 +1,5 @@ import { getDebugFunction, setDebugFunction } from '@ember/debug'; +import { DEBUG } from '@glimmer/env'; import { setupAssertionHelpers } from './assertion'; import { setupContainersCheck } from './containers'; @@ -8,6 +9,19 @@ import { setupRunLoopCheck } from './run-loop'; import { DebugEnv } from './utils'; import { setupWarningHelpers } from './warning'; +declare global { + interface Assert { + rejects(promise: Promise, expected?: string | RegExp, message?: string): Promise; + + throwsAssertion(block: () => any, expected?: string | RegExp, message?: string): any; + rejectsAssertion( + promise: Promise, + expected?: string | RegExp, + message?: string + ): Promise; + } +} + export default function setupQUnit({ runningProdBuild }: { runningProdBuild: boolean }) { let env = { runningProdBuild, @@ -29,4 +43,57 @@ export default function setupQUnit({ runningProdBuild }: { runningProdBuild: boo callback(hooks); }); }; + + QUnit.assert.rejects = async function( + promise: Promise, + expected?: RegExp | string, + message?: string + ) { + let threw = false; + + try { + await promise; + } catch (e) { + threw = true; + + QUnit.assert.throws( + () => { + throw e; + }, + expected, + message + ); + } + + if (!threw) { + QUnit.assert.ok(false, `expected an error to be thrown: ${expected}`); + } + }; + + QUnit.assert.throwsAssertion = function( + block: () => any, + expected?: string | RegExp, + message?: string + ) { + if (!DEBUG) { + QUnit.assert.ok(true, 'Assertions disabled in production builds.'); + return; + } + + return QUnit.assert.throws(block, expected, message); + }; + + QUnit.assert.rejectsAssertion = async function( + promise: Promise, + expected?: string | RegExp, + message?: string + ) { + if (!DEBUG) { + QUnit.assert.ok(true, 'Assertions disabled in production builds.'); + + return promise; + } + + await QUnit.assert.rejects(promise, expected, message); + }; } diff --git a/packages/internal-test-helpers/lib/test-cases/abstract-application.js b/packages/internal-test-helpers/lib/test-cases/abstract-application.js index 4668578d51d..113397cabb3 100644 --- a/packages/internal-test-helpers/lib/test-cases/abstract-application.js +++ b/packages/internal-test-helpers/lib/test-cases/abstract-application.js @@ -1,7 +1,7 @@ import { compile } from 'ember-template-compiler'; import { ENV } from '@ember/-internals/environment'; import AbstractTestCase from './abstract'; -import { runDestroy, runTask } from '../run'; +import { runDestroy, runTask, runLoopSettled } from '../run'; export default class AbstractApplicationTestCase extends AbstractTestCase { _ensureInstance(bootOptions) { @@ -16,13 +16,16 @@ export default class AbstractApplicationTestCase extends AbstractTestCase { })); } - visit(url, options) { - // TODO: THIS IS HORRIBLE - // the promise returned by `ApplicationInstance.protoype.visit` does **not** - // currently guarantee rendering is completed - return runTask(() => { - return this._ensureInstance(options).then(instance => instance.visit(url)); - }); + async visit(url, options) { + // Create the instance + let instance = await runTask(() => + this._ensureInstance(options).then(instance => instance.visit(url)) + ); + + // Await all asynchronous actions + await runLoopSettled(); + + return instance; } get element() { diff --git a/tests/index.html b/tests/index.html index b1c4a3a7648..52acb31a76c 100644 --- a/tests/index.html +++ b/tests/index.html @@ -9,7 +9,7 @@ display: none; } - + diff --git a/yarn.lock b/yarn.lock index 418e056d919..57bbd602489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -379,6 +379,15 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" +"@babel/plugin-transform-async-to-generator@^7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.0.tgz#234fe3e458dce95865c0d152d256119b237834b0" + integrity sha512-EeaFdCeUULM+GPFEsf7pFcNSxM7hYjoj5fiYbyuiXobW4JhFnjAv9OWzNwHyHcKoPNpAfeRDuW6VyaXEDUBa7g== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/plugin-transform-block-scoped-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" @@ -539,6 +548,13 @@ dependencies: regenerator-transform "^0.13.3" +"@babel/plugin-transform-regenerator@^7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.3.tgz#2a697af96887e2bbf5d303ab0221d139de5e739c" + integrity sha512-kEzotPuOpv6/iSlHroCDydPkKYw7tiJGKlmYp6iJn4a6C/+b2FdttlJsLKYxolYHgotTJ5G5UY5h0qey5ka3+A== + dependencies: + regenerator-transform "^0.13.4" + "@babel/plugin-transform-runtime@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.2.0.tgz#566bc43f7d0aedc880eaddbd29168d0f248966ea" @@ -6940,6 +6956,13 @@ regenerator-transform@^0.13.3: dependencies: private "^0.1.6" +regenerator-transform@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" + integrity sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A== + dependencies: + private "^0.1.6" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" From 495c93178a08c5a800ef9b254349810a560b7c1c Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Tue, 23 Apr 2019 14:53:02 -0700 Subject: [PATCH 2/2] add promise polyfill --- broccoli/test-polyfills.js | 3 ++- .../components/link-to/query-params-curly-test.js | 7 ++++++- packages/ember/tests/routing/decoupled_basic_test.js | 4 +++- packages/ember/tests/routing/query_params_test.js | 2 -- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/broccoli/test-polyfills.js b/broccoli/test-polyfills.js index 5787ecd33aa..1acff85794e 100644 --- a/broccoli/test-polyfills.js +++ b/broccoli/test-polyfills.js @@ -7,8 +7,9 @@ module.exports = function polyfills() { let polyfillEntry = writeFile( 'polyfill-entry.js', ` + require('core-js/modules/es6.promise'); require('core-js/modules/es6.symbol'); - require('regenerator-runtime/runtime') + require('regenerator-runtime/runtime'); ` ); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js index ead477d711e..0a366a7a447 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/link-to/query-params-curly-test.js @@ -1,6 +1,7 @@ import Controller from '@ember/controller'; import { RSVP } from '@ember/-internals/runtime'; import { Route } from '@ember/-internals/routing'; +import { DEBUG } from '@glimmer/env'; import { ApplicationTestCase, classes as classMatcher, @@ -59,8 +60,12 @@ moduleFor( `{{#let (query-params foo='456' bar='NAW') as |qp|}}{{link-to 'Index' 'index' qp}}{{/let}}` ); + // TODO If we visit this page at all in production mode, it'll fail for + // entirely different reasons than what this test is trying to test. + let promise = DEBUG ? this.visit('/') : null; + await assert.rejectsAssertion( - this.visit('/'), + promise, /The `\(query-params\)` helper can only be used when invoking the `{{link-to}}` component\./ ); } diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 095a5220d84..694dffca0a1 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -844,9 +844,11 @@ moduleFor( }) ); - this.handleURLRejectsWith(this, assert, '/specials/1', 'Setup error'); + let promise = this.handleURLRejectsWith(this, assert, '/specials/1', 'Setup error'); resolve(menuItem); + + return promise; } ['@test Moving from one page to another triggers the correct callbacks'](assert) { diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index 9a913b9d9b6..5ce921f3784 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -312,8 +312,6 @@ moduleFor( } async ['@test error is thrown if dynamic segment and query param have same name'](assert) { - assert.expect(1); - this.router.map(function() { this.route('index', { path: '/:foo' }); });