1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
10 // - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find
11 // "go_js_wasm_exec").
12 // - GOOS=js GOARCH=wasm go test
14 // See -exec in "go help test", and "go help run" for details.
26 var dummys
= js
.Global().Call("eval", `({
28 someString: "abc\u1234",
31 someArray: [41, 42, 43],
42 NegInfinity: -Infinity,
43 objNumber0: new Number(0),
44 objBooleanFalse: new Boolean(false),
47 func TestBool(t
*testing
.T
) {
49 o
:= dummys
.Get("someBool")
50 if got
:= o
.Bool(); got
!= want
{
51 t
.Errorf("got %#v, want %#v", got
, want
)
53 dummys
.Set("otherBool", want
)
54 if got
:= dummys
.Get("otherBool").Bool(); got
!= want
{
55 t
.Errorf("got %#v, want %#v", got
, want
)
57 if !dummys
.Get("someBool").Equal(dummys
.Get("someBool")) {
58 t
.Errorf("same value not equal")
62 func TestString(t
*testing
.T
) {
64 o
:= dummys
.Get("someString")
65 if got
:= o
.String(); got
!= want
{
66 t
.Errorf("got %#v, want %#v", got
, want
)
68 dummys
.Set("otherString", want
)
69 if got
:= dummys
.Get("otherString").String(); got
!= want
{
70 t
.Errorf("got %#v, want %#v", got
, want
)
72 if !dummys
.Get("someString").Equal(dummys
.Get("someString")) {
73 t
.Errorf("same value not equal")
76 if got
, want
:= js
.Undefined().String(), "<undefined>"; got
!= want
{
77 t
.Errorf("got %#v, want %#v", got
, want
)
79 if got
, want
:= js
.Null().String(), "<null>"; got
!= want
{
80 t
.Errorf("got %#v, want %#v", got
, want
)
82 if got
, want
:= js
.ValueOf(true).String(), "<boolean: true>"; got
!= want
{
83 t
.Errorf("got %#v, want %#v", got
, want
)
85 if got
, want
:= js
.ValueOf(42.5).String(), "<number: 42.5>"; got
!= want
{
86 t
.Errorf("got %#v, want %#v", got
, want
)
88 if got
, want
:= js
.Global().Call("Symbol").String(), "<symbol>"; got
!= want
{
89 t
.Errorf("got %#v, want %#v", got
, want
)
91 if got
, want
:= js
.Global().String(), "<object>"; got
!= want
{
92 t
.Errorf("got %#v, want %#v", got
, want
)
94 if got
, want
:= js
.Global().Get("setTimeout").String(), "<function>"; got
!= want
{
95 t
.Errorf("got %#v, want %#v", got
, want
)
99 func TestInt(t
*testing
.T
) {
101 o
:= dummys
.Get("someInt")
102 if got
:= o
.Int(); got
!= want
{
103 t
.Errorf("got %#v, want %#v", got
, want
)
105 dummys
.Set("otherInt", want
)
106 if got
:= dummys
.Get("otherInt").Int(); got
!= want
{
107 t
.Errorf("got %#v, want %#v", got
, want
)
109 if !dummys
.Get("someInt").Equal(dummys
.Get("someInt")) {
110 t
.Errorf("same value not equal")
112 if got
:= dummys
.Get("zero").Int(); got
!= 0 {
113 t
.Errorf("got %#v, want %#v", got
, 0)
117 func TestIntConversion(t
*testing
.T
) {
118 testIntConversion(t
, 0)
119 testIntConversion(t
, 1)
120 testIntConversion(t
, -1)
121 testIntConversion(t
, 1<<20)
122 testIntConversion(t
, -1<<20)
123 testIntConversion(t
, 1<<40)
124 testIntConversion(t
, -1<<40)
125 testIntConversion(t
, 1<<60)
126 testIntConversion(t
, -1<<60)
129 func testIntConversion(t
*testing
.T
, want
int) {
130 if got
:= js
.ValueOf(want
).Int(); got
!= want
{
131 t
.Errorf("got %#v, want %#v", got
, want
)
135 func TestFloat(t
*testing
.T
) {
137 o
:= dummys
.Get("someFloat")
138 if got
:= o
.Float(); got
!= want
{
139 t
.Errorf("got %#v, want %#v", got
, want
)
141 dummys
.Set("otherFloat", want
)
142 if got
:= dummys
.Get("otherFloat").Float(); got
!= want
{
143 t
.Errorf("got %#v, want %#v", got
, want
)
145 if !dummys
.Get("someFloat").Equal(dummys
.Get("someFloat")) {
146 t
.Errorf("same value not equal")
150 func TestObject(t
*testing
.T
) {
151 if !dummys
.Get("someArray").Equal(dummys
.Get("someArray")) {
152 t
.Errorf("same value not equal")
155 // An object and its prototype should not be equal.
156 proto
:= js
.Global().Get("Object").Get("prototype")
157 o
:= js
.Global().Call("eval", "new Object()")
159 t
.Errorf("object equals to its prototype")
163 func TestFrozenObject(t
*testing
.T
) {
164 o
:= js
.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()")
166 if got
:= o
.Get("field").Int(); want
!= got
{
167 t
.Errorf("got %#v, want %#v", got
, want
)
171 func TestEqual(t
*testing
.T
) {
172 if !dummys
.Get("someFloat").Equal(dummys
.Get("someFloat")) {
173 t
.Errorf("same float is not equal")
175 if !dummys
.Get("emptyObj").Equal(dummys
.Get("emptyObj")) {
176 t
.Errorf("same object is not equal")
178 if dummys
.Get("someFloat").Equal(dummys
.Get("someInt")) {
179 t
.Errorf("different values are not unequal")
183 func TestNaN(t
*testing
.T
) {
184 if !dummys
.Get("NaN").IsNaN() {
185 t
.Errorf("JS NaN is not NaN")
187 if !js
.ValueOf(math
.NaN()).IsNaN() {
188 t
.Errorf("Go NaN is not NaN")
190 if dummys
.Get("NaN").Equal(dummys
.Get("NaN")) {
191 t
.Errorf("NaN is equal to NaN")
195 func TestUndefined(t
*testing
.T
) {
196 if !js
.Undefined().IsUndefined() {
197 t
.Errorf("undefined is not undefined")
199 if !js
.Undefined().Equal(js
.Undefined()) {
200 t
.Errorf("undefined is not equal to undefined")
202 if dummys
.IsUndefined() {
203 t
.Errorf("object is undefined")
205 if js
.Undefined().IsNull() {
206 t
.Errorf("undefined is null")
208 if dummys
.Set("test", js
.Undefined()); !dummys
.Get("test").IsUndefined() {
209 t
.Errorf("could not set undefined")
213 func TestNull(t
*testing
.T
) {
214 if !js
.Null().IsNull() {
215 t
.Errorf("null is not null")
217 if !js
.Null().Equal(js
.Null()) {
218 t
.Errorf("null is not equal to null")
221 t
.Errorf("object is null")
223 if js
.Null().IsUndefined() {
224 t
.Errorf("null is undefined")
226 if dummys
.Set("test", js
.Null()); !dummys
.Get("test").IsNull() {
227 t
.Errorf("could not set null")
229 if dummys
.Set("test", nil); !dummys
.Get("test").IsNull() {
230 t
.Errorf("could not set nil")
234 func TestLength(t
*testing
.T
) {
235 if got
:= dummys
.Get("someArray").Length(); got
!= 3 {
236 t
.Errorf("got %#v, want %#v", got
, 3)
240 func TestGet(t
*testing
.T
) {
241 // positive cases get tested per type
243 expectValueError(t
, func() {
244 dummys
.Get("zero").Get("badField")
248 func TestSet(t
*testing
.T
) {
249 // positive cases get tested per type
251 expectValueError(t
, func() {
252 dummys
.Get("zero").Set("badField", 42)
256 func TestDelete(t
*testing
.T
) {
257 dummys
.Set("test", 42)
258 dummys
.Delete("test")
259 if dummys
.Call("hasOwnProperty", "test").Bool() {
260 t
.Errorf("property still exists")
263 expectValueError(t
, func() {
264 dummys
.Get("zero").Delete("badField")
268 func TestIndex(t
*testing
.T
) {
269 if got
:= dummys
.Get("someArray").Index(1).Int(); got
!= 42 {
270 t
.Errorf("got %#v, want %#v", got
, 42)
273 expectValueError(t
, func() {
274 dummys
.Get("zero").Index(1)
278 func TestSetIndex(t
*testing
.T
) {
279 dummys
.Get("someArray").SetIndex(2, 99)
280 if got
:= dummys
.Get("someArray").Index(2).Int(); got
!= 99 {
281 t
.Errorf("got %#v, want %#v", got
, 99)
284 expectValueError(t
, func() {
285 dummys
.Get("zero").SetIndex(2, 99)
289 func TestCall(t
*testing
.T
) {
291 if got
:= dummys
.Call("add", i
, 2).Int(); got
!= 42 {
292 t
.Errorf("got %#v, want %#v", got
, 42)
294 if got
:= dummys
.Call("add", js
.Global().Call("eval", "40"), 2).Int(); got
!= 42 {
295 t
.Errorf("got %#v, want %#v", got
, 42)
298 expectPanic(t
, func() {
301 expectValueError(t
, func() {
302 dummys
.Get("zero").Call("badMethod")
306 func TestInvoke(t
*testing
.T
) {
308 if got
:= dummys
.Get("add").Invoke(i
, 2).Int(); got
!= 42 {
309 t
.Errorf("got %#v, want %#v", got
, 42)
312 expectValueError(t
, func() {
313 dummys
.Get("zero").Invoke()
317 func TestNew(t
*testing
.T
) {
318 if got
:= js
.Global().Get("Array").New(42).Length(); got
!= 42 {
319 t
.Errorf("got %#v, want %#v", got
, 42)
322 expectValueError(t
, func() {
323 dummys
.Get("zero").New()
327 func TestInstanceOf(t
*testing
.T
) {
328 someArray
:= js
.Global().Get("Array").New()
329 if got
, want
:= someArray
.InstanceOf(js
.Global().Get("Array")), true; got
!= want
{
330 t
.Errorf("got %#v, want %#v", got
, want
)
332 if got
, want
:= someArray
.InstanceOf(js
.Global().Get("Function")), false; got
!= want
{
333 t
.Errorf("got %#v, want %#v", got
, want
)
337 func TestType(t
*testing
.T
) {
338 if got
, want
:= js
.Undefined().Type(), js
.TypeUndefined
; got
!= want
{
339 t
.Errorf("got %s, want %s", got
, want
)
341 if got
, want
:= js
.Null().Type(), js
.TypeNull
; got
!= want
{
342 t
.Errorf("got %s, want %s", got
, want
)
344 if got
, want
:= js
.ValueOf(true).Type(), js
.TypeBoolean
; got
!= want
{
345 t
.Errorf("got %s, want %s", got
, want
)
347 if got
, want
:= js
.ValueOf(0).Type(), js
.TypeNumber
; got
!= want
{
348 t
.Errorf("got %s, want %s", got
, want
)
350 if got
, want
:= js
.ValueOf(42).Type(), js
.TypeNumber
; got
!= want
{
351 t
.Errorf("got %s, want %s", got
, want
)
353 if got
, want
:= js
.ValueOf("test").Type(), js
.TypeString
; got
!= want
{
354 t
.Errorf("got %s, want %s", got
, want
)
356 if got
, want
:= js
.Global().Get("Symbol").Invoke("test").Type(), js
.TypeSymbol
; got
!= want
{
357 t
.Errorf("got %s, want %s", got
, want
)
359 if got
, want
:= js
.Global().Get("Array").New().Type(), js
.TypeObject
; got
!= want
{
360 t
.Errorf("got %s, want %s", got
, want
)
362 if got
, want
:= js
.Global().Get("Array").Type(), js
.TypeFunction
; got
!= want
{
363 t
.Errorf("got %s, want %s", got
, want
)
367 type object
= map[string]interface{}
368 type array
= []interface{}
370 func TestValueOf(t
*testing
.T
) {
371 a
:= js
.ValueOf(array
{0, array
{0, 42, 0}, 0})
372 if got
:= a
.Index(1).Index(1).Int(); got
!= 42 {
373 t
.Errorf("got %v, want %v", got
, 42)
376 o
:= js
.ValueOf(object
{"x": object
{"y": 42}})
377 if got
:= o
.Get("x").Get("y").Int(); got
!= 42 {
378 t
.Errorf("got %v, want %v", got
, 42)
382 func TestZeroValue(t
*testing
.T
) {
384 if !v
.IsUndefined() {
385 t
.Error("zero js.Value is not js.Undefined()")
389 func TestFuncOf(t
*testing
.T
) {
390 c
:= make(chan struct{})
391 cb
:= js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
392 if got
:= args
[0].Int(); got
!= 42 {
393 t
.Errorf("got %#v, want %#v", got
, 42)
399 js
.Global().Call("setTimeout", cb
, 0, 42)
403 func TestInvokeFunction(t
*testing
.T
) {
405 cb
:= js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
406 cb2
:= js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
414 if got
:= cb
.Invoke().Int(); got
!= 42 {
415 t
.Errorf("got %#v, want %#v", got
, 42)
418 t
.Error("function not called")
422 func TestInterleavedFunctions(t
*testing
.T
) {
423 c1
:= make(chan struct{})
424 c2
:= make(chan struct{})
426 js
.Global().Get("setTimeout").Invoke(js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
434 // this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now
435 f
:= js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
441 func ExampleFuncOf() {
443 cb
= js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
444 fmt
.Println("button clicked")
445 cb
.Release() // release the function if the button will not be clicked again
448 js
.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb
)
452 // - https://developer.mozilla.org/en-US/docs/Glossary/Truthy
453 // - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953
454 // - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
455 func TestTruthy(t
*testing
.T
) {
457 for _
, key
:= range []string{
458 "someBool", "someString", "someInt", "someFloat", "someArray", "someDate",
459 "stringZero", // "0" is truthy
460 "add", // functions are truthy
461 "emptyObj", "emptyArray", "Infinity", "NegInfinity",
462 // All objects are truthy, even if they're Number(0) or Boolean(false).
463 "objNumber0", "objBooleanFalse",
465 if got
:= dummys
.Get(key
).Truthy(); got
!= want
{
466 t
.Errorf("%s: got %#v, want %#v", key
, got
, want
)
471 if got
:= dummys
.Get("zero").Truthy(); got
!= want
{
472 t
.Errorf("got %#v, want %#v", got
, want
)
474 if got
:= dummys
.Get("NaN").Truthy(); got
!= want
{
475 t
.Errorf("got %#v, want %#v", got
, want
)
477 if got
:= js
.ValueOf("").Truthy(); got
!= want
{
478 t
.Errorf("got %#v, want %#v", got
, want
)
480 if got
:= js
.Null().Truthy(); got
!= want
{
481 t
.Errorf("got %#v, want %#v", got
, want
)
483 if got
:= js
.Undefined().Truthy(); got
!= want
{
484 t
.Errorf("got %#v, want %#v", got
, want
)
488 func expectValueError(t
*testing
.T
, fn
func()) {
491 if _
, ok
:= err
.(*js
.ValueError
); !ok
{
492 t
.Errorf("expected *js.ValueError, got %T", err
)
498 func expectPanic(t
*testing
.T
, fn
func()) {
502 t
.Errorf("expected panic")
508 var copyTests
= []struct {
518 func TestCopyBytesToGo(t
*testing
.T
) {
519 for _
, tt
:= range copyTests
{
520 t
.Run(fmt
.Sprintf("%d-to-%d", tt
.srcLen
, tt
.dstLen
), func(t
*testing
.T
) {
521 src
:= js
.Global().Get("Uint8Array").New(tt
.srcLen
)
525 dst
:= make([]byte, tt
.dstLen
)
527 if got
, want
:= js
.CopyBytesToGo(dst
, src
), tt
.copyLen
; got
!= want
{
528 t
.Errorf("copied %d, want %d", got
, want
)
531 if got
, want
:= int(dst
[1]), 42; got
!= want
{
532 t
.Errorf("got %d, want %d", got
, want
)
539 func TestCopyBytesToJS(t
*testing
.T
) {
540 for _
, tt
:= range copyTests
{
541 t
.Run(fmt
.Sprintf("%d-to-%d", tt
.srcLen
, tt
.dstLen
), func(t
*testing
.T
) {
542 src
:= make([]byte, tt
.srcLen
)
546 dst
:= js
.Global().Get("Uint8Array").New(tt
.dstLen
)
548 if got
, want
:= js
.CopyBytesToJS(dst
, src
), tt
.copyLen
; got
!= want
{
549 t
.Errorf("copied %d, want %d", got
, want
)
552 if got
, want
:= dst
.Index(1).Int(), 42; got
!= want
{
553 t
.Errorf("got %d, want %d", got
, want
)
560 func TestGarbageCollection(t
*testing
.T
) {
561 before
:= js
.JSGo
.Get("_values").Length()
562 for i
:= 0; i
< 1000; i
++ {
563 _
= js
.Global().Get("Object").New().Call("toString").String()
566 after
:= js
.JSGo
.Get("_values").Length()
567 if after
-before
> 500 {
568 t
.Errorf("garbage collection ineffective")
572 // BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations.
573 // It creates a div, and sets its id. Then searches by that id and sets some data.
574 // Finally it removes that div.
575 func BenchmarkDOM(b
*testing
.B
) {
576 document
:= js
.Global().Get("document")
577 if document
.IsUndefined() {
578 b
.Skip("Not a browser environment. Skipping.")
580 const data
= "someString"
581 for i
:= 0; i
< b
.N
; i
++ {
582 div
:= document
.Call("createElement", "div")
583 div
.Call("setAttribute", "id", "myDiv")
584 document
.Get("body").Call("appendChild", div
)
585 myDiv
:= document
.Call("getElementById", "myDiv")
586 myDiv
.Set("innerHTML", data
)
588 if got
, want
:= myDiv
.Get("innerHTML").String(), data
; got
!= want
{
589 b
.Errorf("got %s, want %s", got
, want
)
591 document
.Get("body").Call("removeChild", div
)