diff --git a/addon-test-support/@ember/test-helpers/index.js b/addon-test-support/@ember/test-helpers/index.js index 36f840124..2d165e869 100644 --- a/addon-test-support/@ember/test-helpers/index.js +++ b/addon-test-support/@ember/test-helpers/index.js @@ -11,6 +11,13 @@ export { export { default as teardownContext } from './teardown-context'; export { default as setupRenderingContext, render, clearRender } from './setup-rendering-context'; export { default as teardownRenderingContext } from './teardown-rendering-context'; +export { + default as setupApplicationContext, + visit, + currentRouteName, + currentURL, +} from './setup-application-context'; +export { default as teardownApplicationContext } from './teardown-application-context'; export { default as settled, isSettled, getState as getSettledState } from './settled'; export { default as waitUntil } from './wait-until'; export { default as validateErrorHandler } from './validate-error-handler'; diff --git a/addon-test-support/@ember/test-helpers/setup-application-context.js b/addon-test-support/@ember/test-helpers/setup-application-context.js new file mode 100644 index 000000000..e7758fc88 --- /dev/null +++ b/addon-test-support/@ember/test-helpers/setup-application-context.js @@ -0,0 +1,41 @@ +import { get } from '@ember/object'; +import { nextTickPromise } from './-utils'; +import { getContext } from './setup-context'; +import hasEmberVersion from './has-ember-version'; +import settled from './settled'; + +export function visit() { + let context = getContext(); + let { owner } = context; + + return nextTickPromise() + .then(() => { + return owner.visit(...arguments); + }) + .then(() => { + context.element = document.querySelector('#ember-testing > .ember-view'); + }) + .then(settled); +} + +export function currentRouteName() { + let { owner } = getContext(); + let router = owner.lookup('router:main'); + return get(router, 'currentRouteName'); +} + +const HAS_CURRENT_URL_ON_ROUTER = hasEmberVersion(2, 13); +export function currentURL() { + let { owner } = getContext(); + let router = owner.lookup('router:main'); + + if (HAS_CURRENT_URL_ON_ROUTER) { + return get(router, 'currentURL'); + } else { + return get(router, 'location').getURL(); + } +} + +export default function() { + return nextTickPromise(); +} diff --git a/addon-test-support/@ember/test-helpers/teardown-application-context.js b/addon-test-support/@ember/test-helpers/teardown-application-context.js new file mode 100644 index 000000000..6ab80bc8d --- /dev/null +++ b/addon-test-support/@ember/test-helpers/teardown-application-context.js @@ -0,0 +1 @@ +export default function() {} diff --git a/tests/unit/setup-application-context-test.js b/tests/unit/setup-application-context-test.js new file mode 100644 index 000000000..aabfa1744 --- /dev/null +++ b/tests/unit/setup-application-context-test.js @@ -0,0 +1,132 @@ +import { module, test } from 'qunit'; +import EmberRouter from '@ember/routing/router'; +import Route from '@ember/routing/route'; +import Service from '@ember/service'; +import { + setupContext, + setupApplicationContext, + teardownContext, + teardownApplicationContext, + setApplication, + click, + visit, + currentRouteName, + currentURL, +} from '@ember/test-helpers'; +import hasEmberVersion from 'ember-test-helpers/has-ember-version'; +import { setResolverRegistry, application } from '../helpers/resolver'; +import hbs from 'htmlbars-inline-precompile'; + +const Router = EmberRouter.extend({ location: 'none' }); +Router.map(function() { + this.route('widgets'); + this.route('posts', function() { + this.route('post', { path: ':post_id' }); + }); +}); + +module('setupApplicationContext', function(hooks) { + if (!hasEmberVersion(2, 4)) { + return; + } + + hooks.beforeEach(async function() { + setResolverRegistry({ + 'router:main': Router, + 'template:application': hbs` + + {{outlet}} + `, + 'template:index': hbs`

Hello World!

`, + 'template:posts': hbs`

Posts Page

{{outlet}}`, + 'template:posts/post': hbs`
{{model.post_id}}
`, + 'service:foo': Service.extend({ isFoo: true }), + 'route:posts/post': Route.extend({ + model(params) { + return params; + }, + }), + 'route:widgets': Route.extend({ + model() { + throw new Error('Model hook error from /widgets'); + }, + }), + }); + + setApplication(application); + + await setupContext(this); + await setupApplicationContext(this); + }); + + hooks.afterEach(async function() { + await teardownApplicationContext(this); + await teardownContext(this); + }); + + test('can perform a basic template rendering', async function(assert) { + await visit('/'); + + assert.equal(currentRouteName(), 'index'); + assert.equal(currentURL(), '/'); + + assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets'); + assert.equal(this.element.querySelector('h1').textContent, 'Hello World!'); + }); + + test('can perform a basic template rendering for nested route', async function(assert) { + await visit('/posts/1'); + + assert.equal(currentRouteName(), 'posts.post'); + assert.equal(currentURL(), '/posts/1'); + + assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets'); + assert.equal(this.element.querySelector('.post-id').textContent, '1'); + }); + + test('can visit multiple times', async function(assert) { + await visit('/posts/1'); + + assert.equal(currentRouteName(), 'posts.post'); + assert.equal(currentURL(), '/posts/1'); + + assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets'); + assert.equal(this.element.querySelector('.post-id').textContent, '1'); + + await visit('/'); + + assert.equal(currentRouteName(), 'index'); + assert.equal(currentURL(), '/'); + + assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets'); + assert.equal(this.element.querySelector('h1').textContent, 'Hello World!'); + + await visit('/posts/2'); + + assert.equal(currentRouteName(), 'posts.post'); + assert.equal(currentURL(), '/posts/2'); + + assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets'); + assert.equal(this.element.querySelector('.post-id').textContent, '2'); + }); + + test('can navigate amongst routes', async function(assert) { + await visit('/'); + + assert.equal(currentRouteName(), 'index'); + assert.equal(currentURL(), '/'); + + await click('a[href="/posts"]'); + + assert.equal(currentRouteName(), 'posts.index'); + assert.equal(currentURL(), '/posts'); + + assert.equal(this.element.querySelector('h1').textContent, 'Posts Page'); + }); + + test('bubbles up errors', function(assert) { + assert.rejects(() => { + return visit('/widgets'); + }, /Model hook error from \/widgets/); + }); +});