mardi 6 octobre 2020

Passing DOM element as argument to exposed function in Puppeteer fails

I'm using Puppeteer to test a client function within a react environment - the function itself doesn't use React, but is meant to be imported in es6 react modules and run inside a end user DOM environment. I need Puppeteer since this function relies on properties such as innerText, that aren't available in jsdom.

This function takes a DOM element as an argument, however I am having trouble writing test files for it. Here is a sample of my code:

import path from 'path';
import puppeteer from 'puppeteer';
import {getSelectionRange, setSelectionRange} from './selection';

describe(
  'getSelection should match setSelection',
  () => {
    let browser;
    let page;

    beforeAll(async done => {
      try {
        browser = await puppeteer.launch();
        page = await browser.newPage();
        await page.goto(
          `file://${path.join(process.env.ROOT,
          'testFiles/selection_range_test.html')}`
        );
        await page.exposeFunction(
          'setSelectionRange', 
          (el, start, end) => setSelectionRange(el, start, end)
        );
        await page.exposeFunction(
          'getSelectionRange', 
          el => getSelectionRange(el)
        );
      } catch(error) {
        console.error(error);
      }

      done();
    });

    afterAll(async done => {
      await browser.close();
      done();
    });

    it('should match on a node with only one text node children', async () => {
      const {selection, element, argEl} = await page.evaluate(async () => {
        const stn = document.getElementById('single-text-node');

        // Since console.log will output in the Puppeteer browser and not in node console,
        // I added a line inside the selectionRange function to return the element it receives
        // as an argument.
        const argEl = await window.setSelectionRange(stn, 1, 10);

        const selectionRange = await window.getSelectionRange(stn);
        return {selection: selectionRange, element: stn, argEl};
      });

      // Outputs <div id="single-text-node">...</div> 
      // (the content is long so I skipped it, but it displays the correct value here)
      console.log(element.outerHTML);

      // Outputs {}
      console.log(argEl);
    });
  }
);

As described in the comments, the element that is directly returned from page.evaluate() is correct, but when passed as an argument, the function receives an empty object. I suspect a scope issue but I am totally out of solutions here.

Aucun commentaire:

Enregistrer un commentaire