gitee.com/quant1x/pkg@v0.2.8/goja_nodejs/require/module_test.go (about)

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