Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a custom method to puppeteer.Page object

I would like to add custom methods to the puppeteer.Page object, so I could invoke them like so:

let page = await browser.newPage();
page.myNewCustomMethod();

Here is one out of many custom methods I have created. It finds first available element by the XPath expression, using the array of expressions:

const findAnyByXPath = async function (page: puppeteer.Page, expressions: string[]) {
    for (const exp of expressions) {
        const elements = await page.$x(exp);

        if (elements.length) {
            return elements[0];
        }
    }

    return null;
}

I have to invoke it like so...

let element = await findAnyByXPath(page, arrayOfExpressions);

To me, that looks weird in the editor, especially in a region where many custom methods are being invoked. It looks to me, a bit of "out of context". So I would rather invoke it like that:

page.findAnyByXPath(arrayOfExpressions);

I'm aware that there is a page.exposeFunction method, but it is not what I'm looking for.

What is a way to achieve this?

like image 739
RA. Avatar asked Jul 11 '19 02:07

RA.


1 Answers

Can you do this? Yes.

You can extend any object in JavaScript by modifying its prototype. In order to add a function to a Page object, you can access the prototype of a Page object by using the __proto__ property.

Here is a simple example adding the function customMethod to all Page objects:

const page = await browser.newPage();
page.__proto__.customMethod = async function () {
    // ...
    return 123;
}
console.log(await page.customMethod()); // 123

const anotherPage = await browser.newPage();
console.log(await anotherPage.customMethod()); // 123

Note, that you need a Page object first, to access the prototype as the constructor function (or class) is not itself exposed.

Should you do this? No.

You probably already noticed the red warnings on the linked MDN docs above. Read them carefully. In general, it is not recommended to change the prototype of objects you are using and haven't created yourself. Someone has created the prototype and he did not expect anyone to tinker around with it. For further information check out this stackoverflow question:

  • "Why is extending native objects a bad practice?"

How to do it instead?

Instead, you should just use your own functions. There is nothing wrong with having your own functions and call them with page as argument like this:

// simple function
findAnyByXPath(page);

// your own "namespace" with more functionality
myLibrary.findAnyByXPath(page);
myLibrary.anotherCustomFunction(page);

Normally, you could also extend the class Page, but in this case the library is not exporting the class itself. Therefore, you can only create a wrapper class which executes the same functions inside but offers more functionality on top. But this would be a very sophisticated approach and is really worth the effort in this case.

like image 150
Thomas Dondorf Avatar answered Sep 26 '22 01:09

Thomas Dondorf