我该如何在Jest测试中使用webpack的html-loader导入?

18

我刚刚开始使用Jest测试框架,直接的单元测试工作正常,但是当我需要测试任何一个模块(通过babel+webpack的ES模块)需要一个HTML文件的组件时,我遇到了巨大的问题。

这里有一个例子:

import './errorHandler.scss';
import template from './errorHandler.tmpl';

class ErrorHandler {
    ...

我正在加载组件特定的 SCSS 文件,这个文件已经在 Jest 的 package.json 配置中设为返回一个空对象,但是当 Jest 尝试运行 import template from './errorHandler.tmpl'; 这一行时,它会中断并报错:

/Users/jannis/Sites/my-app/src/scripts/errorHandler/errorHandler.tmpl.html:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<div class="overlay--top">
                                                                                         ^
    SyntaxError: Unexpected token <

        at transformAndBuildScript (node_modules/jest-runtime/build/transform.js:284:10)

package.json中的 Jest 配置如下:

"jest": {
    "setupTestFrameworkScriptFile": "<rootDir>/test/setupFile.js",
    "moduleDirectories": ["node_modules"],
    "moduleFileExtensions": ["js", "json", "html", "scss"],
    "moduleNameMapper": {
        "^.+\\.scss$": "<rootDir>/test/styleMock.js"
    }
}

看起来 webpack 的 html-loader 在 Jest 中无法正确工作,但我找不到任何解决方案。

有人知道如何让这些 html-loaderimport 在我的测试中生效吗?它们加载我的 lodash 模板标记,有时我希望不将这些大量的 HTML 块放入我的 .js 文件中,以便我可以省略 import template from x 部分。

附注:这不是一个 React 项目,只是普通的 webpack、babel 和 es6。

6个回答

25

最近我遇到了这个具体问题,创建自己的 转换预处理器 可以解决它。这是我的设置:

package.json

"jest": {
    "moduleFileExtensions": [
      "js",
      "html"
    ],
    "transform": {
      "^.+\\.js$": "babel-jest",
      "^.+\\.html$": "<rootDir>/test/utils/htmlLoader.js"
    }
 }

注意:babel-jest通常会默认包含,但如果您指定了自定义转换预处理器,则似乎必须手动包含它。

test/utils/htmlLoader.js:

const htmlLoader = require('html-loader');

module.exports = {
    process(src, filename, config, options) {
        return htmlLoader(src);
    }
}

1
这非常有帮助,谢谢。我还想补充一点,你也可以使用 underscore-template-loader 来实现这个功能。 - Allen
3
当我尝试这个时,出现了错误:“转换的process函数必须返回一个字符串,或者是一个包含该字符串的code键的对象。”这可能只适用于旧版本的jest或html-loader吗? - Sam Hasler
2
@SamHasler 是的,“html-loader”变成了异步函数,使用“html-loader-jest”。它基本上做的是相同的事情,但需要旧版本的“html-loader”与同步接口。 - Dmitrii Dushkin

11
稍有些晚,但我想要补充一下,如果你想走这条路的话,也可以使用html-loader-jest npm包来实现。

安装后,你需要将它添加到你的jest配置中:

"transform": {
        "^.+\\.js$": "babel-jest",
        "^.+\\.html?$": "html-loader-jest"
    }

谢谢你,你让我省去了数小时的调查!突然使用 moduleNameMapper 导入 HTML 模块导致 Jest 无法导入 fake-indexeddb,不知道为什么。 - Yara_M

5

对于 Jest > 28.x.xhtml-loader 的使用:

  1. 按照此处的文档创建自定义转换器。
jest/html-loader.js

const htmlLoader = require("html-loader");

module.exports = {
  process(sourceText) {
    return {
      code: `module.exports = ${htmlLoader(sourceText)};`,
    };
  },
};

将其添加到你的jest配置中。
jest.config.js

...

  // A map from regular expressions to paths to transformers
  transform: {
    "^.+\\.html$": "<rootDir>/jest/html-loader.js",
  },

...

这将修复错误:代码转换器的process()或/和processAsync()方法在“<PATH>”处应该返回一个对象或解析为对象的Promise,无效的返回值。


2
我尝试了这个,但开始出现错误:`({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){module.exports = [object Promise]; ^^^^^^^ SyntaxError: Unexpected identifier` - abhishek khandait
@abhishekkhandait 找到解决方案了吗? - Leonardo Rick
不好意思,我不得不降级到 Jest 27。 - abhishek khandait
2
@abhishekkhandait 哦,那是因为您使用的html-loader版本肯定是最新的,因此它返回了一个Promise而不是值。它不兼容。html-loader-jest曾经通过依赖于旧版本的html-loader来解决这个问题。但它无法与Jest 28+一起使用。我创建了https://www.npmjs.com/package/jest-html-loader来解决这个问题,适用于Jest 28+。 - nicoespeon
感谢 @nicoespeon 制作了这个 npm 包!你救了我的命! - ArcadeRenegade

2

对于 Jest 28+ 版本,您可以使用 jest-html-loader 来使 Jest 能够处理需要 HTML 文件的代码。

npm install --save-dev jest-html-loader

在你的Jest配置中,将其添加为.HTML文件的转换器:
"transform": {
  "^.+\\.html?$": "jest-html-loader"
},

0
也许你自己的预处理器文件会是解决方案: ScriptPreprocessor Custom-preprocessors

scriptpreprocessor:提供源文件预处理同步函数的模块路径。例如,如果您想在模块或测试中使用尚未由node支持的新语言特性(例如ES6类),则可以插入许多将ES6编译为ES5的转换器。

当我将transform-decorators-legacy添加到我的webpack模块加载程序后,我创建了自己的预处理器来解决测试问题。

0

html-loader-jest 对我来说不起作用。我的解决方法是:

"transform": {
  '\\.(html)$': '<rootDir>/htmlTemplateMock.html'
}

htmlTemplateMock.html 是一个空文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接