github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/tests/js_test.go (about)

     1  // +build js
     2  
     3  package tests_test
     4  
     5  import (
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/goplusjs/gopherjs/js"
    13  )
    14  
    15  var dummys = js.Global.Call("eval", `({
    16  	someBool: true,
    17  	someString: "abc\u1234",
    18  	someInt: 42,
    19  	someFloat: 42.123,
    20  	someArray: [41, 42, 43],
    21  	add: function(a, b) {
    22  		return a + b;
    23  	},
    24  	mapArray: function(array, f) {
    25  		var newArray = new Array(array.length);
    26  		for (var i = 0; i < array.length; i++) {
    27  			newArray[i] = f(array[i]);
    28  		}
    29  		return newArray;
    30  	},
    31  	toUnixTimestamp: function(d) {
    32  		return d.getTime() / 1000;
    33  	},
    34  	testField: function(o) {
    35  		return o.Field;
    36  	},
    37  	testMethod: function(o) {
    38  		return o.Method(42);
    39  	},
    40  	isEqual: function(a, b) {
    41  		return a === b;
    42  	},
    43  	call: function(f, a) {
    44  		f(a);
    45  	},
    46  	return: function(x) {
    47  		return x;
    48  	},
    49  })`)
    50  
    51  func TestBool(t *testing.T) {
    52  	e := true
    53  	o := dummys.Get("someBool")
    54  	if v := o.Bool(); v != e {
    55  		t.Errorf("expected %#v, got %#v", e, v)
    56  	}
    57  	if i := o.Interface().(bool); i != e {
    58  		t.Errorf("expected %#v, got %#v", e, i)
    59  	}
    60  	if dummys.Set("otherBool", e); dummys.Get("otherBool").Bool() != e {
    61  		t.Fail()
    62  	}
    63  }
    64  
    65  func TestStr(t *testing.T) {
    66  	e := "abc\u1234"
    67  	o := dummys.Get("someString")
    68  	if v := o.String(); v != e {
    69  		t.Errorf("expected %#v, got %#v", e, v)
    70  	}
    71  	if i := o.Interface().(string); i != e {
    72  		t.Errorf("expected %#v, got %#v", e, i)
    73  	}
    74  	if dummys.Set("otherString", e); dummys.Get("otherString").String() != e {
    75  		t.Fail()
    76  	}
    77  }
    78  
    79  func TestInt(t *testing.T) {
    80  	e := 42
    81  	o := dummys.Get("someInt")
    82  	if v := o.Int(); v != e {
    83  		t.Errorf("expected %#v, got %#v", e, v)
    84  	}
    85  	if i := int(o.Interface().(float64)); i != e {
    86  		t.Errorf("expected %#v, got %#v", e, i)
    87  	}
    88  	if dummys.Set("otherInt", e); dummys.Get("otherInt").Int() != e {
    89  		t.Fail()
    90  	}
    91  }
    92  
    93  func TestFloat(t *testing.T) {
    94  	e := 42.123
    95  	o := dummys.Get("someFloat")
    96  	if v := o.Float(); v != e {
    97  		t.Errorf("expected %#v, got %#v", e, v)
    98  	}
    99  	if i := o.Interface().(float64); i != e {
   100  		t.Errorf("expected %#v, got %#v", e, i)
   101  	}
   102  	if dummys.Set("otherFloat", e); dummys.Get("otherFloat").Float() != e {
   103  		t.Fail()
   104  	}
   105  }
   106  
   107  func TestUndefined(t *testing.T) {
   108  	if dummys == js.Undefined || dummys.Get("xyz") != js.Undefined {
   109  		t.Fail()
   110  	}
   111  }
   112  
   113  func TestNull(t *testing.T) {
   114  	var null *js.Object
   115  	dummys.Set("test", nil)
   116  	if null != nil || dummys == nil || dummys.Get("test") != nil {
   117  		t.Fail()
   118  	}
   119  }
   120  
   121  func TestLength(t *testing.T) {
   122  	if dummys.Get("someArray").Length() != 3 {
   123  		t.Fail()
   124  	}
   125  }
   126  
   127  func TestIndex(t *testing.T) {
   128  	if dummys.Get("someArray").Index(1).Int() != 42 {
   129  		t.Fail()
   130  	}
   131  }
   132  
   133  func TestSetIndex(t *testing.T) {
   134  	dummys.Get("someArray").SetIndex(2, 99)
   135  	if dummys.Get("someArray").Index(2).Int() != 99 {
   136  		t.Fail()
   137  	}
   138  }
   139  
   140  func TestCall(t *testing.T) {
   141  	var i int64 = 40
   142  	if dummys.Call("add", i, 2).Int() != 42 {
   143  		t.Fail()
   144  	}
   145  	if dummys.Call("add", js.Global.Call("eval", "40"), 2).Int() != 42 {
   146  		t.Fail()
   147  	}
   148  }
   149  
   150  func TestInvoke(t *testing.T) {
   151  	var i int64 = 40
   152  	if dummys.Get("add").Invoke(i, 2).Int() != 42 {
   153  		t.Fail()
   154  	}
   155  }
   156  
   157  func TestNew(t *testing.T) {
   158  	if js.Global.Get("Array").New(42).Length() != 42 {
   159  		t.Fail()
   160  	}
   161  }
   162  
   163  type StructWithJsField1 struct {
   164  	*js.Object
   165  	Length int                  `js:"length"`
   166  	Slice  func(int, int) []int `js:"slice"`
   167  }
   168  
   169  type StructWithJsField2 struct {
   170  	object *js.Object           // to hide members from public API
   171  	Length int                  `js:"length"`
   172  	Slice  func(int, int) []int `js:"slice"`
   173  }
   174  
   175  type Wrapper1 struct {
   176  	StructWithJsField1
   177  	WrapperLength int `js:"length"`
   178  }
   179  
   180  type Wrapper2 struct {
   181  	innerStruct   *StructWithJsField2
   182  	WrapperLength int `js:"length"`
   183  }
   184  
   185  func TestReadingJsField(t *testing.T) {
   186  	a := StructWithJsField1{Object: js.Global.Get("Array").New(42)}
   187  	b := &StructWithJsField2{object: js.Global.Get("Array").New(42)}
   188  	wa := Wrapper1{StructWithJsField1: a}
   189  	wb := Wrapper2{innerStruct: b}
   190  	if a.Length != 42 || b.Length != 42 || wa.Length != 42 || wa.WrapperLength != 42 || wb.WrapperLength != 42 {
   191  		t.Fail()
   192  	}
   193  }
   194  
   195  func TestWritingJsField(t *testing.T) {
   196  	a := StructWithJsField1{Object: js.Global.Get("Object").New()}
   197  	b := &StructWithJsField2{object: js.Global.Get("Object").New()}
   198  	a.Length = 42
   199  	b.Length = 42
   200  	if a.Get("length").Int() != 42 || b.object.Get("length").Int() != 42 {
   201  		t.Fail()
   202  	}
   203  }
   204  
   205  func TestCallingJsField(t *testing.T) {
   206  	a := &StructWithJsField1{Object: js.Global.Get("Array").New(100)}
   207  	b := &StructWithJsField2{object: js.Global.Get("Array").New(100)}
   208  	a.SetIndex(3, 123)
   209  	b.object.SetIndex(3, 123)
   210  	f := a.Slice
   211  	a2 := a.Slice(2, 44)
   212  	b2 := b.Slice(2, 44)
   213  	c2 := f(2, 44)
   214  	if len(a2) != 42 || len(b2) != 42 || len(c2) != 42 || a2[1] != 123 || b2[1] != 123 || c2[1] != 123 {
   215  		t.Fail()
   216  	}
   217  }
   218  
   219  func TestReflectionOnJsField(t *testing.T) {
   220  	a := StructWithJsField1{Object: js.Global.Get("Array").New(42)}
   221  	wa := Wrapper1{StructWithJsField1: a}
   222  	if reflect.ValueOf(a).FieldByName("Length").Int() != 42 || reflect.ValueOf(&wa).Elem().FieldByName("WrapperLength").Int() != 42 {
   223  		t.Fail()
   224  	}
   225  	reflect.ValueOf(&wa).Elem().FieldByName("WrapperLength").Set(reflect.ValueOf(10))
   226  	if a.Length != 10 {
   227  		t.Fail()
   228  	}
   229  }
   230  
   231  func TestUnboxing(t *testing.T) {
   232  	a := StructWithJsField1{Object: js.Global.Get("Object").New()}
   233  	b := &StructWithJsField2{object: js.Global.Get("Object").New()}
   234  	if !dummys.Call("isEqual", a, a.Object).Bool() || !dummys.Call("isEqual", b, b.object).Bool() {
   235  		t.Fail()
   236  	}
   237  	wa := Wrapper1{StructWithJsField1: a}
   238  	wb := Wrapper2{innerStruct: b}
   239  	if !dummys.Call("isEqual", wa, a.Object).Bool() || !dummys.Call("isEqual", wb, b.object).Bool() {
   240  		t.Fail()
   241  	}
   242  }
   243  
   244  func TestBoxing(t *testing.T) {
   245  	o := js.Global.Get("Object").New()
   246  	dummys.Call("call", func(a StructWithJsField1) {
   247  		if a.Object != o {
   248  			t.Fail()
   249  		}
   250  	}, o)
   251  	dummys.Call("call", func(a *StructWithJsField2) {
   252  		if a.object != o {
   253  			t.Fail()
   254  		}
   255  	}, o)
   256  	dummys.Call("call", func(a Wrapper1) {
   257  		if a.Object != o {
   258  			t.Fail()
   259  		}
   260  	}, o)
   261  	dummys.Call("call", func(a Wrapper2) {
   262  		if a.innerStruct.object != o {
   263  			t.Fail()
   264  		}
   265  	}, o)
   266  }
   267  
   268  func TestFunc(t *testing.T) {
   269  	a := dummys.Call("mapArray", []int{1, 2, 3}, func(e int64) int64 { return e + 40 })
   270  	b := dummys.Call("mapArray", []int{1, 2, 3}, func(e ...int64) int64 { return e[0] + 40 })
   271  	if a.Index(1).Int() != 42 || b.Index(1).Int() != 42 {
   272  		t.Fail()
   273  	}
   274  
   275  	add := dummys.Get("add").Interface().(func(...interface{}) *js.Object)
   276  	var i int64 = 40
   277  	if add(i, 2).Int() != 42 {
   278  		t.Fail()
   279  	}
   280  }
   281  
   282  func TestDate(t *testing.T) {
   283  	d := time.Date(2013, time.August, 27, 22, 25, 11, 0, time.UTC)
   284  	if dummys.Call("toUnixTimestamp", d).Int() != int(d.Unix()) {
   285  		t.Fail()
   286  	}
   287  
   288  	d2 := js.Global.Get("Date").New(d.UnixNano() / 1000000).Interface().(time.Time)
   289  	if !d2.Equal(d) {
   290  		t.Fail()
   291  	}
   292  }
   293  
   294  // https://github.com/gopherjs/gopherjs/issues/287
   295  func TestInternalizeDate(t *testing.T) {
   296  	var a = time.Unix(0, (123 * time.Millisecond).Nanoseconds())
   297  	var b time.Time
   298  	js.Global.Set("internalizeDate", func(t time.Time) { b = t })
   299  	js.Global.Call("eval", "(internalizeDate(new Date(123)))")
   300  	if a != b {
   301  		t.Fail()
   302  	}
   303  }
   304  
   305  func TestEquality(t *testing.T) {
   306  	if js.Global.Get("Array") != js.Global.Get("Array") || js.Global.Get("Array") == js.Global.Get("String") {
   307  		t.Fail()
   308  	}
   309  	type S struct{ *js.Object }
   310  	o1 := js.Global.Get("Object").New()
   311  	o2 := js.Global.Get("Object").New()
   312  	a := S{o1}
   313  	b := S{o1}
   314  	c := S{o2}
   315  	if a != b || a == c {
   316  		t.Fail()
   317  	}
   318  }
   319  
   320  func TestUndefinedEquality(t *testing.T) {
   321  	var ui interface{} = js.Undefined
   322  	if ui != js.Undefined {
   323  		t.Fail()
   324  	}
   325  }
   326  
   327  func TestInterfaceEquality(t *testing.T) {
   328  	o := js.Global.Get("Object").New()
   329  	var i interface{} = o
   330  	if i != o {
   331  		t.Fail()
   332  	}
   333  }
   334  
   335  func TestUndefinedInternalization(t *testing.T) {
   336  	undefinedEqualsJsUndefined := func(i interface{}) bool {
   337  		return i == js.Undefined
   338  	}
   339  	js.Global.Set("undefinedEqualsJsUndefined", undefinedEqualsJsUndefined)
   340  	if !js.Global.Call("eval", "(undefinedEqualsJsUndefined(undefined))").Bool() {
   341  		t.Fail()
   342  	}
   343  }
   344  
   345  func TestSameFuncWrapper(t *testing.T) {
   346  	a := func(_ string) {} // string argument to force wrapping
   347  	b := func(_ string) {} // string argument to force wrapping
   348  	if !dummys.Call("isEqual", a, a).Bool() || dummys.Call("isEqual", a, b).Bool() {
   349  		t.Fail()
   350  	}
   351  	if !dummys.Call("isEqual", somePackageFunction, somePackageFunction).Bool() {
   352  		t.Fail()
   353  	}
   354  	if !dummys.Call("isEqual", (*T).someMethod, (*T).someMethod).Bool() {
   355  		t.Fail()
   356  	}
   357  	t1 := &T{}
   358  	t2 := &T{}
   359  	if !dummys.Call("isEqual", t1.someMethod, t1.someMethod).Bool() || dummys.Call("isEqual", t1.someMethod, t2.someMethod).Bool() {
   360  		t.Fail()
   361  	}
   362  }
   363  
   364  func somePackageFunction(_ string) {
   365  }
   366  
   367  type T struct{}
   368  
   369  func (t *T) someMethod() {
   370  	println(42)
   371  }
   372  
   373  func TestError(t *testing.T) {
   374  	defer func() {
   375  		err := recover()
   376  		if err == nil {
   377  			t.Fail()
   378  		}
   379  		if _, ok := err.(error); !ok {
   380  			t.Fail()
   381  		}
   382  		jsErr, ok := err.(*js.Error)
   383  		if !ok || !strings.Contains(jsErr.Error(), "throwsError") {
   384  			t.Fail()
   385  		}
   386  	}()
   387  	js.Global.Get("notExisting").Call("throwsError")
   388  }
   389  
   390  type F struct {
   391  	Field int
   392  }
   393  
   394  func TestExternalizeField(t *testing.T) {
   395  	if dummys.Call("testField", map[string]int{"Field": 42}).Int() != 42 {
   396  		t.Fail()
   397  	}
   398  	if dummys.Call("testField", F{42}).Int() != 42 {
   399  		t.Fail()
   400  	}
   401  }
   402  
   403  func TestMakeFunc(t *testing.T) {
   404  	o := js.Global.Get("Object").New()
   405  	for i := 3; i < 5; i++ {
   406  		x := i
   407  		if i == 4 {
   408  			break
   409  		}
   410  		o.Set("f", js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
   411  			if this != o {
   412  				t.Fail()
   413  			}
   414  			if len(arguments) != 2 || arguments[0].Int() != 1 || arguments[1].Int() != 2 {
   415  				t.Fail()
   416  			}
   417  			return x
   418  		}))
   419  	}
   420  	if o.Call("f", 1, 2).Int() != 3 {
   421  		t.Fail()
   422  	}
   423  }
   424  
   425  type M struct {
   426  	f int
   427  }
   428  
   429  func (m *M) Method(a interface{}) map[string]string {
   430  	if a.(map[string]interface{})["x"].(float64) != 1 || m.f != 42 {
   431  		return nil
   432  	}
   433  	return map[string]string{
   434  		"y": "z",
   435  	}
   436  }
   437  
   438  func TestMakeWrapper(t *testing.T) {
   439  	m := &M{42}
   440  	if !js.Global.Call("eval", `(function(m) { return m.Method({x: 1})["y"] === "z"; })`).Invoke(js.MakeWrapper(m)).Bool() {
   441  		t.Fail()
   442  	}
   443  
   444  	if js.MakeWrapper(m).Interface() != m {
   445  		t.Fail()
   446  	}
   447  
   448  	f := func(m *M) {
   449  		if m.f != 42 {
   450  			t.Fail()
   451  		}
   452  	}
   453  	js.Global.Call("eval", `(function(f, m) { f(m); })`).Invoke(f, js.MakeWrapper(m))
   454  }
   455  
   456  func TestCallWithNull(t *testing.T) {
   457  	c := make(chan int, 1)
   458  	js.Global.Set("test", func() {
   459  		c <- 42
   460  	})
   461  	js.Global.Get("test").Call("call", nil)
   462  	if <-c != 42 {
   463  		t.Fail()
   464  	}
   465  }
   466  
   467  func TestReflection(t *testing.T) {
   468  	o := js.Global.Call("eval", "({ answer: 42 })")
   469  	if reflect.ValueOf(o).Interface().(*js.Object) != o {
   470  		t.Fail()
   471  	}
   472  
   473  	type S struct {
   474  		Field *js.Object
   475  	}
   476  	s := S{o}
   477  
   478  	v := reflect.ValueOf(&s).Elem()
   479  	if v.Field(0).Interface().(*js.Object).Get("answer").Int() != 42 {
   480  		t.Fail()
   481  	}
   482  	if v.Field(0).MethodByName("Get").Call([]reflect.Value{reflect.ValueOf("answer")})[0].Interface().(*js.Object).Int() != 42 {
   483  		t.Fail()
   484  	}
   485  	v.Field(0).Set(reflect.ValueOf(js.Global.Call("eval", "({ answer: 100 })")))
   486  	if s.Field.Get("answer").Int() != 100 {
   487  		t.Fail()
   488  	}
   489  
   490  	if fmt.Sprintf("%+v", s) != "{Field:[object Object]}" {
   491  		t.Fail()
   492  	}
   493  }
   494  
   495  func TestNil(t *testing.T) {
   496  	type S struct{ X int }
   497  	var s *S
   498  	if !dummys.Call("isEqual", s, nil).Bool() {
   499  		t.Fail()
   500  	}
   501  
   502  	type T struct{ Field *S }
   503  	if dummys.Call("testField", T{}) != nil {
   504  		t.Fail()
   505  	}
   506  }
   507  
   508  func TestNewArrayBuffer(t *testing.T) {
   509  	b := []byte("abcd")
   510  	a := js.NewArrayBuffer(b[1:3])
   511  	if a.Get("byteLength").Int() != 2 {
   512  		t.Fail()
   513  	}
   514  }
   515  
   516  func TestInternalizeExternalizeNull(t *testing.T) {
   517  	type S struct {
   518  		*js.Object
   519  	}
   520  	r := js.Global.Call("eval", "(function(f) { return f(null); })").Invoke(func(s S) S {
   521  		if s.Object != nil {
   522  			t.Fail()
   523  		}
   524  		return s
   525  	})
   526  	if r != nil {
   527  		t.Fail()
   528  	}
   529  }
   530  
   531  func TestInternalizeExternalizeUndefined(t *testing.T) {
   532  	type S struct {
   533  		*js.Object
   534  	}
   535  	r := js.Global.Call("eval", "(function(f) { return f(undefined); })").Invoke(func(s S) S {
   536  		if s.Object != js.Undefined {
   537  			t.Fail()
   538  		}
   539  		return s
   540  	})
   541  	if r != js.Undefined {
   542  		t.Fail()
   543  	}
   544  }
   545  
   546  func TestDereference(t *testing.T) {
   547  	s := *dummys
   548  	p := &s
   549  	if p != dummys {
   550  		t.Fail()
   551  	}
   552  }
   553  
   554  func TestSurrogatePairs(t *testing.T) {
   555  	js.Global.Set("str", "\U0001F600")
   556  	str := js.Global.Get("str")
   557  	if str.Get("length").Int() != 2 || str.Call("charCodeAt", 0).Int() != 55357 || str.Call("charCodeAt", 1).Int() != 56832 {
   558  		t.Fail()
   559  	}
   560  	if str.String() != "\U0001F600" {
   561  		t.Fail()
   562  	}
   563  }
   564  
   565  func TestUint8Array(t *testing.T) {
   566  	uint8Array := js.Global.Get("Uint8Array")
   567  	if dummys.Call("return", []byte{}).Get("constructor") != uint8Array {
   568  		t.Errorf("Empty byte array is not externalized as a Uint8Array")
   569  	}
   570  	if dummys.Call("return", []byte{0x01}).Get("constructor") != uint8Array {
   571  		t.Errorf("Non-empty byte array is not externalized as a Uint8Array")
   572  	}
   573  }
   574  
   575  func TestTypeSwitchJSObject(t *testing.T) {
   576  	obj := js.Global.Get("Object").New()
   577  	obj.Set("foo", "bar")
   578  
   579  	want := "bar"
   580  
   581  	if got := obj.Get("foo").String(); got != want {
   582  		t.Errorf("Direct access to *js.Object field gave %q, want %q", got, want)
   583  	}
   584  
   585  	var x interface{} = obj
   586  
   587  	switch x := x.(type) {
   588  	case *js.Object:
   589  		if got := x.Get("foo").String(); got != want {
   590  			t.Errorf("Value passed through interface and type switch gave %q, want %q", got, want)
   591  		}
   592  	}
   593  
   594  	if y, ok := x.(*js.Object); ok {
   595  		if got := y.Get("foo").String(); got != want {
   596  			t.Errorf("Value passed through interface and type assert gave %q, want %q", got, want)
   597  		}
   598  	}
   599  }
   600  
   601  func TestStructWithNonIdentifierJSTag(t *testing.T) {
   602  	type S struct {
   603  		*js.Object
   604  		Name string `js:"@&\"'<>//my name"`
   605  	}
   606  	s := S{Object: js.Global.Get("Object").New()}
   607  
   608  	// externalise a value via field
   609  	s.Name = "Paul"
   610  
   611  	// internalise via field
   612  	got := s.Name
   613  	if want := "Paul"; got != want {
   614  		t.Errorf("value via field with non-identifier js tag gave %q, want %q", got, want)
   615  	}
   616  
   617  	// verify we can do a Get with the struct tag
   618  	got = s.Get("@&\"'<>//my name").String()
   619  	if want := "Paul"; got != want {
   620  		t.Errorf("value via js.Object.Get gave %q, want %q", got, want)
   621  	}
   622  }