Using async/await in test mode without Ember.run - ember.js

I'm trying to use async/await functions in an Ember 2.9 project.I have this simple code :
import Route from 'ember-route';
export default Route.extend({
async model(params) {
const activity = await this.store.findRecord('activity', params.activity_id);
this.set('activity', activity);
return activity.get('courses');
}
});
I works on dev mode but when I run a test, I have this message :
You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run
I understood than I can use Ember.run but using async/await become completly useless.
Is there a solution ?
Update 1
I tried to do Ember.testing = false and it works but I'm not sure it's safe. I think I will have problems later. What you think?
Update 2
import destroyApp from '../helpers/destroy-app';
import {describe, it, beforeEach, afterEach} from 'mocha';
import {expect} from 'chai';
import startApp from '../helpers/start-app';
import {authenticateSession} from '../helpers/ember-simple-auth';
import page from 'tiny/tests/pages/courses';
describe('Acceptance: Courses', ()=> {
let application;
beforeEach(function () {
$.Velocity.mock = true;
application = startApp();
authenticateSession(application, {data: {attributes: {fullaccess: true, 'access-token': 123}}});
});
afterEach(function () {
destroyApp(application);
});
it('can view list of courses', function () {
const activity = server.create('activity');
server.create('course', {activity: activity, name: 'Test course'});
visit('/kohorde-admin/activities/1/courses');
andThen(()=> {
expect(page.coursesCount()).to.eq(1);
});
});
});

Related

TypeError when using before all hook in ember mocha

I'm running into some issues with the before all hook in ember-mocha (version 0.14.0). Here's an example from the docs that's been slightly modified to include a beforeEach hook:
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { setupApplicationTest } from 'ember-mocha';
import { visit, currentURL } from '#ember/test-helpers';
describe('basic acceptance test', function() {
setupApplicationTest();
beforeEach(async function() {
await visit('index');
});
it('can visit /', async function() {
await visit('/');
expect(currentURL()).to.equal('/');
});
});
The above test runs as expected with no issues. However, when I substitute before for beforeEach I encounter an error:
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { setupApplicationTest } from 'ember-mocha';
import { visit, currentURL } from '#ember/test-helpers';
describe('basic acceptance test', function() {
setupApplicationTest();
before(async function() {
await visit('index');
});
it('can visit /', async function() {
await visit('/');
expect(currentURL()).to.equal('/');
});
});
TypeError: Cannot destructure property `owner` of 'undefined' or 'null'.
at visit (assets/test-support.js:24931:9)
at Context.<anonymous> (assets/tests.js:339:36)
at invoke (assets/test-support.js:22801:21)
at Context.asyncFn (assets/test-support.js:22786:11)
at callFnAsync (assets/test-support.js:14070:8)
at Hook.Runnable.run (assets/test-support.js:14022:7)
at next (assets/test-support.js:14386:10)
at assets/test-support.js:14408:5
at timeslice (assets/test-support.js:9651:27)
Please let me know if any clarification is needed. Thanks in advance for your help!
Thats expected!
before only runs once for all tests.
That means before all beforeEach hooks
However setupApplicationTest utilizes beforeEach to setup the app (and the container), and afterEach to tear it down again.
This means you get a fresh app for all tests.
However you can not really visit anything without an app.
This means for every test you get a new app instance.
This means there is no app for all tests so there is no app that could visit a route.
The same question was asked in the ember discord channel. This answer tries to take the essence of the discussion to archive it on SO.

How to setup ember-intl service in Ember integration tests?

I have just begun adding ember-intl into an application for which I had working tests. My acceptance tests are still working, but my integration tests on components whose templates are using ember-intl for string translation are failing with:
"No locale defined. Unable to resolve translation:..."
In the ember-intl docs there is a section on Integration Testing, which seems to be out of date:
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';
import { moduleForComponent, test } from 'ember-qunit';
let service;
moduleForComponent('x-product', 'XProductComponent', {
integration: true,
setup() {
service = this.container.lookup('service:intl');
service.setLocale('en-us');
}
});
test('it renders', function(assert) {
assert.expect(1);
this.render(hbs`{{x-product price=price deadline=deadline}}`);
this.set('price', 1000);
this.set('deadline', new Date());
let output = this.$().text();
assert.ok(output);
});
test('it translates', function(assert) {
assert.expect(1);
/* waits for async behavior (loading translations on app boot) to settle */
return wait().then(() => {
assert.equal(service.t('some.key'), 'Hello world');
});
});
I've looked in the Ember docs and I can see how to stub a service for testing, but not how to just load the service in a test and then work with it.
Instead of using this.container, we now need to use this.owner in the new format tests. Here's a snippet of code showing how to use it in context:
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { find, render } from '#ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | login-form', function(hooks) {
setupRenderingTest(hooks);
let service;
hooks.beforeEach(function() {
service = this.owner.lookup('service:intl');
service.setLocale('en-us');
});
test('it renders', async function(assert) {
await render(hbs`{{login-form}}`);
assert.equal(find('[data-test-login-title]').textContent.trim(), 'Login');
});
});
A PR has been submitted to ember-intl, so hopefully the docs will reflect the latest best-practice soon.

Unable to mock session with Emberfire

I'm writing a very basic acceptance test against a route that has both admin and non-admin capabilities. My test makes an assertion that if I'm coming to the app for the first time, I don't see logged in capabilities. In my application, I'm using password authentication as follows:
this.get('session').open('firebase', {
provider: 'password',
email: email,
password: password
});
I have found that when I am not authenticated in the app, then run the acceptance test, it passes. However, if I then log in on the app, then run tests, my assertion fails because the session is restored, when I think it shouldn't be. Here's the test:
import { test } from 'qunit';
import moduleForAcceptance from 'app/tests/helpers/module-for-acceptance';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';
import replaceAppRef from '../helpers/replace-app-ref';
import replaceFirebaseAppService from '../helpers/replace-firebase-app-service';
import stubFirebase from '../helpers/stub-firebase';
import unstubFirebase from '../helpers/unstub-firebase';
import { emptyApplication } from '../helpers/create-test-ref';
moduleForAcceptance('Acceptance | index', {
beforeEach: function() {
stubFirebase();
application = startApp();
replaceFirebaseAppService(application, { });
replaceAppRef(application, emptyApplication());
},
afterEach: function() {
unstubFirebase();
destroyApp(application);
}
});
test('empty app - not authenticated', function(assert) {
visit('/');
andThen(function() {
assert.equal(currentURL(), page.url, 'on the correct page');
// this works if there's no session - fails otherwise
assert.notOk(page.something.isVisible, 'cannot do something');
});
});
I think replaceFirebaseAppService should be overriding the torii-adapter but it doesn't appear to be. Any help would be greatly appreciated.
I'm using:
Ember : 2.7.0
Ember Data : 2.7.0
Firebase : 3.2.1
EmberFire : 2.0.1
jQuery : 2.2.4
Upon looking at Emberfire closer, replaceFirebaseAppService is trying to replace the torii adapter registered at torii-adapter:firebase when it was being registered by my application as torii-adapter:application.
What I ended up doing was basically replicating replaceFirebaseAppService in my own helper:
import stubFirebase from '../helpers/stub-firebase';
import startApp from '../helpers/start-app';
import replaceAppRef from '../helpers/replace-app-ref';
import createOfflineRef from './create-offline-ref';
export default function startFirebaseApp(fixtures = { }) {
stubFirebase();
let application = startApp();
// override default torii-adapter
const mock = { };
application.register('service:firebaseMock', mock, {
instantiate: false,
singleton: true
});
application.inject('torii-provider:application', 'firebaseApp', 'service:firebaseMock');
application.inject('torii-adapter:application', 'firebaseApp', 'service:firebaseMock');
// setup any fixture data and return instance
replaceAppRef(application, createOfflineRef(fixtures));
return application;
}
This prevents the torii-adapter from resolving any session data that I may have from using my application. Then I can use the provided torii helper to mock my session where I need it:
// torii helper
import { stubValidSession } from 'app/tests/helpers/torii';
// mock a valid session
stubValidSession(application, { });
Hope that saves someone else some time.

Ember Simple Auth testing window.location.reload

I've got ESA working nicely with Ember 2.0.1 but stumbled on an interesting case whilst testing:
Given the following test:
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'notifier/tests/helpers/start-app';
import Pretender from 'pretender';
import { authenticateSession } from '../../helpers/ember-simple-auth';
let server;
let application;
module('Acceptance | signout', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
server.shutdown();
}
});
test('successfully sign out and get redirected', function(assert) {
server = new Pretender(function() {
this.post('/oauth/revoke', function() {
return [200, {"Content-Type": "application/json"}];
});
});
authenticateSession(application);
visit('/admin');
click('#sign-out');
andThen(() => {
assert.equal(currentRouteName(), 'users.sign-in');
});
});
The test result is the route never changes. It remains on /admin. This only occurs in testing, it works fine if I manually interact with the app.
The reason this happens is the page never gets reloaded (window.location.reload()) after the session gets invalidated as per https://github.com/simplabs/ember-simple-auth/blob/jj-abrams/addon/mixins/application-route-mixin.js#L99-L101.
Therefore the beforeModel hook in AuthenticatedRouteMixin never get triggered so the test never redirects out of /admin to /users/sign-in.
I get that this happens because you can't run window.location.reload() in testing but I'm not sure what alternative to use. I could override sessionInvalidated() in my application route and just have the app redirect to /users/sign-in when testing but that's no longer actually testing the app I suppose.
Any suggestions?
You cannot actually reload the location in testing mode as that would restart the test suite, thus leading to an infinite loop. You could maybe stub it with sinon and assert that the stub gets called.

Ember.js - Integration Testing with Ember-CLI

I am following a Dockyard Tutorial on using ember-cli with rails. This particular section is on basic integration testing. Unfortunately, it doesn't seem to be registering properly (at least I don't think so). The test should fail and say something along the lines of "Expected: 'Welcome to Boston Ember'"; rather, it says "should pass jshint" which it is and is therefore passing. Any idea what I am doing wrong?
tests/integration/landing-page-test.js
import Ember from 'ember';
import startApp from 'bostonember/tests/helpers/start-app';
var App;
module('Integration - Landing Page', {
setup: function() {
App = startApp();
},
teardown: function() {
Ember.run(App, 'destroy');
}
});
test('Should welcome me to Boston Ember', function() {
visit('/').then(function() {
equal(find('h2#title').text(), 'Welcome bloopde bloopasa to Boston Ember');
});
});
tests/helpers/start-app.js
/* global require */
var Application = require('bostonember/app')['default'];
var Router = require('bostonember/router')['default'];
import Ember from 'ember';
export default function startApp(attrs) {
var App;
var attributes = Ember.merge({
// useful Test defaults
rootElement: '#ember-testing',
LOG_ACTIVE_GENERATION:false,
LOG_VIEW_LOOKUPS: false
}, attrs); // but you can override;
Router.reopen({
location: 'none'
});
Ember.run(function(){
App = Application.create(attributes);
App.setupForTesting();
App.injectTestHelpers();
});
App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL"
return App;
}
Will provide additional info upon request. Thank you!
Must have just been a hiccup?
Solution was to recreate the file, same code, but works now.