40

We have some routing logic that kicks you to the homepage if you dont have a JWT_TOKEN set... I want to set this before the page loads/before the js is invoked.

how do i do this ?

christoffee
  • 633
  • 1
  • 6
  • 15

5 Answers5

49

You have to register localStorage item like this:

await page.evaluate(() => {
  localStorage.setItem('token', 'example-token');
});

You should do it after page page.goto - browser must have an url to register local storage item on it. After this, enter the same page once again, this time token should be here before the page is loaded.

Here is a fully working example:

const puppeteer = require('puppeteer');
const http = require('http');

const html = `
<html>
  <body>
    <div id="element"></div>

    <script>
      document.getElementById('element').innerHTML = 
        localStorage.getItem('token') ? 'signed' : 'not signed';
    </script>
  </body>
</html>`;

http
  .createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(html);
    res.end();
  })
  .listen(8080);

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('http://localhost:8080/');

  await page.evaluate(() => {
    localStorage.setItem('token', 'example-token');
  });

  await page.goto('http://localhost:8080/');

  const text = await page.evaluate(
    () => document.querySelector('#element').textContent
  );

  console.log(text);
  await browser.close();

  process.exit(0);
})();
Everettss
  • 15,475
  • 9
  • 72
  • 98
19

in 2021 it work with following code:

// store in localstorage the token
   await page.evaluateOnNewDocument (
     token => {
       localStorage.clear();
       localStorage.setItem('token', token);
     }, 'eyJh...9_8cw');
   // open the url
 await page.goto('http://localhost:3000/Admin', { waitUntil: 'load' });

The next line from the first comment does not work unfortunately

await page.evaluate(() => {
  localStorage.setItem('token', 'example-token'); // not work, produce errors :(
});
Igor Kurkov
  • 4,318
  • 2
  • 30
  • 31
  • 1
    Thanks for this solution. Note that the `waitUntil` option is set to `load` by default, hence `{ waitUntil: 'load' }` is redundant. – Tiago Apr 09 '21 at 21:43
18

There's some discussion about this in Puppeteer's GitHub issues.

You can load a page on the domain, set your localStorage, then go to the actual page you want to load with localStorage ready. You can also intercept the first url load to return instantly instead of actually load the page, potentially saving a lot of time.

const doSomePuppeteerThings = async () => {
  const url = 'http://example.com/';
  const browser = await puppeteer.launch();
  const localStorage = { storageKey: 'storageValue' };
  await setDomainLocalStorage(browser, url, localStorage);

  const page = await browser.newPage();
  // do your actual puppeteer things now
};

const setDomainLocalStorage = async (browser, url, values) => {
  const page = await browser.newPage();
  await page.setRequestInterception(true);
  page.on('request', r => {
    r.respond({
      status: 200,
      contentType: 'text/plain',
      body: 'tweak me.',
    });
  });
  await page.goto(url);
  await page.evaluate(values => {
    for (const key in values) {
      localStorage.setItem(key, values[key]);
    }
  }, values);
  await page.close();
};
kuzdogan
  • 554
  • 7
  • 13
markdon
  • 784
  • 8
  • 19
  • 2
    Thank you so much for this answer, its probably the best solution I've found. I can easily be tweaked for cookies as well by changing the `evaluate` line to `await page.setCookie(...cookies)`. Its nice to see that this answer comes from one of the primary puppeteer devs as well. – Chris Sandvik Mar 05 '21 at 19:20
7

Without requiring to double goTo this would work:

const browser = await puppeteer.launch();
browser.on('targetchanged', async (target) => {
  const targetPage = await target.page();
  const client = await targetPage.target().createCDPSession();
  await client.send('Runtime.evaluate', {
    expression: `localStorage.setItem('hello', 'world')`,
  });
});
// newPage, goTo, etc...

Adapted from the lighthouse doc for puppeteer that do something similar: https://github.com/GoogleChrome/lighthouse/blob/master/docs/puppeteer.md

Rémi Gebski
  • 131
  • 2
  • 4
  • 1
    Can you please elaborate on what this does? – jayarjo May 04 '20 at 05:03
  • 1
    @jayarjo It looks like this adds `targetchanged` event listener and runs evaluation on each page switch – Pavlo Zhukov Jun 13 '20 at 14:31
  • Prefer this approach in that you just have to `goto` the page once. I did have to add a conditional to check for the existence of `targetPage` though, which the lighthouse doc also includes. – Joe Van Leeuwen Aug 26 '22 at 00:44
-5

Try and additional script tag. Example:

Say you have a main.js script that houses your routing logic.

Then a setJWT.js script that houses your token logic.

Then within your html that is loading these scripts order them in this way:

<script src='setJWT.js'></script>
<script src='main.js'></script>

This would only be good for initial start of the page.

Most routing libraries, however, usually have an event hook system that you can hook into before a route renders. I would store the setJWT logic somewhere in that callback.

Andrew Kim
  • 3,145
  • 4
  • 22
  • 42