Reputation: 61
I am currently implementing the page object pattern in protractor/selenium.
Since every method in protractor returns a promise, for consitency the methods in my page object should also return promises.
Further more my page objects may have functions which return another page object or a custom of a page object (like LeftNavigation, MainContent). Instead of returning the page object itself, the page object should be returned within a promise. Currently I do not really undestand how to do that.
Additionally I would like chain my method calls without using the .then(..) method. For WebElements it is possible to call further functions without calling the .then(..) method, e.g.
browser.driver.findElement(By.css('#someid')).findElement(By.css('#somebutton')).click();
I would also like achieve this with the page object pattern:
let pagePromise = AdminBaseBage.get(); // returns a Promise<AdminBasePage>
let mContent = page.mainContent;// should return a Promise<MainContent>
let titlePromise = mContent.getModuleTitle(); // returns a Promise<string>
or even better
AdminBaseBage.get().mainContent.getModuleTitle();
Below an extract of my PageObjects with some questions here:
AdminBasePage.js
var LeftNavigation = require('../../pageobject/LeftNavigation.js');
var MainContent = require('../../pageobject/MainContent.js');
class AdminBasePage {
constructor() {
this._leftNavigation = new LeftNavigation();
this._mainContent = new MainContent();
}
/**
* @returns {Promise<AdminBasePage>}
*/
static getPage() {
return browser.driver.get("index.php").then(function() {
return new AdminBasePage();
});
}
/**
* @returns <LoginPage>
*/
logout() {
this.leftNavigation.logout();
return new LoginPage(); //also here I would like to return a promise.
}
/**
* @returns {LeftNavigation}
*/
get leftNavigation() {
//Instead of return the object directly, I would like to return a promise here.
//But how?
return this._leftNavigation;
};
/**
* @returns {MainContent}
*/
get mainContent() {
//Instead of return the object directly, I would like to return a promise here.
//But how?
return this._mainContent;
};
}
module.exports = AdminBasePage;
MainContent.js
class MainContent {
constructor() {
/** @type {WebElementPromise} */
this._element_mainContent = this.webDriver.findElement(By.css('#maincontent'));
}
/**
* Gets the title of the main content
*
* @returns {webdriver.promise.Promise<string>}
*/
getMainContentTitle() {
return this._element_mainContent
.findElement(By.id('moduleTitle'))
.getText();
}
}
/** @type {MainContent} */
module.exports = MainContent;
Can you give any advice? I hope it is somehow clear what I am trying to explain :-)
Regards
Upvotes: 3
Views: 806
Reputation: 61
thanks for your input.
You are right that page objects should not constraint the execution flow. I will forget about trying to make a promise here :-). I also extracted the element initialization in the constrcturos to getter methods.
My main page object now consists of several page elements (LeftNavigation, TopMenu etc.) which I have created. Each of these page elements now access the WebElements they need (only via the getter methods).
class AdminBasePage extends BasePage {
constructor{
super();
/** @type {LeftNavigation} */
this._leftNavigation = new LeftNavigation();
/** @type {TopMenu} */
this._topMenu = new TopMenu();
/** @type {PathNavi} */
this._pathNavi = new PathNavi();
/** @type {ContentTopBar} */
this._contentTopBar = new ContentTopBar();
/** @type MainContent*/
this._mainContent = new MainContent()
}
/**
* @returns {Promise<AdminBasePage>}
*/
static getPage() {
return browser.driver.get("index.php").then(function() {
return new AdminBasePage();
});
}
....getters + other methods follow
}
My test now looks as followed:
describe('module_checklist', function () {
it('Check number of elements in list', function () {
let page = AdminBasePage.getPage();//returns Promise<AdminBage>
// check number of list rows
page.then(function (templateListPage) {
return templateListPage.mainContent.getArrListRows();//returns Promise<ListRow[]>
})
.then(function (arrRows) {
expect(arrRows.length).toEqual(2);
});
//check total number in pagination
page.then(function (templateListPage) {
expect(templateListPage.mainContent.getPagination().getIntPaginationTotalNumber()).toEqual(2);
});
});
}
Upvotes: 0
Reputation: 42518
You shouldn't try to make a PageObject a Promise. A PageObject is supposed to be a method/property factory and thus shouldn't be a constraint in the execution flow. I would keep it simple by returning an element with a property rather than trying to locate all the elements in a constructor:
describe('Suite', function() {
it('should module title be ...', function() {
let pageAdmin = AdminBaseBage.get();
let mContent = pageAdmin.mainContent;
let titlePromise = mContent.getModuleTitle();
expect(titlePromise).toEqual('module title');
});
});
class MainContent {
constructor() {
}
get element_module_title() { return element(By.css('#maincontent #moduleTitle')); }
/**
* Gets the title of the main content
*
* @returns {webdriver.promise.Promise<string>}
*/
getModuleTitle() {
return this.element_module_title.getText();
}
}
Upvotes: 1