Ember Test: input focus - unit-testing

I really don't understand the chain of events that's happening here. Trying to follow the guide as well as possible. I have:
test('Tab focus', function(assert) {
visit('/demo/form');
click('input[type=text]');
andThen(function() {
assert.equal(
find('input[type=text]').css('borderTopColor'), 'rgb(0, 125, 164)', 'Text input has focus'
);
});
});
only to have it fail:
There are no transitions on the color change, and if I hit rerun, it DOES pass.

for anyone still looking for an answer - you have to trigger "focus" event manually in your test:
triggerEvent(<alement selector>, 'focus');
more info: https://guides.emberjs.com/v2.14.0/testing/acceptance/#toc_asynchronous-helpers

Related

How to test an action gets called in a component ember testing

How can I test that an action was called in a component?
There are multiple ways of triggering an action like clicking on a button. Now I want to test that the action that is called when clicking on that button is actually called. Something like expect.functionName.to.be.called or something.
I have the following code
test('it closes the create dialog when close btn is clicked', function(assert) {
this.render(hbs`{{group-create cancelCreateAction="cancelAction"}}`)
this.$('button.btn--primary').click()
expect('myAction').to.be.called?
})
so i'm just wondering what I can do there?
Well your action does something we don't know. But here's a small test i have written checking some DOM elements and the current route. Hard to tell without you telling us what your action does.
click('.someSavingButton');
andThen(function() {
assert.equal(currentRouteName(), 'index');
assert.equal(find('.something-new-in-the-dom').length, 1, "New item in HTML");
I stumbled upon this question while also searching for a way to test bubble up actions in an integration test (instead
of closure actions). Maybe you already found a solution, but I will answer to have the next person find it earlier than me.
The idiomatic way to test if an action was called is to write a mock function and assert that it will be called.
In your example - before closure actions - the way to write this kind of test is as follows:
test('it closes the create dialog when close btn is clicked', function(assert) {
// make sure our assertion is actually tested
assert.expect(1);
// bind the action in the current test
this.on('cancelAction', (actual) => {
let expected = { whatever: 'you have expected' };
assert.deepEquals(actual, expected);
// or maybe just an assert.ok(true) - but I am not sure if this is "good" style
});
this.render(hbs`{{group-create cancelCreateAction="cancelAction"}}`)
this.$('button.btn--primary').click()
expect('myAction').to.be.called?
});
Nowadays, with the closure action paradigm, the correct way to bind the mock function would be
// bind the action in the current test
this.set('cancelAction', (actual) => {
let expected = { whatever: 'you have expected' };
assert.deepEquals(actual, expected);
});
this.render(hbs`{{group-create cancelCreateAction=(action cancelAction)}}`)

Click not working in Ember-CLI integration test with PhantomJS

I'm trying to click a button in my Ember-CLI integration test, and it works with Chrome, but tells me click is undefined in PhantomJS. I've seen some other posts that recommend defining your own click event for PhantomJS, but I couldn't get that to work.
$("a:contains('Next'):visible")[0].click();
That works in Chrome, but not PhantomJS. The built in click helper, in Ember-CLI, appears to not work with "a:contains('Next'):visible".
Can anyone help?
Update:
I talked to some guys on the Ember-CLI IRC and got my selectors to work with the Ember click helper in my tests, but now the clicks appear to do nothing. Any ideas?
test("Tour next, back, and cancel builtInButtons work", function(assert) {
assert.expect(6);
visit('/').then(function() {
assert.equal(find('.shepherd-active', 'html').length, 1, "Body gets class of shepherd-active, when shepherd becomes active");
assert.equal(find('.shepherd-enabled', 'body').length, 2, "attachTo element and tour get shepherd-enabled class");
assert.equal(find('#shepherdOverlay', 'body').length, 1, "#shepherdOverlay should exist, since isModal=true");
click('.next-button', '.shepherd-enabled');
andThen(function() {
assert.equal(find('.back-button', '.shepherd-enabled').length, 1, "Ensure that the back button appears");
});
click('.back-button', '.shepherd-enabled');
andThen(function() {
assert.equal(find('.back-button', '.shepherd-enabled').length, 0, "Ensure that the back button disappears");
});
click('.cancel-button', '.shepherd-enabled');
andThen(function() {
assert.equal(find('[class^=shepherd-button]', '.shepherd-enabled').length, 0, "Ensure that all buttons are gone, after exit");
});
});
});

Testing: how to assert between two sequential promises/run.laters? How to skip `run.later` waiting in tests?

Here's a simple component:
App.FooBarComponent = Ember.Component.extend({
tagName: "button",
status: "Ready",
revertStatusPeriodMs: 2000,
click: function() {
this.set('status', 'Pending');
// A fake ajax
this.run()
.then( function() {
this.updateStatus('Finished');
}.bind(this))
.catch( function() {
this.updateStatus('Error');
}.bind(this));
},
run: function() {
return new Ember.RSVP.Promise( function(resolve) {
Ember.run.later( function() {
resolve();
}, 500);
});
},
updateStatus: function(statusText) {
this.set('status', statusText);
var periodMs = this.get('revertStatusPeriodMs') || 1000;
Ember.run.later( function() {
this.set('status', 'Ready');
}.bind(this), periodMs);
}
});
It does a simple thing: when clicked, it displays some text. Later replaces the text with another one. Even later, it reverts the text to the initial one.
The code works fine. My problem is that i'm unable to write a test for it.
test('clicking the button should set the label', function(assert) {
expect(4);
visit('/');
assert.equal( find('button').text().trim(), 'Ready', 'Should initially be "Ready"');
andThen(function() {
click('button');
assert.equal( find('button').text().trim(), 'Pending', 'Should "Pending" right after click');
});
// This one fires too late!
andThen(function() {
assert.equal( find('button').text().trim(), 'Finished', 'Should become "Finished" after promise fulfills');
});
andThen(function() {
assert.equal( find('button').text().trim(), 'Ready', 'Should eventually return to the "Ready" state');
});
});
I have two problems:
I'm unable to test the Finished state. It seems that andThen waits for all promises and run.laters to finish, while i want to test an intermediate state. How do i run an assertion between two sequential promises/run.laters?
Times can be long, and tests can take forever to complete. I generally don't mind refactoring the code for better testability, but i refuse to adjust times in the app based on the environment (e. g. 2000 for dev/prod, 0 for test). Instead, i would like to use a timer mock or some other solution.
I've tried Sinon and failed: when i mock the timer, andThen never returns. Neither of these solutions helped me.
JSBin: http://emberjs.jsbin.com/fohava/2/edit?html,js,output (Sinon is included)
None of your test code seems to use asynch – which would seem to be crucial since without anything to distinguish them, the last 2 tests will execute in the same tick (tests 3 & 4 can't both be true at the same time). I'm not familiar with QUnit, but I did find it's async method, which would seem to be pertinent. However when I tried calling assert.async, it blew up. I tried updating QUnit 1.17, still no dice.
So I don't have a solution for you. The problem, though, is that Ember will only execute both andThen tests once all the asynchronous execution has finished – this means that test 3 will never be true.
I did some code where I basically got it working but it seems really finicky and not something you would want to be do be doing..
I did this a few days ago but never replied because I wasn't happy with it - it doesn't give you enough control..
The reason I'm replying now however is because I've found a PR that may be of use to you: PR 10463
I didn't look into too much detail but apparently it "allows custom async test helpers to pause execution by returning a promise"
I'm not sure what version of Ember you're using - but if you can hold out for this PR it may help you..

Testing jQuery UI "Dialog" logic using Jasmine

In my JS view-code I am using a jQuery UI Dialog component to render a popup.
I instantiate it like this:
var popupDialog = $("#myPopupDiv").dialog({
title: "My dialog",
dialogClass: "myDialogClass",
create: createHandler,
draggable: false,
width: width,
height: height,
autoOpen: false
});
Notice it's got autoOpen set to "false". I open it in the "create"-handler:
var createHandler = function(event, ui) {
//Vi venter litt for å sikre at popupen er "klar"
setTimeout(function () {
popupDialog.dialog("open");
}, 5);
};
The open-logic is wrapped in a setTimeout to ensure the popup is ready.
The code works fine in app the browser, but when I run this code using Jasmine test-framework I get an error:
Error: cannot call methods on dialog prior to initialization; attempted to call method 'open'
The test actually passes, so clearly the item is rendered. But I don't like the error showing up when I run the tests!
I suspect that since the Jasmine tests run so fast, the component has not had time to initialize itself. So how can I assure that the component is initialized? I thought putting this logic in the "create"-handler would take care of that since that event is "Triggered when the dialog is created.", but clearly that is not the case.
Here is how I test it:
it("should show my popup", function () {
var myPopupLink = $('.popupLink');
myPopupLink.click();
//Wait until popup is shown
waitsFor(function () {
return !$('.myDialogClass').is(":hidden");
}, "Popupen didn't show", 1000);
//Check that the DOM is as expected
expect($('.myDialogClass .popupContentDiv')).toExist();
expect(...
//Close popup
myPopupLink.click();
expect($('.myDialogClass .popupContentDiv')).not.toExist();
});
Anybody have a clue how I can verify the initialization-status of the popup-dialog?
Or any other workarounds?
Thanks!
The problem with your test is, that it is more an acceptance test then a unit test. Most of stuff that you try to test is functionality of jQueryUi. What you really wanna test is that the createHandler opened the dialog with a delay. So your popupDialog.dialog should be a spy where you can check that it was called after the delay.
At the moment your code is really hard to test cause it is based directly on jquery. You should think about to have functions where you can inject your depenedencies instead of relying on global variables like popupDialog.
Here is an example on how to mock out all dependencies:
//mock out setTimeout so you dont have to wait in your test
jasmine.Clock.useMock();
//create a mock that will return from $().dialog()
var mockDialog = jasmine.createSpy('dialog');
// mock $ to return {dialog: mock that return {dialog: mockDialog}}
var mock$ = spyOn(window, '$').andReturn({
dialog:jasmine.createSpy('$').andReturn({
dialog: mockDialog
})
})
expect(mock$).toHaveBeenCalled();
// call the create function
window[mock$.mostRecentCall.args[0].create]();
jasmine.Clock.tick(4999);
expect(mockDialog$).not.toHaveBeenCalled();
jasmine.Clock.tick(5001);
expect(mockDialog$).toHaveBeenCalledWith('open');
As you can see its very complicated to mock out all the jQuery dependencies. So ether you rewrite your code for better testability or test this stuff as acceptance test with selenium capybara etc.

Ember.js - login example: Ember.run.later

I copied this login example for my own needs. It runs fine. But I am asking myself: why do I need the Ember.run.later(this, this._serverLogin, 100); line? Like the comment said it is only for simulating the delay. Ok. But if I change it to this:
// Create the login controller
MyApp.loginController = Ember.Object.create({
username: '',
password: '',
isError: false,
tryLogin: function() {
if(this.get('username') === MyApp.USERNAME &&
this.get('password') === MyApp.PASSWORD) {
this.set('isError', false);
this.set('username', '');
this.set('password', '');
MyApp.stateManager.send('loginSuccess');
} else {
this.set('isError', true);
MyApp.stateManager.send('loginFail');
}
},
});
without Ember.run.later(this, this._serverLogin, 100);, I get Uncaught Error: <Ember.StateManager:ember270> could not respond to event loginSuccess in state loggedOut.awaitingCredentials. So I thought probably I need this delay to get the stateManager changed before or somethign like that. But when I run the old code with Ember.run.later(this, this._serverLogin, 0); it still works. So, whats different? The documentation of ember didnt gave any hints.
It's because your StateManager is still in the early state setup process when calling a sendEvent (loginSuccess/loginFailed).
By delaying the event sending w/ Ember.run.later, your code is processed in a next run loop, and the state is properly setup.
That being said, you are using Ember in a very old fashion. You should have a look at the state-of-the-art way to manage app routes.