| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // +build js,wasm | |
| // To run these tests: | |
| // | |
| // - Install Node | |
| // - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find | |
| // "go_js_wasm_exec"). | |
| // - GOOS=js GOARCH=wasm go test | |
| // | |
| // See -exec in "go help test", and "go help run" for details. | |
| package js_test | |
| import ( | |
| "fmt" | |
| "math" | |
| "syscall/js" | |
| "testing" | |
| ) | |
| var dummys = js.Global().Call("eval", `({ | |
| someBool: true, | |
| someString: "abc\u1234", | |
| someInt: 42, | |
| someFloat: 42.123, | |
| someArray: [41, 42, 43], | |
| someDate: new Date(), | |
| add: function(a, b) { | |
| return a + b; | |
| }, | |
| zero: 0, | |
| stringZero: "0", | |
| NaN: NaN, | |
| emptyObj: {}, | |
| emptyArray: [], | |
| Infinity: Infinity, | |
| NegInfinity: -Infinity, | |
| objNumber0: new Number(0), | |
| objBooleanFalse: new Boolean(false), | |
| })`) | |
| func TestBool(t *testing.T) { | |
| want := true | |
| o := dummys.Get("someBool") | |
| if got := o.Bool(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| dummys.Set("otherBool", want) | |
| if got := dummys.Get("otherBool").Bool(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if dummys.Get("someBool") != dummys.Get("someBool") { | |
| t.Errorf("same value not equal") | |
| } | |
| } | |
| func TestString(t *testing.T) { | |
| want := "abc\u1234" | |
| o := dummys.Get("someString") | |
| if got := o.String(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| dummys.Set("otherString", want) | |
| if got := dummys.Get("otherString").String(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if dummys.Get("someString") != dummys.Get("someString") { | |
| t.Errorf("same value not equal") | |
| } | |
| if got, want := js.Undefined().String(), "<undefined>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.Null().String(), "<null>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.Global().String(), "<object>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func TestInt(t *testing.T) { | |
| want := 42 | |
| o := dummys.Get("someInt") | |
| if got := o.Int(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| dummys.Set("otherInt", want) | |
| if got := dummys.Get("otherInt").Int(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if dummys.Get("someInt") != dummys.Get("someInt") { | |
| t.Errorf("same value not equal") | |
| } | |
| if got := dummys.Get("zero").Int(); got != 0 { | |
| t.Errorf("got %#v, want %#v", got, 0) | |
| } | |
| } | |
| func TestIntConversion(t *testing.T) { | |
| testIntConversion(t, 0) | |
| testIntConversion(t, 1) | |
| testIntConversion(t, -1) | |
| testIntConversion(t, 1<<20) | |
| testIntConversion(t, -1<<20) | |
| testIntConversion(t, 1<<40) | |
| testIntConversion(t, -1<<40) | |
| testIntConversion(t, 1<<60) | |
| testIntConversion(t, -1<<60) | |
| } | |
| func testIntConversion(t *testing.T, want int) { | |
| if got := js.ValueOf(want).Int(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func TestFloat(t *testing.T) { | |
| want := 42.123 | |
| o := dummys.Get("someFloat") | |
| if got := o.Float(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| dummys.Set("otherFloat", want) | |
| if got := dummys.Get("otherFloat").Float(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if dummys.Get("someFloat") != dummys.Get("someFloat") { | |
| t.Errorf("same value not equal") | |
| } | |
| } | |
| func TestObject(t *testing.T) { | |
| if dummys.Get("someArray") != dummys.Get("someArray") { | |
| t.Errorf("same value not equal") | |
| } | |
| // An object and its prototype should not be equal. | |
| proto := js.Global().Get("Object").Get("prototype") | |
| o := js.Global().Call("eval", "new Object()") | |
| if proto == o { | |
| t.Errorf("object equals to its prototype") | |
| } | |
| } | |
| func TestFrozenObject(t *testing.T) { | |
| o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()") | |
| want := 5 | |
| if got := o.Get("field").Int(); want != got { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func TestTypedArrayOf(t *testing.T) { | |
| testTypedArrayOf(t, "[]int8", []int8{0, -42, 0}, -42) | |
| testTypedArrayOf(t, "[]int16", []int16{0, -42, 0}, -42) | |
| testTypedArrayOf(t, "[]int32", []int32{0, -42, 0}, -42) | |
| testTypedArrayOf(t, "[]uint8", []uint8{0, 42, 0}, 42) | |
| testTypedArrayOf(t, "[]uint16", []uint16{0, 42, 0}, 42) | |
| testTypedArrayOf(t, "[]uint32", []uint32{0, 42, 0}, 42) | |
| testTypedArrayOf(t, "[]float32", []float32{0, -42.5, 0}, -42.5) | |
| testTypedArrayOf(t, "[]float64", []float64{0, -42.5, 0}, -42.5) | |
| } | |
| func testTypedArrayOf(t *testing.T, name string, slice interface{}, want float64) { | |
| t.Run(name, func(t *testing.T) { | |
| a := js.TypedArrayOf(slice) | |
| got := a.Index(1).Float() | |
| a.Release() | |
| if got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| }) | |
| } | |
| func TestNaN(t *testing.T) { | |
| want := js.ValueOf(math.NaN()) | |
| got := dummys.Get("NaN") | |
| if got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func TestUndefined(t *testing.T) { | |
| dummys.Set("test", js.Undefined()) | |
| if dummys == js.Undefined() || dummys.Get("test") != js.Undefined() || dummys.Get("xyz") != js.Undefined() { | |
| t.Errorf("js.Undefined expected") | |
| } | |
| } | |
| func TestNull(t *testing.T) { | |
| dummys.Set("test1", nil) | |
| dummys.Set("test2", js.Null()) | |
| if dummys == js.Null() || dummys.Get("test1") != js.Null() || dummys.Get("test2") != js.Null() { | |
| t.Errorf("js.Null expected") | |
| } | |
| } | |
| func TestLength(t *testing.T) { | |
| if got := dummys.Get("someArray").Length(); got != 3 { | |
| t.Errorf("got %#v, want %#v", got, 3) | |
| } | |
| } | |
| func TestGet(t *testing.T) { | |
| // positive cases get tested per type | |
| expectValueError(t, func() { | |
| dummys.Get("zero").Get("badField") | |
| }) | |
| } | |
| func TestSet(t *testing.T) { | |
| // positive cases get tested per type | |
| expectValueError(t, func() { | |
| dummys.Get("zero").Set("badField", 42) | |
| }) | |
| } | |
| func TestIndex(t *testing.T) { | |
| if got := dummys.Get("someArray").Index(1).Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| expectValueError(t, func() { | |
| dummys.Get("zero").Index(1) | |
| }) | |
| } | |
| func TestSetIndex(t *testing.T) { | |
| dummys.Get("someArray").SetIndex(2, 99) | |
| if got := dummys.Get("someArray").Index(2).Int(); got != 99 { | |
| t.Errorf("got %#v, want %#v", got, 99) | |
| } | |
| expectValueError(t, func() { | |
| dummys.Get("zero").SetIndex(2, 99) | |
| }) | |
| } | |
| func TestCall(t *testing.T) { | |
| var i int64 = 40 | |
| if got := dummys.Call("add", i, 2).Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| expectPanic(t, func() { | |
| dummys.Call("zero") | |
| }) | |
| expectValueError(t, func() { | |
| dummys.Get("zero").Call("badMethod") | |
| }) | |
| } | |
| func TestInvoke(t *testing.T) { | |
| var i int64 = 40 | |
| if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| expectValueError(t, func() { | |
| dummys.Get("zero").Invoke() | |
| }) | |
| } | |
| func TestNew(t *testing.T) { | |
| if got := js.Global().Get("Array").New(42).Length(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| expectValueError(t, func() { | |
| dummys.Get("zero").New() | |
| }) | |
| } | |
| func TestInstanceOf(t *testing.T) { | |
| someArray := js.Global().Get("Array").New() | |
| if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func TestType(t *testing.T) { | |
| if got, want := js.Undefined().Type(), js.TypeUndefined; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.Null().Type(), js.TypeNull; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.ValueOf("test").Type(), js.TypeString; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want { | |
| t.Errorf("got %s, want %s", got, want) | |
| } | |
| } | |
| type object = map[string]interface{} | |
| type array = []interface{} | |
| func TestValueOf(t *testing.T) { | |
| a := js.ValueOf(array{0, array{0, 42, 0}, 0}) | |
| if got := a.Index(1).Index(1).Int(); got != 42 { | |
| t.Errorf("got %v, want %v", got, 42) | |
| } | |
| o := js.ValueOf(object{"x": object{"y": 42}}) | |
| if got := o.Get("x").Get("y").Int(); got != 42 { | |
| t.Errorf("got %v, want %v", got, 42) | |
| } | |
| } | |
| func TestZeroValue(t *testing.T) { | |
| var v js.Value | |
| if v != js.Undefined() { | |
| t.Error("zero js.Value is not js.Undefined()") | |
| } | |
| } | |
| func TestFuncOf(t *testing.T) { | |
| c := make(chan struct{}) | |
| cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { | |
| if got := args[0].Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| c <- struct{}{} | |
| return nil | |
| }) | |
| defer cb.Release() | |
| js.Global().Call("setTimeout", cb, 0, 42) | |
| <-c | |
| } | |
| func TestInvokeFunction(t *testing.T) { | |
| called := false | |
| cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { | |
| cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} { | |
| called = true | |
| return 42 | |
| }) | |
| defer cb2.Release() | |
| return cb2.Invoke() | |
| }) | |
| defer cb.Release() | |
| if got := cb.Invoke().Int(); got != 42 { | |
| t.Errorf("got %#v, want %#v", got, 42) | |
| } | |
| if !called { | |
| t.Error("function not called") | |
| } | |
| } | |
| func ExampleFuncOf() { | |
| var cb js.Func | |
| cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { | |
| fmt.Println("button clicked") | |
| cb.Release() // release the function if the button will not be clicked again | |
| return nil | |
| }) | |
| js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb) | |
| } | |
| // See | |
| // - https://developer.mozilla.org/en-US/docs/Glossary/Truthy | |
| // - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953 | |
| // - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2 | |
| func TestTruthy(t *testing.T) { | |
| want := true | |
| for _, key := range []string{ | |
| "someBool", "someString", "someInt", "someFloat", "someArray", "someDate", | |
| "stringZero", // "0" is truthy | |
| "add", // functions are truthy | |
| "emptyObj", "emptyArray", "Infinity", "NegInfinity", | |
| // All objects are truthy, even if they're Number(0) or Boolean(false). | |
| "objNumber0", "objBooleanFalse", | |
| } { | |
| if got := dummys.Get(key).Truthy(); got != want { | |
| t.Errorf("%s: got %#v, want %#v", key, got, want) | |
| } | |
| } | |
| want = false | |
| if got := dummys.Get("zero").Truthy(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got := dummys.Get("NaN").Truthy(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got := js.ValueOf("").Truthy(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got := js.Null().Truthy(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| if got := js.Undefined().Truthy(); got != want { | |
| t.Errorf("got %#v, want %#v", got, want) | |
| } | |
| } | |
| func expectValueError(t *testing.T, fn func()) { | |
| defer func() { | |
| err := recover() | |
| if _, ok := err.(*js.ValueError); !ok { | |
| t.Errorf("expected *js.ValueError, got %T", err) | |
| } | |
| }() | |
| fn() | |
| } | |
| func expectPanic(t *testing.T, fn func()) { | |
| defer func() { | |
| err := recover() | |
| if err == nil { | |
| t.Errorf("expected panic") | |
| } | |
| }() | |
| fn() | |
| } |