Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access to React state with Puppeteer

I have a React application running. I'd like to know if it's possible to access state values using Puppeteer.

For example, in React I have:

const [gridBlocks, setGridBlocks] = useState([])

This value is later updated to set gridBlocks to an array of values.

Then, using Puppeteer I have:

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://localhost:3000/', {
    waitUntil: 'networkidle2'
  });

  // Can I get access to React and other javascript values now?
  // Something like console.log(await page.evaluate(() => <are React variables available here>));
  // Another way?

await browser.close();
})();

The gridBlocks state has values that I'd like to loop through to update the UI and grab screenshot of each. I won't know what the gridBlocks values are ahead of time so I can't just "hard code" them in my Puppeteer script. I really like to be able to read it from the loaded page, from page.goto.

Most articles I've seen deal with testing and they pass props to components. I'd like to read directly from the loaded page ... if possible.

Thanks!

like image 502
awolfe76 Avatar asked Sep 13 '19 19:09

awolfe76


1 Answers

I found a quick and dirty way to do this on puppeteer. First you need to grab the extension file from chrome. This method is faster than cloning the react repository from github and whatnot.

There are two major steps,

  1. Load the extension and devtools.
  2. Trick the devtools into selecting the component.

First steps:

  1. First install Chrome extension source viewer
  2. Now download the zip file for React Developers Tools.
  3. Now unzip it into your project as a folder, I chose extension for name.

Load the extension, make sure to keep the devtools open.

const browser = await puppeteer.launch({
    headless: false,
    devtools: true,
    args: [
      '--disable-extensions-except=./extension/',
      '--load-extension=./extension/',
    ]
});

Open the react page, and make sure it is fully loaded.

const page = await browser.newPage();
await page.goto('http://localhost:3000', {waitUntil: 'networkidle0'});

Now, we will find the child element from the root element and use the internal commands used in the devtools.

The waitFor is important as well. We should give it some time to initializing the selection.

await page.evaluate(()=>{
    const rootElement = document.querySelector('#root').childNodes[0];
    __REACT_DEVTOOLS_GLOBAL_HOOK__.reactDevtoolsAgent.selectNode(rootElement);
})

await page.waitFor(1000);

Finally we have the state for this particular element inside $r,

const data = await page.evaluate(()=>{
    return $r;
})

I couldn't do much with it on the node context, but I'm sure you can modify and execute different stuff using this.

Full code,

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    devtools: true,
    args: [
      '--disable-extensions-except=./extension/',
      '--load-extension=./extension/',
    ]
  });
  const page = await browser.newPage();
  await page.goto('http://localhost:3000', {waitUntil: 'networkidle0'});

  await page.evaluate(()=>{
    const rootElement = document.querySelector('#root').childNodes[0];
    __REACT_DEVTOOLS_GLOBAL_HOOK__.reactDevtoolsAgent.selectNode(rootElement);
  })

  await page.waitFor(1000);

  const data = await page.evaluate(()=>{
    return $r;
  })

  console.log(data)
})();

Here is the example react app,

import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Example;

enter image description here

Note:

  • If you don't open the devtools or select the node, it won't work.
  • It probably won't work on headless mode on current 1.19 version.
like image 91
Md. Abu Taher Avatar answered Sep 22 '22 17:09

Md. Abu Taher