改完之后「某个行为在哪测」就是查同名文件:
lazyglobal_test.go → engine_globals_test.go
static_test.go → script_static_test.go
scope_pool_test.go → script_vm_test.go 它测的是 VM 池,不是作用域
esmwrap_test.go → bundle_finalize_test.go
missing_global_test.go → errors_hints_test.go
entry_test.go → bundle_entry_test.go 并收下 bundle_test.go 里
那三个 TestEntry_*
删掉 5 组复制粘贴。其中 TestNew_同实例调用串行 和 TestInstance_调用串行 **函数体
完全一致**,只差 mustCompile 的文件名参数 counter.js / counter.ts。Instance 的
测试全部归 instance_test.go 并统一前缀成 TestInstance_,scope_test.go 只留作用域。
vmstate_test.go + govalue_test.go → goja_facts_test.go。这两个测的都是 **goja
本身**,不是这个库:Program 能不能给多个 Runtime 共用、顶层变量跟不跟 Runtime 走、
Go 的切片进到脚本里是不是真数组。它们是「为什么这个库要这么设计」的实验记录,
goja 行为变了会先红。原来那个名字看不出这层定位。
helper 集中到 helper_test.go:newEngine / mustCompile 被十几个文件依赖,原来住在
script_test.go 里,那个名字暗示「测 Script」,找不到。
caller_example_test.go 并进 caller_test.go——同一个类型没必要两个文件。
overlay_test.go 和 script_vm_test.go 保持内部测试,各写了一句为什么:前者要读
Engine.loader 和 isPrepared,后者要直接断言 vmHandle.scoped。我试过把 overlay
挪成外部测试,挪不动。
101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
package jscriptx_test
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.fsdpf.net/go/jscriptx"
|
|
"github.com/evanw/esbuild/pkg/api"
|
|
)
|
|
|
|
// WithBundleDefine:编译期常量替换。
|
|
func TestBundleOption_Define(t *testing.T) {
|
|
e := newEngine(t, jscriptx.WithBundleOptions(
|
|
jscriptx.WithBundleDefine(map[string]string{"__MODE__": `"生产"`}),
|
|
))
|
|
s := mustCompile(t, e, "def.ts", `
|
|
declare const __MODE__: string
|
|
export function mode(): string { return __MODE__ }
|
|
`)
|
|
|
|
got, err := s.Call(context.Background(), "mode")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != "生产" {
|
|
t.Errorf("常量没被替换, got %#v", got)
|
|
}
|
|
}
|
|
|
|
// WithBundleTarget:把语法降级到指定的 ECMAScript 版本。
|
|
// goja 对新语法的覆盖不是 100%,调低 target 是遇到问题时的出路。
|
|
func TestBundleOption_Target(t *testing.T) {
|
|
// 用可选链和空值合并这类较新的语法,降级到 ES2015 后应该被改写掉
|
|
src := `export function pick(o) { return o?.a?.b ?? "兜底" }`
|
|
|
|
for _, target := range []struct {
|
|
name string
|
|
t api.Target
|
|
}{{"ES2015", api.ES2015}, {"ES2020", api.ES2020}} {
|
|
t.Run(target.name, func(t *testing.T) {
|
|
e := newEngine(t, jscriptx.WithBundleOptions(jscriptx.WithBundleTarget(target.t)))
|
|
s := mustCompile(t, e, "t.ts", src)
|
|
ctx := context.Background()
|
|
|
|
got, err := s.Call(ctx, "pick", map[string]any{"a": map[string]any{"b": "命中"}})
|
|
if err != nil {
|
|
t.Fatalf("调用失败: %v", err)
|
|
}
|
|
if got != "命中" {
|
|
t.Errorf("got = %#v", got)
|
|
}
|
|
if got, err := s.Call(ctx, "pick", nil); err != nil || got != "兜底" {
|
|
t.Errorf("空值合并没生效: got=%#v err=%v", got, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// WithNodePaths / WithAlias:给直接 Compile 的源码指定模块来源。
|
|
func TestBundleOption_NodePathsAndAlias(t *testing.T) {
|
|
libs, err := filepath.Abs("esm/testdata/app/node_modules")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
src := `import { upper } from "tinylib"
|
|
export function shout(s: string): string { return upper(s) }`
|
|
|
|
// 不指定时找不到:这段源码没有文件系统上下文
|
|
e0 := newEngine(t)
|
|
if _, err := e0.Compile("x.ts", src); err == nil {
|
|
t.Error("没有解析基准时应该报错")
|
|
}
|
|
|
|
// NodePaths 指向 node_modules 所在目录
|
|
e1 := newEngine(t, jscriptx.WithBundleOptions(jscriptx.WithNodePaths(libs)))
|
|
s1, err := e1.Compile("x.ts", src)
|
|
if err != nil {
|
|
t.Fatalf("WithNodePaths 没生效: %v", err)
|
|
}
|
|
if got, _ := s1.Call(context.Background(), "shout", "hi"); got != "HI" {
|
|
t.Errorf("got = %#v", got)
|
|
}
|
|
|
|
// Alias 把模块名钉到具体文件
|
|
impl, err := filepath.Abs("esm/testdata/app/node_modules/tinylib/index.js")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
e2 := newEngine(t, jscriptx.WithBundleOptions(
|
|
jscriptx.WithAlias(map[string]string{"tinylib": impl}),
|
|
))
|
|
s2, err := e2.Compile("x.ts", src)
|
|
if err != nil {
|
|
t.Fatalf("WithAlias 没生效: %v", err)
|
|
}
|
|
if got, _ := s2.Call(context.Background(), "shout", "hi"); got != "HI" {
|
|
t.Errorf("got = %#v", got)
|
|
}
|
|
}
|