Disclaimer: This is going to be a long question, so apologies up front.
I'm working to get a more reliable UI Automation suite to run and execute for a project. However, there are some outstanding questions and concerns regarding working with the NightwatchJS API that I have been unable to track down an actual answer to.
The primary question I have is when should I chain calls vs using the callback methods in the API?
Here is a very very simple example showcasing what I'm after.
Given the following very, very simplistic HTML :
<html>
<body>
<iframe src="..." class="depth-one-frame">
<html>
<body>
<iframe src="..." id="depth-two-frame">
<html>
<body>
<div id="main">
<a href="#" class="pageLink">Page Link</a>
</div>
<iframe src="..." id="depth-three-frame">
<html>
<body>
<div id="container">
<button class="pageButton">Page Button</button>
</div>
</body>
</html>
</iframe>
</body>
</html>
</iframe>
</body>
</html>
</iframe>
</body>
</html>
Say we have a page consisting of 3 or 4 iFrames. For this, we have a basic PageObject like the following:
const MyPageObj = {
elements: {
// in depth-two frame
pageLink: {
selector: '#main .pageLink'
},
// in depth-three frame
pageButton: {
selector: '#container .pageButton'
}
},
commands: [{
getDepth2Frame: function() {
this.api.frame(null)
.waitForElementVisible('.depth-one-frame', 15000)
.element('css selector', '.depth-one-frame', (frame) => {
self.api.frame({
ELEMENT: frame.value.ELEMENT
})
})
.waitForElementVisible('#depth-two-frame', 15000)
.frame('depth-two-frame');
return this;
},
getDepth3Frame: function() {
this.api.frame(null)
.waitForElementVisible('.depth-one-frame', 15000)
.element('css selector', '.depth-one-frame', (frame) => {
self.api.frame({
ELEMENT: frame.value.ELEMENT
})
})
.waitForElementVisible('#depth-two-frame', 15000)
.frame('depth-two-frame')
.waitForElementVisible('#depth-three-frame', 15000)
.frame('depth-three-frame');
return this;
},
waitForView: function(browser) {
return this.waitForElementVisible('@myView', 5000);
},
clickLink: function() {
return this.waitForElementVisible('@pageLink', 5000)
.click('@pageLink');
},
clickButton: function() {
return this.waitForElementVisible('@pageButton', 5000)
.click('@pageButton');
}
}]
};
module.exports = MyPageObj;
There are two ways to interact with this PageObject and I'm having trouble understanding the proper, most efficient way of doing so.
Path #1 (using chaining):
module.exports = {
'The page should load, and we should be able to click a link and a button @smoke': function(browser) {
const pageObj = browser.page.MyPageObj;
pageObj.switchToDepth2Frame()
.waitForView()
.clickLink()
.switchToDepth3Frame()
.clickButton();
}
};
Path #2 (using callbacks):
module.exports = {
'The page should load, and we should be able to click a link and a button @smoke': function(browser) {
const pageObj = browser.page.MyPageObj;
browser.frame(null, () => {
pageObj.waitForElementVisible('.depth-one-frame', 15000, d1Frame => {
browser.execute(function(iframe) {
document.querySelector(iframe).setAttribute('id', 'depth-one-frame')
}, ['.depth-one-frame'])
.frame('depth-one-frame', () => {
pageObj.waitForElementVisible('#depth-two-frame', 15000, () => {
browser.frame('depth-two-frame', d2Frame => {
pageObj.waitForElementVisible('@myView', 5000, myViewEl => {
pageObj.waitForElementVisible('@pageLink', 5000, linkEl => {
pageObj.click('@pageLink');
pageObj.waitForElementVisible('#depth-three-frame', 15000, () => {
browser.frame('depth-three-frame', d3Frame => {
pageObj.waitForElementVisible('@pageButton', 15000, buttonEl => {
pageObj.click('@pageButton');
});
});
});
});
});
});
});
});
});
});
}
};
In Path #1, we use chaining. In Path #2, we use callbacks.
Primary Question: When should you using chaining (like in Path #1) vs callbacks (like in Path #2)?
Secondary Question: Should page objects just return this
or the result of the method calls? Ex:
waitForView: function(browser) {
return this.waitForElementVisible('@myView', 5000);
},
vs
waitForView: function(browser) {
this.waitForElementVisible('@myView', 5000);
return this;
},
The issue I'm seeing is that sometimes, the page just sits there and times out while attempting to find elements or sometimes clicks too quickly and doesn't actually perform the action.
Any insight into understanding the best practices path of working with the amazing NightwatchJS API would be muchos appreciated! Thanks!