github.com/mariotoffia/goja_nodejs@v0.0.0-20221208130254-70b39c043ea9/require/module_test.go (about) 1 package require 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 "path" 10 "testing" 11 12 js "github.com/dop251/goja" 13 ) 14 15 func mapFileSystemSourceLoader(files map[string]string) SourceLoader { 16 return func(path string) ([]byte, error) { 17 s, ok := files[path] 18 if !ok { 19 return nil, ModuleFileDoesNotExistError 20 } 21 return []byte(s), nil 22 } 23 } 24 25 func TestRequireNativeModule(t *testing.T) { 26 const SCRIPT = ` 27 var m = require("test/m"); 28 m.test(); 29 ` 30 31 vm := js.New() 32 33 registry := new(Registry) 34 registry.Enable(vm) 35 36 RegisterNativeModule("test/m", func(runtime *js.Runtime, module *js.Object) { 37 o := module.Get("exports").(*js.Object) 38 o.Set("test", func(call js.FunctionCall) js.Value { 39 return runtime.ToValue("passed") 40 }) 41 }) 42 43 v, err := vm.RunString(SCRIPT) 44 if err != nil { 45 t.Fatal(err) 46 } 47 48 if !v.StrictEquals(vm.ToValue("passed")) { 49 t.Fatalf("Unexpected result: %v", v) 50 } 51 } 52 53 func TestRequireRegistryNativeModule(t *testing.T) { 54 const SCRIPT = ` 55 var log = require("test/log"); 56 log.print('passed'); 57 ` 58 59 logWithOutput := func(w io.Writer, prefix string) ModuleLoader { 60 return func(vm *js.Runtime, module *js.Object) { 61 o := module.Get("exports").(*js.Object) 62 o.Set("print", func(call js.FunctionCall) js.Value { 63 fmt.Fprint(w, prefix, call.Argument(0).String()) 64 return js.Undefined() 65 }) 66 } 67 } 68 69 vm1 := js.New() 70 buf1 := &bytes.Buffer{} 71 72 registry1 := new(Registry) 73 registry1.Enable(vm1) 74 75 registry1.RegisterNativeModule("test/log", logWithOutput(buf1, "vm1 ")) 76 77 vm2 := js.New() 78 buf2 := &bytes.Buffer{} 79 80 registry2 := new(Registry) 81 registry2.Enable(vm2) 82 83 registry2.RegisterNativeModule("test/log", logWithOutput(buf2, "vm2 ")) 84 85 _, err := vm1.RunString(SCRIPT) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 s := buf1.String() 91 if s != "vm1 passed" { 92 t.Fatalf("vm1: Unexpected result: %q", s) 93 } 94 95 _, err = vm2.RunString(SCRIPT) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 s = buf2.String() 101 if s != "vm2 passed" { 102 t.Fatalf("vm2: Unexpected result: %q", s) 103 } 104 } 105 106 func TestRequire(t *testing.T) { 107 const SCRIPT = ` 108 var m = require("./testdata/m.js"); 109 m.test(); 110 ` 111 112 vm := js.New() 113 114 registry := new(Registry) 115 registry.Enable(vm) 116 117 v, err := vm.RunString(SCRIPT) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 if !v.StrictEquals(vm.ToValue("passed")) { 123 t.Fatalf("Unexpected result: %v", v) 124 } 125 } 126 127 func TestSourceLoader(t *testing.T) { 128 const SCRIPT = ` 129 var m = require("m.js"); 130 m.test(); 131 ` 132 133 const MODULE = ` 134 function test() { 135 return "passed1"; 136 } 137 138 exports.test = test; 139 ` 140 141 vm := js.New() 142 143 registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) { 144 if name == "m.js" { 145 return []byte(MODULE), nil 146 } 147 return nil, errors.New("Module does not exist") 148 })) 149 registry.Enable(vm) 150 151 v, err := vm.RunString(SCRIPT) 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 if !v.StrictEquals(vm.ToValue("passed1")) { 157 t.Fatalf("Unexpected result: %v", v) 158 } 159 } 160 161 func TestStrictModule(t *testing.T) { 162 const SCRIPT = ` 163 var m = require("m.js"); 164 m.test(); 165 ` 166 167 const MODULE = ` 168 "use strict"; 169 170 function test() { 171 var a = "passed1"; 172 eval("var a = 'not passed'"); 173 return a; 174 } 175 176 exports.test = test; 177 ` 178 179 vm := js.New() 180 181 registry := NewRegistry(WithGlobalFolders("."), WithLoader(func(name string) ([]byte, error) { 182 if name == "m.js" { 183 return []byte(MODULE), nil 184 } 185 return nil, errors.New("Module does not exist") 186 })) 187 registry.Enable(vm) 188 189 v, err := vm.RunString(SCRIPT) 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 if !v.StrictEquals(vm.ToValue("passed1")) { 195 t.Fatalf("Unexpected result: %v", v) 196 } 197 } 198 199 func TestResolve(t *testing.T) { 200 testRequire := func(src, fpath string, globalFolders []string, fs map[string]string) (*js.Runtime, js.Value, error) { 201 vm := js.New() 202 r := NewRegistry(WithGlobalFolders(globalFolders...), WithLoader(mapFileSystemSourceLoader(fs))) 203 r.Enable(vm) 204 t.Logf("Require(%s)", fpath) 205 ret, err := vm.RunScript(path.Join(src, "test.js"), fmt.Sprintf("require('%s')", fpath)) 206 if err != nil { 207 return nil, nil, err 208 } 209 return vm, ret, nil 210 } 211 212 globalFolders := []string{ 213 "/usr/lib/node_modules", 214 "/home/src/.node_modules", 215 } 216 217 fs := map[string]string{ 218 "/home/src/app/app.js": `exports.name = "app"`, 219 "/home/src/app2/app2.json": `{"name": "app2"}`, 220 "/home/src/app3/index.js": `exports.name = "app3"`, 221 "/home/src/app4/index.json": `{"name": "app4"}`, 222 "/home/src/app5/package.json": `{"main": "app5.js"}`, 223 "/home/src/app5/app5.js": `exports.name = "app5"`, 224 "/home/src/app6/package.json": `{"main": "."}`, 225 "/home/src/app6/index.js": `exports.name = "app6"`, 226 "/home/src/app7/package.json": `{"main": "./a/b/c/file.js"}`, 227 "/home/src/app7/a/b/c/file.js": `exports.name = "app7"`, 228 "/usr/lib/node_modules/app8": `exports.name = "app8"`, 229 "/home/src/app9/app9.js": `exports.name = require('./a/file.js').name`, 230 "/home/src/app9/a/file.js": `exports.name = require('./b/file.js').name`, 231 "/home/src/app9/a/b/file.js": `exports.name = require('./c/file.js').name`, 232 "/home/src/app9/a/b/c/file.js": `exports.name = "app9"`, 233 "/home/src/.node_modules/app10": `exports.name = "app10"`, 234 "/home/src/app11/app11.js": `exports.name = require('d/file.js').name`, 235 "/home/src/app11/a/b/c/app11.js": `exports.name = require('d/file.js').name`, 236 "/home/src/app11/node_modules/d/file.js": `exports.name = "app11"`, 237 "/app12.js": `exports.name = require('a/file.js').name`, 238 "/node_modules/a/file.js": `exports.name = "app12"`, 239 "/app13/app13.js": `exports.name = require('b/file.js').name`, 240 "/node_modules/b/file.js": `exports.name = "app13"`, 241 "node_modules/app14/index.js": `exports.name = "app14"`, 242 "../node_modules/app15/index.js": `exports.name = "app15"`, 243 } 244 245 for i, tc := range []struct { 246 src string 247 path string 248 ok bool 249 field string 250 value string 251 }{ 252 {"/home/src", "./app/app", true, "name", "app"}, 253 {"/home/src", "./app/app.js", true, "name", "app"}, 254 {"/home/src", "./app/bad.js", false, "", ""}, 255 {"/home/src", "./app2/app2", true, "name", "app2"}, 256 {"/home/src", "./app2/app2.json", true, "name", "app2"}, 257 {"/home/src", "./app/bad.json", false, "", ""}, 258 {"/home/src", "./app3", true, "name", "app3"}, 259 {"/home/src", "./appbad", false, "", ""}, 260 {"/home/src", "./app4", true, "name", "app4"}, 261 {"/home/src", "./appbad", false, "", ""}, 262 {"/home/src", "./app5", true, "name", "app5"}, 263 {"/home/src", "./app6", true, "name", "app6"}, 264 {"/home/src", "./app7", true, "name", "app7"}, 265 {"/home/src", "app8", true, "name", "app8"}, 266 {"/home/src", "./app9/app9", true, "name", "app9"}, 267 {"/home/src", "app10", true, "name", "app10"}, 268 {"/home/src", "./app11/app11.js", true, "name", "app11"}, 269 {"/home/src", "./app11/a/b/c/app11.js", true, "name", "app11"}, 270 {"/", "./app12", true, "name", "app12"}, 271 {"/", "./app13/app13", true, "name", "app13"}, 272 {".", "app14", true, "name", "app14"}, 273 {"..", "nonexistent", false, "", ""}, 274 } { 275 vm, mod, err := testRequire(tc.src, tc.path, globalFolders, fs) 276 if err != nil { 277 if tc.ok { 278 t.Errorf("%d: require() failed: %v", i, err) 279 } 280 continue 281 } else { 282 if !tc.ok { 283 t.Errorf("%d: expected to fail, but did not", i) 284 continue 285 } 286 } 287 f := mod.ToObject(vm).Get(tc.field) 288 if f == nil { 289 t.Errorf("%v: field %q not found", i, tc.field) 290 continue 291 } 292 value := f.String() 293 if value != tc.value { 294 t.Errorf("%v: got %q expected %q", i, value, tc.value) 295 } 296 } 297 } 298 299 func TestRequireCycle(t *testing.T) { 300 vm := js.New() 301 r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{ 302 "a.js": `var b = require('./b.js'); exports.done = true;`, 303 "b.js": `var a = require('./a.js'); exports.done = true;`, 304 }))) 305 r.Enable(vm) 306 res, err := vm.RunString(` 307 var a = require('./a.js'); 308 var b = require('./b.js'); 309 a.done && b.done; 310 `) 311 if err != nil { 312 t.Fatal(err) 313 } 314 if v := res.Export(); v != true { 315 t.Fatalf("Unexpected result: %v", v) 316 } 317 } 318 319 func TestErrorPropagation(t *testing.T) { 320 vm := js.New() 321 r := NewRegistry(WithLoader(mapFileSystemSourceLoader(map[string]string{ 322 "m.js": `throw 'test passed';`, 323 }))) 324 rr := r.Enable(vm) 325 _, err := rr.Require("./m") 326 if err == nil { 327 t.Fatal("Expected an error") 328 } 329 if ex, ok := err.(*js.Exception); ok { 330 if !ex.Value().StrictEquals(vm.ToValue("test passed")) { 331 t.Fatalf("Unexpected Exception: %v", ex) 332 } 333 } else { 334 t.Fatal(err) 335 } 336 } 337 338 func TestSourceMapLoader(t *testing.T) { 339 vm := js.New() 340 r := NewRegistry(WithLoader(func(p string) ([]byte, error) { 341 switch p { 342 case "dir/m.js": 343 return []byte(`throw 'test passed'; 344 //# sourceMappingURL=m.js.map`), nil 345 case "dir/m.js.map": 346 return []byte(`{"version":3,"file":"m.js","sourceRoot":"","sources":["m.ts"],"names":[],"mappings":";AAAA"} 347 `), nil 348 } 349 return nil, ModuleFileDoesNotExistError 350 })) 351 352 rr := r.Enable(vm) 353 _, err := rr.Require("./dir/m") 354 if err == nil { 355 t.Fatal("Expected an error") 356 } 357 if ex, ok := err.(*js.Exception); ok { 358 if !ex.Value().StrictEquals(vm.ToValue("test passed")) { 359 t.Fatalf("Unexpected Exception: %v", ex) 360 } 361 } else { 362 t.Fatal(err) 363 } 364 } 365 366 func testsetup() (string, func(), error) { 367 name, err := os.MkdirTemp("", "goja-nodejs-require-test") 368 if err != nil { 369 return "", nil, err 370 } 371 return name, func() { 372 os.RemoveAll(name) 373 }, nil 374 } 375 376 func TestDefaultModuleLoader(t *testing.T) { 377 workdir, teardown, err := testsetup() 378 if err != nil { 379 t.Fatal(err) 380 } 381 defer teardown() 382 383 err = os.Chdir(workdir) 384 if err != nil { 385 t.Fatal(err) 386 } 387 err = os.Mkdir("module", 0755) 388 if err != nil { 389 t.Fatal(err) 390 } 391 err = os.WriteFile("module/index.js", []byte(`throw 'test passed';`), 0644) 392 if err != nil { 393 t.Fatal(err) 394 } 395 vm := js.New() 396 r := NewRegistry() 397 rr := r.Enable(vm) 398 _, err = rr.Require("./module") 399 if err == nil { 400 t.Fatal("Expected an error") 401 } 402 if ex, ok := err.(*js.Exception); ok { 403 if !ex.Value().StrictEquals(vm.ToValue("test passed")) { 404 t.Fatalf("Unexpected Exception: %v", ex) 405 } 406 } else { 407 t.Fatal(err) 408 } 409 }