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/setup-context.js b/addon-test-support/@ember/test-helpers/setup-context.js index 4eaab3368..81b76d615 100644 --- a/addon-test-support/@ember/test-helpers/setup-context.js +++ b/addon-test-support/@ember/test-helpers/setup-context.js @@ -68,6 +68,15 @@ export default function(context, options = {}) { let contextGuid = guidFor(context); CLEANUP[contextGuid] = []; + let testElementContainer = document.getElementById('ember-testing-container'); + let fixtureResetValue = testElementContainer.innerHTML; + + // push this into the final cleanup bucket, to be ran _after_ the owner + // is destroyed and settled (e.g. flushed run loops, etc) + CLEANUP[contextGuid].push(() => { + testElementContainer.innerHTML = fixtureResetValue; + }); + return nextTickPromise() .then(() => { let { resolver } = options; diff --git a/addon-test-support/@ember/test-helpers/setup-rendering-context.js b/addon-test-support/@ember/test-helpers/setup-rendering-context.js index 4426e4528..5d100e5d1 100644 --- a/addon-test-support/@ember/test-helpers/setup-rendering-context.js +++ b/addon-test-support/@ember/test-helpers/setup-rendering-context.js @@ -2,7 +2,7 @@ import { guidFor } from '@ember/object/internals'; import { run } from '@ember/runloop'; import Ember from 'ember'; import global from './global'; -import { getContext, CLEANUP } from './setup-context'; +import { getContext } from './setup-context'; import { nextTickPromise } from './-utils'; import settled from './settled'; @@ -41,15 +41,6 @@ export default function(context) { let contextGuid = guidFor(context); RENDERING_CLEANUP[contextGuid] = []; - let testElementContainer = document.getElementById('ember-testing-container'); - let fixtureResetValue = testElementContainer.innerHTML; - - // push this into the final cleanup bucket, to be ran _after_ the owner - // is destroyed and settled (e.g. flushed run loops, etc) - CLEANUP[contextGuid].push(() => { - testElementContainer.innerHTML = fixtureResetValue; - }); - return nextTickPromise().then(() => { let { owner } = context; 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/helpers/resolver.js b/tests/helpers/resolver.js index 9fdfb11a7..71023eb75 100644 --- a/tests/helpers/resolver.js +++ b/tests/helpers/resolver.js @@ -1,11 +1,14 @@ import Ember from 'ember'; import { dasherize } from '@ember/string'; +import { merge } from '@ember/polyfills'; import { setRegistry } from '../../resolver'; import { setResolver, setApplication } from 'ember-test-helpers'; import require from 'require'; import App from '../../app'; +import config from '../../config/environment'; -export const application = App.create({ autoboot: false }); +const AppConfig = merge({ autoboot: false }, config.APP); +export const application = App.create(AppConfig); export const resolver = application.Resolver.create({ namespace: application, isResolverFromTestHelpers: true, 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/); + }); +});