繁体   English   中英

如何从 Puppeteer 客户端环境运行 Node 包以进行测试

[英]How to run Node packages from Puppeteer client environment for testing purposes

我正在使用Puppeteer在 react 环境中测试客户端函数 - 该函数本身不使用 React,但旨在导入 es6 react 模块并在最终用户 DOM 环境中运行。 我需要 Puppeteer,因为此函数依赖于诸如innerText属性,而这些属性在 jsdom 中不可用。

此函数将 DOM 元素作为参数,但是我无法为其编写测试文件。 这是我的代码示例:

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);
    });
  }
);

如评论中所述,直接从page.evaluate()返回的元素是正确的,但是当作为参数传递时,该函数接收一个空对象。 我怀疑范围问题,但我在这里完全没有解决方案。

遗憾的是,我找不到任何不会调用我的文件转译的解决方案,但希望我设法使其正常工作。

关键点是创建第二个转译配置,该配置将使用 UMD 格式生成可由 Web 浏览器直接使用的代码。 由于我使用 rollup,这里是我的rollup,config.js文件:

import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import resolve from 'rollup-plugin-node-resolve';
import pkg from './package.json';

// The content that is actually exported to be used within a React or Node application.
const libConfig = [
    {
        inlineDynamicImports: true,
        input: './src/index.js',
        output: [
            {
                file: './lib/index.js',
                format: 'cjs'
            },
        ],
        external: [...Object.keys(pkg.dependencies || {})],
        plugins: [
            commonjs(),
            resolve(),
            babel({exclude: 'node_modules/**'})
        ]
    }
];

// Used to generate a bundle that is directly executable within a browser environment, for E2E testing.
const testConfig = [
    {
        inlineDynamicImports: true,
        input: './src/index.js',
        output: [
            {
                file: './dist/index.js',
                format: 'umd',
                name: 'tachyon'
            },
        ],
        external: [...Object.keys(pkg.dependencies || {})],
        plugins: [
            commonjs(),
            resolve(),
            babel({runtimeHelpers: true})
        ]
    }
];

const config = process.env.NODE_ENV === 'test' ? testConfig : libConfig;
export default config;

然后我稍微重写了我的脚本,以便在每次测试运行时生成我的测试包。

包.json

{
  "scripts": {
    "build:test": "NODE_ENV=test rollup -c && NODE_ENV=",
    "build": "rollup -c",
    "test": "yarn build:test && jest"
  },
}

最后,我将转换后的脚本添加到我的selection_test.html文件中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Selection range test</title>
    <script src="../dist/index.js"></script>
</head>
...

这让我可以像这样编写我的测试文件:

import path from 'path';
import puppeteer from 'puppeteer';
import {describe, beforeAll, afterAll, it} from '@jest/globals';

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

        beforeAll(async done => {
            try {
                browser = await puppeteer.launch({
                    headless: true,
                    args: ['--disable-web-security', '--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure'],
                });
                page = await browser.newPage();
                await page.goto(`file://${path.join(process.env.ROOT, 'tests/selection_test.html')}`, {waitUntil: 'networkidle0'});
                await page.setBypassCSP(true);
            } 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 data = await page.evaluate(() => {
                // Fix eslint warnings.
                window.tachyon = window.tachyon || null;
                if (window.tachyon == null) {
                    return new Error(`cannot find tachyon module`);
                }

                const stn = document.getElementById('single-text-node');
                const witnessRange = tachyon.setRange(stn, 1, 10);
                const selectionRange = tachyon.getRange(stn);

                return {witnessRange, selectionRange, element: stn.outerHTML};
            });

            console.log(data); // Outputs the correct values
            /*
            {
                witnessRange: { start: 1, end: 10 },
                selectionRange: {
                    absolute: { start: 1, end: 10 },
                    start: { container: {}, offset: 1 },
                    end: { container: {}, offset: 10 }
                },
                element: '<div id="single-text-node">Lorem ... sem.</div>'
            }
             */
        });
    }
);

唯一剩下的问题是, start.containerend.container的结果中getRange是不确定的,但它的木偶不能处理的范围似乎更容易的问题startContainerendContainer性-我能够通过内容之间的DOM引用page.evaluate 和我的模块功能没有任何问题,所以它看起来不再是问题了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM