簡體   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