浏览器测试设置:如何使已经全局的 SystemJS 模块可用于 require('systemjs') 测试代码?

Browser test setup: How to make already global SystemJS module available to require('systemjs') of tested code?

我的代码相当独立于系统,并且在 node.js 或浏览器上只有很少的系统相关代码行 运行s。我已经设法设置了基于 mocha 的测试,以便它可以在这两种环境中工作,使用相同的测试和相同的代码模块。我还管理浏览器测试环境以自动交换对那些总是调用浏览器版本的 node.js 版本的极少数系统依赖包的调用。

所以一切 运行s,包括 mocha 和 sinon 以及所有测试。

直到我决定不仅将 SystemJS 用于 运行 测试,而且还用于代码本身。

node.js 部分仍然有效,包括测试。当我 运行 浏览器测试时出现问题。

对于那些我使用 index.html 文件的 运行 测试,下面是完整的文件。

请注意,这与任何后来的生产浏览器环境无关,在这个早期阶段,我只想测试代码 (纯粹基于网络和存储,没有任何 GUI 的东西,在浏览器上使用 IndexedDB,在 node.js 和浏览器上使用 websockets) 我已经对浏览器使用了。

关键是线路 - 唯一的线路是新的,其他一切都已经工作了几个月

SystemJS.set('systemjs', SystemJS.newModule(SystemJS));

sinon 的一个类似方法已经运行了几个月,我希望我可以使用完全相同的方法使已经可用的 SystemJS 在模块使用 require('systemjs') 时可用。但是没用。

这是模块在 require:

之后看到的

实际的 SystemJS 模块内容在 Symbol(): tt.

下面

我确定答案非常简单,但是 SystemJS 的文档对我没有帮助,当然这很可能完全是我自己的错,但那是到底是怎么回事,我问了两个小时后没有结果。我也花了一些时间来完成当前的设置,包括必须阅读 SystemJS 源代码,我自己弄明白了这一切,但我认为这个微小且非常具体的问题对于 SO 问题来说可能是合理的?

只是为了完成设置的描述(对于那些好奇的人):我 运行 这一切都来自 WebStorm,目录设置是一个通常的 node.js 项目 ./test/./src/./lib/,当然还有 ./node_modules/。 html 文件是 ./test/index.html,我告诉 WebStorm "run" 它,所以 WS 充当测试的网络服务器。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Browser Tests</title>
    <link href="https://cdn.rawgit.com/mochajs/mocha/v3.4.1/mocha.css" rel="stylesheet"/>
    <script src="https://cdn.rawgit.com/mochajs/mocha/v3.4.1/mocha.js"></script>
    <script src="http://sinonjs.org/releases/sinon-2.2.0.js"></script>
    <script src="../node_modules/systemjs/dist/system.js"></script>
    <style>
        #errors pre {
            width: 50em;
            margin: 2em 4em;
            padding: 1em;
            border: 1px solid red;
        }
    </style>
</head>
<body>
<div id="errors"></div>
<div id="mocha"></div>
<script>
    /*global SystemJS */
    mocha.setup('bdd');

    // This hack switches the system-dependent components so that require()
    // statements for modules with node.js specific code are exchanged for their
    // browser versions.
    const resolveOrig = SystemJS.constructor.prototype.resolve;
    SystemJS.constructor.prototype.resolve = function (key, parents) {
        return resolveOrig.call(SystemJS, key.replace('-nodejs.js', '-browser.js'), parents);
    };

    // ======================================================
    // RELEVANT SECTION (what I tried)
    // THIS WORKS:
    SystemJS.set('sinon', SystemJS.newModule(sinon));
    // THIS DOES NOT WORK:
    SystemJS.set('systemjs', SystemJS.newModule(SystemJS));
    // ======================================================

    // These are the test scripts in ./test/ that I want to run in the browser
    const mochaTestScripts = [
        // ... ABRIDGED LIST ....
        'crypto-helpers',
        'map-query',
        'object-helpers',
        'storage'
    ];

    SystemJS.config({
        map: {
            'chai': '../node_modules/chai/chai.js',
            'chai-as-promised': '../node_modules/chai-as-promised/lib/chai-as-promised.js',
            // dependency of chai-as-promised
            'check-error': '../node_modules/check-error/check-error.js',
            'js-sha256': '../node_modules/js-sha256/build/sha256.min.js'
        }
    });

    Promise.all(
        mochaTestScripts.map(testScript =>
            SystemJS.import('./' + testScript + '-test.js')
            .catch(err => {
                const div = document.getElementById('errors');
                const pre = document.createElement('pre');
                pre.appendChild(document.createTextNode('Test: ' + testScript + '\n\n' + err));
                div.appendChild(pre);
            })
        )
    )
    .then(() => {
        mocha.checkLeaks();
        mocha.globals([]);
        mocha.run();
    })
    .catch(err => {
        const div = document.getElementById('errors');
        const pre = document.createElement('pre');
        pre.appendChild(document.createTextNode('GLOBAL ERROR\n' + err));
        div.appendChild(pre);
    });
</script>
</body>
</html>

我自己解决了:

我不得不去SystemJS的源代码中寻找答案。问题似乎是 SystemJS 符号不像通常那样只是一个普通对象,其中方法是直接在此对象内的属性。相反,SystemJS 是一个实例,方法在原型上。

最终起作用的是 SystemJS 在内部使用 newModule 的方式,最终起作用的命令是

SystemJS.registry.set(
    'systemjs',
    SystemJS.newModule({default: SystemJS, __useDefault: true})
);

这将替换上述 index.html 测试运行程序文件中 // THIS DOES NOT WORK: 下的行。