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 }