1package goja
2
3import (
4	"testing"
5	"time"
6)
7
8const TESTLIB = `
9function $ERROR(message) {
10	throw new Error(message);
11}
12
13function Test262Error() {
14}
15
16function assert(mustBeTrue, message) {
17    if (mustBeTrue === true) {
18        return;
19    }
20
21    if (message === undefined) {
22        message = 'Expected true but got ' + String(mustBeTrue);
23    }
24    $ERROR(message);
25}
26
27assert._isSameValue = function (a, b) {
28    if (a === b) {
29        // Handle +/-0 vs. -/+0
30        return a !== 0 || 1 / a === 1 / b;
31    }
32
33    // Handle NaN vs. NaN
34    return a !== a && b !== b;
35};
36
37assert.sameValue = function (actual, expected, message) {
38    if (assert._isSameValue(actual, expected)) {
39        return;
40    }
41
42    if (message === undefined) {
43        message = '';
44    } else {
45        message += ' ';
46    }
47
48    message += 'Expected SameValue(«' + String(actual) + '», «' + String(expected) + '») to be true';
49
50    $ERROR(message);
51};
52
53assert.throws = function (expectedErrorConstructor, func, message) {
54  if (typeof func !== "function") {
55    $ERROR('assert.throws requires two arguments: the error constructor ' +
56      'and a function to run');
57    return;
58  }
59  if (message === undefined) {
60    message = '';
61  } else {
62    message += ' ';
63  }
64
65  try {
66    func();
67  } catch (thrown) {
68    if (typeof thrown !== 'object' || thrown === null) {
69      message += 'Thrown value was not an object!';
70      $ERROR(message);
71    } else if (thrown.constructor !== expectedErrorConstructor) {
72      message += 'Expected a ' + expectedErrorConstructor.name + ' but got a ' + thrown.constructor.name;
73      $ERROR(message);
74    }
75    return;
76  }
77
78  message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all';
79  $ERROR(message);
80};
81
82function compareArray(a, b) {
83  if (b.length !== a.length) {
84    return false;
85  }
86
87  for (var i = 0; i < a.length; i++) {
88    if (b[i] !== a[i]) {
89      return false;
90    }
91  }
92  return true;
93}
94`
95
96const TESTLIBX = TESTLIB +
97	`function looksNative(fn) {
98		return /native code/.test(Function.prototype.toString.call(fn));
99	}
100
101	function deepEqual(a, b) {
102		if (typeof a === "object") {
103			if (typeof b === "object") {
104				if (a === b) {
105					return true;
106				}
107				if (Reflect.getPrototypeOf(a) !== Reflect.getPrototypeOf(b)) {
108					return false;
109				}
110				var keysA = Object.keys(a);
111				var keysB = Object.keys(b);
112				if (keysA.length !== keysB.length) {
113					return false;
114				}
115				if (!compareArray(keysA.sort(), keysB.sort())) {
116					return false;
117				}
118				for (var i = 0; i < keysA.length; i++) {
119					var key = keysA[i];
120					if (!deepEqual(a[key], b[key])) {
121						return false;
122					}
123				}
124				return true;
125			} else {
126				return false;
127			}
128		}
129		return assert._isSameValue(a, b);
130	}
131`
132
133func TestDateUTC(t *testing.T) {
134	const SCRIPT = `
135	assert.sameValue(Date.UTC(1970, 0), 0, '1970, 0');
136	assert.sameValue(Date.UTC(2016, 0), 1451606400000, '2016, 0');
137	assert.sameValue(Date.UTC(2016, 6), 1467331200000, '2016, 6');
138
139	assert.sameValue(Date.UTC(2016, 6, 1), 1467331200000, '2016, 6, 1');
140	assert.sameValue(Date.UTC(2016, 6, 5), 1467676800000, '2016, 6, 5');
141
142	assert.sameValue(Date.UTC(2016, 6, 5, 0), 1467676800000, '2016, 6, 5, 0');
143	assert.sameValue(Date.UTC(2016, 6, 5, 15), 1467730800000, '2016, 6, 5, 15');
144
145	assert.sameValue(
146  		Date.UTC(2016, 6, 5, 15, 0), 1467730800000, '2016, 6, 5, 15, 0'
147	);
148	assert.sameValue(
149  		Date.UTC(2016, 6, 5, 15, 34), 1467732840000, '2016, 6, 5, 15, 34'
150	);
151
152	assert.sameValue(
153  		Date.UTC(2016, 6, 5, 15, 34, 0), 1467732840000, '2016, 6, 5, 15, 34, 0'
154	);
155	assert.sameValue(
156  		Date.UTC(2016, 6, 5, 15, 34, 45), 1467732885000, '2016, 6, 5, 15, 34, 45'
157	);
158
159	`
160
161	testScript1(TESTLIB+SCRIPT, _undefined, t)
162}
163
164func TestNewDate(t *testing.T) {
165	const SCRIPT = `
166	var d1 = new Date("2016-09-01T12:34:56Z");
167	d1.getUTCHours() === 12;
168
169	`
170	testScript1(SCRIPT, valueTrue, t)
171}
172
173func TestNewDate0(t *testing.T) {
174	const SCRIPT = `
175	(new Date(0)).toUTCString();
176
177	`
178	testScript1(SCRIPT, asciiString("Thu, 01 Jan 1970 00:00:00 GMT"), t)
179}
180
181func TestSetHour(t *testing.T) {
182	l := time.Local
183	defer func() {
184		time.Local = l
185	}()
186	var err error
187	time.Local, err = time.LoadLocation("America/New_York")
188	if err != nil {
189		t.Fatal(err)
190	}
191
192	const SCRIPT = `
193	var d = new Date(2016, 8, 1, 12, 23, 45)
194	assert.sameValue(d.getHours(), 12);
195	assert.sameValue(d.getUTCHours(), 16);
196
197	d.setHours(13);
198	assert.sameValue(d.getHours(), 13);
199	assert.sameValue(d.getMinutes(), 23);
200	assert.sameValue(d.getSeconds(), 45);
201
202	d.setUTCHours(13);
203	assert.sameValue(d.getHours(), 9);
204	assert.sameValue(d.getMinutes(), 23);
205	assert.sameValue(d.getSeconds(), 45);
206
207	`
208	testScript1(TESTLIB+SCRIPT, _undefined, t)
209
210}
211
212func TestSetMinute(t *testing.T) {
213	l := time.Local
214	defer func() {
215		time.Local = l
216	}()
217	time.Local = time.FixedZone("Asia/Delhi", 5*60*60+30*60)
218
219	const SCRIPT = `
220	var d = new Date(2016, 8, 1, 12, 23, 45)
221	assert.sameValue(d.getHours(), 12);
222	assert.sameValue(d.getUTCHours(), 6);
223	assert.sameValue(d.getMinutes(), 23);
224	assert.sameValue(d.getUTCMinutes(), 53);
225
226	d.setMinutes(55);
227	assert.sameValue(d.getMinutes(), 55);
228	assert.sameValue(d.getSeconds(), 45);
229
230	d.setUTCMinutes(52);
231	assert.sameValue(d.getMinutes(), 22);
232	assert.sameValue(d.getHours(), 13);
233
234	`
235	testScript1(TESTLIB+SCRIPT, _undefined, t)
236
237}
238
239func TestTimezoneOffset(t *testing.T) {
240	const SCRIPT = `
241	var d = new Date(0);
242	d.getTimezoneOffset();
243	`
244
245	l := time.Local
246	defer func() {
247		time.Local = l
248	}()
249	var err error
250	time.Local, err = time.LoadLocation("Europe/London")
251	if err != nil {
252		t.Fatal(err)
253	}
254
255	testScript1(SCRIPT, intToValue(-60), t)
256}
257
258func TestDateValueOf(t *testing.T) {
259	const SCRIPT = `
260	var d9 = new Date(1.23e15);
261	d9.valueOf();
262	`
263
264	testScript1(SCRIPT, intToValue(1.23e15), t)
265}
266
267func TestDateSetters(t *testing.T) {
268	const SCRIPT = `
269	assert.sameValue((new Date(0)).setMilliseconds(2345), 2345, "setMilliseconds(2345)");
270	assert.sameValue(new Date(1000).setMilliseconds(23450000000000), 23450000001000, "setMilliseconds(23450000000000)");
271	assert.sameValue((new Date(0)).setUTCMilliseconds(2345), 2345, "setUTCMilliseconds()");
272	assert.sameValue((new Date(0)).setSeconds(12), 12000, "setSeconds()");
273	assert.sameValue((new Date(0)).setUTCSeconds(12), 12000, "setUTCSeconds()");
274	assert.sameValue((new Date(0)).setMinutes(12), 12 * 60 * 1000, "setMinutes()");
275	assert.sameValue((new Date(0)).setUTCMinutes(12), 12 * 60 * 1000, "setUTCMinutes()");
276	assert.sameValue((new Date("2016-06-01")).setHours(1), 1464739200000, "setHours()");
277	assert.sameValue((new Date("2016-06-01")).setUTCHours(1), 1464742800000, "setUTCHours()");
278	assert.sameValue((new Date(0)).setDate(2), 86400000, "setDate()");
279	assert.sameValue((new Date(0)).setUTCDate(2), 86400000, "setUTCDate()");
280	assert.sameValue((new Date(0)).setMonth(2), 5097600000, "setMonth()");
281	assert.sameValue((new Date(0)).setUTCMonth(2), 5097600000, "setUTCMonth()");
282	assert.sameValue((new Date(0)).setFullYear(1971), 31536000000, "setFullYear()");
283	assert.sameValue((new Date(0)).setFullYear(1971, 2, 3), 36806400000, "setFullYear(Y,M,D)");
284	assert.sameValue((new Date(0)).setUTCFullYear(1971), 31536000000, "setUTCFullYear()");
285	assert.sameValue((new Date(0)).setUTCFullYear(1971, 2, 3), 36806400000, "setUTCFullYear(Y,M,D)");
286
287	var d = new Date();
288	d.setTime(1151877845000);
289	assert.sameValue(d.getHours(), 23, "d.getHours()");
290	`
291
292	l := time.Local
293	defer func() {
294		time.Local = l
295	}()
296	var err error
297	time.Local, err = time.LoadLocation("Europe/London")
298	if err != nil {
299		t.Fatal(err)
300	}
301
302	testScript1(TESTLIB+SCRIPT, _undefined, t)
303}
304
305func TestDateParse(t *testing.T) {
306	const SCRIPT = `
307	var zero = new Date(0);
308
309	assert.sameValue(zero.valueOf(), Date.parse(zero.toString()),
310					 "Date.parse(zeroDate.toString())");
311	assert.sameValue(zero.valueOf(), Date.parse(zero.toUTCString()),
312					 "Date.parse(zeroDate.toUTCString())");
313	assert.sameValue(zero.valueOf(), Date.parse(zero.toISOString()),
314					 "Date.parse(zeroDate.toISOString())");
315
316	function testParse(str, expected) {
317		assert.sameValue(Date.parse(str), expected, str);
318	}
319
320	testParse("Mon, 02 Jan 2006 15:04:05 MST",							1136239445000);
321	testParse("Tue, 22 Jun 2021 13:54:40 GMT",							1624370080000);
322	testParse("Tuesday, 22 Jun 2021 13:54:40 GMT",						1624370080000);
323	testParse("Mon, 02 Jan 2006 15:04:05 GMT-07:00 (MST)",				1136239445000);
324	testParse("Mon, 02 Jan 2006 15:04:05 -07:00 (MST)",					1136239445000);
325	testParse("Monday, 02 Jan 2006 15:04:05 -0700 (MST)",				1136239445000);
326	testParse("Mon Jan 02 2006 15:04:05 GMT-0700 (GMT Standard Time)",	1136239445000);
327	testParse("Mon Jan 2 15:04:05 MST 2006",							1136239445000);
328	testParse("Mon Jan 02 15:04:05 MST 2006",							1136239445000);
329	testParse("Mon Jan 02 15:04:05 -0700 2006",							1136239445000);
330
331	testParse("December 04, 1986",	534038400000);
332	testParse("Dec 04, 1986",		534038400000);
333	testParse("Dec 4, 1986",		534038400000);
334
335	testParse("2006-01-02T15:04:05.000Z",	1136214245000);
336	testParse("2006-06-02T15:04:05.000",	1149275045000);
337	testParse("2006-01-02T15:04:05",		1136232245000);
338	testParse("2006-01-02",					1136160000000);
339	testParse("2006T15:04-0700",			1136153040000);
340	testParse("2006T15:04Z",				1136127840000);
341	testParse("2019-01-01T12:00:00.52Z",	1546344000520);
342
343	var d = new Date("Mon, 02 Jan 2006 15:04:05 MST");
344
345	assert.sameValue(d.getUTCHours(), 22,
346					"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getUTCHours()");
347
348	assert.sameValue(d.getHours(), 17,
349					"new Date(\"Mon, 02 Jan 2006 15:04:05 MST\").getHours()");
350
351	assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 zzz"), NaN,
352					 "Date.parse(\"Mon, 02 Jan 2006 15:04:05 zzz\")");
353
354	assert.sameValue(Date.parse("Mon, 02 Jan 2006 15:04:05 ZZZ"), NaN,
355					 "Date.parse(\"Mon, 02 Jan 2006 15:04:05 ZZZ\")");
356
357	var minDateStr = "-271821-04-20T00:00:00.000Z";
358	var minDate = new Date(-8640000000000000);
359
360	assert.sameValue(minDate.toISOString(), minDateStr, "minDateStr");
361	assert.sameValue(Date.parse(minDateStr), minDate.valueOf(), "parse minDateStr");
362
363	var maxDateStr = "+275760-09-13T00:00:00.000Z";
364	var maxDate = new Date(8640000000000000);
365
366	assert.sameValue(maxDate.toISOString(), maxDateStr, "maxDateStr");
367	assert.sameValue(Date.parse(maxDateStr), maxDate.valueOf(), "parse maxDateStr");
368
369	var belowRange = "-271821-04-19T23:59:59.999Z";
370	var aboveRange = "+275760-09-13T00:00:00.001Z";
371
372	assert.sameValue(Date.parse(belowRange), NaN, "parse below minimum time value");
373	assert.sameValue(Date.parse(aboveRange), NaN, "parse above maximum time value");
374	`
375
376	l := time.Local
377	defer func() {
378		time.Local = l
379	}()
380	var err error
381	time.Local, err = time.LoadLocation("America/New_York")
382	if err != nil {
383		t.Fatal(err)
384	}
385
386	testScript1(TESTLIB+SCRIPT, _undefined, t)
387}
388
389func TestDateMaxValues(t *testing.T) {
390	const SCRIPT = `
391	assert.sameValue((new Date(0)).setUTCMilliseconds(8.64e15), 8.64e15);
392	assert.sameValue((new Date(0)).setUTCSeconds(8640000000000), 8.64e15);
393	assert.sameValue((new Date(0)).setUTCMilliseconds(-8.64e15), -8.64e15);
394	assert.sameValue((new Date(0)).setUTCSeconds(-8640000000000), -8.64e15);
395	`
396	testScript1(TESTLIB+SCRIPT, _undefined, t)
397}
398
399func TestDateExport(t *testing.T) {
400	vm := New()
401	res, err := vm.RunString(`new Date(1000)`)
402	if err != nil {
403		t.Fatal(err)
404	}
405	exp := res.Export()
406	if d, ok := exp.(time.Time); ok {
407		if d.UnixNano()/1e6 != 1000 {
408			t.Fatalf("Invalid exported date: %v", d)
409		}
410		if loc := d.Location(); loc != time.Local {
411			t.Fatalf("Invalid timezone: %v", loc)
412		}
413	} else {
414		t.Fatalf("Invalid export type: %T", exp)
415	}
416}
417
418func TestDateToJSON(t *testing.T) {
419	const SCRIPT = `
420	Date.prototype.toJSON.call({ toISOString: function () { return 1; } })
421	`
422	testScript1(SCRIPT, intToValue(1), t)
423}
424