multiple expect in a single function - javascript

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);
});

Related

Protractor onPrepare() native example from GitHub

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")

Events after clickMouseButton

In one of my tests I have the mouse moved to a specific location and then clickMouseButton() is called. This action is to change the data that is displayed (paging the data.) However, after the click, when I try to get the text in a column of the table to verify that the data changed the data changes back to what was originally displayed and my test fails. My test code is:
return Remote
.moveMouseTo(JE.Buttons.Scrolling(), 5, 100)
.clickMouseButton()
.end(Infinity)
.sleep(500)
.findByXpath('//*[#id="vwNJEAll_grid_header_tbody"]/tr[2]/td[2]/div')
.getVisibleText()
.then(function (result) {
expect(result).not.to.equal(firstSeenJENumber);
});
The code behind JE.Buttons.Scrolling() is:
return Remote
.setFindTimeout(5000)
.findByXpath('//*[#id="vwNJEAll_grid_table_container"]/div[2]/div');
It really seems to me like the locator in Leadfoot is binding to what is on the page when it first loads. Is this the case, and how should I go about getting what I need to happen? I have no other explanation for this, but hopefully you do.
moveMouseTo doesn’t accept a Promise as its first argument, so that moveMouseTo call is wrong and won’t do what you want. The JE.Buttons.Scrolling() call in your example code also occurs immediately (during command chain construction), it doesn’t occur only once moveMouseTo is actually performed.
To retrieve and use an element with your abstraction function, pass the function to then and then use the element:
return Remote
.then(JE.Buttons.Scrolling)
.then(function (element) {
return Remote.moveMouseTo(element, 5, 100);
})
// …remainder of test…

Angularjs chaining .then to depend on each other

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')
}
});

"Function factory" doesn't seem to work, JavaScript

I'm displaying a series of images in a loop, and I'm trying to implement some sort of nudity filter so I'm using nude.js, a library that can somewhat detect nudity. Here's the code:
// we're inside a loop
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
});
However, it detaches all of the wrong images because nude.js is slow and it completes after the loop has gone on to the later iterations, detaching those images instead of the one it was working on.
I've tried using a function factory:
function generateCallback(arg) {
return function(result) { if (!result) $(arg).detach(); };
}
and
nude.scan( generateCallback(e.target) )
but the same thing happens.
What I want is a load event that will remove the image if it seems to contain nudity. How can I do this properly?
EDIT: nude.js works like this:
nude.load(imageid);
nude.scan(callback); // it'll pass true or false into the callback
another edit: accidentally omitted the id setting from the code I posted, but it was there in my real code, so I added it here.
I suspect the case here is that this kind of sequential processing won't work with nude.js.
Looking at the nude.js code, I think your problem is occurring in the call to nude.scan. nude.js has a variable that stores the function to invoke after the scan has completed. When calling nude.scan(callback), this variable is set to be callback.
From your PasteBin, it seems as though the callback gets assigned as expected on the first call, but on the second and subsequent calls, it gets replaced, hence why the second image is detached and not the first.
What happends to your script, is that the e var is global to the function and so after each loop it gets replaced with the new one. So when the first image is scanned, e already became the event of the second image, which get detached.
To solve your problem, use closures. If you want to know more about closures, have a look here.
Otherway, here's the solution to your problem :
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
}) (e);
});
EDIT: AS nick_w said, there is var that contains the callback and probably gets replaced each time so this is why it isn't the right picture getting detached. You will probably have to modify the script yourself

Prompt user for confirmation in the middle of a process

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>

Categories

Resources