nightwatchjs / nightwatch

Integrated end-to-end testing framework written in Node.js and using W3C Webdriver API. Developed at @browserstack
https://nightwatchjs.org
MIT License
11.8k stars 1.31k forks source link

Problems with Page Objects #570

Closed zigjay closed 9 years ago

zigjay commented 9 years ago

Hi!

I just upgraded to Nightwatch 0.7.9 to take advantage of the new page object support.

I understand you have to define a page object's behavior (functions) in the commands-property.

var loginCommands = { load: function () {
return this.client;
}

module.exports = { commands: [loginCommands] };

When I try to run a simple test that leverages the page object

var login = client.page.login(); login.load()

I get the error

TypeError: Object # has no method 'load'

Could this be an issue of Nightwatch or am I just overseeing something?

Cheers & thanks, Johannes

sknopf commented 9 years ago

In your page object, do you also have either elements or sections defined or just commands?

zigjay commented 9 years ago

Good question. No. Just commands for now.

I'm currently in the process of refactoring my code that uses "nightwatch-pages": "0.0.1" to pure Nightwatch.js. Thought it wouldn't matter to have no elements for just some testing.

Does it?

sknopf commented 9 years ago

OK, that would be the reason. For backwards compatibility, we check that the page object is an object AND has either elements or sections properties. So once you add either one of those, you should see the expected behavior.

zigjay commented 9 years ago

Wow - thanks!

xonev commented 8 years ago

Wow, this should really be in the documentation, since I'm sure many people will be trying out just the URL property first to make sure it's working (happened to me). http://nightwatchjs.org/guide#page-objects

etler commented 8 years ago

This also confused me. This should definitely be in the documentation since the current one has an example that is broken in that it only has a url.

trueter commented 8 years ago

+1

geelen commented 8 years ago

:sob:

geelen commented 8 years ago

Apologies for the unhelpful issue comment in my frustration. Going to have a quick look to see if I can PR a fix.

sknopf commented 8 years ago

@geelen thanks a lot for contributing the PR. Now that it's been several months with the enhanced page object support, we expect most if not all people have adopted the new support and that we can actually remove this backwards compatibility check all together. Of course, this would be a breaking change and we prefer not to do this in a minor release so we'd like to wait for the next major release to do so.

In the mean time we will update the docs so it's much more clear. The workaround is quite easy (add either elements or sections property). I suspect most people are running into this issue when following the docs and setting this up for the first time, so we'll make it much more clear. Sorry for the confusion and frustration around this, hopefully you'll consider this a good outcome.

momenator commented 8 years ago

Hi, I'm getting undefined when I'm invoking navigate method. I've followed the instruction here and nothing seems to work. I'm using es6 syntax:

pageobject:

const loginCommands = {
  login(email, password) {
    return this
      .waitForElementVisible('@emailInput', 2000)
      .setValue('@emailInput', email)
      .setValue('@passwordInput', password)
      .click('@loginButton')
      .pause(2000);
  }
};

export default {
  url: "http://some_website.com",
  commands: [loginCommands],
  elements: {
    emailInput: {
      selector: 'input#email-input'
    },
    passwordInput: {
      selector: 'input#password-input'
    },
    loginButton: {
      selector: 'button'
    }
  }
};

test script


module.exports = {

  'Logs in to app with valid creds' : (client) => {
    var loginPage = client.page.loginPage();
    loginPage
      .navigate()
      .login(cred.email, cred.password)
      .end();
  },
}

error message: TypeError: loginPage.navigate is not a function

jdlee23 commented 8 years ago

@mombi93 did you find a fix for your issue? i'm having the same issue as well

theoutlander commented 8 years ago

I'm having the same issue. This seems like a bug!

ghost commented 8 years ago

I'm having the same issue...

jdlee23 commented 8 years ago

thought i'd shoot an update. i got it working by using a function like below:

  url: function () {
    return this.api.launchUrl + "/login";
  },

config

  test_settings: {
    default: {
      launch_url: "http://localhost:8080",
  ...
  }
theoutlander commented 8 years ago

I got it working as well with a similar change as above. Thanks.

xamedow commented 7 years ago

I have a similar issue with commands, so i'm guessing, i'm doing it wrong maybe. Here is what my page object looks like:

const homeCommands = {
  startProcess: function() {
    this
      .waitForElementVisible('@startProcessButton', 1000)
      .click('@startProcessButton');
    this.api.pause(2000);

    return this;
  }
};

module.exports = {
  commands: [homeCommands],
  url: function() {
    return this.api.launchUrl
  },
  elements: {
    startProcessButton: {
      selector: '[data-test="start-process-button"]'
    }
  }
};

So when i'm trying to run test, it throws an error that neither of the command methods, like waitForElementVisible or click, exists.

senocular commented 7 years ago

@xamedow I tested your code (NW v0.9.9, Mac, Chrome) and its working for me.

xamedow commented 7 years ago

It's rather strange, but after moving commands prop after elements, tests have started without errors.

idrisadetunmbi commented 6 years ago

Above worked for me too. Page-objects don't just work well in nightwatch.

bertcurtis commented 6 years ago

None of the above has worked for me.

Getting this error:

Running:  getPricesForFlights
    TypeError: expedia.navigate(...).addLocations(...).clickOnDate is not a function
    at Object.getPricesForFlights (/Users/bertcurtis/divvy-test/tests/getPricesForFlights.js:6:4)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)

This is my config file:

{
    "src_folders": ["tests"],
    "output_folder": "reports",
    "custom_commands_path": "",
    "custom_assertions_path": "",
    "page_objects_path": "./pages",
    "globals_path": "./external_globals.js",

    "selenium": {
        "start_process": false
    },

    "test_settings": {
        "default": {
            "launch_url": "http://www.expedia.com",
            "selenium_port": 9515,
            "selenium_host": "localhost",
            "silent": true,
            "screenshots": {
                "enabled": true,
                "on_failure": true,
                "on_error": true,
                "path": "./screenshots"
            },
            "desiredCapabilities": {
                "browserName": "chrome",
                "chromeOptions": {
                    "args": ["--no-sandbox"]
                },
                "acceptSslCerts": true
            }
        },

        "firefox": {
            "desiredCapabilities": {
                "browserName": "firefox"
            }
        },

        "edge": {
            "desiredCapabilities": {
                "browserName": "MicrosoftEdge"
            }
        }
    }
}

This is my page object file:

var homePageCommands = {
    clickOnDate: function() {
        this.api.pause(1000);
        return this;
        var buttons = this.api.browser.element('@dateDropdown').elements('button');
        return buttons.every(function(date) {
            if (
                date.getAttribute('data-month') === '11' &&
                date.getAttribute('data-day') === '20'
            ) {
                return date.click();
            } else {
                console.log('No attributes with the passed in values found');
                return this;
            }
        });
    },
    clickFirstFlight: function() {
        var first = this.api.browser.elements(
            'css selector',
            'div[class=grid-container standard-padding]'
        );
        first[0].click('button[type=button]');
        this.api.pause(2000);
        first[0].click('button[type=button]');
        return this.api.pause(1000).click('href[id=forcedChoiceNoThanks]');
    },
    addLocations: function() {
        this.api.pause(1000);
        this.api.waitForElementVisible('@flight', 5000);
        this.api.click('@flight');
        this.api.pause(2000);
        this.api.waitForElementVisible('@origin', 2000);
        this.api.setValue('@origin', 'Salt Lake City');
        this.api.setValue('@destination', 'Rome');
        this.api.pause(2000);
        this.api.setValue('@departing', '12/11/2018');
        return this.api.click('@returning');
    },
    verifyFlightPicker: function() {
        this.api.pause(2000);
        this.api.waitForElementVisible('@success');
        return this.api.browser.assert.valueContains('@success', 'Nice Job!');
    }
};

module.exports = {
    url: function() {
        return this.api.launchUrl;
    },
    elements: {
        dateDropdown: 'div[class=datepicker-dropdown]',
        flight: 'button[data-lob=flight]',
        origin: 'input[id=flight-origin-hp-flight]',
        destination: 'input[id=flight-destination-hp-flight]',
        departing: 'input[id=flight-departing-hp-flight]',
        returning: 'input[id=flight-returning-hp-flight]',
        search: 'button[type=submit]',
        success: 'div[class=alert alert-success superlativeAlert bestvalueAlert]'
    },
    commands: [homePageCommands]
};

And this is my test file:

this.getPricesForFlights = function(browser) {
    var expedia = browser.page.expedia();
    expedia
        .navigate()
        .addLocations()
        .clickOnDate()
        .pause(1000)
        .click('button[type=submit]')
        .pause(2000)
        .clickFirstFlight()
        .verifyFlightPicker();
    browser.end();
};

Nothing I try has worked, including the things listed here. I've changed up the syntax of the test multiple times, I've moved functions around in the page object, I've changed the steps in the function to reflect what is documented in the nightwatch documentation but unless I do this.api.whateverActionHere, it says function not found.

I'm at a loss here.

PJUllrich commented 5 years ago

Not all browser commands are supported on the page object!.

Please make sure that you only call commands on the page object that are specified in the docs. Commands that are not supported are printed as the last command in the call chain. For example, the error message: expedia.navigate(...).addLocations(...).clickOnDate is not a function means that the command clickOnDate is not supported. In this case, the command was written by hand, which means that within that command a command is used that is not supported (in this case the elements command, I guess).