Source file
src/syscall/js/js_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package js_test
17
18 import (
19 "fmt"
20 "math"
21 "runtime"
22 "syscall/js"
23 "testing"
24 )
25
26 var dummys = js.Global().Call("eval", `({
27 someBool: true,
28 someString: "abc\u1234",
29 someInt: 42,
30 someFloat: 42.123,
31 someArray: [41, 42, 43],
32 someDate: new Date(),
33 add: function(a, b) {
34 return a + b;
35 },
36 zero: 0,
37 stringZero: "0",
38 NaN: NaN,
39 emptyObj: {},
40 emptyArray: [],
41 Infinity: Infinity,
42 NegInfinity: -Infinity,
43 objNumber0: new Number(0),
44 objBooleanFalse: new Boolean(false),
45 })`)
46
47 func TestBool(t *testing.T) {
48 want := true
49 o := dummys.Get("someBool")
50 if got := o.Bool(); got != want {
51 t.Errorf("got %#v, want %#v", got, want)
52 }
53 dummys.Set("otherBool", want)
54 if got := dummys.Get("otherBool").Bool(); got != want {
55 t.Errorf("got %#v, want %#v", got, want)
56 }
57 if !dummys.Get("someBool").Equal(dummys.Get("someBool")) {
58 t.Errorf("same value not equal")
59 }
60 }
61
62 func TestString(t *testing.T) {
63 want := "abc\u1234"
64 o := dummys.Get("someString")
65 if got := o.String(); got != want {
66 t.Errorf("got %#v, want %#v", got, want)
67 }
68 dummys.Set("otherString", want)
69 if got := dummys.Get("otherString").String(); got != want {
70 t.Errorf("got %#v, want %#v", got, want)
71 }
72 if !dummys.Get("someString").Equal(dummys.Get("someString")) {
73 t.Errorf("same value not equal")
74 }
75
76 if got, want := js.Undefined().String(), "<undefined>"; got != want {
77 t.Errorf("got %#v, want %#v", got, want)
78 }
79 if got, want := js.Null().String(), "<null>"; got != want {
80 t.Errorf("got %#v, want %#v", got, want)
81 }
82 if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want {
83 t.Errorf("got %#v, want %#v", got, want)
84 }
85 if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want {
86 t.Errorf("got %#v, want %#v", got, want)
87 }
88 if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want {
89 t.Errorf("got %#v, want %#v", got, want)
90 }
91 if got, want := js.Global().String(), "<object>"; got != want {
92 t.Errorf("got %#v, want %#v", got, want)
93 }
94 if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want {
95 t.Errorf("got %#v, want %#v", got, want)
96 }
97 }
98
99 func TestInt(t *testing.T) {
100 want := 42
101 o := dummys.Get("someInt")
102 if got := o.Int(); got != want {
103 t.Errorf("got %#v, want %#v", got, want)
104 }
105 dummys.Set("otherInt", want)
106 if got := dummys.Get("otherInt").Int(); got != want {
107 t.Errorf("got %#v, want %#v", got, want)
108 }
109 if !dummys.Get("someInt").Equal(dummys.Get("someInt")) {
110 t.Errorf("same value not equal")
111 }
112 if got := dummys.Get("zero").Int(); got != 0 {
113 t.Errorf("got %#v, want %#v", got, 0)
114 }
115 }
116
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)
127 }
128
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)
132 }
133 }
134
135 func TestFloat(t *testing.T) {
136 want := 42.123
137 o := dummys.Get("someFloat")
138 if got := o.Float(); got != want {
139 t.Errorf("got %#v, want %#v", got, want)
140 }
141 dummys.Set("otherFloat", want)
142 if got := dummys.Get("otherFloat").Float(); got != want {
143 t.Errorf("got %#v, want %#v", got, want)
144 }
145 if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
146 t.Errorf("same value not equal")
147 }
148 }
149
150 func TestObject(t *testing.T) {
151 if !dummys.Get("someArray").Equal(dummys.Get("someArray")) {
152 t.Errorf("same value not equal")
153 }
154
155
156 proto := js.Global().Get("Object").Get("prototype")
157 o := js.Global().Call("eval", "new Object()")
158 if proto.Equal(o) {
159 t.Errorf("object equals to its prototype")
160 }
161 }
162
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; })()")
165 want := 5
166 if got := o.Get("field").Int(); want != got {
167 t.Errorf("got %#v, want %#v", got, want)
168 }
169 }
170
171 func TestEqual(t *testing.T) {
172 if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
173 t.Errorf("same float is not equal")
174 }
175 if !dummys.Get("emptyObj").Equal(dummys.Get("emptyObj")) {
176 t.Errorf("same object is not equal")
177 }
178 if dummys.Get("someFloat").Equal(dummys.Get("someInt")) {
179 t.Errorf("different values are not unequal")
180 }
181 }
182
183 func TestNaN(t *testing.T) {
184 if !dummys.Get("NaN").IsNaN() {
185 t.Errorf("JS NaN is not NaN")
186 }
187 if !js.ValueOf(math.NaN()).IsNaN() {
188 t.Errorf("Go NaN is not NaN")
189 }
190 if dummys.Get("NaN").Equal(dummys.Get("NaN")) {
191 t.Errorf("NaN is equal to NaN")
192 }
193 }
194
195 func TestUndefined(t *testing.T) {
196 if !js.Undefined().IsUndefined() {
197 t.Errorf("undefined is not undefined")
198 }
199 if !js.Undefined().Equal(js.Undefined()) {
200 t.Errorf("undefined is not equal to undefined")
201 }
202 if dummys.IsUndefined() {
203 t.Errorf("object is undefined")
204 }
205 if js.Undefined().IsNull() {
206 t.Errorf("undefined is null")
207 }
208 if dummys.Set("test", js.Undefined()); !dummys.Get("test").IsUndefined() {
209 t.Errorf("could not set undefined")
210 }
211 }
212
213 func TestNull(t *testing.T) {
214 if !js.Null().IsNull() {
215 t.Errorf("null is not null")
216 }
217 if !js.Null().Equal(js.Null()) {
218 t.Errorf("null is not equal to null")
219 }
220 if dummys.IsNull() {
221 t.Errorf("object is null")
222 }
223 if js.Null().IsUndefined() {
224 t.Errorf("null is undefined")
225 }
226 if dummys.Set("test", js.Null()); !dummys.Get("test").IsNull() {
227 t.Errorf("could not set null")
228 }
229 if dummys.Set("test", nil); !dummys.Get("test").IsNull() {
230 t.Errorf("could not set nil")
231 }
232 }
233
234 func TestLength(t *testing.T) {
235 if got := dummys.Get("someArray").Length(); got != 3 {
236 t.Errorf("got %#v, want %#v", got, 3)
237 }
238 }
239
240 func TestGet(t *testing.T) {
241
242
243 expectValueError(t, func() {
244 dummys.Get("zero").Get("badField")
245 })
246 }
247
248 func TestSet(t *testing.T) {
249
250
251 expectValueError(t, func() {
252 dummys.Get("zero").Set("badField", 42)
253 })
254 }
255
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")
261 }
262
263 expectValueError(t, func() {
264 dummys.Get("zero").Delete("badField")
265 })
266 }
267
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)
271 }
272
273 expectValueError(t, func() {
274 dummys.Get("zero").Index(1)
275 })
276 }
277
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)
282 }
283
284 expectValueError(t, func() {
285 dummys.Get("zero").SetIndex(2, 99)
286 })
287 }
288
289 func TestCall(t *testing.T) {
290 var i int64 = 40
291 if got := dummys.Call("add", i, 2).Int(); got != 42 {
292 t.Errorf("got %#v, want %#v", got, 42)
293 }
294 if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 {
295 t.Errorf("got %#v, want %#v", got, 42)
296 }
297
298 expectPanic(t, func() {
299 dummys.Call("zero")
300 })
301 expectValueError(t, func() {
302 dummys.Get("zero").Call("badMethod")
303 })
304 }
305
306 func TestInvoke(t *testing.T) {
307 var i int64 = 40
308 if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 {
309 t.Errorf("got %#v, want %#v", got, 42)
310 }
311
312 expectValueError(t, func() {
313 dummys.Get("zero").Invoke()
314 })
315 }
316
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)
320 }
321
322 expectValueError(t, func() {
323 dummys.Get("zero").New()
324 })
325 }
326
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)
331 }
332 if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want {
333 t.Errorf("got %#v, want %#v", got, want)
334 }
335 }
336
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)
340 }
341 if got, want := js.Null().Type(), js.TypeNull; got != want {
342 t.Errorf("got %s, want %s", got, want)
343 }
344 if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want {
345 t.Errorf("got %s, want %s", got, want)
346 }
347 if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want {
348 t.Errorf("got %s, want %s", got, want)
349 }
350 if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want {
351 t.Errorf("got %s, want %s", got, want)
352 }
353 if got, want := js.ValueOf("test").Type(), js.TypeString; got != want {
354 t.Errorf("got %s, want %s", got, want)
355 }
356 if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want {
357 t.Errorf("got %s, want %s", got, want)
358 }
359 if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want {
360 t.Errorf("got %s, want %s", got, want)
361 }
362 if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want {
363 t.Errorf("got %s, want %s", got, want)
364 }
365 }
366
367 type object = map[string]interface{}
368 type array = []interface{}
369
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)
374 }
375
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)
379 }
380 }
381
382 func TestZeroValue(t *testing.T) {
383 var v js.Value
384 if !v.IsUndefined() {
385 t.Error("zero js.Value is not js.Undefined()")
386 }
387 }
388
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)
394 }
395 c <- struct{}{}
396 return nil
397 })
398 defer cb.Release()
399 js.Global().Call("setTimeout", cb, 0, 42)
400 <-c
401 }
402
403 func TestInvokeFunction(t *testing.T) {
404 called := false
405 cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
406 cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
407 called = true
408 return 42
409 })
410 defer cb2.Release()
411 return cb2.Invoke()
412 })
413 defer cb.Release()
414 if got := cb.Invoke().Int(); got != 42 {
415 t.Errorf("got %#v, want %#v", got, 42)
416 }
417 if !called {
418 t.Error("function not called")
419 }
420 }
421
422 func TestInterleavedFunctions(t *testing.T) {
423 c1 := make(chan struct{})
424 c2 := make(chan struct{})
425
426 js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} {
427 c1 <- struct{}{}
428 <-c2
429 return nil
430 }), 0)
431
432 <-c1
433 c2 <- struct{}{}
434
435 f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
436 return nil
437 })
438 f.Invoke()
439 }
440
441 func ExampleFuncOf() {
442 var cb js.Func
443 cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
444 fmt.Println("button clicked")
445 cb.Release()
446 return nil
447 })
448 js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb)
449 }
450
451
452
453
454
455 func TestTruthy(t *testing.T) {
456 want := true
457 for _, key := range []string{
458 "someBool", "someString", "someInt", "someFloat", "someArray", "someDate",
459 "stringZero",
460 "add",
461 "emptyObj", "emptyArray", "Infinity", "NegInfinity",
462
463 "objNumber0", "objBooleanFalse",
464 } {
465 if got := dummys.Get(key).Truthy(); got != want {
466 t.Errorf("%s: got %#v, want %#v", key, got, want)
467 }
468 }
469
470 want = false
471 if got := dummys.Get("zero").Truthy(); got != want {
472 t.Errorf("got %#v, want %#v", got, want)
473 }
474 if got := dummys.Get("NaN").Truthy(); got != want {
475 t.Errorf("got %#v, want %#v", got, want)
476 }
477 if got := js.ValueOf("").Truthy(); got != want {
478 t.Errorf("got %#v, want %#v", got, want)
479 }
480 if got := js.Null().Truthy(); got != want {
481 t.Errorf("got %#v, want %#v", got, want)
482 }
483 if got := js.Undefined().Truthy(); got != want {
484 t.Errorf("got %#v, want %#v", got, want)
485 }
486 }
487
488 func expectValueError(t *testing.T, fn func()) {
489 defer func() {
490 err := recover()
491 if _, ok := err.(*js.ValueError); !ok {
492 t.Errorf("expected *js.ValueError, got %T", err)
493 }
494 }()
495 fn()
496 }
497
498 func expectPanic(t *testing.T, fn func()) {
499 defer func() {
500 err := recover()
501 if err == nil {
502 t.Errorf("expected panic")
503 }
504 }()
505 fn()
506 }
507
508 var copyTests = []struct {
509 srcLen int
510 dstLen int
511 copyLen int
512 }{
513 {5, 3, 3},
514 {3, 5, 3},
515 {0, 0, 0},
516 }
517
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)
522 if tt.srcLen >= 2 {
523 src.SetIndex(1, 42)
524 }
525 dst := make([]byte, tt.dstLen)
526
527 if got, want := js.CopyBytesToGo(dst, src), tt.copyLen; got != want {
528 t.Errorf("copied %d, want %d", got, want)
529 }
530 if tt.dstLen >= 2 {
531 if got, want := int(dst[1]), 42; got != want {
532 t.Errorf("got %d, want %d", got, want)
533 }
534 }
535 })
536 }
537 }
538
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)
543 if tt.srcLen >= 2 {
544 src[1] = 42
545 }
546 dst := js.Global().Get("Uint8Array").New(tt.dstLen)
547
548 if got, want := js.CopyBytesToJS(dst, src), tt.copyLen; got != want {
549 t.Errorf("copied %d, want %d", got, want)
550 }
551 if tt.dstLen >= 2 {
552 if got, want := dst.Index(1).Int(), 42; got != want {
553 t.Errorf("got %d, want %d", got, want)
554 }
555 }
556 })
557 }
558 }
559
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()
564 runtime.GC()
565 }
566 after := js.JSGo.Get("_values").Length()
567 if after-before > 500 {
568 t.Errorf("garbage collection ineffective")
569 }
570 }
571
572
573
574
575 func BenchmarkDOM(b *testing.B) {
576 document := js.Global().Get("document")
577 if document.IsUndefined() {
578 b.Skip("Not a browser environment. Skipping.")
579 }
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)
587
588 if got, want := myDiv.Get("innerHTML").String(), data; got != want {
589 b.Errorf("got %s, want %s", got, want)
590 }
591 document.Get("body").Call("removeChild", div)
592 }
593 }
594
595 func TestGlobal(t *testing.T) {
596 ident := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
597 return args[0]
598 })
599 defer ident.Release()
600
601 if got := ident.Invoke(js.Global()); !got.Equal(js.Global()) {
602 t.Errorf("got %#v, want %#v", got, js.Global())
603 }
604 }
605
View as plain text