问题
JQuery在import
上初始化。如果window
和window.document
存在(而module
不存在),JQuery保存引用并在此后使用它们。
有没有一种方法可以“重新初始化”或“重置”JQuery后,它已经被import
艾德给给予不同的引用window
和document
?
A failing testcase part 1
项目结构
.
├── .eslintrc.json
├── .prettierrc
├── index.spec.js
├── package.json
├── README.md
├── spec/
│ └── support/
│ ├── jasmine-spec.json
│ ├── logger.js
│ ├── slow-spec-reporter.js
│ └── type-check.js
├── template1.html
└── template2.html
字符串
./index.spec.js
import { JSDOM } from 'jsdom';
describe('jquery', () => {
it('uses the currently available document', async () => {
const { document: template1Document, jquery: template1Jquery } = await parseHtml('template1.html');
expect(template1Document.querySelector('p').textContent).toEqual('Hello world');
expect(template1Jquery.find('p').text()).toEqual('Hello world');
const { document: template2Document, jquery: template2Jquery } = await parseHtml('template2.html');
expect(template2Document.querySelector('p').textContent).toEqual('Goodbye world');
expect(template2Jquery.find('p').text()).toEqual('Goodbye world'); // !!!
// Expected 'Hello world' to equal 'Goodbye world'.
});
});
async function parseHtml(fileName) {
const dom = await JSDOM.fromFile(fileName, {
url: 'http://localhost',
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true,
});
const window = dom.window;
const document = window.document;
globalThis.window = window;
globalThis.document = document;
const dynamicImport = await import('jquery');
const $ = dynamicImport.default;
return {
document: document,
jquery: $(`html`),
};
}
型
./template1.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>template1.html</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
型
./template2.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>template2.html</title>
</head>
<body>
<p>Goodbye world</p>
</body>
</html>
型
这是代码的摘录,但你可以在这里找到a complete github repository。
重新初始化一个或几个import
艾德jquery是不够的,所有的jquery引用都需要重新初始化。例如,如果一个生产文件以import jQuery from 'jquery'
开头,那么jQuery
需要是重新初始化的jquery的引用。这很难解释,所以我将用另一个失败的测试案例来说明我的意思:
一个失败的测试用例第二部分
项目结构
.
├── .eslintrc.json
├── .prettierrc
├── index.spec.js
├── package.json
├── prod-code.js
├── README.md
├── spec/
│ └── support/
│ ├── jasmine-spec.json
│ ├── logger.js
│ ├── slow-spec-reporter.js
│ └── type-check.js
├── template1.html
├── template2.html
└── test-setup.js
型
./index.spec.js
import * as testSetup from './test-setup.js';
import * as prodCode from './prod-code.js';
describe('jquery', () => {
it('dynamic imports affect static imports template1', async () => {
await testSetup.parseHtmlWithDynamicImport('template1.html');
const { document: template1Document, jquery: template1Jquery } =
await prodCode.parseHtmlWithJQueryStaticImport('template1.html'); // !!!
// Error: jQuery requires a window with a document
expect(template1Document.querySelector('p').textContent).toEqual(
'Hello world'
);
expect(template1Jquery.find('p').text()).toEqual('Hello world');
});
it('dynamic imports affect static imports template2', async () => {
await testSetup.parseHtmlWithDynamicImport('template2.html');
const { document: template2Document, jquery: template2Jquery } =
await prodCode.parseHtmlWithJQueryStaticImport('template2.html'); // !!!
// Error: jQuery requires a window with a document
expect(template2Document.querySelector('p').textContent).toEqual(
'Goodbye world'
);
expect(template2Jquery.find('p').text()).toEqual('Goodbye world');
});
});
型
./test-setup.js
import { JSDOM} from 'jsdom'
export async function parseHtmlWithDynamicImport(fileName) {
const dom = await JSDOM.fromFile(fileName, {
url: 'http://localhost',
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true,
});
const dynamicImport = await import('jquery');
const $ = dynamicImport.default(dom.window);
return {
document: dom.window.document,
jquery: $(`html`),
};
}
型
./prod-code.js
import { JSDOM } from 'jsdom';
import jQuery from 'jquery';
export async function parseHtmlWithJQueryStaticImport(fileName) {
const dom = await JSDOM.fromFile(fileName, {
url: 'http://localhost',
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true,
});
return {
document: dom.window.document,
jquery: jQuery(`html`),
};
}
型
./template1.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>template1.html</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
型
./template2.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>template2.html</title>
</head>
<body>
<p>Goodbye world</p>
</body>
</html>
型
这是代码的摘录,但you can find a complete github repository在这里。
上下文
我正在开发一个仅限前端的单页应用项目。尽管该项目仅在浏览器中运行,但自动化测试在Node.JS中运行。测试在JSDOM中加载html并执行部分生产代码。1
在创建时,JSDOM返回一个在Node.JS中工作的DOM API,包括一个window
对象。如果没有这个,jQuery将在导入时出错,因为现代版本的have code like this:
(function(global, factory) {
"use strict";
if (typeof module === "object" && typeof module.exports === "object") {
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket trac-14549 for more info.
module.exports = global.document ?
factory(global, true) :
function(w) {
if (!w.document) {
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
} else {
factory(global);
}
// Pass this if window is not defined yet
})(typeof window !== "undefined" ? window : this, function(window, noGlobal) {
console.log(`window`, window);
console.log(`noGlobal`, noGlobal);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
正如您所看到的,这是importing jQuery的副作用。这引起了相当大的麻烦,因为在导入jQuery之前创建JSDOM并不总是那么简单。
例如,如果生产代码导入jQuery,测试导入生产代码,JSDOM(因此window
)还不存在。~~这会抛出一个错误。~~当以后使用它时,window
不会被设置。
但这里有一个更重要的用例:我希望能够在测试中使用不同的HTML文件,但这限制了我每次测试只能使用一个。
总有一天,我想把这个测试代码作为一个测试框架向公众发布。有些人可能会显式地使用import jQuery from 'jquery'
或import $ from 'jquery'
,或者在全局范围内使用jQuery
/$
。理想情况下,他们不必修改他们的生产代码来使用我的框架。
注意事项
我使用的是jQuery 3.7.1,但是stackoverflow代码片段并没有给予我选择该版本的选项,我认为这很好,因为据我所知,这段代码在两个版本中是相同的。
1:不幸的是,这意味着违背了JSDOM的官方建议。但在 this 上下文中,我看不到解决方法。
2条答案
按热度按时间o2gm4chl1#
一切都在你的代码中:
字符串
这意味着如果在全局作用域中没有
document
可用的window
,则导入将提供它使用的工厂,而不是使用可用的窗口对象执行它并返回示例化的jQuery。如果你修改你的parseHTML代码来利用这个:
型
如果你没有让window对象在全局作用域中可用,你将收到jQuery工厂。然后你就可以通过传递你关心的window对象,以你需要的方式示例化它。
有关解决方案不起作用的更多详细信息
当你导入代码/模块时,导入文件的解析和执行只进行一次(第一次导入时),结果值保存在缓存中,所以以后的导入不需要那么多工作,但这会导致导入值总是相同的。
vdzxcuhz2#
再读一遍这张纸条:
字符串
在html解析函数中,您可以获得对 *jQuery工厂 * 的引用,您可以使用它来创建 *jQuery对象 *。
也就是说,你可以使用沿着以下几行的东西来初始化OP中描述的两种场景中的jQuery:
型