webdriverio / codemod

A codemod to transform Protractor into WebdriverIO tests
MIT License
25 stars 12 forks source link

Converting Page Objects Generates Browser Initialization Error #25

Closed DesmondFinks closed 3 years ago

DesmondFinks commented 3 years ago

When attempting to convert my page objects from Protractor to WDIO v7, the format of the page objects change as follows:

FROM

class targetPage extends basePage {
constructor() {
super();
this.pageObjects = {
minimumPaymentDue: element(by.id("rdStatementBalance")),
nextButton: element(by.xpath("//[@id='smartwizard']/nav/div[2]/button[2]")),
agreeCheckBox: element(by.className("ccheckbox-primary ccheckbox")),
submitButton: element(by.css("button[id=ubmit]")),
payMyBillSubmit: element(by.id("btnSubmitPayMyBill"))

TO

class targetPage extends basePage {
constructor() {
super();
this.pageObjects = {
minimumPaymentDue: $("#rdStatementBalance"),
nextButton: $("//[@id='smartwizard']/nav/div[2]/button[2]"),
agreeCheckBox: $(".ccheckbox-primary ccheckbox"),
submitButton: $("button[id=ubmit]"),
payMyBillSubmit: $("#btnSubmitPayMyBill")
}}}

When this occurs and I attempt to run the test to validate, I get the following response:

[0-0] 2021-05-24T20:22:51.368Z ERROR @wdio/runner: Error: Unable to load spec files quite likely because they rely on browser object that is not fully initialised.
[0-0] browser object has only capabilities and some flags like isMobile.
[0-0] Helper files that use other browser commands have to be moved to before hook.
[0-0] Spec file(s): [spec file path]
[0-0] Error: ReferenceError: $ is not defined

Now, I understand if I update my page objects file to be more like this, the test will run correctly without the browser object initialization error:

class targetPage extends basePage {
constructor() {
super();
this.PageObjects = {
spinner: "#ioBB",
pageHeader: ".HeaderLabel",
requiredLoanAmount: "#txtReqLoanamt",
firstName: "#txtFIRSTNAME"
}}}

My basePage helper function is set up as follows for a command to select a page object (usually a field):

async select(locator) {

    const elem = await $(locator);

    await elem.waitForDisplayed();

    await elem.scrollIntoView();

    await elem.moveTo();

    await elem.click();

}

So, while I can get it to work, it sort of ruins the point of using the code mod as i'm having to update just about all of the page objects in my project manually, which appears to be the heaviest lift for conversion.

Can you provide solution (if already exists), or validate this needs to be solutioned?

christian-bromann commented 3 years ago

Currently the codemod only detects direct assignments, e.g.:

class targetPage extends basePage {
  constructor() {
    super();
    this.minimumPaymentDue = element(by.id("rdStatementBalance"))
    // ...

In WebdriverIO it is not advised to initiate elements in constructors as it just creates a bunch of useless WebDriver calls. I believe it should be feasible to enhance the codemod to also recognise properties stored in this.PageObjects. PRs welcome!

christian-bromann commented 3 years ago

Closing due to inactivity. I recommend to define elements through getters, so instead of this:

class targetPage extends basePage {
constructor() {
super();
this.pageObjects = {
minimumPaymentDue: $("#rdStatementBalance"),
nextButton: $("//[@id='smartwizard']/nav/div[2]/button[2]"),
agreeCheckBox: $(".ccheckbox-primary ccheckbox"),
submitButton: $("button[id=ubmit]"),
payMyBillSubmit: $("#btnSubmitPayMyBill")
}}}

it would be better to do this:

class targetPage extends basePage {
    get minimumPaymentDue () { return $("#rdStatementBalance") }
    get nextButton () { return $("//[@id='smartwizard']/nav/div[2]/button[2]") }
    get agreeCheckBox () { return $(".ccheckbox-primary ccheckbox") }
    get submitButton () { return $("button[id=ubmit]") }
    get payMyBillSubmit () { return $("#btnSubmitPayMyBill") }
}

This way, element calls are initiated once the property is accessed. For more information on the page object pattern in WebdriverIO please have a look into our docs. Let me know if you have any further questions.