1package goja
2
3import (
4	"testing"
5)
6
7func TestGoMapReflectGetSet(t *testing.T) {
8	const SCRIPT = `
9	m.c = m.a + m.b;
10	`
11
12	vm := New()
13	m := map[string]string{
14		"a": "4",
15		"b": "2",
16	}
17	vm.Set("m", m)
18
19	_, err := vm.RunString(SCRIPT)
20	if err != nil {
21		t.Fatal(err)
22	}
23
24	if c := m["c"]; c != "42" {
25		t.Fatalf("Unexpected value: '%s'", c)
26	}
27}
28
29func TestGoMapReflectIntKey(t *testing.T) {
30	const SCRIPT = `
31	m[2] = m[0] + m[1];
32	`
33
34	vm := New()
35	m := map[int]int{
36		0: 40,
37		1: 2,
38	}
39	vm.Set("m", m)
40
41	_, err := vm.RunString(SCRIPT)
42	if err != nil {
43		t.Fatal(err)
44	}
45
46	if c := m[2]; c != 42 {
47		t.Fatalf("Unexpected value: '%d'", c)
48	}
49}
50
51func TestGoMapReflectDelete(t *testing.T) {
52	const SCRIPT = `
53	delete m.a;
54	`
55
56	vm := New()
57	m := map[string]string{
58		"a": "4",
59		"b": "2",
60	}
61	vm.Set("m", m)
62
63	_, err := vm.RunString(SCRIPT)
64	if err != nil {
65		t.Fatal(err)
66	}
67
68	if _, exists := m["a"]; exists {
69		t.Fatal("a still exists")
70	}
71
72	if b := m["b"]; b != "2" {
73		t.Fatalf("Unexpected b: '%s'", b)
74	}
75}
76
77func TestGoMapReflectJSON(t *testing.T) {
78	const SCRIPT = `
79	function f(m) {
80		return JSON.stringify(m);
81	}
82	`
83
84	vm := New()
85	m := map[string]string{
86		"t": "42",
87	}
88	_, err := vm.RunString(SCRIPT)
89	if err != nil {
90		t.Fatal(err)
91	}
92	f := vm.Get("f")
93	if call, ok := AssertFunction(f); ok {
94		v, err := call(nil, ([]Value{vm.ToValue(m)})...)
95		if err != nil {
96			t.Fatal(err)
97		}
98		if !v.StrictEquals(asciiString(`{"t":"42"}`)) {
99			t.Fatalf("Unexpected value: %v", v)
100		}
101	} else {
102		t.Fatalf("Not a function: %v", f)
103	}
104}
105
106func TestGoMapReflectProto(t *testing.T) {
107	const SCRIPT = `
108	m.hasOwnProperty("t");
109	`
110
111	vm := New()
112	m := map[string]string{
113		"t": "42",
114	}
115	vm.Set("m", m)
116	v, err := vm.RunString(SCRIPT)
117	if err != nil {
118		t.Fatal(err)
119	}
120	if !v.StrictEquals(valueTrue) {
121		t.Fatalf("Expected true, got %v", v)
122	}
123}
124
125type gomapReflect_noMethods map[string]interface{}
126type gomapReflect_withMethods map[string]interface{}
127
128func (m gomapReflect_withMethods) Method() bool {
129	return true
130}
131
132func TestGoMapReflectNoMethods(t *testing.T) {
133	const SCRIPT = `
134	typeof m === "object" && m.hasOwnProperty("t") && m.t === 42;
135	`
136
137	vm := New()
138	m := make(gomapReflect_noMethods)
139	m["t"] = 42
140	vm.Set("m", m)
141	v, err := vm.RunString(SCRIPT)
142	if err != nil {
143		t.Fatal(err)
144	}
145	if !v.StrictEquals(valueTrue) {
146		t.Fatalf("Expected true, got %v", v)
147	}
148
149}
150
151func TestGoMapReflectWithMethods(t *testing.T) {
152	const SCRIPT = `
153	typeof m === "object" && !m.hasOwnProperty("t") && m.hasOwnProperty("Method") && m.Method();
154	`
155
156	vm := New()
157	m := make(gomapReflect_withMethods)
158	m["t"] = 42
159	vm.Set("m", m)
160	v, err := vm.RunString(SCRIPT)
161	if err != nil {
162		t.Fatal(err)
163	}
164	if !v.StrictEquals(valueTrue) {
165		t.Fatalf("Expected true, got %v", v)
166	}
167
168}
169
170func TestGoMapReflectWithProto(t *testing.T) {
171	vm := New()
172	m := map[string]string{
173		"t": "42",
174	}
175	vm.Set("m", m)
176	_, err := vm.RunString(TESTLIB + `
177	(function() {
178	'use strict';
179	var proto = {};
180	var getterAllowed = false;
181	var setterAllowed = false;
182	var tHolder = "proto t";
183	Object.defineProperty(proto, "t", {
184		get: function() {
185			if (!getterAllowed) throw new Error("getter is called");
186			return tHolder;
187		},
188		set: function(v) {
189			if (!setterAllowed) throw new Error("setter is called");
190			tHolder = v;
191		}
192	});
193	var t1Holder;
194	Object.defineProperty(proto, "t1", {
195		get: function() {
196			return t1Holder;
197		},
198		set: function(v) {
199			t1Holder = v;
200		}
201	});
202	Object.setPrototypeOf(m, proto);
203	assert.sameValue(m.t, "42");
204	m.t = 43;
205	assert.sameValue(m.t, "43");
206	t1Holder = "test";
207	assert.sameValue(m.t1, "test");
208	m.t1 = "test1";
209	assert.sameValue(m.t1, "test1");
210	delete m.t;
211	getterAllowed = true;
212	assert.sameValue(m.t, "proto t", "after delete");
213	setterAllowed = true;
214	m.t = true;
215	assert.sameValue(m.t, true, "m.t === true");
216	assert.sameValue(tHolder, true, "tHolder === true");
217	Object.preventExtensions(m);
218	assert.throws(TypeError, function() {
219		m.t2 = 1;
220	});
221	m.t1 = "test2";
222	assert.sameValue(m.t1, "test2");
223	})();
224	`)
225	if err != nil {
226		t.Fatal(err)
227	}
228}
229
230func TestGoMapReflectProtoProp(t *testing.T) {
231	const SCRIPT = `
232	(function() {
233	"use strict";
234	var proto = {};
235	Object.defineProperty(proto, "ro", {value: 42});
236	Object.setPrototypeOf(m, proto);
237	assert.throws(TypeError, function() {
238		m.ro = 43;
239	});
240	Object.defineProperty(m, "ro", {value: 43});
241	assert.sameValue(m.ro, "43");
242	})();
243	`
244
245	r := New()
246	r.Set("m", map[string]string{})
247	_, err := r.RunString(TESTLIB + SCRIPT)
248	if err != nil {
249		t.Fatal(err)
250	}
251}
252
253func TestGoMapReflectUnicode(t *testing.T) {
254	const SCRIPT = `
255	Object.setPrototypeOf(m, s);
256	if (m.Тест !== "passed") {
257		throw new Error("m.Тест: " + m.Тест);
258	}
259	m["é"];
260	`
261	type S struct {
262		Тест string
263	}
264	vm := New()
265	m := map[string]int{
266		"é": 42,
267	}
268	s := S{
269		Тест: "passed",
270	}
271	vm.Set("m", m)
272	vm.Set("s", &s)
273	res, err := vm.RunString(SCRIPT)
274	if err != nil {
275		t.Fatal(err)
276	}
277	if res == nil || !res.StrictEquals(valueInt(42)) {
278		t.Fatalf("Unexpected value: %v", res)
279	}
280}
281