Skip to content

✅ 抽离 example/tests 共用测试框架 (sctest) 并迁移 12 个测试脚本 - #1631

Merged
CodFrm merged 44 commits into
mainfrom
feat/example-tests-framework
Aug 18, 2026
Merged

✅ 抽离 example/tests 共用测试框架 (sctest) 并迁移 12 个测试脚本#1631
CodFrm merged 44 commits into
mainfrom
feat/example-tests-framework

Conversation

@CodFrm

@CodFrm CodFrm commented Jul 21, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

  • Changes tested / 已完成测试
  • Code reviewed by human / 代码通过人工检查
  • Fixes mentioned issues / 修复已提及的问题(无关联 issue)

背景

example/tests/ 下 15 个用户脚本形态的测试文件各自内嵌一份手写的测试运行器、结果面板与断言函数,风格不一(三种控制台汇总方言、5 份独立结果 UI),维护成本高;后台/定时脚本也缺乏统一的无界面日志通道。

本次改动

  • 新增单文件、零依赖、零构建的测试框架 example/tests/lib/sctest.js@require 直接加载),提供 describe/it/itManual/expect/run 与三个展示通道:
    • ConsoleReporter(恒开,三行汇总 总测试数/通过/失败,是 e2e 的解析契约)
    • PanelReporter(Shadow DOM 面板,跟随系统 prefers-color-scheme
    • LogReporter(后台/定时脚本经 GM_log 输出带 sctest/status 标签的结构化日志,可在运行日志页按标签过滤)
  • 断言统一为 6 个 matcher(toBe/toEqual/toBeTruthy/toBeTypeOf/toMatch/toThrow,实际值在前);toEqual 用真实结构化递归深比较(键顺序不敏感、Object.is 语义、区分 NaN/null 与 undefined 键)。
  • 用例内可 SCTest.skip(reason) 运行时跳过(用 sentinel 而非消息前缀嗅探)。
  • 12 个测试脚本迁移到该框架,删除各自的手写基建;e2e/gm-api.spec.ts 扩展为从本地 mock server 提供框架(@require 本地重写),并新接入 gm_xhr_redirect / gm_xhr 两个脚本的 e2e。
  • 新增 example/tests/lib/{README.md, sctest.test.js}:用法文档 + 66 个框架单测。
  • 更新 docs/references/verification-methods.md 的 in-page self-test 小节以匹配统一后的输出(该小节在 ♻️ 重构本地验证流程:常驻会话 + 命令驱动,替代一次性 spec #1674 中已从 docs/verification.md 拆出,本 PR 合并 main 后归位到拆出后的文件)。

实现考虑

  • 单文件@require 只接受一个 URL 的硬约束,非疏忽;文件内按上下文检测 / expect / 运行核心 / 三个 reporter 分区。
  • 运行上下文(page/background/crontab)解析 GM_info.scriptMetaStr@background/@crontab,而非 typeof document(后台脚本跑在 offscreen 文档里,document 存在)。
  • 手动 suite 跑完后重新广播 onEnd,否则所有 auto:false 文件的三行汇总会停在加载时的 0/0。
  • gm_xhr_cookie 矩阵段的 9 个依赖用例用 matrixOk 标志 + SCTest.skip 复刻原 if (matrixRequestPassed) 门控(声明式框架无法条件注册用例)。

已知限制

  • gm_download_test.js / gm_menu_test.js 已回退到迁移前的自包含实现a6bad63a):迁移版有两个未修复的行为回归 —— gm_download 的人工用例经 itManual 注册却从不执行动作(fn:null),面板可点通过/失败造成假阳性;gm_menu 丢失了 waitActions 的时间屏障,后续用例会改写待检查的菜单状态。相应地 e2e/gm-api.spec.ts 里的 GM_download e2e 用例一并移除,共享的 @require 本地重写与 mock server 保留(gm_xhr / gm_xhr_redirect 仍在用)。
  • gm_value_test.js 刻意不迁移:它是 GM_addValueChangeListener 的交互式多框架 dashboard 演示,无可判定断言、不打印汇总;强套 pass/fail 框架会毁掉其跨框架可视化价值。
  • gm_xhr_cookie_test.js 依赖外部 mockhttp.org,仍无常驻 e2e(补 e2e 需在 mock server 实现整套 cookie 回显语义,属独立工作)。
  • deepEqual 把 Date/RegExp/Map/Set 当普通对象比较;当前所有 toEqual 用例未触及这些类型(已在源码注释)。
  • 迁移中发现一个产品 bug(重复 @connect 值导致脚本静默安装失败),已实测复现,超出本 PR 范围,将另开 issue 跟进。

建议审查重点

  • example/tests/lib/sctest.jsdeepEqual、运行核心(runCase/runManualSuites 的 onEnd 重发)、SkipSignal 与三个 reporter。
  • e2e/gm-api.spec.ts 的 mock server 路由对既有消费者是否零干扰、@require 是否始终命中本地重写。
  • 迁移是否行为等价:各脚本用例数与通过/失败是否与迁移前基线一致(见验证)。

验证

已合并 origin/main9e31780f,含 #1674 验证流程重构)。以下为合并后复核:

  • pnpm exec tsc --noEmit → exit 0
  • pnpm exec eslint / prettier --check → exit 0
  • pnpm test:ci → 330 files / 3788 tests,3787 通过(含 sctest 66 单测全绿)。唯一失败是 LocalBackupSection.test.tsx 的负载超时 flake:该文件不在本 PR 改动范围内,单独重跑通过,属 vitest.config.ts 注释已记录的 fast 项目 340ms 预算在满载下的既有抖动。
  • pnpm exec playwright test e2e/gm-api.spec.ts → 12/12 全绿(于合并点 66092c79 跑过),各脚本计数:gm_xhr 138 / sandbox 32 / gm_api_sync 29 / gm_api_async 29 / gm_xhr_redirect 12 / inject_content 11 / window_message 5 / unwrap_e2e 3,failed 全 0。
  • 无常驻 e2e 的脚本(gm_menu / gm_xhr_cookie / gm_value / gm_download)未纳入自动化,仍靠面板人工确认。

已合并 origin/main9e31780f);冲突面为 docs/verification.mde2e/gm-api.spec.ts,解决记录见合并提交 66092c79

CodFrm added 26 commits July 21, 2026 13:21
toEqual 原先用 stringify(actual) !== stringify(expected) 做深比较,导致
NaN 与 null 被误判相等、显式 undefined 键与缺失键被误判相等、对象键顺序
影响比较结果。改为手写的递归结构比较(Object.is 语义 + hasOwnProperty
探测键存在性 + 数组/对象类型互斥 + 循环引用防护),并补齐 toBeTruthy 的
真值/假值用例。

ConsoleReporter 的 MANUAL 分支此前丢弃了 c.hint,console-only 场景下
用户看不到人工确认需要做什么,现追加 hint 到既有 (待人工确认) 文案后。
typeof GM_log === "function" 的判断已完整覆盖未 @grant GM_log 的降级场景,
外层 try/catch 实际只是把已授权 GM_log 抛出的真实异常静默吞掉。移除
try/catch,让已授权 GM_log 的异常正常抛出。

同时为开始日志(sctest:"run")与跳过/人工用例日志(sctest:"case",
status:"skip")补上内容校验断言 —— 此前只有 key 数量断言,不会在
level/label 内容错误时失败。
onCase 的更新分支此前只更新图标/耗时/统计,从不生成 .sc-detail,
而 auto:false 的 suite 每个用例首次真正执行时都会先被预渲染成 skip、
从而永远走这条分支——失败用例因此从不展示期望/实际/错误详情。
新增 renderDetail 统一由两条分支调用,重跑时先移除旧详情再按需重建,
避免重复追加;更新分支同时改用既有的 applyStatus 消掉重复表达式。
强化重跑用例的断言,校验行数不翻倍、跳过数清零,并补充详情展示与
失败转通过后详情清除的覆盖。
GM_addValueChangeListener/GM_addElement 迁移时被误删的三条+两条日志,
均在断言之前/之间无条件执行,超时或挂起时仍会打印,是排查这两个
用例卡住位置的唯一线索,应予保留。
迁移前后计数(全部一致):
- inject_content_test.js:      e2e passed=11 failed=0 → passed=11 failed=0
- early_inject_content_test.js: 总计 14 通过 14 失败 0 → 总测试数 14 通过 14 失败 0
- early_inject_page_test.js:    总计 14 通过 14 失败 0 → 总测试数 14 通过 14 失败 0

后两个文件无 e2e 覆盖,用一次性 Playwright scratch 脚本核对。

两个 early_inject 文件显式指定 reporter: "console":它们断言 document-start 时
DOM 保持原始态,而面板会往 document.documentElement 挂 #sctest-panel-host,
正好破坏 expect(firstElement.innerHTML).toBe("") 这条断言。这两个文件因此
不显示页面面板——注入型 DOM 面板与 DOM 纯净断言无法共存。
e2e gate (e2e/gm-api.spec.ts -g "Sandbox Test"): passed=32, failed=0
before migration; passed=32, failed=0 after migration. N unchanged.
e2e gate (e2e/gm-api.spec.ts -g "WindowMessage Transport Test"): passed=5, failed=0
before migration; passed=5, failed=0 after migration. N unchanged.

e2e gate (e2e/gm-api.spec.ts -g "Unwrap scriptlet tests"): passed=3, failed=0
before migration; passed=3, failed=0 after migration. N unchanged.

unwrap_test.js has no e2e coverage; verified with a throwaway Playwright scratch
script (e2e/scratch/verify-unwrap-test.spec.ts, git-ignored) that installs the
migrated script and confirms it injects and reports passed=3/failed=0 on
https://example.com/?test_unwrap_123, and does not inject at all (no panel, no
console output) on https://example.com/?test_unwrap_excluded per its @exclude.
首个 B 类文件迁移:删除手写面板 + assertEq,接入 SCTest 框架,首次获得
可解析的汇总行与 e2e 覆盖。tests 数组(basicTests + useFetch 变体)保持
数据驱动,映射为 it(),未手动展开。

e2e 新增用例需要:
- patchTargetMatchCode 正则备选组加 GM_XHR_REDIRECT_TEST_SC token
- patchGMApiTestCode 新增 HB 常量重写规则(该文件及未迁移的
  gm_download_test.js/gm_xhr_test.js 都用 `const HB = "https://httpbun.com"`
  拼 URL,走模板字符串后原有的字面量 URL 替换规则匹配不到)
- mock server 补 /redirect-to 路由(302 + Location)
- mock server /get 路由补上查询串回显(该文件断言 response.url 带 query)

用例数:迁移前(原手写面板,真实 httpbun.com,一次性 scratch 脚本量得)
passed=12 failed=0;迁移后(新 e2e,走 mock server)passed=12 failed=0,
完全对齐。
纯机械改写:删除手写面板与 logLine/setCounts/setStatus/setQueue 及本地
assertEq/assertTrue,26 个用例按 manual 标志拆成「自动套件」与「手动用例」
两个 auto:false suite(沿用 runAuto 原本 filter((t) => !t.manual) 的区分),
prefix 从 suite params 读取。

assertEq(a, b) 是实际在前,转成 expect(a).toBe(b) 不换位。断言语义逐条保持
不变——包括 test 18「empty URL」这条迁移前就在失败的用例,本提交不碰它的
判定,只做形式转换。

e2e 接入放在后续提交:本提交后该文件对真实 httpbun 仍是 19 通过 / 2 失败,
与迁移前基线一致。
该用例断言空 url 必须触发 onerror 或抛异常,但 ScriptCat 从未如此表现:
src/app/service/content/gm_api/gm_xhr.ts:230 对 url 统一做
new URL(urlResolved, window.location.href),GM_download 在同一函数的 :265-269
分支复用这条解析,空串按 RFC 3986 解析为当前页地址,下载因此正常成功。

不是迁移引入的回归——迁移前打真实 httpbun.com 就是失败的,对 e2e mock server
与解析源码三处一致且确定。属于 docs/references/develop-testing.md 里
「一开始就写错的断言」这条例外,单独提交以便独立复核或回退。

若日后判定「空 url 应当被拒绝」是正确的产品行为,改的是实现,本用例随之
翻回原判定即可。
@match token GM_DOWNLOAD_TEST_SC 加进 patchTargetMatchCode;两个 suite 都是
auto:false,页面加载不会自动跑,给 runTestScript 加 beforeCollect 钩子在
page.goto 之后点一次「GM_download 自动套件」的运行按钮,手动用例保持不跑。
runManualSuites 只逐条调用 onCase,从不调用 onEnd。后果是对**全部 auto:false 的
文件**,ConsoleReporter 的三行汇总永远停在页面加载时打的 "通过: 0 / 失败: 0"
(那时这些用例都被预置为 skip),LogReporter 的汇总日志同样永不出现。
面板因为靠 onCase 实时累加,看起来正常,把这个缺陷掩盖了。

三行汇总是 e2e 的解析契约,所以这等于 B 类文件根本无法用标准路径接入 e2e。
Task 12 当时是在 e2e 侧绕过去的——加了个 runSuiteAndCollectFromPanel 去爬面板
Shadow DOM 读结果。现在根因修好,这段绕行代码一并删除,B 类文件回到与其余文件
相同的 console 汇总路径。

- sctest.js: runManualSuites 结束时 buildSummary + 广播 onEnd,并返回 summary
- gm-api.spec.ts: beforeCollect 简化为「只点按钮」,返回 void;
  轮询改为「先等首次汇总 → 快照计数 → 点击 → 等下一组汇总」,
  避免快照取早了被首次的 0/0 立即满足

验证:sctest 单测 45/45(新增 3 条覆盖 Console/Log/返回值三条契约);
gm-api.spec.ts 全部 9 个用例通过,计数与各自基线一致
(inject_content 11、sandbox 32、gm_api_sync 29、gm_xhr_redirect 12、
unwrap_e2e 3、window_message 5、gm_api_async 29、gm_download 21,failed 均为 0)
迁移前 gm_download_test 的 runOne 特判错误消息的 "SKIP:" 前缀来区分跳过,
迁移到共用框架后这个通道没了,5 个手动用例超时或人工点 Skip 全部记为失败。

改用独立的 SkipSignal 类型而非消息前缀嗅探:前缀嗅探会把消息碰巧同名的
真实错误一并吞成跳过。STATUS.SKIP、summary.skipped、面板 sc-chip-skip 与
LogReporter 的 ○ 分支本来就在,这里补的是从用例体内产生 skip 的入口。

顺带修一个真实浏览器里复现的遮挡:verdict bar 原本 fixed 在右上角,与
右下角最高 80vh 的 sctest 面板重叠 2px,两者 z-index 同为最大值而面板挂载
更晚,Skip/Pass/Fail 按钮被吃掉点击。改到左上角。
8 处 GM_registerMenuCommand 返回值契约检查从软打印 console.log(x === y) 升级为真断言 expect(x).toBe(y),放进 it() 用例;scratch 自动核过全部通过(总20 通过11 失败0 跳过9)。菜单点击观察点转 itManual() 按注册顺序交错;保留三个调试开关与全部注册/注销调用及回调内 console.log 面包屑,仅删除 waitActions/myResolve/waitNext 等待机制。gm_value_test.js 按决定不在本次范围,保持原样。
12 个 test() → 12 个 it(),归入 3 个 describe。assert(expected,actual) 是期望在前,
转成 expect(actual).toBe(expected) 逐条换位;assertTrue → toBeTruthy。领域 helper
assertCookieValues 保签名与 slice().sort() 集合语义不变,内部 assert 改写为
expect(JSON.stringify(actual)).toBe(JSON.stringify(expected))。

矩阵段的 9 个依赖用例:原文用 `if (matrixRequestPassed && lastCookieMap)` 门控
(请求失败则这 9 个 test 不注册)。声明式框架无法条件注册,故引入 matrixOk 标志 +
每个依赖用例开头 `if (!matrixOk) SCTest.skip(...)`,复刻「前置请求失败则跳过依赖用例
而非各自级联失败」的原语义。

对真实 mockhttp.org 各跑 2 次稳定:迁移前基线 12/12/0,迁移后 12/12/0(含 skip 守卫,
happy path 无跳过)。
example/tests 下 14 个脚本迁移到 sctest.js 后,in-page self-test 只剩一种汇总格式,
不再有原来三种方言。改写「self-test pattern」小节:统一为框架的三行汇总,说明
background/crontab 走 GM_log、人工用例走 itManual,并标注 gm_value_test.js 是刻意
不迁移的交互式演示(无可判定断言、不打印汇总)。正则示例保留,注释从「三种布局」改为
「框架汇总行」。
终审建议(Minor ①): deepEqual 把 Date/RegExp/Map/Set 当普通对象比较,两个不同 Date
会相等。当前迁移用例的 toEqual 均未触及这些类型,仅补注释说明契约边界,行为不变。
@cyfung1031

Copy link
Copy Markdown
Collaborator

應該要先合併 httpbun改 httpbingo那個。。。
否則合併這個後那個 conflict resolve很麻煩吧

@cyfung1031

Copy link
Copy Markdown
Collaborator
https://cdn.jsdelivr.net/gh/scriptscat/scriptcat@main/example/tests/lib/sctest.js

不要用 @main. 要指定 commit

@cyfung1031

Copy link
Copy Markdown
Collaborator

老实说我觉得这样搞不好
userscript 应该能独立跑
硬要套一个外部引用很奇怪

@cyfung1031
cyfung1031 marked this pull request as draft July 21, 2026 22:18
@CodFrm

CodFrm commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

一个普通用户想看看某个 GM API 怎样用

直接看 userscript test 就行

结果原来要先研究一下一个不知名的测试工具是怎么设计

根本不需要研究测试工具,代码里面只有describe、it、expect这些通用的方法/断言和vitest框架保持一致,相比原来,反而结构更加清晰,就像脚本猫代码一样,你也不会去研究那些单元测试框架怎么跑的吧

而且当想添加一些特性的时候,不用修改每一个测试,只需要修改库就好了,我的观点和你完全相反

每个测试的内容都不一样,很难统一的

只统一 describe、it、expect 这些测试断言的,现在好几个脚本都是如此的了,不用这些的不引用就是

整体和方向我认为没问题,只是可能有些点不合适,可以进行修改和调整


QQ_1784690733824

这种面板来显示测试不清晰得多么,还可以再优化一下,现在的面板的UI/UX都不统一,也非常的难用

另外这个PR还不算完成,还有些问题需要调整,昨天交给AI后就睡觉了

@cyfung1031

cyfung1031 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

另外这个PR还不算完成,还有些问题需要调整,昨天交给AI后就睡觉了

好吧, 等完成再重新看

@CodFrm

CodFrm commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

另外这个PR还不算完成,还有些问题需要调整,昨天交给AI后就睡觉了

好吧, 等完成再重新看

现在差不多就是完成了

@require 支持opfs 吧

可以接受,可以列到下个月,这个月看看能不能把 外部接入mcp 搞完了

Copy link
Copy Markdown
Collaborator

按 PickInvariant 从“执行语义是否可重建”的角度看,这里有两个和 rebase / 落后 main 无关的行为回归,建议修复后再合并:

  1. gm_download_test.js 的人工用例实际上已经不可执行。
    迁移后 tests.filter((t) => t.manual) 只注册成 itManual(t.name, ...),但 SCTestrunCase()kind === "manual" 只标记 MANUAL,不会调用用例函数;rerunSuites() 又显式 if (c.kind === "manual") continue。因此原来的 saveAs / cancel / abort / visual check 等 t.run() 永远不会执行,面板却允许直接点“通过/失败”,会产生假阳性。

    建议让人工 case 具备可执行的 callback(并在执行中进入人工 verdict 流程),或者把这些用例保留成 auto:false 的普通 it(...),由明确的按钮逐条触发。

  2. gm_menu_test.js 丢失了原来的人工检查“时间屏障”。
    旧实现每个 await waitActions(...) 都会暂停后续注册/注销,确保人检查的是当前那个菜单状态。现在 itManual(...) 只是登记一个人工结果,不会等待用户确认,随后后面的 it(...) 会继续修改甚至注销菜单。这样前面的人工提示(例如“此时只应有 abc-2”)到用户真正检查时,对应状态通常已经不存在了。

    这里需要让 manual checkpoint 真正阻塞后续步骤,或者把“构造该状态 + 人工确认”封装成一个能暂停 suite 的交互式 case。

用 PickInvariant 的对比来说:当前表示把“人工项显示为可判定”当成了足够信息,但 “实际动作已执行 / 根本未执行”“检查的是目标中间状态 / 已被后续步骤改写的状态” 都能得到同一个 manual UI 状态,却要求不同的测试结论;缺失的是执行与时序边界(B/χ)信息,所以目前不能重建原测试语义。

@CodFrm

CodFrm commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

@cyfung1031 关于 gm_download_test 和 gm_menu_test,我之前也发现了,可以先直接回退一下这两个test

#1632 能不能 close,抽离共用测试框架这个方向没问题的话,就集中在这里处理

Copy link
Copy Markdown
Collaborator

@CodFrm

补充提交 98f09b04 修复了两项在审查中额外发现的 sctest 框架问题(不是讨论里已提到的 gm_download_test.jsgm_menu_test.js 两项):

  1. expect(fn).toThrow() 原先用抛出值的 truthiness 判断是否发生异常,因此 throw 0throw falsethrow nullthrow undefined 都会被误报为“未抛异常”。现在改用独立的 didThrow 标志,保留所有合法的抛出值。
  2. 用例重跑时原先只清理状态和错误文本,没有清理 expected/actual。一个用例第一次失败、随后通过时,面板或结构化结果仍可能带着旧的失败详情。现在每次执行前都会重置这三个结果字段。

同时新增了对应回归测试,覆盖 falsy 异常值和“失败后重跑通过”的结果清理。定向 sctest 测试 65/65 通过,全量测试 311 个文件、3561 个测试通过。

Copy link
Copy Markdown
Collaborator

@CodFrm

已将 feat/example-tests-framework 同步到最新 main,当前基线包含 125d58b5(修复 GM_download 传入空 url 时应触发 onerror,而不是把空字符串解析为当前页面并发起下载)。

这次 CI 日志中的 gm_download_test 失败原因是:PR 原测试仍断言“空 url 会下载当前页面并成功”,与最新 main 的兼容性契约冲突。因此我把该用例改为断言“不触发 onload,而是触发 onerror/失败”,生产代码和对应单测直接使用 main 已有实现,避免重复改动。

验证:目标 E2E GM_downloadpassed=21, failed=0;TypeScript、格式检查和构建通过。日志中另一个 storage-name 超时在重试及隔离重跑后通过,判定为并行负载下的 flaky,本次未改动其业务逻辑。

同步提交:b7b5fc47;测试契约修正:cad35eb3

@cyfung1031

Copy link
Copy Markdown
Collaborator

已基于最新 main88e73d8a)同步并重工本 PR。原始 PR head 为 b7b5fc47,当前已快进到 18f6eaa4;标题和 PR 正文未改。

相对原 PR 新增的修正 commits:

  • 5cf3b6c2:修正面板“跳过”筛选,使待人工确认用例也能显示。
  • 753c74addd1d68f2c317eff2:修正 E2E 权限确认页监听时序,覆盖安装前注册、确认页真实导航以及已存在页面,消除下载测试的权限弹窗竞态。
  • f3c27a56:澄清文档,只有 sctest 框架本身被重写到本地 mock server,脚本其他 @require/@resource 仍按声明加载。
  • de0ee50a:移除 GM_xhr DNS 失败用例中与 onerror 断言竞争的主动 abort()
  • 18f6eaa4:把 blocked-host 下载用例改为本机拒绝端口,保留缺少 @connect 的契约并去除外部 DNS 依赖。

最终 head 的本地验证:

  • pnpm exec tsc --noEmit:通过。
  • pnpm exec vitest run example/tests/lib/sctest.test.js:66/66 通过。
  • pnpm exec prettier --check ...:通过。
  • pnpm build:通过;仅有仓库现有的 bundle 体积和 Monaco 动态依赖警告。
  • pnpm exec playwright test e2e/gm-api.spec.ts:13/13 通过;GM_xhr 138/138、GM_download 21/0,其余场景均为 0 failed。人工用例按设计保持跳过。

GitHub CI 已在最终 head 18f6eaa4 上启动,目前仍是进行中,未将 pending 状态提前视为通过。

CodFrm added 2 commits August 17, 2026 16:39
8/8 审查指出的两个行为回归仍未修复:
- gm_download 人工用例经 itManual 注册但从不执行动作(fn:null),
  面板可点通过/失败造成假阳性;
- gm_menu 丢失了 waitActions 的时间屏障,后续用例会改写待检查的菜单状态。

按 PR 讨论的回退方案,恢复这两个脚本迁移前的自包含实现:
- 恢复 example/tests/gm_download_test.js / gm_menu_test.js(无 @require、自带
  运行器与人工确认流程);
- 移除 e2e/gm-api.spec.ts 中依赖 sctest 面板/统一输出的 GM_download E2E 用例
  及其 @match 重写条目;共享的 sctest 本地重写与 mock server 保留(gm_xhr /
  gm_xhr_redirect 等仍在使用)。
- docs/verification.md:采用 #1674 重写后的“常驻会话驱动”结构,
  将 PR 的 in-page self-test 说明作为子节并入 “Driving the session”,
  并注明 gm_download/gm_menu(已回退)与 gm_value 不输出统一汇总。
- e2e/gm-api.spec.ts:合并 main 的 headlessArgs 重构(保留 type Page
  与 mock server 的 http 类型导入),去除 gm_download E2E 后共享设施不变。
- 回退后的 gm_download_test.js / gm_menu_test.js 与 main 完全一致
  (自包含实现 + main 的 httpbingo 更新)。

验证:tsc/lint/build 通过;sctest 单测 66/66;gm-api E2E 12/12(gm_xhr 138、
gm_xhr_redirect 12、gm_api_sync 29、gm_api_async 29、inject_content 11、
window_message 5、sandbox 32、unwrap 3,failed 全 0)。
@CodFrm
CodFrm marked this pull request as ready for review August 17, 2026 08:55
合并 #1674 时只有 docs/verification.md 被 git 标成冲突,但 #1674 已把
in-page self-test 这一节拆去新建的 references/verification-methods.md,
拆出去的那份没跟着改,留下了本 PR 已推翻的描述:

- "the line varies by script" —— 12/15 脚本共用 sctest,输出一致;
- 代码块里的「总计: N | 通过: N」「Total/Passed」两种格式全树 grep 零命中;
- "matches all three layouts" —— 只剩一种;
- 缺 跳过 行、itManual、GM_log 后台通道。

docs/README.md 指明这一节归 verification-methods.md 所有,因此把内容
移过去替换过期段落(保留该文件「会话 + spec 两写」的体例),
verification.md 只留指向它的链接 —— 顺带补上 #1674 漏写的反向链接。

同时修正 gm-api.spec.ts 两处随本 PR 失效的注释:
- beforeCollect 的「B 类文件 / 真实下载副作用」——「B 类」全树无定义,
  且 GM_download E2E 已在 a6bad63 回退,现存唯一消费者是 gm_xhr_test.js;
  该套件 auto:false 的理由在脚本自身已写明,此处不复述。
- 「三行汇总里的最后一行」—— 汇总现为四行,且末行是「跳过:」。

验证:tsc/eslint/prettier 通过;vitest 78/78(sctest 66 + verification-tools 12)。
@CodFrm CodFrm changed the title ✅ 抽离 example/tests 共用测试框架 (sctest) 并全量迁移 14 个测试脚本 ✅ 抽离 example/tests 共用测试框架 (sctest) 并迁移 12 个测试脚本 Aug 17, 2026
@CodFrm
CodFrm merged commit a51bec9 into main Aug 18, 2026
10 checks passed
@CodFrm
CodFrm deleted the feat/example-tests-framework branch August 18, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants