Example below is taken from Protractor GitHub. Since I'm new to protractor I'd like to understand everything thoroughly.
onPrepare: function() {
browser.driver.get(env.baseUrl + '/ng1/login.html');
browser.driver.findElement(by.id('username')).sendKeys('Jane');
browser.driver.findElement(by.id('password')).sendKeys('1234');
browser.driver.findElement(by.id('clickme')).click();
// Login takes some time, so wait until it's done.
// For the test app's login, we know it's done when it redirects to
// index.html.
return browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /index/.test(url);
});
}, 10000);
}
So I don't entirely understand what last 3 return statements do? Especially this line
return /index/.test(url);
Any ideas?
Two of those functions are explained in the Protractor API
getCurrentUrl is self explanatory, it retrieves the current URL.
wait is also fairly self explanatory, it waits for a promise, condition object, or a function to evaluate as a condition
The final one, .test, is a javascript regular expression method that "executes a search for a match"
So that whole block just says wait for the current URL to pass the condition of .test (that condition being that the URL contains "index")
Related
I am writing the test in protractor and I wrote a function with 2 expect statements like this
this.Then(/^I should see a pane on the right with an Interactions and Remarks tab$/, () => {
return waitForPresence(mobileQADashboard.getTabPanel()).then(()=>{
return mobileQADashboard.selectInteractionsTab().then(()=>{
return waitForPresence(mobileQADashboard.pageElements.viewAllInteractionsLink).then(()=>{
return expect(mobileQADashboard.pageElements.viewAllInteractionsLink.isDisplayed()).to.eventually.be.true;
});
});
});
return waitForPresence(mobileQADashboard.getTabPanel()).then(()=>{
return mobileQADashboard.selectRemarksTab().then(()=>{
return waitForLoader().then(()=>{
return waitForPresence(mobileQADashboard.pageElements.addRemarkButton).then(()=>{
return expect(mobileQADashboard.pageElements.addRemarkButton.isDisplayed()).to.eventually.be.true;
});
})
});
})
});
Is it a fool proof methord and i wanted to know weather it is right to write a function like that
To me it looks like you overdid it a bit. Protractor should already take care of a synchronous execution for normal cases.
As long as your commands execute in the listed order (and it looks like that), you shouldn't need to build such pyramids.
Though, when you use then(), you can resolve a promise immediately, but you also start a new async task and let protractor continue with the lines outside then(). So, the moment you enter the first then() in line 2, the second part of the function gets executed in parallel with the first part (not sure, if that's intended).
About expect in the middle of a case: it works, though it's not best practice. If your expect in the middle fails, the test case continues until the end, but the test case status stays failed. More likely you should have two test cases instead of one.
this.Then(/^I should see a pane on the right with an Interactions and Remarks tab$/, () => {
waitForPresence(mobileQADashboard.getTabPanel());
mobileQADashboard.selectInteractionsTab();
waitForPresence(mobileQADashboard.pageElements.viewAllInteractionsLink);
expect(mobileQADashboard.pageElements.viewAllInteractionsLink.isDisplayed()).to.eventually.be.true;
//gets now executed after the firt expect. In your code it's executed in parallel to the first.
waitForPresence(mobileQADashboard.getTabPanel());
mobileQADashboard.selectRemarksTab();
waitForLoader();
waitForPresence(mobileQADashboard.pageElements.addRemarkButton);
expect(mobileQADashboard.pageElements.addRemarkButton.isDisplayed()).to.eventually.be.true;
});
Also to have expect inside a pageObject is not desired. To write a test case (an it()-block) you should a) stay in control of passed/fail and b) at the same time have no need to look into a pageObject. One should understand the test case without that.
So all in all the proper way seems more something like this:
it(/first case/,function(){
this.ThenFirst();
expect(mobileQADashboard.pageElements.viewAllInteractionsLink.isDisplayed()).to.eventually.be.true;
});
it(/second case/,function(){
this.ThenSecond();
expect(mobileQADashboard.pageElements.addRemarkButton.isDisplayed()).to.eventually.be.true;
});
and then these Page Objects:
this.ThenFirst(/^I should see a pane on the right with an Interactions and Remarks tab$/, () => {
waitForPresence(mobileQADashboard.getTabPanel());
mobileQADashboard.selectInteractionsTab();
waitForPresence(mobileQADashboard.pageElements.viewAllInteractionsLink);
};
this.ThenSecond(/^I should see a pane on the right with an Interactions and Remarks tab$/, () => {
waitForPresence(mobileQADashboard.getTabPanel());
mobileQADashboard.selectRemarksTab();
waitForLoader();
waitForPresence(mobileQADashboard.pageElements.addRemarkButton);
});
I have a chain of events set in .then statements. I wish I understood a way to use .when() in this case. This function is called on a ngn-click. The issue is that I have to click twice for it to go through. The $rootScope.csa has data going into the function that is used in the .then( functions ). I when in inspect in the chrome debugger the and step through everything works fine I believe it is because the debugger is slowing down the application and allowing it to keep up with its self. Other wise when I go through with out the debugger it goes so fast that it takes two clicks for $rootScope.csa.section.data to be populated for the next function to work as expected. The first two then statement functions are services that are wrapped in a promise and $timeout on there end and the $timeouts do not seem to be delaying the process. I have looked over q.derer() many times but cannot wrap my head around how it would be implemented in this case. Any help or information to get to the needs that I am looking for would ne appreciated.
audit.LockData('section', $scope.csa.ID, user.ID, $scope.csa.Revision)
.then($rootScope.csa = audit.RecordsCheck($rootScope.csa)) // gets data and pupulates it to the $rootscope.csa.section.data
.then($rootScope.csa = audit.GetInstance($rootScope.csa), function(){ return $rootScope.csa}) // gets ID(s) from $rootScope.csa.section.data.instances.ID(s) and populates them to the $rootScope.csa.section.instances
.then(function() {if($rootScope.csa.section.data) $location.path('/Update')})
.then(function() {if($rootScope.csa.section.data) $rootScope.$broadcast('root_updated')
});
You always need to pass a callback function to then, not some call result (even if it is a promise). I have not wrapped my head around what these functions do, but try
audit.LockData('section', $scope.csa.ID, user.ID, $scope.csa.Revision)
.then(function(lockResult) {
return audit.RecordsCheck($rootScope.csa)) // gets data and pupulates it to the $rootscope.csa.section.data
})
.then(function(checkResult) {
return audit.GetInstance($rootScope.csa) // gets ID(s) from $rootScope.csa.section.data.instances.ID(s) and populates them to the $rootScope.csa.section.instances
})
.then(function(instance) {
if ($rootScope.csa.section.data) {
$location.path('/Update')
$rootScope.$broadcast('root_updated')
}
});
I've searched all over and it appears this error is due to not using asyncTest properly. However, per the documentation, it appears that I am doing it correctly. I'm guessing I'm missing a small detail somewhere and need an extra pair of eyes...
I'm trying to test some code that makes an ajax request to get a page and then loads it in a lightbox. lightbox-content does not show up in the DOM until after the ajax call has completed and can be displayed. So, I can only check for it in my onComplete call back, which is where I have my test to see if it loaded it correctly.
Here is my code:
asyncTest('mytest', 1, function() {
utils.lightbox.show('/login', {
onComplete: function() {
ok($('#lighbox-content').is(':visible'), 'Lightbox loaded the /login page.');
start();
}
});
});
I get the error:
Uncaught Error: assertion outside test context, was at HTMLDivElement.window.utils
Can anyone see where I'm going wrong?
I agree that your code matches the documentation as far as I can tell.
Update
Even though the documentation doesn't show it, I wonder if you must tell QUnit to stop at some point so it knows to wait after the test function returns. I would think that QUnit assumes this since it's an async test, but it's worth a shot.
asyncTest('mytest', 1, function() {
stop();
...
});
I've been using Sinon.JS to avoid making the AJAX calls in the first place. This has three immediate benefits:
I don't depend on a server to respond to the requests.
I can specify different results for each test.
The tests run much faster.
The mocking can be done at the XMLHttpRequest level or on the jQuery method and is quite easy. Here's an example from one of my tests:
module("geo", {
setup: function () {
this.server = sinon.fakeServer.create();
},
teardown: function () {
this.server.restore();
}
}
test("returns detected ZIP code", function () {
this.server.respondWith("/geo/detect-zip-from-ip",
[ 200, { "Content-Type": "text/html" }, '90210' ]);
geo.detectZip(function (zip) {
assertThat(zip, is('90210'));
});
this.server.respond();
});
I have found a solution for my case, hope your problem has the same source.
Explaining in words:
I have a complicated asynchronous test
I have delayed events, and there are ok and equal assertions inside
Of course, all this is wrapped inside asyncTest
But, when the test is "completed" and I call start(), the event handlers remain there
After calling start(), all further calls of ok inside that asyncTest become illegal
And throw exceptions
I wonder what happens if the number in expect(in your example it's the second parameter) is exceeded. The same exception?
Explaining in code:
asyncTest('mytest', /*1,*/ function() {
function imgLoadedOrFailed (result) {
clearTimeout(imageTimeToLive);
img.off();
ok(result, 'Image in carousel pane has been loaded');
}
var imageTimeToLive = setTimeout(
imgLoadedOrFailed.bind(this, false),
5000),
img = panes[index].find('img:first');
if (img) {
img.on('load', imgLoadedOrFailed.bind(this, true));
img.on('error', imgLoadedOrFailed.bind(this, false));
}
});
// at some point I call: start();
In this example, when I "finish" the test calling start(), the onload and onerror events can still happen.
I have a two tests that are causing side effects with each other. I understand why as I am replacing a jQuery built-in function that is being called internally in the second test. However what I don't understand is why the test alternately passes and fails.
This question is similar However, I am not doing anything directly on the qunit-fixture div.
Here are my tests
test('always passing test', function() { // Always passes
var panelId = '#PanelMyTab';
var event = {};
var ui = {
tab: {
name: 'MyTab',
},
panel: panelId,
};
$('<div id="' + panelId + '">')
.append('Test')
.append('Show Form')
.appendTo('#qunit-fixture');
jQuery.fn.on = function(event, callback) {
ok(this.selector == panelId + ' .export', 'Setting export click event');
equal(callback, tickets.search.getReport, 'Callback being set');
};
loadTab(event, ui);
});
test('alternately passing and failing', function() { // Alternates between passing and failing on page refresh
expect(5);
var testUrl = 'test';
$('<div class="ui-tabs-panel">')
.append('Get Report')
.append('<form action="notest" target="" class="ticketSearch"></form>')
.appendTo('#qunit-fixture');
// Setup form mocking
$('form.ticketSearch').submit(function() {
var urlPattern = new RegExp(testUrl + '$');
ok(urlPattern.test($(this).prop('action')), 'Form action set to link href');
equal($(this).prop('target'), '_blank', 'Open form on a new page');
});
var event = {
target: 'a#getReport',
};
var result = getReport(event);
var form = $('form.ticketSearch');
ok(/notest$/.test($(form).prop('action')), 'Making sure action is not replaced');
equal($(form).prop('target'), '', 'Making sure that target is not replaced');
ok(false === result, 'click event returns false to not refresh page');
});
The tests will start off passing but when I refresh they will alternate between passing and failing.
Why is this happening? Even adding GET parameters to the url result in the same behavior on the page.
In the failing cases, the test is failing because internal jQuery is calling .on() when the submit() handler is set. But why isn't the test always failing in that case? What is the browser doing that a state is being retained during page refresh?
Update:
Here is the code that is being tested:
var tickets = function() {
var self = {
loadTab: function(event, ui) {
$(panel).find('.export').button().on('click', this.getReport);
},
search: {
getReport: function(event) {
var button = event.target;
var form = $(button).closest('div.ui-tabs-panel').find('form.ticketSearch').clone(true);
$(form).prop('action', $(button).prop('href'));
$(form).prop('target', '_blank');
$(form).submit();
return false;
}
}
};
return self;
}();
I've modified #Ben's fiddle to include your code with both of your tests. I modified some of your code to make it run correctly. When you hit the run button all of the tests will pass. When you hit the run button again, the second test ("alternately passing and failing") will fail -- this is basically simulating your original issue.
The issue is your first test ("always passing test") alters the global state by replacing the jQuery.fn.on function with an overridden one. Because of this, when the tests are run in order, the second test ("alternately passing and failing") uses the incorrect overridden jQuery.fn.on function and fails. Each unit test should return the global state back to its pre-test state so that other tests can run based on the same assumptions.
The reason why it's alternating between pass and fail is that under the hood QUnit always runs failed tests first (it remembers this somehow via cookie or local storage, I'm not exactly sure). When it runs the failed tests first, the second test runs before the first one; as a result, the second test gets jQuery's native on function and works. When you run it a third time, the tests will run in their "original" order and the second test will use the overridden on function and fail.
Here's the working fiddle. I've add the fix to "un-override" the on function after the test by caching the original var jQueryOn = jQuery.fn.on; function and resetting it at the end of the test via: jQuery.fn.on = jQueryOn;. You can probably better implement this using QUnit's module teardown() method instead.
You can check out https://github.com/jquery/qunit/issues/74 for more info.
I'm not sure I can solve this without some more info, but I can point out some possible issues.
The first test seems to have invalid syntax on line 2
var panelId = '#PanelMyTab');
But that's probably a type mistake, seeing as you say the first always passes.
I'm assuming that for the first test to pass(and be valid) the loadTab(event,ui) must run the jQuery.fn.on(), without it no assertions have been run. Which doing some testing with jQuery UI Tabs, seems to be the case (just not sure if it was your intention).
I'm not sure it's advisable putting these assertions within that function, and you must understand that you have overwritten the jquery function with a function that doesn't do anything, so it's likely to cause issues.
You seem to be doing something similar in the second test, you are expecting 5 assertions, but I can only see how the final 3 can be run
ok(/notest$/.test($(form).prop('action')), 'Making sure action is not replaced');
equal($(form).prop('target'), '', 'Making sure that target is not replaced');
ok(false === result, 'click event returns false to not refresh page');
The other 2 are within a submit function that doesn't look like it is invoked as part of the test.
Remember these tests are synchronous so it won't wait for you to hit submit before running the test and failing.
Here is an example
test('asynchronous test', function() {
setTimeout(function() {
ok(true);
}, 100)
})
Would fail as the ok is run 100ms after the test.
test('asynchronous test', function() {
// Pause the test first
stop();
setTimeout(function() {
ok(true);
// After the assertion has been called,
// continue the test
start();
}, 100)
})
The stop() tells qunit to wait and the start() to go!
There is also a asyncTest() detailed in the api here
Finally, it seems like you are trying to debug your code with these tests. It would be much easier to use chrome developer tools or firebug in firefox to set breakpoints on your code, and use console.log() and console.dir() to output information.
That being said I have no idea how it works for you at all, so I could be missing something :) If you're still stuck, see if you can add some more of the surrounding code and what your trying to achieve. Hope this helps.
PS: there is also a }; at the end which is invalid in the code you have given us, probably relevant in the actual application though ;)
I'm looking for a good approach to sometimes pause an action (function/method call) until the user confirms that he wants to do a specific part of that action. I need to do this in an environment that doesn't allow code execution to stop (ActionScript in my case, but an approach for JavaScript should be identical).
To illustrate, this is a mock-up of the action before introducing the user prompt:
<preliminary-phase> // this contains data needed by all the following phases //
<mandatory-phase> // this will be always be executed //
<optional-phase> // this will always execute too, if in this form, but in some cases we need to ask the user if he wants to do it //
<ending-phase> // also mandatory //
What I need is to insert a conditional user prompt, a "Do you want to do this part?", and do <optional-phase> only if the user wants to.
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed> and not <user-response-is-positive>){
<do-nothing>
}
else{
<optional-phase>
}
<ending-phase>
When trying to do this in ActionScript/JavaScript I got something like this:
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(callback = function(){
if(<user-response-is-positive>)
<optional-phase>
<ending-phase>
});
return;
}
<optional-phase>
<ending-phase>
Now both <optional-phase> and <ending-phase> are duplicated. Also because they use objects created in <preliminary-phase> I can't move them to external functions without passing all the data to those functions.
My current solution is that I enclosed each of <optional-phase> and <ending-phase> in some local functions (so that they have access to data in <preliminary-phase>) declared before I ask for confirmation and I call those functions instead of duplicating the code, but it doesn't seem right that the code is no longer in the order it's executed.
What would you guys recommend?
Notes:
1. askForConfirmation is a non-blocking function. This means that the code that follows its call is executed immediately (this is why I have a return; in my approach).
Note: I'm not 100% sure I get your exact circumstances.
The Command Pattern might be suitable here. It's similar to what people are suggesting.
You have an array of commands that get executed in order.
[<preliminary-phase>, <mandatory-phase>, <optional-phase>, <ending-phase>]
Just shift the commands off the array one at a time and call the execute method.
In the optional-phase, check to see if the user confirmation is required, if not then execute an optional code method which dispatches a command complete event, if it is required then show the alert, wait for an event, check the result and either dispatch a command complete event or call the optional method (which will run and then dispatch a command complete).
You can also create a tree of commands so can clearly state the flow of execution without having to mess with the array.
This is how programs like installation wizards work.
It's good in that the order of execution is nice and visible and your code is nicely broken down in to chunks, and the complexity of each step is encapsulated. For example, the optional-phase doesn't know anything about the ending-phase. The optional-phase only knows that the user might need prompted before executing and it handles all of that internally.
http://en.wikipedia.org/wiki/Command_pattern
"Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing..."
"the code is no longer in the order it's executed" seems fine to me actually. It's fine to have code that isn't written in the order it's executed just as long as it's clear. In fact, since your code executes in variable orders I think it's impossible for you to write it in the order it will execute without duplicating code, which is a far greater evil. Pick good function names and your approach would pass my code review.
<preliminary-phase>
<mandatory-phase>
var optional_phase = function() {
<optional-phase>
}
var ending_phase = function() {
<ending-phase>
}
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-positive>)
optional_phase();
ending_phase();
});
return;
}
optional_phase();
ending_phase();
Does this do what you're asking for?
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-positive>)
<optional-phase-as-local-function>
<ending-phase-as-local-function>
});
} else {
<optional-phase-as-local-function>
<ending-phase-as-local-function>
}
Not a huge change , but provided this flow works, optional phase is not repeated
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-negative>)
{
<ending-phase>
return;
}
});
}
<optional-phase>
<ending-phase>