package goja import ( "encoding/json" "errors" "strings" "testing" "time" ) func TestJSONMarshalObject(t *testing.T) { vm := New() o := vm.NewObject() o.Set("test", 42) o.Set("testfunc", vm.Get("Error")) b, err := json.Marshal(o) if err != nil { t.Fatal(err) } if string(b) != `{"test":42}` { t.Fatalf("Unexpected value: %s", b) } } func TestJSONMarshalGoDate(t *testing.T) { vm := New() o := vm.NewObject() o.Set("test", time.Unix(86400, 0).UTC()) b, err := json.Marshal(o) if err != nil { t.Fatal(err) } if string(b) != `{"test":"1970-01-02T00:00:00Z"}` { t.Fatalf("Unexpected value: %s", b) } } func TestJSONMarshalObjectCircular(t *testing.T) { vm := New() o := vm.NewObject() o.Set("o", o) _, err := json.Marshal(o) if err == nil { t.Fatal("Expected error") } if !strings.HasSuffix(err.Error(), "Converting circular structure to JSON") { t.Fatalf("Unexpected error: %v", err) } } func TestJSONStringifyCircularWrappedGo(t *testing.T) { type CircularType struct { Self *CircularType } vm := New() v := CircularType{} v.Self = &v vm.Set("v", &v) _, err := vm.RunString("JSON.stringify(v)") if err == nil { t.Fatal("Expected error") } if !strings.HasPrefix(err.Error(), "TypeError: Converting circular structure to JSON") { t.Fatalf("Unexpected error: %v", err) } } func TestJSONParseReviver(t *testing.T) { // example from // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse const SCRIPT = ` JSON.parse('{"p": 5}', function(key, value) { return typeof value === 'number' ? value * 2 // return value * 2 for numbers : value // return everything else unchanged })["p"] ` testScript(SCRIPT, intToValue(10), t) } func TestQuoteMalformedSurrogatePair(t *testing.T) { testScript(`JSON.stringify("\uD800")`, asciiString(`"\ud800"`), t) } func TestEOFWrapping(t *testing.T) { vm := New() _, err := vm.RunString("JSON.parse('{')") if err == nil { t.Fatal("Expected error") } if !strings.Contains(err.Error(), "Unexpected end of JSON input") { t.Fatalf("Error doesn't contain human-friendly wrapper: %v", err) } } type testMarshalJSONErrorStruct struct { e error } func (s *testMarshalJSONErrorStruct) MarshalJSON() ([]byte, error) { return nil, s.e } func TestMarshalJSONError(t *testing.T) { vm := New() v := testMarshalJSONErrorStruct{e: errors.New("test error")} vm.Set("v", &v) _, err := vm.RunString("JSON.stringify(v)") if !errors.Is(err, v.e) { t.Fatalf("Unexpected error: %v", err) } } func BenchmarkJSONStringify(b *testing.B) { b.StopTimer() vm := New() var createObj func(level int) *Object createObj = func(level int) *Object { o := vm.NewObject() o.Set("field1", "test") o.Set("field2", 42) if level > 0 { level-- o.Set("obj1", createObj(level)) o.Set("obj2", createObj(level)) } return o } o := createObj(3) json := vm.Get("JSON").(*Object) stringify, _ := AssertFunction(json.Get("stringify")) b.StartTimer() for i := 0; i < b.N; i++ { stringify(nil, o) } }