Page Object Model structure for a complex applicat

2019-08-24 10:17发布

I've in the past couple of months used Puppeteer to drive an automation for a couple of small level projects. Now I want to scale the framework for a medium/large complex application.

I want to use the famed Page Object Model, where in I have separated the locators, page methods in separate files and I'm calling them in the corresponding page execution code.

My directory structure is like this

e2e_tests
    - locators
        - common-locators.js
        - page1locators.js
        - page2locators.js

    - constants
        - config.js

    - utils
        - base_functions.js
        - page1methods.js
        - page2methods.js

    - urls
        - urls.json

    - screenshots

    - test
        - bootstrap.js
        - page1.js
        - page2.js

The problem I'm facing right now is that I am not able to get the page to initialise in the method body for that particular page.

For e.g. if I have an input box in page1, I want to define a method inside utils/page1methods.js which can take care of this - something like

module.exports = {
    fillFirstInputBox(){
        await page.type(locator, "ABCDEFG");
     }
}

And then I want to call this inside the page1.js it block - something like this

const firstPage = require('../utils/page1methods.js').
.
.
.
it('fills first input box', async function (){
   firstPage.fillFirstInputBox();
});

I've tried this approach and ran into all kinds of .js errors regarding page being not defined in the page1methods.js file. I can copy paste the errors if that's necessary.

What can I do so that I

  • I am able to achieve this kind of modularisation.
  • If I can improve on this structure, what should be my approach.

1条回答
一纸荒年 Trace。
2楼-- · 2019-08-24 10:37

You can return an arrow function that will return the modules/set of functions with page variable. Be sure to wrap the whole thing in first braces, or manually return it.

module.exports = (page) => ({ // <-- to have page in scope
    async fillFirstInputBox(){ // <-- make this function async
        await page.type(locator, "ABCDEFG");
     }
})

And then pass the variable up there,

// make page variable
const firstPage = require('../utils/page1methods.js')(page)

That's it. Now all functions have access to page variable. There are other ways like extending classes, binding page etc. But this will be the easiest way as you can see. You can split it if you need.

We are halfway there. That itself won't solve this problem. The module still won't work due to async-await and class issue.

Here is a full working example,

const puppeteer = require("puppeteer");
const extras = require("./dummy"); // call it

puppeteer.launch().then(async browser => {
    const page = await browser.newPage();
    await page.goto("https://www.example.com");

    const title = await extras(page).getTitle(); // use it here
    console.log({ title }); // prints { title: 'Example Domain' }

    await browser.close();
});
查看更多
登录 后发表回答