deadcoder0904
deadcoder0904

Reputation: 8683

Get multiple attributes from a single selector in Puppeteer

I just created a new React app with the following contents in App.js:

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

If you don’t know React, then just consider the contents inside the return statement which is basic HTML.

I want to perform E2E testing with Puppeteer so my **App.test.js•• looks like:

import puppeteer from "puppeteer";

let browser;
let page;

beforeAll(async () => {
  browser = await puppeteer.launch({
    headless: false
  });
  page = await browser.newPage();
  await page.goto("http://localhost:3000/");
});

test("renders learn react link", async () => {
  await page.waitForSelector(".App");

  const header = await page.$eval(".App-header>p", e => e.innerHTML);
  expect(header).toBe(`Edit <code>src/App.js</code> and save to reload.`);

  const link = await page.$eval(".App-header>a", e => e);
  expect(link.innerHTML).toBe(`Learn React`);
  expect(link.href).toBe("https://reactjs.org");
  console.log(link);
});

afterAll(() => {
  browser.close();
});

Notice, the link constant and it’s next 4 lines. They don’t work. One way it works is if I do the following:

const linkInnerHTML = await page.$eval(".App-header>a", e => e.innerHTML);
const linkHref = await page.$eval(".App-header>a", e => e.href);
expect(linkInnerHTML).toBe(`Learn React`);
expect(linkHref).toBe("https://reactjs.org");

But, I want to be able to call a selector .App-header>a once & then call innerHTML and href on it. I know this is pretty simple but can’t seem to properly search on Google 😂

Any clues?

Upvotes: 1

Views: 956

Answers (1)

Vaviloff
Vaviloff

Reputation: 16838

You can't return whole node elements from page.$eval because they're not serializable, but you can return simple objects like this:

const link = await page.$eval(".App-header>a", e => {
    return {
        InnerHTML : e.innerHTML,
        Href : e.href,
    }
});

expect(link.InnerHTML).toBe(`Learn React`);
expect(link.Href).toBe("https://reactjs.org");

Upvotes: 1

Related Questions