github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goarray_reflect_test.go (about)

     1  package goja
     2  
     3  import (
     4  	"testing"
     5  )
     6  
     7  func TestGoReflectArray(t *testing.T) {
     8  	vm := New()
     9  	vm.Set("a", [...]int{1, 2, 3})
    10  	_, err := vm.RunString(`
    11  	if (!Array.isArray(a)) {
    12  		throw new Error("isArray() returned false");
    13  	}
    14  	if (a[0] !== 1 || a[1] !== 2 || a[2] !== 3) {
    15  		throw new Error("Array contents is incorrect");
    16  	}
    17  	if (!a.hasOwnProperty("length")) {
    18  		throw new Error("hasOwnProperty() returned false");
    19  	}
    20  	let desc = Object.getOwnPropertyDescriptor(a, "length");
    21  	if (desc.value !== 3 || desc.writable || desc.enumerable || desc.configurable) {
    22  		throw new Error("incorrect property descriptor: " + JSON.stringify(desc));
    23  	}
    24  	`)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  }
    29  
    30  func TestGoReflectArraySort(t *testing.T) {
    31  	vm := New()
    32  	vm.Set("a", [...]int{3, 1, 2})
    33  	v, err := vm.RunString(`
    34  		a.sort();
    35  		if (a[0] !== 1 || a[1] !== 2 || a[2] !== 3) {
    36  			throw new Error(a.toString());
    37  		}
    38  		a;
    39  	`)
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	res := v.Export()
    44  	if a, ok := res.([3]int); ok {
    45  		if a[0] != 1 || a[1] != 2 || a[2] != 3 {
    46  			t.Fatal(a)
    47  		}
    48  	} else {
    49  		t.Fatalf("Wrong type: %T", res)
    50  	}
    51  }
    52  
    53  func TestGoReflectArrayCopyOnChange(t *testing.T) {
    54  	vm := New()
    55  
    56  	v, err := vm.RunString(`
    57  	a => {
    58  		let tmp = a[0];
    59  		if (tmp !== a[0]) {
    60  			throw new Error("tmp !== a[0]");
    61  		}
    62  
    63  		a[0] = a[1];
    64  		if (tmp === a[0]) {
    65  			throw new Error("tmp === a[0]");
    66  		}
    67  		if (tmp.Test !== "1") {
    68  			throw new Error("tmp.Test: " + tmp.Test + " (" + typeof tmp.Test + ")");
    69  		}
    70  		if (a[0].Test !== "2") {
    71  			throw new Error("a[0].Test: " + a[0].Test);
    72  		}
    73  
    74  		a[0].Test = "3";
    75  		if (a[0].Test !== "3") {
    76  			throw new Error("a[0].Test (1): " + a[0].Test);
    77  		}
    78  
    79  		tmp = a[0];
    80  		tmp.Test = "4";
    81  		if (a[0].Test !== "4") {
    82  			throw new Error("a[0].Test (2): " + a[0].Test);
    83  		}
    84  
    85  		delete a[0];
    86  		if (a[0] && a[0].Test !== "") {
    87  			throw new Error("a[0].Test (3): " + a[0].Test);
    88  		}
    89  		if (tmp.Test !== "4") {
    90  			throw new Error("tmp.Test (1): " + tmp.Test);
    91  		}
    92  
    93  		a[1] = tmp;
    94  		if (a[1].Test !== "4") {
    95  			throw new Error("a[1].Test: " + a[1].Test);
    96  		}
    97  
    98          // grow
    99  		tmp = a[1];
   100  		a.push(null);
   101  		if (a.length !== 3) {
   102  			throw new Error("a.length after push: " + a.length);
   103  		}
   104  
   105  		tmp.Test = "5";
   106  		if (a[1].Test !== "5") {
   107  			throw new Error("a[1].Test (1): " + a[1].Test);
   108  		}
   109  
   110  		// shrink
   111  		a.length = 1;
   112  		if (a.length !== 1) {
   113  			throw new Error("a.length after shrink: " + a.length);
   114  		}
   115  
   116  		if (tmp.Test !== "5") {
   117  			throw new Error("tmp.Test (shrink): " + tmp.Test);
   118  		}
   119  	}
   120  	`)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	fn, ok := AssertFunction(v)
   126  	if !ok {
   127  		t.Fatal("Not a function")
   128  	}
   129  
   130  	t.Run("[]struct", func(t *testing.T) {
   131  		a := []struct {
   132  			Test string
   133  		}{{"1"}, {"2"}}
   134  		_, err := fn(nil, vm.ToValue(a))
   135  		if err != nil {
   136  			t.Fatal(err)
   137  		}
   138  		if a[0].Test != "" {
   139  			t.Fatalf("a[0]: %#v", a[0])
   140  		}
   141  
   142  		if a[1].Test != "4" {
   143  			t.Fatalf("a0[1]: %#v", a[1])
   144  		}
   145  	})
   146  
   147  	// The copy-on-change mechanism doesn't apply to the types below because the contained values are references.
   148  	// These tests are here for completeness and to prove that the behaviour is consistent.
   149  
   150  	t.Run("[]I", func(t *testing.T) {
   151  		type I interface {
   152  			Get() string
   153  		}
   154  
   155  		a := []I{&testGoReflectMethod_O{Test: "1"}, &testGoReflectMethod_O{Test: "2"}}
   156  
   157  		_, err = fn(nil, vm.ToValue(a))
   158  		if err != nil {
   159  			t.Fatal(err)
   160  		}
   161  	})
   162  
   163  	t.Run("[]interface{}", func(t *testing.T) {
   164  		a := []interface{}{&testGoReflectMethod_O{Test: "1"}, &testGoReflectMethod_O{Test: "2"}}
   165  
   166  		_, err = fn(nil, vm.ToValue(a))
   167  		if err != nil {
   168  			t.Fatal(err)
   169  		}
   170  	})
   171  }
   172  
   173  func TestCopyOnChangeReflectSlice(t *testing.T) {
   174  	vm := New()
   175  	v, err := vm.RunString(`
   176  	s => {
   177  		s.A.push(1);
   178  		if (s.A.length !== 1) {
   179  			throw new Error("s.A.length: " + s.A.length);
   180  		}
   181  		if (s.A[0] !== 1) {
   182  			throw new Error("s.A[0]: " + s.A[0]);
   183  		}
   184  		let tmp = s.A;
   185  		if (tmp !== s.A) {
   186  			throw new Error("tmp !== s.A");
   187  		}
   188  		s.A = [2];
   189  		if (tmp === s.A) {
   190  			throw new Error("tmp === s.A");
   191  		}
   192  		if (tmp[0] !== 1) {
   193  			throw new Error("tmp[0]: " + tmp[0]);
   194  		}
   195  		if (s.A[0] !== 2) {
   196  			throw new Error("s.A[0] (1): " + s.A[0]);
   197  		}
   198  	}
   199  	`)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	fn, ok := AssertFunction(v)
   204  	if !ok {
   205  		t.Fatal("Not a function")
   206  	}
   207  
   208  	t.Run("[]int", func(t *testing.T) {
   209  		type S struct {
   210  			A []int
   211  		}
   212  		var s S
   213  		_, err := fn(nil, vm.ToValue(&s))
   214  		if err != nil {
   215  			t.Fatal(err)
   216  		}
   217  		if len(s.A) != 1 {
   218  			t.Fatal(s)
   219  		}
   220  		if s.A[0] != 2 {
   221  			t.Fatal(s.A)
   222  		}
   223  	})
   224  
   225  	t.Run("[]interface{}", func(t *testing.T) {
   226  		type S struct {
   227  			A []interface{}
   228  		}
   229  		var s S
   230  		_, err := fn(nil, vm.ToValue(&s))
   231  		if err != nil {
   232  			t.Fatal(err)
   233  		}
   234  		if len(s.A) != 1 {
   235  			t.Fatal(s)
   236  		}
   237  		if s.A[0] != int64(2) {
   238  			t.Fatal(s.A)
   239  		}
   240  	})
   241  }
   242  
   243  func TestCopyOnChangeSort(t *testing.T) {
   244  	a := []struct {
   245  		Test string
   246  	}{{"2"}, {"1"}}
   247  
   248  	vm := New()
   249  	vm.Set("a", &a)
   250  
   251  	_, err := vm.RunString(`
   252  		let a0 = a[0];
   253  		let a1 = a[1];
   254  		a.sort((a, b) => a.Test.localeCompare(b.Test));
   255  		if (a[0].Test !== "1") {
   256  			throw new Error("a[0]: " + a[0]);
   257  		}
   258  		if (a[1].Test !== "2") {
   259  			throw new Error("a[1]: " + a[1]);
   260  		}
   261  		if (a0 !== a[1]) {
   262  			throw new Error("a0 !== a[1]");
   263  		}
   264  		if (a1 !== a[0]) {
   265  			throw new Error("a1 !== a[0]");
   266  		}
   267  	`)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	if a[0].Test != "1" || a[1].Test != "2" {
   273  		t.Fatal(a)
   274  	}
   275  }
   276  
   277  type testStringerArray [8]byte
   278  
   279  func (a testStringerArray) String() string {
   280  	return "X"
   281  }
   282  
   283  func TestReflectArrayToString(t *testing.T) {
   284  	vm := New()
   285  	var a testStringerArray
   286  	vm.Set("a", &a)
   287  	res, err := vm.RunString("`${a}`")
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	if exp := res.Export(); exp != "X" {
   292  		t.Fatal(exp)
   293  	}
   294  
   295  	var a1 [2]byte
   296  	vm.Set("a", &a1)
   297  	res, err = vm.RunString("`${a}`")
   298  	if err != nil {
   299  		t.Fatal(err)
   300  	}
   301  	if exp := res.Export(); exp != "0,0" {
   302  		t.Fatal(exp)
   303  	}
   304  }