Skip to content

Commit 080a859

Browse files
committed
✅ 添加测试脚本验证功能 #801
1 parent 7bd9b16 commit 080a859

File tree

4 files changed

+1337
-0
lines changed

4 files changed

+1337
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// ==UserScript==
2+
// @name 早期脚本 注入到 content环境测试
3+
// @namespace https://docs.scriptcat.org/
4+
// @version 0.1.0
5+
// @description 早期脚本可以比页面更早到执行
6+
// @match https://content-security-policy.com/
7+
// @inject-into content
8+
// @early-start
9+
// @grant GM_addElement
10+
// @grant GM_addStyle
11+
// @grant GM_log
12+
// @grant GM_info
13+
// @grant GM_setValue
14+
// @grant GM_getValue
15+
// @grant GM_deleteValue
16+
// @grant GM_listValues
17+
// @run-at document-start
18+
// ==/UserScript==
19+
20+
(async function () {
21+
"use strict";
22+
23+
console.log("%c=== Content环境 GM API 测试开始 ===", "color: blue; font-size: 16px; font-weight: bold;");
24+
25+
let testResults = {
26+
passed: 0,
27+
failed: 0,
28+
total: 0,
29+
};
30+
31+
// 测试辅助函数(支持同步和异步)
32+
async function test(name, fn) {
33+
testResults.total++;
34+
try {
35+
await fn();
36+
testResults.passed++;
37+
console.log(`%c✓ ${name}`, "color: green;");
38+
return true;
39+
} catch (error) {
40+
testResults.failed++;
41+
console.error(`%c✗ ${name}`, "color: red;", error);
42+
return false;
43+
}
44+
}
45+
46+
// assert(expected, actual, message) - 比较两个值是否相等
47+
function assert(expected, actual, message) {
48+
if (expected !== actual) {
49+
const valueInfo = `期望 ${JSON.stringify(expected)}, 实际 ${JSON.stringify(actual)}`;
50+
const error = message ? `${message} - ${valueInfo}` : `断言失败: ${valueInfo}`;
51+
throw new Error(error);
52+
}
53+
}
54+
55+
// assertTrue(condition, message) - 断言条件为真
56+
function assertTrue(condition, message) {
57+
if (!condition) {
58+
throw new Error(message || "断言失败: 期望条件为真");
59+
}
60+
}
61+
62+
// ============ 早期脚本环境检查 ============
63+
console.log("\n%c--- 早期脚本环境检查 ---", "color: orange; font-weight: bold;");
64+
65+
test("检查 document.head 不存在", () => {
66+
console.log("document.head 存在:", !!document.head);
67+
console.log("document.head 值:", document.head);
68+
// 早期脚本运行时 document.head 应该不存在
69+
assertTrue(document.head === null || document.head === undefined, "早期脚本运行时 document.head 应该不存在");
70+
});
71+
72+
test("检查 document.body 不存在", () => {
73+
console.log("document.body 存在:", !!document.body);
74+
console.log("document.body 值:", document.body);
75+
// 早期脚本运行时 document.body 应该不存在
76+
assertTrue(document.body === null || document.body === undefined, "早期脚本运行时 document.body 应该不存在");
77+
});
78+
79+
test("检查可用的DOM节点应该是HTML元素", () => {
80+
const firstElement = document.querySelector("*");
81+
console.log("querySelector('*') 找到的第一个元素:", firstElement?.tagName);
82+
assertTrue(firstElement !== null, "应该能找到第一个DOM节点");
83+
assert("HTML", firstElement.tagName, "早期脚本运行时,第一个可用节点应该是HTML元素");
84+
console.log("节点详情:", {
85+
tagName: firstElement.tagName,
86+
childNodes: firstElement.childNodes.length,
87+
children: firstElement.children.length,
88+
});
89+
});
90+
91+
// ============ CSP绕过测试 ============
92+
console.log("\n%c--- CSP绕过测试 ---", "color: orange; font-weight: bold;");
93+
94+
test("CSP绕过 - 内联脚本", () => {
95+
const script = document.createElement("script");
96+
script.textContent = 'console.log("Content环境绕过CSP测试");';
97+
// 早期脚本运行时 document.head 和 document.body 不存在
98+
// 使用 querySelector("*") 查找第一个可用的元素(应该是HTML元素)进行注入
99+
let node = document.querySelector("*");
100+
assertTrue(node !== null, "应该能找到可注入的DOM节点");
101+
node.appendChild(script);
102+
assertTrue(script.parentNode === node, "脚本应该成功插入到找到的节点中");
103+
assert("HTML", node.tagName, "注入节点应该是HTML元素");
104+
console.log("脚本注入到:", node.tagName);
105+
});
106+
107+
// ============ GM_addElement/GM_addStyle 测试 ============
108+
console.log("\n%c--- DOM操作 API 测试 ---", "color: orange; font-weight: bold;");
109+
110+
test("GM_addElement", () => {
111+
const element = GM_addElement("div", {
112+
textContent: "GM_addElement测试元素",
113+
style: "display:none;",
114+
id: "gm-test-element",
115+
});
116+
assertTrue(element !== null && element !== undefined, "GM_addElement应该返回元素");
117+
assert("gm-test-element", element.id, "元素ID应该正确");
118+
assert("DIV", element.tagName, "元素标签应该是DIV");
119+
console.log("返回元素:", element);
120+
});
121+
122+
test("GM_addStyle", () => {
123+
const styleElement = GM_addStyle(`
124+
.gm-style-test {
125+
color: #10b981 !important;
126+
}
127+
`);
128+
assertTrue(styleElement !== null && styleElement !== undefined, "GM_addStyle应该返回样式元素");
129+
assertTrue(styleElement.tagName === "STYLE" || styleElement.sheet, "应该返回STYLE元素或样式对象");
130+
console.log("返回样式元素:", styleElement);
131+
});
132+
133+
// ============ GM_log 测试 ============
134+
console.log("\n%c--- GM_log 测试 ---", "color: orange; font-weight: bold;");
135+
136+
test("GM_log", () => {
137+
GM_log("测试日志输出", { type: "test", value: 123 });
138+
// GM_log本身不返回值,只要不抛出异常就算成功
139+
assertTrue(true, "GM_log应该能正常输出");
140+
});
141+
142+
// ============ GM_info 测试 ============
143+
console.log("\n%c--- GM_info 测试 ---", "color: orange; font-weight: bold;");
144+
145+
test("GM_info", () => {
146+
assertTrue(typeof GM_info === "object", "GM_info应该是对象");
147+
assertTrue(!!GM_info.script, "GM_info.script应该存在");
148+
assertTrue(!!GM_info.script.name, "GM_info.script.name应该存在");
149+
console.log("脚本信息:", GM_info.script.name);
150+
});
151+
152+
// ============ GM 存储 API 测试 ============
153+
console.log("\n%c--- GM 存储 API 测试 ---", "color: orange; font-weight: bold;");
154+
155+
test("GM_setValue - 字符串", () => {
156+
GM_setValue("test_key", "content环境测试值");
157+
const value = GM_getValue("test_key");
158+
assert("content环境测试值", value, "应该正确保存和读取字符串");
159+
});
160+
161+
test("GM_setValue - 数字", () => {
162+
GM_setValue("test_number", 12345);
163+
const value = GM_getValue("test_number");
164+
assert(12345, value, "应该正确保存和读取数字");
165+
});
166+
167+
test("GM_setValue - 对象", () => {
168+
const obj = { name: "ScriptCat", type: "content" };
169+
GM_setValue("test_object", obj);
170+
const value = GM_getValue("test_object", {});
171+
assert("ScriptCat", value.name, "对象的name属性应该正确");
172+
assert("content", value.type, "对象的type属性应该正确");
173+
});
174+
175+
test("GM_getValue - 默认值", () => {
176+
const value = GM_getValue("non_existent_key", "默认值");
177+
assert("默认值", value, "不存在的键应该返回默认值");
178+
});
179+
180+
test("GM_listValues", () => {
181+
const keys = GM_listValues();
182+
assertTrue(Array.isArray(keys), "GM_listValues应该返回数组");
183+
assertTrue(keys.length >= 3, "应该至少有3个存储键");
184+
console.log("存储的键:", keys);
185+
});
186+
187+
test("GM_deleteValue", () => {
188+
GM_setValue("test_delete", "to_be_deleted");
189+
assert("to_be_deleted", GM_getValue("test_delete"), "值应该存在");
190+
GM_deleteValue("test_delete");
191+
assert(null, GM_getValue("test_delete", null), "值应该被删除");
192+
});
193+
194+
// ============ 输出测试结果 ============
195+
console.log("\n%c=== 测试完成 ===", "color: blue; font-size: 16px; font-weight: bold;");
196+
console.log(
197+
`%c总计: ${testResults.total} | 通过: ${testResults.passed} | 失败: ${testResults.failed}`,
198+
testResults.failed === 0 ? "color: green; font-weight: bold;" : "color: red; font-weight: bold;"
199+
);
200+
201+
if (testResults.failed === 0) {
202+
console.log("%c🎉 所有测试通过!", "color: green; font-size: 14px; font-weight: bold;");
203+
} else {
204+
console.log("%c⚠️ 部分测试失败,请检查上面的错误信息", "color: red; font-size: 14px; font-weight: bold;");
205+
}
206+
})();

0 commit comments

Comments
 (0)