I'm simply trying to write some tests to make sure logging in and out works, including everything that goes with it. Here's what I'm doing so far:
tests/integration/sessions-test.js
import Ember from "ember";
import { test } from 'ember-qunit';
import startApp from '../helpers/start-app';
var App;
module('Integrations: Sessions', {
setup: function() {
App = startApp();
},
teardown: function() {
Ember.run(App, App.destroy);
}
});
test('Unsuccessful Sign In', function() {
expect(3);
visit('/sign-in');
andThen(function() {
fillIn('input#email', 'test#user.com');
fillIn('input#password', 'bad_password');
click('input#submit');
andThen(function() {
equal(currentRouteName(), 'sign-in', 'Unsuccessfull sign in stays on the sign in page.');
ok($('input#email, input#password').hasClass('error'), 'Inputs have a class of "error."');
equal($('input#submit').prop('disabled'), false, 'Submit button is not disabled.');
});
});
});
test('Successful Sign In', function() {
expect(2);
visit('/sign-in');
andThen(function() {
fillIn('input#email', 'test#user.com');
fillIn('input#password', 'password');
click('input#submit');
andThen(function() {
equal(currentRouteName(), 'welcome', 'Successfull sign in redirects to welcome route.');
ok(find('.message').length, "Page contains a list of messages.");
});
});
});
And, here's a trimmed down version of the sign in logic behind the scenes:
app/controllers/sign-in.js
import Ember from 'ember';
export default Ember.Controller.extend({
needs: ['application'],
actions: {
signIn: function() {
var self = this;
var data = this.getProperties('email', 'password');
// Attempt to sign in and handle the response.
var promise = Ember.$.post('/v3/sessions', data);
promise.done(function(response) {
Ember.run(function() {
self.get('controllers.application').set('token', response.access_token);
self.transitionToRoute('welcome');
});
});
...
}
}
});
The "Unsuccessful Sign In" test works just fine. The "Successful Sign In" starts to work, then quits halfway through. It signs in, then redirects correctly. On the welcome page, when it makes a call to get the messages, the node server is responding with Error: Not enough or too many segments and a 500 status. What in the world does that mean and how can I fix it, assuming I don't have any control over the API?
Also, the API is written primarily using Koa and Passport, as far as I know.
Figured it out. Apparently, it was an authentication error, not that you'd ever be able to guess that by the error message.
In the sign in controller, there's a line where I was setting the token property of the application controller. The application controller had an observer to watch that property for changes, then setup the AJAX headers when it changed. Problem is, observes use Ember's run loop, which is disabled while testing.
To fix the issue, I set the AJAX headers there in the sign in controller, just before transitioning to the welcome route.
Related
I'm trying to write an acceptance test to see if a certain property in the model for the route I visit equals what I am asserting.
I am not outputting information to the page with this route, instead I will be saving some portion of it to localstorage using an ember addon. So normally I realize I could use a find() to find an element on the page and check it's content to determine if the model is being resolved but that won't work for this case.
In the acceptance test I have this setup (using mirage btw)
test('Returns a user', function(assert) {
// Generate a user
var user = server.create('user',{first_name: 'Jordan'});
// Visit the index page with the users short_url
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
// Assert that the model the user we created by checking the first name we passed in
assert.equal(route.model.first_name,'Jordan','Model returns user with first name Jordan');
});
But when I run the test it shows the result as being undefined
UPDATE:
After trying Daniel Kmak's answer I still cannot get it to pass. This is the route code I am working with
import Ember from 'ember';
import LocalUser from 'bidr/models/user-local';
export default Ember.Route.extend({
localUser: LocalUser.create(),
navigationService: Ember.inject.service('navigation'),
activate() {
this.get('navigationService').set('navigationMenuItems', []);
},
beforeModel() {
this.localUser.clear();
},
model(params) {
var self = this;
return this.store.queryRecord('user',{short_url: params.short_url}).then(function(result){
if(result){
self.set('localUser.user', {
"id": result.get('id'),
"first_name": result.get('first_name'),
"active_auction": result.get('active_auction'),
"phone": result.get('phone')
});
// transition to event page
self.transitionTo('items');
} else {
self.transitionTo('home');
}
});
}
});
And the test looks like this
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'bidr/tests/helpers/start-app';
module('Acceptance | index route', {
beforeEach: function() {
this.application = startApp();
},
afterEach: function() {
Ember.run(this.application, 'destroy');
}
});
test('Returns a user', function(assert) {
var user = server.create('user',{first_name: 'Jordan'});
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
andThen(function() {
assert.equal(route.get('currentModel.first_name'),'Jordan','Model returns user with first name Jordan');
});
});
All the code works as it should in development.
Ok, so I've experimented with testing in Ember and it seems you should be good with getting model in andThen hook:
test('returns a user', function(assert) {
visit('/'); // visit your route
var route = this.application.__container__.lookup('route:index'); // find your route where you have model function defined
andThen(function() {
console.log(route.get('currentModel')); // your model value is correct here
assert.equal(currentURL(), '/'); // make sure you've transitioned to correct route
});
});
Taking your code it should run just fine:
test('Returns a user', function(assert) {
var user = server.create('user',{first_name: 'Jordan'});
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
andThen(function() {
assert.equal(route.get('currentModel.first_name'),'Jordan','Model returns user with first name Jordan');
});
});
Another thing to note is that you can access model via route.currentModel property.
For my model:
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
simple: 'simpleValue',
promise: Ember.RSVP.resolve(5)
});
}
});
In andThen with console.log(route.get('currentModel')); I got:
Object {simple: "simpleValue", promise: 5}
Logged.
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.
I'm trying to test my registration and login processes, the integration tests were passing perfectly prior to creating an initializer to extend the Ember-Simple-Auth Session object with the currentUser property.
It all works correctly in the browser, its just the tests now fail all in the sessionAuthenticationSucceeded action in the application route on the following line:
this.get('session.currentUser').then(function(user) {
with : TypeError: Cannot read property 'then' of undefined
/routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
actions: {
sessionAuthenticationSucceeded: function () {
var self = this;
this.get('session.currentUser').then(function(user) {
if (user.get('account') && user.get('status') === 'complete'){
self.transtionTo('home');
} else {
console.log('Need to complete Registration');
self.transitionTo('me');
}
});
}
}
}
/initializers/custom-session.js
import Ember from 'ember';
import Session from 'simple-auth/session';
export default {
name: 'custom-session',
before: 'simple-auth',
initialize: function(container) {
// application.deferReadiness();
Session.reopen({
currentUser: function() {
var id = this.get('user_id');
if (!Ember.isEmpty(id)) {
console.log('getting the current user');
return container.lookup('store:main').find('user', id);
}
}.property('user_id')
});
// application.advanceReadiness();
}
};
/tests/integration/visitor-signs-up-test.js
test('As a user with valid email and password', function(){
var email = faker.internet.email();
signUpUser(email, 'correctpassword', 'correctpassword');
andThen(function(){
equal(find('#logged-in-user').text(), email, 'User registered successfully as ' + email);
equal(sessionIsAuthenticated(App), true, 'The session is Authenticated');
});
});
test/helpers/registration-login.js
export function signUpUser(email, password, passwordConfirmation) {
visit('/register').then(function(){
fillIn('input.email', email);
fillIn('input.password', password);
fillIn('input.password-confirmation', passwordConfirmation);
click('button.submit');
});
}
I have tried using
application.deferReadiness()
as you can see commented out in the initializer (also pass in application in that instance) to ensure the async request has completed and user is available but that hasn't worked either.
I am using Pretender to intercept the api requests, but the call to api/v1/users/:id isn't being made at all during the tests.
The strange part is it works perfectly in the browser.
I'm trying to understand why this won't this work? Any guidance would be appreciated!
NB: I have also tried solution listed here and here with same outcome as above.
I have figured out the problem, turns out I wasn't returning a user_id from the api/v1/users/sign_in request Pretender was intercepting hence when sessionAuthenticationSucceeded fired, there was no user_id available and thus currentUser was never being updated/triggered.
I'll leave all the code up there in case it helps somebody else. Comments or improvements to it are still very welcome!
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.
I am writing an integration test for my Ember.js application in QUnit. Before a test, I want to seed some test data by issuing HTTP requests to a dedicated testing API. I use jQuery.post to issue POST requests and I use Ember.RSVP.Promise.cast to turn the jQuery promise into an RSVP promise. However, it never seems to resolve. In the code below, it just hangs. The string "STARTING" is printed but neither "DONE" nor "FAIL" is printed.
I also tried creating a new RSVP Promise as described in the "Advanced usage" section of http://emberjs.com/api/classes/Ember.RSVP.Promise.html, to no avail (it also hanged). If I don't wrap the jQuery promise into an RSVP Promise, it does reach either the "DONE" or "FAIL".
Why doesn't the RSVP Promise resolve?
function create_teacher() {
var url = "<%= testing_teacher_path %>";
return Ember.RSVP.Promise.cast(
Ember.$.post(
url,
{
user: {
first_name: "John",
last_name: "Doe"
school: "EE3",
email: "john#doe.com",
password: "password"
}
}
)
);
}
module("Teacher Dashboard", {
setup: function() {
console.log("STARTING");
Ember.run(HstryEd, HstryEd.advanceReadiness);
},
teardown: function() {
console.log("TEARING DOWN");
HstryEd.reset();
}
});
asyncTest("Login", function() {
expect(1);
var teacher = create_teacher();
teacher.then(function() {
console.log("DONE");
ok(true, "done");
start();
},
function() {
console.log("FAIL");
ok(false, "fail");
start();
});
});
It could have to do with the Ember runloop being disabled in test mode. Have you checked out ic-ajax? https://github.com/instructure/ic-ajax It gives you promise-style jQuery ajax requests in a form that Ember likes, even in testing. I brought it in to solve my Ember runloop issues in testing, and have had great results so far.
Alternatively, you could try wrapping your teacher.then(.. in an Ember.run.