| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | const fs = require("fs"); | 
					
						
							|  |  |  | const path = require("path"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getPropertyValue = function (property) { | 
					
						
							|  |  |  | 	return this[property]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | module.exports = class FakeDocument { | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 	constructor(basePath) { | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 		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; | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-03 21:04:54 +08:00
										 |  |  | 	_onElementRemoved(element) { | 
					
						
							|  |  |  | 		const type = element._type; | 
					
						
							|  |  |  | 		let list = this._elementsByTagName.get(type); | 
					
						
							|  |  |  | 		const idx = list.indexOf(element); | 
					
						
							|  |  |  | 		list.splice(idx, 1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	getElementsByTagName(name) { | 
					
						
							| 
									
										
										
										
											2019-09-26 21:52:19 +08:00
										 |  |  | 		return this._elementsByTagName.get(name) || []; | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 		this._type = type; | 
					
						
							|  |  |  | 		this._children = []; | 
					
						
							|  |  |  | 		this._attributes = Object.create(null); | 
					
						
							| 
									
										
										
										
											2018-06-28 17:03:08 +08:00
										 |  |  | 		this._src = undefined; | 
					
						
							|  |  |  | 		this._href = undefined; | 
					
						
							| 
									
										
										
										
											2020-06-03 21:04:54 +08:00
										 |  |  | 		this.parentNode = undefined; | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 		this.sheet = type === "link" ? new FakeSheet(this, basePath) : undefined; | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	appendChild(node) { | 
					
						
							| 
									
										
										
										
											2019-09-26 21:52:19 +08:00
										 |  |  | 		this._document._onElementAttached(node); | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 		this._children.push(node); | 
					
						
							| 
									
										
										
										
											2020-06-03 21:04:54 +08:00
										 |  |  | 		node.parentNode = this; | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 		if (node._type === "link") { | 
					
						
							|  |  |  | 			setTimeout(() => { | 
					
						
							|  |  |  | 				if (node.onload) node.onload({ type: "load", target: node }); | 
					
						
							|  |  |  | 			}, 100); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-03 21:04:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	removeChild(node) { | 
					
						
							|  |  |  | 		const idx = this._children.indexOf(node); | 
					
						
							|  |  |  | 		if (idx >= 0) { | 
					
						
							|  |  |  | 			this._children.splice(idx, 1); | 
					
						
							|  |  |  | 			this._document._onElementRemoved(node); | 
					
						
							|  |  |  | 			node.parentNode = undefined; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setAttribute(name, value) { | 
					
						
							|  |  |  | 		this._attributes[name] = value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 	removeAttribute(name) { | 
					
						
							|  |  |  | 		delete this._attributes[name]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | 	getAttribute(name) { | 
					
						
							|  |  |  | 		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)) { | 
					
						
							| 
									
										
										
										
											2022-03-14 05:54:18 +08:00
										 |  |  | 			return `https://test.cases${value.slice(2)}`; | 
					
						
							| 
									
										
										
										
											2018-06-28 17:03:08 +08:00
										 |  |  | 		} else if (/^\.\//.test(value)) { | 
					
						
							| 
									
										
										
										
											2022-03-14 05:54:18 +08:00
										 |  |  | 			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}`; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return `https://test.cases/path/${value}`; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-16 23:43:45 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											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\//, "") | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			"utf-8" | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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\//, "") | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				"utf-8" | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return css; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 	get cssRules() { | 
					
						
							|  |  |  | 		const walkCssTokens = require("../../lib/css/walkCssTokens"); | 
					
						
							|  |  |  | 		const rules = []; | 
					
						
							|  |  |  | 		let currentRule = { getPropertyValue }; | 
					
						
							|  |  |  | 		let selector = undefined; | 
					
						
							|  |  |  | 		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; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2021-12-03 02:29:55 +08:00
										 |  |  | 		let css = fs.readFileSync( | 
					
						
							|  |  |  | 			path.resolve( | 
					
						
							|  |  |  | 				this._basePath, | 
					
						
							| 
									
										
										
										
											2023-04-07 10:46:48 +08:00
										 |  |  | 				this._element.href | 
					
						
							|  |  |  | 					.replace(/^https:\/\/test\.cases\/path\//, "") | 
					
						
							|  |  |  | 					.replace(/^https:\/\/example\.com\//, "") | 
					
						
							| 
									
										
										
										
											2021-12-03 02:29:55 +08:00
										 |  |  | 			), | 
					
						
							|  |  |  | 			"utf-8" | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		css = css.replace(/@import url\("([^"]+)"\);/g, (match, url) => { | 
					
						
							| 
									
										
										
										
											2023-05-19 20:36:06 +08:00
										 |  |  | 			if (!/^https:\/\/test\.cases\/path\//.test(url)) { | 
					
						
							|  |  |  | 				return url; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-02 06:24:01 +08:00
										 |  |  | 			if (url.startsWith("#")) { | 
					
						
							|  |  |  | 				return url; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-03 02:29:55 +08:00
										 |  |  | 			return fs.readFileSync( | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 				path.resolve( | 
					
						
							|  |  |  | 					this._basePath, | 
					
						
							| 
									
										
										
										
											2021-12-03 02:29:55 +08:00
										 |  |  | 					url.replace(/^https:\/\/test\.cases\/path\//, "") | 
					
						
							| 
									
										
										
										
											2021-12-01 16:50:13 +08:00
										 |  |  | 				), | 
					
						
							|  |  |  | 				"utf-8" | 
					
						
							| 
									
										
										
										
											2021-12-03 02:29:55 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		walkCssTokens(css, { | 
					
						
							| 
									
										
										
										
											2021-12-17 03:42:44 +08:00
										 |  |  | 			isSelector() { | 
					
						
							|  |  |  | 				return selector === undefined; | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |