github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/tests/syscalljs/go114_js_test.go (about) 1 // This file is basically copied from $GOROOT/src/syscall/js/js_test.go 2 3 // Copyright 2018 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 // +build js 8 // +build go1.14 9 10 // To run these tests: 11 // 12 // - Install Node 13 // - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find 14 // "go_js_wasm_exec"). 15 // - GOOS=js GOARCH=wasm go test 16 // 17 // See -exec in "go help test", and "go help run" for details. 18 19 package js_test 20 21 import ( 22 "fmt" 23 "math" 24 "syscall/js" 25 "testing" 26 ) 27 28 func TestMain(m *testing.M) { 29 // Suppress the 'deadlock' error on GopherJS by goroutine 30 // (https://github.com/gopherjs/gopherjs/issues/826). 31 go func() { 32 m.Run() 33 }() 34 } 35 36 var dummys = js.Global().Call("eval", `({ 37 someBool: true, 38 someString: "abc\u1234", 39 someInt: 42, 40 someFloat: 42.123, 41 someArray: [41, 42, 43], 42 someDate: new Date(), 43 add: function(a, b) { 44 return a + b; 45 }, 46 zero: 0, 47 stringZero: "0", 48 NaN: NaN, 49 emptyObj: {}, 50 emptyArray: [], 51 Infinity: Infinity, 52 NegInfinity: -Infinity, 53 objNumber0: new Number(0), 54 objBooleanFalse: new Boolean(false), 55 })`) 56 57 func TestBool(t *testing.T) { 58 want := true 59 o := dummys.Get("someBool") 60 if got := o.Bool(); got != want { 61 t.Errorf("got %#v, want %#v", got, want) 62 } 63 dummys.Set("otherBool", want) 64 if got := dummys.Get("otherBool").Bool(); got != want { 65 t.Errorf("got %#v, want %#v", got, want) 66 } 67 if !dummys.Get("someBool").Equal(dummys.Get("someBool")) { 68 t.Errorf("same value not equal") 69 } 70 } 71 72 func TestString(t *testing.T) { 73 want := "abc\u1234" 74 o := dummys.Get("someString") 75 if got := o.String(); got != want { 76 t.Errorf("got %#v, want %#v", got, want) 77 } 78 dummys.Set("otherString", want) 79 if got := dummys.Get("otherString").String(); got != want { 80 t.Errorf("got %#v, want %#v", got, want) 81 } 82 if !dummys.Get("someString").Equal(dummys.Get("someString")) { 83 t.Errorf("same value not equal") 84 } 85 86 if got, want := js.Undefined().String(), "<undefined>"; got != want { 87 t.Errorf("got %#v, want %#v", got, want) 88 } 89 if got, want := js.Null().String(), "<null>"; got != want { 90 t.Errorf("got %#v, want %#v", got, want) 91 } 92 if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want { 93 t.Errorf("got %#v, want %#v", got, want) 94 } 95 if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want { 96 t.Errorf("got %#v, want %#v", got, want) 97 } 98 if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want { 99 t.Errorf("got %#v, want %#v", got, want) 100 } 101 if got, want := js.Global().String(), "<object>"; got != want { 102 t.Errorf("got %#v, want %#v", got, want) 103 } 104 if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want { 105 t.Errorf("got %#v, want %#v", got, want) 106 } 107 } 108 109 func TestInt(t *testing.T) { 110 want := 42 111 o := dummys.Get("someInt") 112 if got := o.Int(); got != want { 113 t.Errorf("got %#v, want %#v", got, want) 114 } 115 dummys.Set("otherInt", want) 116 if got := dummys.Get("otherInt").Int(); got != want { 117 t.Errorf("got %#v, want %#v", got, want) 118 } 119 if !dummys.Get("someInt").Equal(dummys.Get("someInt")) { 120 t.Errorf("same value not equal") 121 } 122 if got := dummys.Get("zero").Int(); got != 0 { 123 t.Errorf("got %#v, want %#v", got, 0) 124 } 125 } 126 127 func TestIntConversion(t *testing.T) { 128 testIntConversion(t, 0) 129 testIntConversion(t, 1) 130 testIntConversion(t, -1) 131 testIntConversion(t, 1<<20) 132 testIntConversion(t, -1<<20) 133 134 // Skip too big integers. They cannot be compiled with 32bit environment, and GopherJS is one of them. 135 // testIntConversion(t, 1<<40) 136 // testIntConversion(t, -1<<40) 137 // testIntConversion(t, 1<<60) 138 // testIntConversion(t, -1<<60) 139 } 140 141 func testIntConversion(t *testing.T, want int) { 142 if got := js.ValueOf(want).Int(); got != want { 143 t.Errorf("got %#v, want %#v", got, want) 144 } 145 } 146 147 func TestFloat(t *testing.T) { 148 want := 42.123 149 o := dummys.Get("someFloat") 150 if got := o.Float(); got != want { 151 t.Errorf("got %#v, want %#v", got, want) 152 } 153 dummys.Set("otherFloat", want) 154 if got := dummys.Get("otherFloat").Float(); got != want { 155 t.Errorf("got %#v, want %#v", got, want) 156 } 157 if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) { 158 t.Errorf("same value not equal") 159 } 160 } 161 162 func TestObject(t *testing.T) { 163 if !dummys.Get("someArray").Equal(dummys.Get("someArray")) { 164 t.Errorf("same value not equal") 165 } 166 167 // An object and its prototype should not be equal. 168 proto := js.Global().Get("Object").Get("prototype") 169 o := js.Global().Call("eval", "new Object()") 170 if proto.Equal(o) { 171 t.Errorf("object equals to its prototype") 172 } 173 } 174 175 func TestFrozenObject(t *testing.T) { 176 o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()") 177 want := 5 178 if got := o.Get("field").Int(); want != got { 179 t.Errorf("got %#v, want %#v", got, want) 180 } 181 } 182 183 func TestEqual(t *testing.T) { 184 if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) { 185 t.Errorf("same float is not equal") 186 } 187 if !dummys.Get("emptyObj").Equal(dummys.Get("emptyObj")) { 188 t.Errorf("same object is not equal") 189 } 190 if dummys.Get("someFloat").Equal(dummys.Get("someInt")) { 191 t.Errorf("different values are not unequal") 192 } 193 } 194 195 func TestNaN(t *testing.T) { 196 if !dummys.Get("NaN").IsNaN() { 197 t.Errorf("JS NaN is not NaN") 198 } 199 if !js.ValueOf(math.NaN()).IsNaN() { 200 t.Errorf("Go NaN is not NaN") 201 } 202 if dummys.Get("NaN").Equal(dummys.Get("NaN")) { 203 t.Errorf("NaN is equal to NaN") 204 } 205 } 206 207 func TestUndefined(t *testing.T) { 208 if !js.Undefined().IsUndefined() { 209 t.Errorf("undefined is not undefined") 210 } 211 if !js.Undefined().Equal(js.Undefined()) { 212 t.Errorf("undefined is not equal to undefined") 213 } 214 if dummys.IsUndefined() { 215 t.Errorf("object is undefined") 216 } 217 if js.Undefined().IsNull() { 218 t.Errorf("undefined is null") 219 } 220 if dummys.Set("test", js.Undefined()); !dummys.Get("test").IsUndefined() { 221 t.Errorf("could not set undefined") 222 } 223 } 224 225 func TestNull(t *testing.T) { 226 if !js.Null().IsNull() { 227 t.Errorf("null is not null") 228 } 229 if !js.Null().Equal(js.Null()) { 230 t.Errorf("null is not equal to null") 231 } 232 if dummys.IsNull() { 233 t.Errorf("object is null") 234 } 235 if js.Null().IsUndefined() { 236 t.Errorf("null is undefined") 237 } 238 if dummys.Set("test", js.Null()); !dummys.Get("test").IsNull() { 239 t.Errorf("could not set null") 240 } 241 if dummys.Set("test", nil); !dummys.Get("test").IsNull() { 242 t.Errorf("could not set nil") 243 } 244 } 245 246 func TestLength(t *testing.T) { 247 if got := dummys.Get("someArray").Length(); got != 3 { 248 t.Errorf("got %#v, want %#v", got, 3) 249 } 250 } 251 252 func TestGet(t *testing.T) { 253 // positive cases get tested per type 254 255 expectValueError(t, func() { 256 dummys.Get("zero").Get("badField") 257 }) 258 } 259 260 func TestSet(t *testing.T) { 261 // positive cases get tested per type 262 263 expectValueError(t, func() { 264 dummys.Get("zero").Set("badField", 42) 265 }) 266 } 267 268 func TestDelete(t *testing.T) { 269 dummys.Set("test", 42) 270 dummys.Delete("test") 271 if dummys.Call("hasOwnProperty", "test").Bool() { 272 t.Errorf("property still exists") 273 } 274 275 expectValueError(t, func() { 276 dummys.Get("zero").Delete("badField") 277 }) 278 } 279 280 func TestIndex(t *testing.T) { 281 if got := dummys.Get("someArray").Index(1).Int(); got != 42 { 282 t.Errorf("got %#v, want %#v", got, 42) 283 } 284 285 expectValueError(t, func() { 286 dummys.Get("zero").Index(1) 287 }) 288 } 289 290 func TestSetIndex(t *testing.T) { 291 dummys.Get("someArray").SetIndex(2, 99) 292 if got := dummys.Get("someArray").Index(2).Int(); got != 99 { 293 t.Errorf("got %#v, want %#v", got, 99) 294 } 295 296 expectValueError(t, func() { 297 dummys.Get("zero").SetIndex(2, 99) 298 }) 299 } 300 301 func TestCall(t *testing.T) { 302 var i int64 = 40 303 if got := dummys.Call("add", i, 2).Int(); got != 42 { 304 t.Errorf("got %#v, want %#v", got, 42) 305 } 306 if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 { 307 t.Errorf("got %#v, want %#v", got, 42) 308 } 309 310 expectPanic(t, func() { 311 dummys.Call("zero") 312 }) 313 expectValueError(t, func() { 314 dummys.Get("zero").Call("badMethod") 315 }) 316 } 317 318 func TestInvoke(t *testing.T) { 319 var i int64 = 40 320 if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 { 321 t.Errorf("got %#v, want %#v", got, 42) 322 } 323 324 expectValueError(t, func() { 325 dummys.Get("zero").Invoke() 326 }) 327 } 328 329 func TestNew(t *testing.T) { 330 if got := js.Global().Get("Array").New(42).Length(); got != 42 { 331 t.Errorf("got %#v, want %#v", got, 42) 332 } 333 334 expectValueError(t, func() { 335 dummys.Get("zero").New() 336 }) 337 } 338 339 func TestInstanceOf(t *testing.T) { 340 someArray := js.Global().Get("Array").New() 341 if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want { 342 t.Errorf("got %#v, want %#v", got, want) 343 } 344 if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want { 345 t.Errorf("got %#v, want %#v", got, want) 346 } 347 } 348 349 func TestType(t *testing.T) { 350 if got, want := js.Undefined().Type(), js.TypeUndefined; got != want { 351 t.Errorf("got %s, want %s", got, want) 352 } 353 if got, want := js.Null().Type(), js.TypeNull; got != want { 354 t.Errorf("got %s, want %s", got, want) 355 } 356 if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want { 357 t.Errorf("got %s, want %s", got, want) 358 } 359 if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want { 360 t.Errorf("got %s, want %s", got, want) 361 } 362 if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want { 363 t.Errorf("got %s, want %s", got, want) 364 } 365 if got, want := js.ValueOf("test").Type(), js.TypeString; got != want { 366 t.Errorf("got %s, want %s", got, want) 367 } 368 if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want { 369 t.Errorf("got %s, want %s", got, want) 370 } 371 if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want { 372 t.Errorf("got %s, want %s", got, want) 373 } 374 if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want { 375 t.Errorf("got %s, want %s", got, want) 376 } 377 } 378 379 type object = map[string]interface{} 380 type array = []interface{} 381 382 func TestValueOf(t *testing.T) { 383 a := js.ValueOf(array{0, array{0, 42, 0}, 0}) 384 if got := a.Index(1).Index(1).Int(); got != 42 { 385 t.Errorf("got %v, want %v", got, 42) 386 } 387 388 o := js.ValueOf(object{"x": object{"y": 42}}) 389 if got := o.Get("x").Get("y").Int(); got != 42 { 390 t.Errorf("got %v, want %v", got, 42) 391 } 392 } 393 394 func TestZeroValue(t *testing.T) { 395 var v js.Value 396 if !v.IsUndefined() { 397 t.Error("zero js.Value is not js.Undefined()") 398 } 399 } 400 401 func TestFuncOf(t *testing.T) { 402 c := make(chan struct{}) 403 cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 404 if got := args[0].Int(); got != 42 { 405 t.Errorf("got %#v, want %#v", got, 42) 406 } 407 c <- struct{}{} 408 return nil 409 }) 410 defer cb.Release() 411 js.Global().Call("setTimeout", cb, 0, 42) 412 <-c 413 } 414 415 func TestInvokeFunction(t *testing.T) { 416 called := false 417 cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 418 cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 419 called = true 420 return 42 421 }) 422 defer cb2.Release() 423 return cb2.Invoke() 424 }) 425 defer cb.Release() 426 if got := cb.Invoke().Int(); got != 42 { 427 t.Errorf("got %#v, want %#v", got, 42) 428 } 429 if !called { 430 t.Error("function not called") 431 } 432 } 433 434 func TestInterleavedFunctions(t *testing.T) { 435 c1 := make(chan struct{}) 436 c2 := make(chan struct{}) 437 438 js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} { 439 c1 <- struct{}{} 440 <-c2 441 return nil 442 }), 0) 443 444 <-c1 445 c2 <- struct{}{} 446 // this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now 447 f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 448 return nil 449 }) 450 f.Invoke() 451 } 452 453 func ExampleFuncOf() { 454 var cb js.Func 455 cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { 456 fmt.Println("button clicked") 457 cb.Release() // release the function if the button will not be clicked again 458 return nil 459 }) 460 js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb) 461 } 462 463 // See 464 // - https://developer.mozilla.org/en-US/docs/Glossary/Truthy 465 // - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953 466 // - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2 467 func TestTruthy(t *testing.T) { 468 want := true 469 for _, key := range []string{ 470 "someBool", "someString", "someInt", "someFloat", "someArray", "someDate", 471 "stringZero", // "0" is truthy 472 "add", // functions are truthy 473 "emptyObj", "emptyArray", "Infinity", "NegInfinity", 474 // All objects are truthy, even if they're Number(0) or Boolean(false). 475 "objNumber0", "objBooleanFalse", 476 } { 477 if got := dummys.Get(key).Truthy(); got != want { 478 t.Errorf("%s: got %#v, want %#v", key, got, want) 479 } 480 } 481 482 want = false 483 if got := dummys.Get("zero").Truthy(); got != want { 484 t.Errorf("got %#v, want %#v", got, want) 485 } 486 if got := dummys.Get("NaN").Truthy(); got != want { 487 t.Errorf("got %#v, want %#v", got, want) 488 } 489 if got := js.ValueOf("").Truthy(); got != want { 490 t.Errorf("got %#v, want %#v", got, want) 491 } 492 if got := js.Null().Truthy(); got != want { 493 t.Errorf("got %#v, want %#v", got, want) 494 } 495 if got := js.Undefined().Truthy(); got != want { 496 t.Errorf("got %#v, want %#v", got, want) 497 } 498 } 499 500 func expectValueError(t *testing.T, fn func()) { 501 defer func() { 502 err := recover() 503 if _, ok := err.(*js.ValueError); !ok { 504 t.Errorf("expected *js.ValueError, got %T", err) 505 } 506 }() 507 fn() 508 } 509 510 func expectPanic(t *testing.T, fn func()) { 511 defer func() { 512 err := recover() 513 if err == nil { 514 t.Errorf("expected panic") 515 } 516 }() 517 fn() 518 } 519 520 var copyTests = []struct { 521 srcLen int 522 dstLen int 523 copyLen int 524 }{ 525 {5, 3, 3}, 526 {3, 5, 3}, 527 {0, 0, 0}, 528 } 529 530 func TestCopyBytesToGo(t *testing.T) { 531 for _, tt := range copyTests { 532 t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) { 533 src := js.Global().Get("Uint8Array").New(tt.srcLen) 534 if tt.srcLen >= 2 { 535 src.SetIndex(1, 42) 536 } 537 dst := make([]byte, tt.dstLen) 538 539 if got, want := js.CopyBytesToGo(dst, src), tt.copyLen; got != want { 540 t.Errorf("copied %d, want %d", got, want) 541 } 542 if tt.dstLen >= 2 { 543 if got, want := int(dst[1]), 42; got != want { 544 t.Errorf("got %d, want %d", got, want) 545 } 546 } 547 }) 548 } 549 } 550 551 func TestCopyBytesToJS(t *testing.T) { 552 for _, tt := range copyTests { 553 t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) { 554 src := make([]byte, tt.srcLen) 555 if tt.srcLen >= 2 { 556 src[1] = 42 557 } 558 dst := js.Global().Get("Uint8Array").New(tt.dstLen) 559 560 if got, want := js.CopyBytesToJS(dst, src), tt.copyLen; got != want { 561 t.Errorf("copied %d, want %d", got, want) 562 } 563 if tt.dstLen >= 2 { 564 if got, want := dst.Index(1).Int(), 42; got != want { 565 t.Errorf("got %d, want %d", got, want) 566 } 567 } 568 }) 569 } 570 } 571 572 func TestGarbageCollection(t *testing.T) { 573 t.Skip("js.JSGo not implemnet") 574 // before := js.JSGo.Get("_values").Length() 575 // for i := 0; i < 1000; i++ { 576 // _ = js.Global().Get("Object").New().Call("toString").String() 577 // runtime.GC() 578 // } 579 // after := js.JSGo.Get("_values").Length() 580 // if after-before > 500 { 581 // t.Errorf("garbage collection ineffective") 582 // } 583 } 584 585 // BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations. 586 // It creates a div, and sets its id. Then searches by that id and sets some data. 587 // Finally it removes that div. 588 func BenchmarkDOM(b *testing.B) { 589 document := js.Global().Get("document") 590 if document.IsUndefined() { 591 b.Skip("Not a browser environment. Skipping.") 592 } 593 const data = "someString" 594 for i := 0; i < b.N; i++ { 595 div := document.Call("createElement", "div") 596 div.Call("setAttribute", "id", "myDiv") 597 document.Get("body").Call("appendChild", div) 598 myDiv := document.Call("getElementById", "myDiv") 599 myDiv.Set("innerHTML", data) 600 601 if got, want := myDiv.Get("innerHTML").String(), data; got != want { 602 b.Errorf("got %s, want %s", got, want) 603 } 604 document.Get("body").Call("removeChild", div) 605 } 606 }