Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page object command callbacks using element references do not use API context #1048

Closed
senocular opened this issue Jun 30, 2016 · 6 comments
Closed

Comments

@senocular
Copy link
Contributor

Note: this was originally mentioned in #1043 but I figured it deserved its own, independent issue.

Callbacks in page object element commands (those referencing an '@'-prefixed element):

https://github.com/nightwatchjs/nightwatch/blob/v0.9.5/lib/page-object/command-wrapper.js#L74

have their callbacks wrapped and re-called with a different context (the Nightwatch object) than normal callbacks:

https://github.com/nightwatchjs/nightwatch/blob/v0.9.5/lib/page-object/command-wrapper.js#L97

Example (using sample from http://nightwatchjs.org/guide#defining-elements ):

module.exports = {
  'Test': function (client) {

    function callback () {
      console.log('is client: ' + (this === client) + '; ctor: ' + this.constructor.name);
    }

    var google = client.page.google();

    google.navigate()
      .click('@searchBar', callback) // (1) page element
      .click('input[type=text]', callback) // (2) page selector
      .api.click('input[type=text]', callback) // (3) client/api selector

    client.end();
  }
};

Output:

is client: false; ctor: Nightwatch // (1)
is client: true; ctor: Object // (2)
is client: true; ctor: Object // (3)

Expected:

a) Context of element command to be consistent and be client/api.

or

b) However, I think it would be more appropriate for the context to be the page instance in the case where the commands are being called from a page object ((1), (2)). That context would allow it to retain access to page-specific commands while also having access to client/api through the api property:

    function callback () {
      console.log(this === google); // true
      console.log(this.api === client); // true
    }

Compatibility:

Changing the context like this is a breaking change that could affect existing projects if they're already depending on the Nightwatch context now. You might need a compatibility flag or whatever it is you do to handle this kind of situation to allow for backwards compatibility.

@weixiaobo88
Copy link

Hi @senocular ,

I think I got a problem related to this, could you have a look and verify if it is the problem you are going to fix.

When I use v0.9.5 and run tests under official site example under Writing Commands(google search one), I got error like this TypeError: Cannot read property 'pause' of undefined.

When I test 0.8.18, it works, if I add url in elements in google.js and visit google.navigate() in google.test.js.

This really blocked me, to work around, I just write code directly in test.js instead of using page object commands.

@senocular
Copy link
Contributor Author

senocular commented Jul 16, 2016

Hi @weixiaobo88,

What you're describing doesn't quite sound like what I'm fixing with this. This fix is specifically for callback functions.

Though the example under Writing Commands did need some tweaking for it to work for me in v0.9.5 (OSX 10.11.4), I saw no error like what you're describing. The pause() method in the page object command worked as expected.

In page commands, the context (this) should always be the page instance. My fix doesn't target page commands, only command callbacks, making sure the value of this in those callbacks is consistent.

Currently, the value of this in page callbacks would be different (at least in v0.9.5, the version I'm working with) depending on whether or not you used an @-element page selector or a normal selector (like css). In the element case the context would be the Nightwatch instance, and in the other case, it would be the client/browser/api object (it has so many different names in the docs) which is what this normally is for callbacks when not using page objects. My fix makes sure the element case also uses the api object and not the Nightwatch instance, though in my original post I did seem to favor using the page instance... but I think consistency is probably more important here.

I'm not sure what has changed since 0.8.18. If you want to provide me with your complete google page object and test code, I can check it out on my end.

@weixiaobo88
Copy link

weixiaobo88 commented Jul 17, 2016

Hi @senocular ,

Thanks for your reply. I will list my codes and expect your help to figure out what's wrong.

Environment

  • OSX EI Capitan 10.11.5
  • npm 2.14.7
  • devDependencies: webdriver-manager 10.2.1, nightwatch 0.9.5, selenium-server-standalone-jar 2.53.1
  • npm scripts (i am using npm run e2e in project)
{"e2e": "./node_modules/.bin/webdriver-manager update && ./node_modules/.bin/nightwatch -c ./test/e2e/nightwatch.json ./test/e2e"}

Folder Structure

test/
└── e2e
    ├── nightwatch.json
    ├── pages
    │   └── google.js
    └── tests
        └── google.test.js

Files
I compress the test folder and attach here.

test.zip

@senocular
Copy link
Contributor Author

senocular commented Jul 17, 2016

Hey @weixiaobo88,

Thanks for being thorough with your information 😸 !

Your problem is in your nightwatch call. In your last argument you specify the test location of ./test/e2e which also includes the folder where your page object is defined. Because the page object is defined in a JS file, its being picked up by the test loader and run as a test. This is where your error is coming from, not the actual google.test.js test.

In your nightwatch.json file, you specify a correct src_folders value of "test/e2e/tests/". This will correctly point to just your test and ignore the page object JS in the run. Ditching the ./test/e2e from your call to nightwatch should fix the problem for you.

{"e2e": "./node_modules/.bin/webdriver-manager update && ./node_modules/.bin/nightwatch -c ./test/e2e/nightwatch.json"}

@weixiaobo88
Copy link

Hi @senocular,

Thanks for your patience, and correctly point out my error 😄 O(∩_∩)O~

Thanks for your help to drag me out. I didn't find it out and I think I didn't understand all stuff. It's a hard way for me to improve (;′⌒`)

@lowang-trover
Copy link

So what is the solution for this? Why can I not use @photos inside certain callbacks? I notice It doesn't work with '.elements' or inside '.moveToElement'functions

/page/photos_page.js

var list = {
    deletePhoto: function () {
        var numPhotos = [];
        return this
            .api.elements('css selector','@photo', function (r) {
                // THROWS ERROR WHEN USING @photo
                numPhotos.push(r.value.length);
            })
    }
};
module.exports = {
    url: function() {
        // something
    },
    commands: [list],
    elements: {
        photo:{
            selector: 'div > div.item-wrapper > ol > li'
        }
    }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants