webpack/test/helpers/FakeDocument.js

284 lines
6.1 KiB
JavaScript
Raw Normal View History

2021-12-01 16:50:13 +08:00
const fs = require("fs");
const path = require("path");
2025-04-16 22:04:11 +08:00
/**
* @this {FakeDocument}
* @param {string} property property
* @returns {EXPECTED_ANY} value
*/
2024-07-31 11:50:02 +08:00
function getPropertyValue(property) {
2021-12-01 16:50:13 +08:00
return this[property];
2024-07-31 11:50:02 +08:00
}
2021-12-01 16:50:13 +08:00
class FakeDocument {
2021-12-01 16:50:13 +08:00
constructor(basePath) {
this.head = this.createElement("head");
2021-12-01 16:50:13 +08:00
this.body = this.createElement("body");
2020-08-05 05:42:29 +08:00
this.baseURI = "https://test.cases/path/index.html";
2021-12-01 16:50:13 +08:00
this._elementsByTagName = new Map([
["head", [this.head]],
["body", [this.body]]
]);
this._basePath = basePath;
}
createElement(type) {
2021-12-01 16:50:13 +08:00
return new FakeElement(this, type, this._basePath);
2019-09-26 21:52:19 +08:00
}
_onElementAttached(element) {
const type = element._type;
let list = this._elementsByTagName.get(type);
if (list === undefined) {
list = [];
this._elementsByTagName.set(type, list);
}
list.push(element);
}
_onElementRemoved(element) {
const type = element._type;
2024-07-31 04:09:42 +08:00
const list = this._elementsByTagName.get(type);
const idx = list.indexOf(element);
list.splice(idx, 1);
}
getElementsByTagName(name) {
2019-09-26 21:52:19 +08:00
return this._elementsByTagName.get(name) || [];
}
2021-12-01 16:50:13 +08:00
getComputedStyle(element) {
const style = { getPropertyValue };
const links = this.getElementsByTagName("link");
for (const link of links) {
for (const rule of link.sheet.cssRules) {
if (rule.selectorText === element._type) {
Object.assign(style, rule.style);
}
}
}
return style;
}
}
class FakeElement {
2021-12-01 16:50:13 +08:00
constructor(document, type, basePath) {
2019-09-26 21:52:19 +08:00
this._document = document;
this._type = type;
this._children = [];
this._attributes = Object.create(null);
2018-06-28 17:03:08 +08:00
this._src = undefined;
this._href = undefined;
this.parentNode = undefined;
2021-12-01 16:50:13 +08:00
this.sheet = type === "link" ? new FakeSheet(this, basePath) : undefined;
}
2025-06-20 22:08:04 +08:00
_attach(node) {
2019-09-26 21:52:19 +08:00
this._document._onElementAttached(node);
this._children.push(node);
node.parentNode = this;
2025-06-20 22:08:04 +08:00
}
_load(node) {
2021-12-01 16:50:13 +08:00
if (node._type === "link") {
setTimeout(() => {
if (node.onload) node.onload({ type: "load", target: node });
}, 100);
2025-06-20 22:08:04 +08:00
} else if (node._type === "script" && this._document.onScript) {
Promise.resolve().then(() => {
this._document.onScript(node.src);
});
2021-12-01 16:50:13 +08:00
}
}
2025-07-08 22:46:17 +08:00
insertBefore(node) {
2025-06-20 22:08:04 +08:00
this._attach(node);
this._load(node);
}
appendChild(node) {
this._attach(node);
this._load(node);
}
removeChild(node) {
const idx = this._children.indexOf(node);
if (idx >= 0) {
this._children.splice(idx, 1);
this._document._onElementRemoved(node);
node.parentNode = undefined;
}
}
setAttribute(name, value) {
2023-06-16 05:39:16 +08:00
if (this._type === "link" && name === "href") {
this.href(value);
} else {
this._attributes[name] = value;
}
}
2021-12-01 16:50:13 +08:00
removeAttribute(name) {
delete this._attributes[name];
}
getAttribute(name) {
2023-06-16 05:39:16 +08:00
if (this._type === "link" && name === "href") {
return this.href;
}
2024-07-31 04:21:27 +08:00
return this._attributes[name];
}
2018-06-28 17:03:08 +08:00
_toRealUrl(value) {
if (/^\//.test(value)) {
return `https://test.cases${value}`;
} else if (/^\.\.\//.test(value)) {
return `https://test.cases${value.slice(2)}`;
2018-06-28 17:03:08 +08:00
} else if (/^\.\//.test(value)) {
return `https://test.cases/path${value.slice(1)}`;
2018-06-28 17:03:08 +08:00
} else if (/^\w+:\/\//.test(value)) {
return value;
} else if (/^\/\//.test(value)) {
return `https:${value}`;
}
2024-07-31 04:21:27 +08:00
return `https://test.cases/path/${value}`;
2018-06-28 17:03:08 +08:00
}
set src(value) {
if (this._type === "script") {
this._src = this._toRealUrl(value);
}
}
get src() {
return this._src;
}
set href(value) {
if (this._type === "link") {
this._href = this._toRealUrl(value);
}
}
get href() {
return this._href;
}
}
2021-12-01 16:50:13 +08:00
class FakeSheet {
constructor(element, basePath) {
this._element = element;
this._basePath = basePath;
}
2023-04-25 22:49:36 +08:00
get css() {
let css = fs.readFileSync(
path.resolve(
this._basePath,
this._element.href
.replace(/^https:\/\/test\.cases\/path\//, "")
.replace(/^https:\/\/example\.com\//, "")
),
"utf8"
2023-04-25 22:49:36 +08:00
);
css = css.replace(/@import url\("([^"]+)"\);/g, (match, url) => {
2023-05-19 20:36:06 +08:00
if (!/^https:\/\/test\.cases\/path\//.test(url)) {
return `@import url("${url}");`;
}
2023-05-02 06:24:01 +08:00
if (url.startsWith("#")) {
return url;
}
2023-04-25 22:49:36 +08:00
return fs.readFileSync(
path.resolve(
this._basePath,
url.replace(/^https:\/\/test\.cases\/path\//, "")
),
"utf8"
2023-04-25 22:49:36 +08:00
);
});
return css;
}
2021-12-01 16:50:13 +08:00
get cssRules() {
const walkCssTokens = require("../../lib/css/walkCssTokens");
2021-12-01 16:50:13 +08:00
const rules = [];
let currentRule = { getPropertyValue };
2024-07-31 06:15:03 +08:00
let selector;
2021-12-01 16:50:13 +08:00
let last = 0;
const processDeclaration = str => {
const colon = str.indexOf(":");
if (colon > 0) {
const property = str.slice(0, colon).trim();
const value = str.slice(colon + 1);
currentRule[property] = value;
}
};
2024-03-21 23:55:58 +08:00
const filepath = /file:\/\//.test(this._element.href)
? new URL(this._element.href)
: path.resolve(
this._basePath,
this._element.href
.replace(/^https:\/\/test\.cases\/path\//, "")
.replace(/^https:\/\/example\.com\/public\/path\//, "")
.replace(/^https:\/\/example\.com\//, "")
2024-07-31 05:43:19 +08:00
);
let css = fs.readFileSync(filepath, "utf8");
2024-08-12 21:53:44 +08:00
css = css
// Remove comments
2025-04-16 22:04:11 +08:00
// @ts-expect-error we use es2018 for such tests
2024-08-12 21:53:44 +08:00
.replace(/\/\*.*?\*\//gms, "")
.replace(/@import url\("([^"]+)"\);/g, (match, url) => {
if (!/^https:\/\/test\.cases\/path\//.test(url)) {
return url;
}
2023-05-19 20:36:06 +08:00
2024-08-12 21:53:44 +08:00
if (url.startsWith("#")) {
return url;
}
2023-05-02 06:24:01 +08:00
2024-08-12 21:53:44 +08:00
return fs.readFileSync(
path.resolve(
this._basePath,
url.replace(/^https:\/\/test\.cases\/path\//, "")
),
"utf8"
2024-08-12 21:53:44 +08:00
);
});
2024-11-07 00:11:54 +08:00
walkCssTokens(css, 0, {
2021-12-03 02:29:55 +08:00
leftCurlyBracket(source, start, end) {
if (selector === undefined) {
selector = source.slice(last, start).trim();
2021-12-01 16:50:13 +08:00
last = end;
}
2021-12-03 02:29:55 +08:00
return end;
},
rightCurlyBracket(source, start, end) {
processDeclaration(source.slice(last, start));
last = end;
rules.push({ selectorText: selector, style: currentRule });
selector = undefined;
currentRule = { getPropertyValue };
return end;
},
semicolon(source, start, end) {
processDeclaration(source.slice(last, start));
last = end;
return end;
2021-12-01 16:50:13 +08:00
}
2021-12-03 02:29:55 +08:00
});
2021-12-01 16:50:13 +08:00
return rules;
}
}
FakeDocument.FakeSheet = FakeSheet;
FakeDocument.FakeElement = FakeDocument;
module.exports = FakeDocument;