mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-02 04:16:38 +08:00
99 lines
3.4 KiB
Go
99 lines
3.4 KiB
Go
package rule
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// 本文件:规则 JS 作用域隔离的回归测试。
|
||
//
|
||
// 同一个 goja Runtime 会被一个书源的所有规则 JS 复用,而顶层 let/const 会留在
|
||
// 全局词法环境里。若不做隔离,「前一个脚本声明过的名字,后一个脚本再声明」就会
|
||
// 报 `SyntaxError: Identifier 'x' has already been declared`。
|
||
//
|
||
// 光遇聚合正好踩中:搜索列表规则是 `const { key, tab, ... } = res`,
|
||
// 单本书的 bookUrl 规则是 `let tab = result.tab || '小说'`。失败发生在「逐条取
|
||
// 字段」阶段且被 continue 跳过,于是表现为「搜索有结果但一条都读不出来」。
|
||
// legado 用的 Rhino 对顶层 let 更宽松,所以同一书源在阅读 App 里正常。
|
||
|
||
// TestRuleJSScopeIsIsolated 先后执行的规则 JS 不应因顶层 let/const 重名而失败。
|
||
func TestRuleJSScopeIsIsolated(t *testing.T) {
|
||
r := NewJSRunner(JSConfig{})
|
||
|
||
// 第一个脚本:解构声明(搜索列表规则的写法)
|
||
v, err := r.Run(nil, `const { tab, key } = {tab:'A', key:'K'}; tab + key`, nil, "")
|
||
if err != nil {
|
||
t.Fatalf("第一个脚本失败: %v", err)
|
||
}
|
||
if got := anyToString(v); got != "AK" {
|
||
t.Fatalf("第一个脚本返回 %q", got)
|
||
}
|
||
|
||
// 第二个脚本重名声明(bookUrl 规则的写法):不得报「已声明」
|
||
for i := 0; i < 3; i++ {
|
||
v, err = r.Run(nil, `let tab = 'B'; let book_id = 'x' + tab; book_id`, nil, "")
|
||
if err != nil {
|
||
t.Fatalf("第 %d 次重名声明冲突: %v", i+2, err)
|
||
}
|
||
if got := anyToString(v); got != "xB" {
|
||
t.Fatalf("第 %d 次返回 %q", i+2, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestRuleJSKeepsCompletionValue 包块不能吃掉「最后一条语句的值」——
|
||
// 书源的 URL 规则几乎都靠这个完成值返回结果。
|
||
func TestRuleJSKeepsCompletionValue(t *testing.T) {
|
||
r := NewJSRunner(JSConfig{})
|
||
|
||
v, err := r.Run(nil, "`data:;base64,AAAA,{\"type\":\"gysearch\"}`", nil, "")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if got := anyToString(v); !strings.HasPrefix(got, "data:;base64,") {
|
||
t.Fatalf("模板字面量的完成值丢了: %q", got)
|
||
}
|
||
|
||
// 末尾带行注释也不能把收尾的块注释掉
|
||
v, err = r.Run(nil, "let a = 'ok'; a // 末尾注释", nil, "")
|
||
if err != nil {
|
||
t.Fatalf("带末尾注释的脚本失败: %v", err)
|
||
}
|
||
if got := anyToString(v); got != "ok" {
|
||
t.Fatalf("返回 %q", got)
|
||
}
|
||
}
|
||
|
||
// TestRuleJSThisStaysGlobal 包块后 this 仍是全局对象,
|
||
// 书源的 this.getVariable / this.BaseUrl / this.request 才照常可用。
|
||
func TestRuleJSThisStaysGlobal(t *testing.T) {
|
||
r := NewJSRunner(JSConfig{})
|
||
v, err := r.Run(nil, `String(this === globalThis) + '|' + typeof java.ajax`, nil, "")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if got := anyToString(v); got != "true|function" {
|
||
t.Fatalf("this 语义被破坏: %q", got)
|
||
}
|
||
}
|
||
|
||
// TestRuleJSCanReadGlobalsFromJSLib 规则 JS 仍能读到 jsLib 定义的全局函数
|
||
// 与顶层 lexical 绑定(hosts 这类)。
|
||
func TestRuleJSCanReadGlobalsFromJSLib(t *testing.T) {
|
||
r := NewJSRunner(JSConfig{
|
||
JSLib: `let hosts = ['https://v1.example.com'];
|
||
function BaseUrl(){ return hosts[0] }
|
||
function pick(k){ return k + '@' + BaseUrl() }`,
|
||
})
|
||
if err := r.JSLibErr(); err != nil {
|
||
t.Fatalf("jsLib 失败: %v", err)
|
||
}
|
||
v, err := r.Run(nil, `{ let x = 'A'; pick(x) }`, nil, "")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if got := anyToString(v); got != "A@https://v1.example.com" {
|
||
t.Fatalf("规则读不到 jsLib 的全局: %q", got)
|
||
}
|
||
}
|