1package goja
2
3import "testing"
4
5func TestGomapProp(t *testing.T) {
6	const SCRIPT = `
7	o.a + o.b;
8	`
9	r := New()
10	r.Set("o", map[string]interface{}{
11		"a": 40,
12		"b": 2,
13	})
14	v, err := r.RunString(SCRIPT)
15	if err != nil {
16		t.Fatal(err)
17	}
18	if i := v.ToInteger(); i != 42 {
19		t.Fatalf("Expected 42, got: %d", i)
20	}
21}
22
23func TestGomapEnumerate(t *testing.T) {
24	const SCRIPT = `
25	var hasX = false;
26	var hasY = false;
27	for (var key in o) {
28		switch (key) {
29		case "x":
30			if (hasX) {
31				throw "Already have x";
32			}
33			hasX = true;
34			break;
35		case "y":
36			if (hasY) {
37				throw "Already have y";
38			}
39			hasY = true;
40			break;
41		default:
42			throw "Unexpected property: " + key;
43		}
44	}
45	hasX && hasY;
46	`
47	r := New()
48	r.Set("o", map[string]interface{}{
49		"x": 40,
50		"y": 2,
51	})
52	v, err := r.RunString(SCRIPT)
53	if err != nil {
54		t.Fatal(err)
55	}
56
57	if !v.StrictEquals(valueTrue) {
58		t.Fatalf("Expected true, got %v", v)
59	}
60}
61
62func TestGomapDeleteWhileEnumerate(t *testing.T) {
63	const SCRIPT = `
64	var hasX = false;
65	var hasY = false;
66	for (var key in o) {
67		switch (key) {
68		case "x":
69			if (hasX) {
70				throw "Already have x";
71			}
72			hasX = true;
73			delete o.y;
74			break;
75		case "y":
76			if (hasY) {
77				throw "Already have y";
78			}
79			hasY = true;
80			delete o.x;
81			break;
82		default:
83			throw "Unexpected property: " + key;
84		}
85	}
86	hasX && !hasY || hasY && !hasX;
87	`
88	r := New()
89	r.Set("o", map[string]interface{}{
90		"x": 40,
91		"y": 2,
92	})
93	v, err := r.RunString(SCRIPT)
94	if err != nil {
95		t.Fatal(err)
96	}
97
98	if !v.StrictEquals(valueTrue) {
99		t.Fatalf("Expected true, got %v", v)
100	}
101}
102
103func TestGomapInstanceOf(t *testing.T) {
104	const SCRIPT = `
105	(o instanceof Object) && !(o instanceof Error);
106	`
107	r := New()
108	r.Set("o", map[string]interface{}{})
109	v, err := r.RunString(SCRIPT)
110	if err != nil {
111		t.Fatal(err)
112	}
113
114	if !v.StrictEquals(valueTrue) {
115		t.Fatalf("Expected true, got %v", v)
116	}
117}
118
119func TestGomapTypeOf(t *testing.T) {
120	const SCRIPT = `
121	typeof o;
122	`
123	r := New()
124	r.Set("o", map[string]interface{}{})
125	v, err := r.RunString(SCRIPT)
126	if err != nil {
127		t.Fatal(err)
128	}
129
130	if !v.StrictEquals(asciiString("object")) {
131		t.Fatalf("Expected object, got %v", v)
132	}
133}
134
135func TestGomapProto(t *testing.T) {
136	const SCRIPT = `
137	o.hasOwnProperty("test");
138	`
139	r := New()
140	r.Set("o", map[string]interface{}{
141		"test": 42,
142	})
143	v, err := r.RunString(SCRIPT)
144	if err != nil {
145		t.Fatal(err)
146	}
147
148	if !v.StrictEquals(valueTrue) {
149		t.Fatalf("Expected true, got %v", v)
150	}
151}
152
153func TestGoMapExtensibility(t *testing.T) {
154	const SCRIPT = `
155	"use strict";
156	o.test = 42;
157	Object.preventExtensions(o);
158	o.test = 43;
159	try {
160		o.test1 = 42;
161	} catch (e) {
162		if (!(e instanceof TypeError)) {
163			throw e;
164		}
165	}
166	o.test === 43 && o.test1 === undefined;
167	`
168
169	r := New()
170	r.Set("o", map[string]interface{}{})
171	v, err := r.RunString(SCRIPT)
172	if err != nil {
173		if ex, ok := err.(*Exception); ok {
174			t.Fatal(ex.String())
175		} else {
176			t.Fatal(err)
177		}
178	}
179
180	if !v.StrictEquals(valueTrue) {
181		t.Fatalf("Expected true, got %v", v)
182	}
183
184}
185
186func TestGoMapWithProto(t *testing.T) {
187	vm := New()
188	m := map[string]interface{}{
189		"t": "42",
190	}
191	vm.Set("m", m)
192	_, err := vm.RunString(TESTLIB + `
193	(function() {
194	'use strict';
195	var proto = {};
196	var getterAllowed = false;
197	var setterAllowed = false;
198	var tHolder = "proto t";
199	Object.defineProperty(proto, "t", {
200		get: function() {
201			if (!getterAllowed) throw new Error("getter is called");
202			return tHolder;
203		},
204		set: function(v) {
205			if (!setterAllowed) throw new Error("setter is called");
206			tHolder = v;
207		}
208	});
209	var t1Holder;
210	Object.defineProperty(proto, "t1", {
211		get: function() {
212			return t1Holder;
213		},
214		set: function(v) {
215			t1Holder = v;
216		}
217	});
218	Object.setPrototypeOf(m, proto);
219	assert.sameValue(m.t, "42");
220	m.t = 43;
221	assert.sameValue(m.t, 43);
222	t1Holder = "test";
223	assert.sameValue(m.t1, "test");
224	m.t1 = "test1";
225	assert.sameValue(m.t1, "test1");
226	delete m.t;
227	getterAllowed = true;
228	assert.sameValue(m.t, "proto t", "after delete");
229	setterAllowed = true;
230	m.t = true;
231	assert.sameValue(m.t, true);
232	assert.sameValue(tHolder, true);
233	Object.preventExtensions(m);
234	assert.throws(TypeError, function() {
235		m.t2 = 1;
236	});
237	m.t1 = "test2";
238	assert.sameValue(m.t1, "test2");
239	})();
240	`)
241	if err != nil {
242		t.Fatal(err)
243	}
244}
245
246func TestGoMapProtoProp(t *testing.T) {
247	const SCRIPT = `
248	(function() {
249	"use strict";
250	var proto = {};
251	Object.defineProperty(proto, "ro", {value: 42});
252	Object.setPrototypeOf(m, proto);
253	assert.throws(TypeError, function() {
254		m.ro = 43;
255	});
256	Object.defineProperty(m, "ro", {value: 43});
257	assert.sameValue(m.ro, 43);
258	})();
259	`
260
261	r := New()
262	r.Set("m", map[string]interface{}{})
263	_, err := r.RunString(TESTLIB + SCRIPT)
264	if err != nil {
265		t.Fatal(err)
266	}
267}
268
269func TestGoMapProtoPropChain(t *testing.T) {
270	const SCRIPT = `
271	(function() {
272	"use strict";
273	var p1 = Object.create(null);
274	m.__proto__ = p1;
275
276	Object.defineProperty(p1, "test", {
277		value: 42
278	});
279
280	Object.defineProperty(m, "test", {
281		value: 43,
282		writable: true,
283	});
284	var o = Object.create(m);
285	o.test = 44;
286	assert.sameValue(o.test, 44);
287
288	var sym = Symbol(true);
289	Object.defineProperty(p1, sym, {
290		value: 42
291	});
292
293	Object.defineProperty(m, sym, {
294		value: 43,
295		writable: true,
296	});
297	o[sym] = 44;
298	assert.sameValue(o[sym], 44);
299	})();
300	`
301
302	r := New()
303	r.Set("m", map[string]interface{}{})
304	_, err := r.RunString(TESTLIB + SCRIPT)
305	if err != nil {
306		t.Fatal(err)
307	}
308}
309
310func TestGoMapUnicode(t *testing.T) {
311	const SCRIPT = `
312	Object.setPrototypeOf(m, s);
313	if (m.Тест !== "passed") {
314		throw new Error("m.Тест: " + m.Тест);
315	}
316	m["é"];
317	`
318	type S struct {
319		Тест string
320	}
321	vm := New()
322	m := map[string]interface{}{
323		"é": 42,
324	}
325	s := S{
326		Тест: "passed",
327	}
328	vm.Set("m", m)
329	vm.Set("s", &s)
330	res, err := vm.RunString(SCRIPT)
331	if err != nil {
332		t.Fatal(err)
333	}
334	if res == nil || !res.StrictEquals(valueInt(42)) {
335		t.Fatalf("Unexpected value: %v", res)
336	}
337}
338