Bug 1921522: Mark WPTs gradient-external-reference.svg and pattern-external-reference...
[gecko.git] / js / src / tests / test262 / intl402 / Temporal / PlainYearMonth / compare / shell.js
blob0a2a0a4cd77e8e6a1ff5e6daa3d4b10b5f42ce84
1 // GENERATED, DO NOT EDIT
2 // file: temporalHelpers.js
3 // Copyright (C) 2021 Igalia, S.L. All rights reserved.
4 // This code is governed by the BSD license found in the LICENSE file.
5 /*---
6 description: |
7     This defines helper objects and functions for testing Temporal.
8 defines: [TemporalHelpers]
9 features: [Symbol.species, Symbol.iterator, Temporal]
10 ---*/
12 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
14 function formatPropertyName(propertyKey, objectName = "") {
15   switch (typeof propertyKey) {
16     case "symbol":
17       if (Symbol.keyFor(propertyKey) !== undefined) {
18         return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
19       } else if (propertyKey.description.startsWith('Symbol.')) {
20         return `${objectName}[${propertyKey.description}]`;
21       } else {
22         return `${objectName}[Symbol('${propertyKey.description}')]`
23       }
24     case "string":
25       if (propertyKey !== String(Number(propertyKey))) {
26         if (ASCII_IDENTIFIER.test(propertyKey)) {
27           return objectName ? `${objectName}.${propertyKey}` : propertyKey;
28         }
29         return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
30       }
31       // fall through
32     default:
33       // integer or string integer-index
34       return `${objectName}[${propertyKey}]`;
35   }
38 const SKIP_SYMBOL = Symbol("Skip");
40 var TemporalHelpers = {
41   /*
42    * Codes and maximum lengths of months in the ISO 8601 calendar.
43    */
44   ISOMonths: [
45     { month: 1, monthCode: "M01", daysInMonth: 31 },
46     { month: 2, monthCode: "M02", daysInMonth: 29 },
47     { month: 3, monthCode: "M03", daysInMonth: 31 },
48     { month: 4, monthCode: "M04", daysInMonth: 30 },
49     { month: 5, monthCode: "M05", daysInMonth: 31 },
50     { month: 6, monthCode: "M06", daysInMonth: 30 },
51     { month: 7, monthCode: "M07", daysInMonth: 31 },
52     { month: 8, monthCode: "M08", daysInMonth: 31 },
53     { month: 9, monthCode: "M09", daysInMonth: 30 },
54     { month: 10, monthCode: "M10", daysInMonth: 31 },
55     { month: 11, monthCode: "M11", daysInMonth: 30 },
56     { month: 12, monthCode: "M12", daysInMonth: 31 }
57   ],
59   /*
60    * List of known calendar eras and their possible aliases.
61    *
62    * https://tc39.es/proposal-intl-era-monthcode/#table-eras
63    */
64   CalendarEras: {
65     buddhist: [
66       { era: "buddhist", aliases: ["be"] },
67     ],
68     chinese: [
69       { era: "chinese" },
70     ],
71     coptic: [
72       { era: "coptic" },
73       { era: "coptic-inverse" },
74     ],
75     dangi: [
76       { era: "dangi" },
77     ],
78     ethiopic: [
79       { era: "ethiopic", aliases: ["incar"] },
80       { era: "ethioaa", aliases: ["ethiopic-amete-alem", "mundi"] },
81     ],
82     ethioaa: [
83       { era: "ethioaa", aliases: ["ethiopic-amete-alem", "mundi"] },
84     ],
85     gregory: [
86       { era: "gregory", aliases: ["ce", "ad"] },
87       { era: "gregory-inverse", aliases: ["bc", "bce"] },
88     ],
89     hebrew: [
90       { era: "hebrew", aliases: ["am"] },
91     ],
92     indian: [
93       { era: "indian", aliases: ["saka"] },
94     ],
95     islamic: [
96       { era: "islamic", aliases: ["ah"] },
97     ],
98     "islamic-civil": [
99       { era: "islamic-civil", aliases: ["islamicc", "ah"] },
100     ],
101     "islamic-rgsa": [
102       { era: "islamic-rgsa", aliases: ["ah"] },
103     ],
104     "islamic-tbla": [
105       { era: "islamic-tbla", aliases: ["ah"] },
106     ],
107     "islamic-umalqura": [
108       { era: "islamic-umalqura", aliases: ["ah"] },
109     ],
110     japanese: [
111       { era: "heisei" },
112       { era: "japanese", aliases: ["gregory", "ad", "ce"] },
113       { era: "japanese-inverse", aliases: ["gregory-inverse", "bc", "bce"] },
114       { era: "meiji" },
115       { era: "reiwa" },
116       { era: "showa" },
117       { era: "taisho" },
118     ],
119     persian: [
120       { era: "persian", aliases: ["ap"] },
121     ],
122     roc: [
123       { era: "roc", aliases: ["minguo"] },
124       { era: "roc-inverse", aliases: ["before-roc"] },
125     ],
126   },
128   /*
129    * Return the canonical era code.
130    */
131   canonicalizeCalendarEra(calendarId, eraName) {
132     assert.sameValue(typeof calendarId, "string", "calendar must be string in canonicalizeCalendarEra");
134     if (calendarId === "iso8601") {
135       assert.sameValue(eraName, undefined);
136       return undefined;
137     }
138     assert(Object.hasOwn(TemporalHelpers.CalendarEras, calendarId));
140     if (eraName === undefined) {
141       return undefined;
142     }
143     assert.sameValue(typeof eraName, "string", "eraName must be string or undefined in canonicalizeCalendarEra");
145     for (let {era, aliases = []} of TemporalHelpers.CalendarEras[calendarId]) {
146       if (era === eraName || aliases.includes(eraName)) {
147         return era;
148       }
149     }
150     throw new Test262Error(`Unsupported era name: ${eraName}`);
151   },
153   /*
154    * assertDuration(duration, years, ...,  nanoseconds[, description]):
155    *
156    * Shorthand for asserting that each field of a Temporal.Duration is equal to
157    * an expected value.
158    */
159   assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
160     const prefix = description ? `${description}: ` : "";
161     assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
162     assert.sameValue(duration.years, years, `${prefix}years result:`);
163     assert.sameValue(duration.months, months, `${prefix}months result:`);
164     assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
165     assert.sameValue(duration.days, days, `${prefix}days result:`);
166     assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
167     assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
168     assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
169     assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
170     assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
171     assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
172   },
174   /*
175    * assertDateDuration(duration, years, months, weeks, days, [, description]):
176    *
177    * Shorthand for asserting that each date field of a Temporal.Duration is
178    * equal to an expected value.
179    */
180   assertDateDuration(duration, years, months, weeks, days, description = "") {
181     const prefix = description ? `${description}: ` : "";
182     assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
183     assert.sameValue(duration.years, years, `${prefix}years result:`);
184     assert.sameValue(duration.months, months, `${prefix}months result:`);
185     assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
186     assert.sameValue(duration.days, days, `${prefix}days result:`);
187     assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
188     assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
189     assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
190     assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
191     assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
192     assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
193   },
195   /*
196    * assertDurationsEqual(actual, expected[, description]):
197    *
198    * Shorthand for asserting that each field of a Temporal.Duration is equal to
199    * the corresponding field in another Temporal.Duration.
200    */
201   assertDurationsEqual(actual, expected, description = "") {
202     const prefix = description ? `${description}: ` : "";
203     assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
204     TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
205   },
207   /*
208    * assertInstantsEqual(actual, expected[, description]):
209    *
210    * Shorthand for asserting that two Temporal.Instants are of the correct type
211    * and equal according to their equals() methods.
212    */
213   assertInstantsEqual(actual, expected, description = "") {
214     const prefix = description ? `${description}: ` : "";
215     assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
216     assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
217     assert(actual.equals(expected), `${prefix}equals method`);
218   },
220   /*
221    * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
222    *
223    * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
224    * an expected value. (Except the `calendar` property, since callers may want
225    * to assert either object equality with an object they put in there, or the
226    * value of date.calendarId.)
227    */
228   assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
229     const prefix = description ? `${description}: ` : "";
230     assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
231     assert.sameValue(
232       TemporalHelpers.canonicalizeCalendarEra(date.calendarId, date.era),
233       TemporalHelpers.canonicalizeCalendarEra(date.calendarId, era),
234       `${prefix}era result:`
235     );
236     assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
237     assert.sameValue(date.year, year, `${prefix}year result:`);
238     assert.sameValue(date.month, month, `${prefix}month result:`);
239     assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
240     assert.sameValue(date.day, day, `${prefix}day result:`);
241   },
243   /*
244    * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
245    *
246    * Shorthand for asserting that each field of a Temporal.PlainDateTime is
247    * equal to an expected value. (Except the `calendar` property, since callers
248    * may want to assert either object equality with an object they put in there,
249    * or the value of datetime.calendarId.)
250    */
251   assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
252     const prefix = description ? `${description}: ` : "";
253     assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
254     assert.sameValue(
255       TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, datetime.era),
256       TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, era),
257       `${prefix}era result:`
258     );
259     assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
260     assert.sameValue(datetime.year, year, `${prefix}year result:`);
261     assert.sameValue(datetime.month, month, `${prefix}month result:`);
262     assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
263     assert.sameValue(datetime.day, day, `${prefix}day result:`);
264     assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
265     assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
266     assert.sameValue(datetime.second, second, `${prefix}second result:`);
267     assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
268     assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
269     assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
270   },
272   /*
273    * assertPlainDateTimesEqual(actual, expected[, description]):
274    *
275    * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
276    * type, equal according to their equals() methods, and additionally that
277    * their calendar internal slots are the same value.
278    */
279   assertPlainDateTimesEqual(actual, expected, description = "") {
280     const prefix = description ? `${description}: ` : "";
281     assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
282     assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
283     assert(actual.equals(expected), `${prefix}equals method`);
284     assert.sameValue(
285       actual.calendarId,
286       expected.calendarId,
287       `${prefix}calendar same value:`
288     );
289   },
291   /*
292    * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
293    *
294    * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
295    * equal to an expected value. (Except the `calendar` property, since callers
296    * may want to assert either object equality with an object they put in there,
297    * or the value of monthDay.calendarId().)
298    */
299   assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
300     const prefix = description ? `${description}: ` : "";
301     assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
302     assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
303     assert.sameValue(monthDay.day, day, `${prefix}day result:`);
304     const isoYear = Number(monthDay.toString({ calendarName: "always" }).split("-")[0]);
305     assert.sameValue(isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
306   },
308   /*
309    * assertPlainTime(time, hour, ..., nanosecond[, description]):
310    *
311    * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
312    * an expected value.
313    */
314   assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
315     const prefix = description ? `${description}: ` : "";
316     assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
317     assert.sameValue(time.hour, hour, `${prefix}hour result:`);
318     assert.sameValue(time.minute, minute, `${prefix}minute result:`);
319     assert.sameValue(time.second, second, `${prefix}second result:`);
320     assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
321     assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
322     assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
323   },
325   /*
326    * assertPlainTimesEqual(actual, expected[, description]):
327    *
328    * Shorthand for asserting that two Temporal.PlainTimes are of the correct
329    * type and equal according to their equals() methods.
330    */
331   assertPlainTimesEqual(actual, expected, description = "") {
332     const prefix = description ? `${description}: ` : "";
333     assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
334     assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
335     assert(actual.equals(expected), `${prefix}equals method`);
336   },
338   /*
339    * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
340    *
341    * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
342    * equal to an expected value. (Except the `calendar` property, since callers
343    * may want to assert either object equality with an object they put in there,
344    * or the value of yearMonth.calendarId.)
345    */
346   assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
347     const prefix = description ? `${description}: ` : "";
348     assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
349     assert.sameValue(
350       TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, yearMonth.era),
351       TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, era),
352       `${prefix}era result:`
353     );
354     assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
355     assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
356     assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
357     assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
358     const isoDay = Number(yearMonth.toString({ calendarName: "always" }).slice(1).split('-')[2].slice(0, 2));
359     assert.sameValue(isoDay, referenceISODay, `${prefix}referenceISODay result:`);
360   },
362   /*
363    * assertZonedDateTimesEqual(actual, expected[, description]):
364    *
365    * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
366    * type, equal according to their equals() methods, and additionally that
367    * their time zones and calendar internal slots are the same value.
368    */
369   assertZonedDateTimesEqual(actual, expected, description = "") {
370     const prefix = description ? `${description}: ` : "";
371     assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
372     assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
373     assert(actual.equals(expected), `${prefix}equals method`);
374     assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
375     assert.sameValue(
376       actual.calendarId,
377       expected.calendarId,
378       `${prefix}calendar same value:`
379     );
380   },
382   /*
383    * assertUnreachable(description):
384    *
385    * Helper for asserting that code is not executed.
386    */
387   assertUnreachable(description) {
388     let message = "This code should not be executed";
389     if (description) {
390       message = `${message}: ${description}`;
391     }
392     throw new Test262Error(message);
393   },
395   /*
396    * checkPlainDateTimeConversionFastPath(func):
397    *
398    * ToTemporalDate and ToTemporalTime should both, if given a
399    * Temporal.PlainDateTime instance, convert to the desired type by reading the
400    * PlainDateTime's internal slots, rather than calling any getters.
401    *
402    * func(datetime) is the actual operation to test, that must
403    * internally call the abstract operation ToTemporalDate or ToTemporalTime.
404    * It is passed a Temporal.PlainDateTime instance.
405    */
406   checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
407     const actual = [];
408     const expected = [];
410     const calendar = "iso8601";
411     const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
412     const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
413     ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
414       Object.defineProperty(datetime, property, {
415         get() {
416           actual.push(`get ${formatPropertyName(property)}`);
417           const value = prototypeDescrs[property].get.call(this);
418           return {
419             toString() {
420               actual.push(`toString ${formatPropertyName(property)}`);
421               return value.toString();
422             },
423             valueOf() {
424               actual.push(`valueOf ${formatPropertyName(property)}`);
425               return value;
426             },
427           };
428         },
429       });
430     });
431     Object.defineProperty(datetime, "calendar", {
432       get() {
433         actual.push("get calendar");
434         return calendar;
435       },
436     });
438     func(datetime);
439     assert.compareArray(actual, expected, `${message}: property getters not called`);
440   },
442   /*
443    * Check that an options bag that accepts units written in the singular form,
444    * also accepts the same units written in the plural form.
445    * func(unit) should call the method with the appropriate options bag
446    * containing unit as a value. This will be called twice for each element of
447    * validSingularUnits, once with singular and once with plural, and the
448    * results of each pair should be the same (whether a Temporal object or a
449    * primitive value.)
450    */
451   checkPluralUnitsAccepted(func, validSingularUnits) {
452     const plurals = {
453       year: 'years',
454       month: 'months',
455       week: 'weeks',
456       day: 'days',
457       hour: 'hours',
458       minute: 'minutes',
459       second: 'seconds',
460       millisecond: 'milliseconds',
461       microsecond: 'microseconds',
462       nanosecond: 'nanoseconds',
463     };
465     validSingularUnits.forEach((unit) => {
466       const singularValue = func(unit);
467       const pluralValue = func(plurals[unit]);
468       const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
469       if (singularValue instanceof Temporal.Duration) {
470         TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
471       } else if (singularValue instanceof Temporal.Instant) {
472         TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
473       } else if (singularValue instanceof Temporal.PlainDateTime) {
474         TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
475       } else if (singularValue instanceof Temporal.PlainTime) {
476         TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
477       } else if (singularValue instanceof Temporal.ZonedDateTime) {
478         TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
479       } else {
480         assert.sameValue(pluralValue, singularValue);
481       }
482     });
483   },
485   /*
486    * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
487    *
488    * Checks the type handling of the roundingIncrement option.
489    * checkFunc(roundingIncrement) is a function which takes the value of
490    * roundingIncrement to test, and calls the method under test with it,
491    * returning the result. assertTrueResultFunc(result, description) should
492    * assert that result is the expected result with roundingIncrement: true, and
493    * assertObjectResultFunc(result, description) should assert that result is
494    * the expected result with roundingIncrement being an object with a valueOf()
495    * method.
496    */
497   checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
498     // null converts to 0, which is out of range
499     assert.throws(RangeError, () => checkFunc(null), "null");
500     // Booleans convert to either 0 or 1, and 1 is allowed
501     const trueResult = checkFunc(true);
502     assertTrueResultFunc(trueResult, "true");
503     assert.throws(RangeError, () => checkFunc(false), "false");
504     // Symbols and BigInts cannot convert to numbers
505     assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
506     assert.throws(TypeError, () => checkFunc(2n), "bigint");
508     // Objects prefer their valueOf() methods when converting to a number
509     assert.throws(RangeError, () => checkFunc({}), "plain object");
511     const expected = [
512       "get roundingIncrement.valueOf",
513       "call roundingIncrement.valueOf",
514     ];
515     const actual = [];
516     const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
517     const objectResult = checkFunc(observer);
518     assertObjectResultFunc(objectResult, "object with valueOf");
519     assert.compareArray(actual, expected, "order of operations");
520   },
522   /*
523    * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
524    *
525    * Checks the type handling of a string option, of which there are several in
526    * Temporal.
527    * propertyName is the name of the option, and value is the value that
528    * assertFunc should expect it to have.
529    * checkFunc(value) is a function which takes the value of the option to test,
530    * and calls the method under test with it, returning the result.
531    * assertFunc(result, description) should assert that result is the expected
532    * result with the option value being an object with a toString() method
533    * which returns the given value.
534    */
535   checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
536     // null converts to the string "null", which is an invalid string value
537     assert.throws(RangeError, () => checkFunc(null), "null");
538     // Booleans convert to the strings "true" or "false", which are invalid
539     assert.throws(RangeError, () => checkFunc(true), "true");
540     assert.throws(RangeError, () => checkFunc(false), "false");
541     // Symbols cannot convert to strings
542     assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
543     // Numbers convert to strings which are invalid
544     assert.throws(RangeError, () => checkFunc(2), "number");
545     // BigInts convert to strings which are invalid
546     assert.throws(RangeError, () => checkFunc(2n), "bigint");
548     // Objects prefer their toString() methods when converting to a string
549     assert.throws(RangeError, () => checkFunc({}), "plain object");
551     const expected = [
552       `get ${propertyName}.toString`,
553       `call ${propertyName}.toString`,
554     ];
555     const actual = [];
556     const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
557     const result = checkFunc(observer);
558     assertFunc(result, "object with toString");
559     assert.compareArray(actual, expected, "order of operations");
560   },
562   /*
563    * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
564    *   resultAssertions):
565    *
566    * Methods of Temporal classes that return a new instance of the same class,
567    * must not take the constructor of a subclass into account, nor the @@species
568    * property. This helper runs tests to ensure this.
569    *
570    * construct(...constructArgs) must yield a valid instance of the Temporal
571    * class. instance[method](...methodArgs) is the method call under test, which
572    * must also yield a valid instance of the same Temporal class, not a
573    * subclass. See below for the individual tests that this runs.
574    * resultAssertions() is a function that performs additional assertions on the
575    * instance returned by the method under test.
576    */
577   checkSubclassingIgnored(...args) {
578     this.checkSubclassConstructorNotObject(...args);
579     this.checkSubclassConstructorUndefined(...args);
580     this.checkSubclassConstructorThrows(...args);
581     this.checkSubclassConstructorNotCalled(...args);
582     this.checkSubclassSpeciesInvalidResult(...args);
583     this.checkSubclassSpeciesNotAConstructor(...args);
584     this.checkSubclassSpeciesNull(...args);
585     this.checkSubclassSpeciesUndefined(...args);
586     this.checkSubclassSpeciesThrows(...args);
587   },
589   /*
590    * Checks that replacing the 'constructor' property of the instance with
591    * various primitive values does not affect the returned new instance.
592    */
593   checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
594     function check(value, description) {
595       const instance = new construct(...constructArgs);
596       instance.constructor = value;
597       const result = instance[method](...methodArgs);
598       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
599       resultAssertions(result);
600     }
602     check(null, "null");
603     check(true, "true");
604     check("test", "string");
605     check(Symbol(), "Symbol");
606     check(7, "number");
607     check(7n, "bigint");
608   },
610   /*
611    * Checks that replacing the 'constructor' property of the subclass with
612    * undefined does not affect the returned new instance.
613    */
614   checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
615     let called = 0;
617     class MySubclass extends construct {
618       constructor() {
619         ++called;
620         super(...constructArgs);
621       }
622     }
624     const instance = new MySubclass();
625     assert.sameValue(called, 1);
627     MySubclass.prototype.constructor = undefined;
629     const result = instance[method](...methodArgs);
630     assert.sameValue(called, 1);
631     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
632     resultAssertions(result);
633   },
635   /*
636    * Checks that making the 'constructor' property of the instance throw when
637    * called does not affect the returned new instance.
638    */
639   checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
640     function CustomError() {}
641     const instance = new construct(...constructArgs);
642     Object.defineProperty(instance, "constructor", {
643       get() {
644         throw new CustomError();
645       }
646     });
647     const result = instance[method](...methodArgs);
648     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
649     resultAssertions(result);
650   },
652   /*
653    * Checks that when subclassing, the subclass constructor is not called by
654    * the method under test.
655    */
656   checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
657     let called = 0;
659     class MySubclass extends construct {
660       constructor() {
661         ++called;
662         super(...constructArgs);
663       }
664     }
666     const instance = new MySubclass();
667     assert.sameValue(called, 1);
669     const result = instance[method](...methodArgs);
670     assert.sameValue(called, 1);
671     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
672     resultAssertions(result);
673   },
675   /*
676    * Check that the constructor's @@species property is ignored when it's a
677    * constructor that returns a non-object value.
678    */
679   checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
680     function check(value, description) {
681       const instance = new construct(...constructArgs);
682       instance.constructor = {
683         [Symbol.species]: function() {
684           return value;
685         },
686       };
687       const result = instance[method](...methodArgs);
688       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
689       resultAssertions(result);
690     }
692     check(undefined, "undefined");
693     check(null, "null");
694     check(true, "true");
695     check("test", "string");
696     check(Symbol(), "Symbol");
697     check(7, "number");
698     check(7n, "bigint");
699     check({}, "plain object");
700   },
702   /*
703    * Check that the constructor's @@species property is ignored when it's not a
704    * constructor.
705    */
706   checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
707     function check(value, description) {
708       const instance = new construct(...constructArgs);
709       instance.constructor = {
710         [Symbol.species]: value,
711       };
712       const result = instance[method](...methodArgs);
713       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
714       resultAssertions(result);
715     }
717     check(true, "true");
718     check("test", "string");
719     check(Symbol(), "Symbol");
720     check(7, "number");
721     check(7n, "bigint");
722     check({}, "plain object");
723   },
725   /*
726    * Check that the constructor's @@species property is ignored when it's null.
727    */
728   checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
729     let called = 0;
731     class MySubclass extends construct {
732       constructor() {
733         ++called;
734         super(...constructArgs);
735       }
736     }
738     const instance = new MySubclass();
739     assert.sameValue(called, 1);
741     MySubclass.prototype.constructor = {
742       [Symbol.species]: null,
743     };
745     const result = instance[method](...methodArgs);
746     assert.sameValue(called, 1);
747     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
748     resultAssertions(result);
749   },
751   /*
752    * Check that the constructor's @@species property is ignored when it's
753    * undefined.
754    */
755   checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
756     let called = 0;
758     class MySubclass extends construct {
759       constructor() {
760         ++called;
761         super(...constructArgs);
762       }
763     }
765     const instance = new MySubclass();
766     assert.sameValue(called, 1);
768     MySubclass.prototype.constructor = {
769       [Symbol.species]: undefined,
770     };
772     const result = instance[method](...methodArgs);
773     assert.sameValue(called, 1);
774     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
775     resultAssertions(result);
776   },
778   /*
779    * Check that the constructor's @@species property is ignored when it throws,
780    * i.e. it is not called at all.
781    */
782   checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
783     function CustomError() {}
785     const instance = new construct(...constructArgs);
786     instance.constructor = {
787       get [Symbol.species]() {
788         throw new CustomError();
789       },
790     };
792     const result = instance[method](...methodArgs);
793     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
794   },
796   /*
797    * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
798    *
799    * Static methods of Temporal classes that return a new instance of the class,
800    * must not use the this-value as a constructor. This helper runs tests to
801    * ensure this.
802    *
803    * construct[method](...methodArgs) is the static method call under test, and
804    * must yield a valid instance of the Temporal class, not a subclass. See
805    * below for the individual tests that this runs.
806    * resultAssertions() is a function that performs additional assertions on the
807    * instance returned by the method under test.
808    */
809   checkSubclassingIgnoredStatic(...args) {
810     this.checkStaticInvalidReceiver(...args);
811     this.checkStaticReceiverNotCalled(...args);
812     this.checkThisValueNotCalled(...args);
813   },
815   /*
816    * Check that calling the static method with a receiver that's not callable,
817    * still calls the intrinsic constructor.
818    */
819   checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
820     function check(value, description) {
821       const result = construct[method].apply(value, methodArgs);
822       assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
823       resultAssertions(result);
824     }
826     check(undefined, "undefined");
827     check(null, "null");
828     check(true, "true");
829     check("test", "string");
830     check(Symbol(), "symbol");
831     check(7, "number");
832     check(7n, "bigint");
833     check({}, "Non-callable object");
834   },
836   /*
837    * Check that calling the static method with a receiver that returns a value
838    * that's not callable, still calls the intrinsic constructor.
839    */
840   checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
841     function check(value, description) {
842       const receiver = function () {
843         return value;
844       };
845       const result = construct[method].apply(receiver, methodArgs);
846       assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
847       resultAssertions(result);
848     }
850     check(undefined, "undefined");
851     check(null, "null");
852     check(true, "true");
853     check("test", "string");
854     check(Symbol(), "symbol");
855     check(7, "number");
856     check(7n, "bigint");
857     check({}, "Non-callable object");
858   },
860   /*
861    * Check that the receiver isn't called.
862    */
863   checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
864     let called = false;
866     class MySubclass extends construct {
867       constructor(...args) {
868         called = true;
869         super(...args);
870       }
871     }
873     const result = MySubclass[method](...methodArgs);
874     assert.sameValue(called, false);
875     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
876     resultAssertions(result);
877   },
879   /*
880    * Check that any calendar-carrying Temporal object has its [[Calendar]]
881    * internal slot read by ToTemporalCalendar, and does not fetch the calendar
882    * by calling getters.
883    */
884   checkToTemporalCalendarFastPath(func) {
885     const plainDate = new Temporal.PlainDate(2000, 5, 2, "iso8601");
886     const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, "iso8601");
887     const plainMonthDay = new Temporal.PlainMonthDay(5, 2, "iso8601");
888     const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, "iso8601");
889     const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601");
891     [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
892       const actual = [];
893       const expected = [];
895       Object.defineProperty(temporalObject, "calendar", {
896         get() {
897           actual.push("get calendar");
898           return calendar;
899         },
900       });
902       func(temporalObject);
903       assert.compareArray(actual, expected, "calendar getter not called");
904     });
905   },
907   checkToTemporalInstantFastPath(func) {
908     const actual = [];
909     const expected = [];
911     const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
912     Object.defineProperty(datetime, 'toString', {
913       get() {
914         actual.push("get toString");
915         return function (options) {
916           actual.push("call toString");
917           return Temporal.ZonedDateTime.prototype.toString.call(this, options);
918         };
919       },
920     });
922     func(datetime);
923     assert.compareArray(actual, expected, "toString not called");
924   },
926   checkToTemporalPlainDateTimeFastPath(func) {
927     const actual = [];
928     const expected = [];
930     const date = new Temporal.PlainDate(2000, 5, 2, "iso8601");
931     const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
932     ["year", "month", "monthCode", "day"].forEach((property) => {
933       Object.defineProperty(date, property, {
934         get() {
935           actual.push(`get ${formatPropertyName(property)}`);
936           const value = prototypeDescrs[property].get.call(this);
937           return TemporalHelpers.toPrimitiveObserver(actual, value, property);
938         },
939       });
940     });
941     ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
942       Object.defineProperty(date, property, {
943         get() {
944           actual.push(`get ${formatPropertyName(property)}`);
945           return undefined;
946         },
947       });
948     });
949     Object.defineProperty(date, "calendar", {
950       get() {
951         actual.push("get calendar");
952         return "iso8601";
953       },
954     });
956     func(date);
957     assert.compareArray(actual, expected, "property getters not called");
958   },
960   /*
961    * observeProperty(calls, object, propertyName, value):
962    *
963    * Defines an own property @object.@propertyName with value @value, that
964    * will log any calls to its accessors to the array @calls.
965    */
966   observeProperty(calls, object, propertyName, value, objectName = "") {
967     Object.defineProperty(object, propertyName, {
968       get() {
969         calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
970         return value;
971       },
972       set(v) {
973         calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
974       }
975     });
976   },
978   /*
979    * observeMethod(calls, object, propertyName, value):
980    *
981    * Defines an own property @object.@propertyName with value @value, that
982    * will log any calls of @value to the array @calls.
983    */
984   observeMethod(calls, object, propertyName, objectName = "") {
985     const method = object[propertyName];
986     object[propertyName] = function () {
987       calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
988       return method.apply(object, arguments);
989     };
990   },
992   /*
993    * Used for substituteMethod to indicate default behavior instead of a
994    * substituted value
995    */
996   SUBSTITUTE_SKIP: SKIP_SYMBOL,
998   /*
999    * substituteMethod(object, propertyName, values):
1000    *
1001    * Defines an own property @object.@propertyName that will, for each
1002    * subsequent call to the method previously defined as
1003    * @object.@propertyName:
1004    *  - Call the method, if no more values remain
1005    *  - Call the method, if the value in @values for the corresponding call
1006    *    is SUBSTITUTE_SKIP
1007    *  - Otherwise, return the corresponding value in @value
1008    */
1009   substituteMethod(object, propertyName, values) {
1010     let calls = 0;
1011     const method = object[propertyName];
1012     object[propertyName] = function () {
1013       if (calls >= values.length) {
1014         return method.apply(object, arguments);
1015       } else if (values[calls] === SKIP_SYMBOL) {
1016         calls++;
1017         return method.apply(object, arguments);
1018       } else {
1019         return values[calls++];
1020       }
1021     };
1022   },
1024   /*
1025    * propertyBagObserver():
1026    * Returns an object that behaves like the given propertyBag but tracks Get
1027    * and Has operations on any of its properties, by appending messages to an
1028    * array. If the value of a property in propertyBag is a primitive, the value
1029    * of the returned object's property will additionally be a
1030    * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
1031    * and valueOf methods in the same array. This is for the purpose of testing
1032    * order of operations that are observable from user code. objectName is used
1033    * in the log.
1034    * If skipToPrimitive is given, it must be an array of property keys. Those
1035    * properties will not have a TemporalHelpers.toPrimitiveObserver returned,
1036    * and instead just be returned directly.
1037    */
1038   propertyBagObserver(calls, propertyBag, objectName, skipToPrimitive) {
1039     return new Proxy(propertyBag, {
1040       ownKeys(target) {
1041         calls.push(`ownKeys ${objectName}`);
1042         return Reflect.ownKeys(target);
1043       },
1044       getOwnPropertyDescriptor(target, key) {
1045         calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1046         return Reflect.getOwnPropertyDescriptor(target, key);
1047       },
1048       get(target, key, receiver) {
1049         calls.push(`get ${formatPropertyName(key, objectName)}`);
1050         const result = Reflect.get(target, key, receiver);
1051         if (result === undefined) {
1052           return undefined;
1053         }
1054         if ((result !== null && typeof result === "object") || typeof result === "function") {
1055           return result;
1056         }
1057         if (skipToPrimitive && skipToPrimitive.indexOf(key) >= 0) {
1058           return result;
1059         }
1060         return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1061       },
1062       has(target, key) {
1063         calls.push(`has ${formatPropertyName(key, objectName)}`);
1064         return Reflect.has(target, key);
1065       },
1066     });
1067   },
1069   /*
1070    * Returns an object that will append logs of any Gets or Calls of its valueOf
1071    * or toString properties to the array calls. Both valueOf and toString will
1072    * return the actual primitiveValue. propertyName is used in the log.
1073    */
1074   toPrimitiveObserver(calls, primitiveValue, propertyName) {
1075     return {
1076       get valueOf() {
1077         calls.push(`get ${propertyName}.valueOf`);
1078         return function () {
1079           calls.push(`call ${propertyName}.valueOf`);
1080           return primitiveValue;
1081         };
1082       },
1083       get toString() {
1084         calls.push(`get ${propertyName}.toString`);
1085         return function () {
1086           calls.push(`call ${propertyName}.toString`);
1087           if (primitiveValue === undefined) return undefined;
1088           return primitiveValue.toString();
1089         };
1090       },
1091     };
1092   },
1094   /*
1095    * An object containing further methods that return arrays of ISO strings, for
1096    * testing parsers.
1097    */
1098   ISO: {
1099     /*
1100      * PlainMonthDay strings that are not valid.
1101      */
1102     plainMonthDayStringsInvalid() {
1103       return [
1104         "11-18junk",
1105         "11-18[u-ca=gregory]",
1106         "11-18[u-ca=hebrew]",
1107         "11-18[U-CA=iso8601]",
1108         "11-18[u-CA=iso8601]",
1109         "11-18[FOO=bar]",
1110       ];
1111     },
1113     /*
1114      * PlainMonthDay strings that are valid and that should produce October 1st.
1115      */
1116     plainMonthDayStringsValid() {
1117       return [
1118         "10-01",
1119         "1001",
1120         "1965-10-01",
1121         "1976-10-01T152330.1+00:00",
1122         "19761001T15:23:30.1+00:00",
1123         "1976-10-01T15:23:30.1+0000",
1124         "1976-10-01T152330.1+0000",
1125         "19761001T15:23:30.1+0000",
1126         "19761001T152330.1+00:00",
1127         "19761001T152330.1+0000",
1128         "+001976-10-01T152330.1+00:00",
1129         "+0019761001T15:23:30.1+00:00",
1130         "+001976-10-01T15:23:30.1+0000",
1131         "+001976-10-01T152330.1+0000",
1132         "+0019761001T15:23:30.1+0000",
1133         "+0019761001T152330.1+00:00",
1134         "+0019761001T152330.1+0000",
1135         "1976-10-01T15:23:00",
1136         "1976-10-01T15:23",
1137         "1976-10-01T15",
1138         "1976-10-01",
1139         "--10-01",
1140         "--1001",
1141       ];
1142     },
1144     /*
1145      * PlainTime strings that may be mistaken for PlainMonthDay or
1146      * PlainYearMonth strings, and so require a time designator.
1147      */
1148     plainTimeStringsAmbiguous() {
1149       const ambiguousStrings = [
1150         "2021-12",  // ambiguity between YYYY-MM and HHMM-UU
1151         "2021-12[-12:00]",  // ditto, TZ does not disambiguate
1152         "1214",     // ambiguity between MMDD and HHMM
1153         "0229",     //   ditto, including MMDD that doesn't occur every year
1154         "1130",     //   ditto, including DD that doesn't occur in every month
1155         "12-14",    // ambiguity between MM-DD and HH-UU
1156         "12-14[-14:00]",  // ditto, TZ does not disambiguate
1157         "202112",   // ambiguity between YYYYMM and HHMMSS
1158         "202112[UTC]",  // ditto, TZ does not disambiguate
1159       ];
1160       // Adding a calendar annotation to one of these strings must not cause
1161       // disambiguation in favour of time.
1162       const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
1163       return ambiguousStrings.concat(stringsWithCalendar);
1164     },
1166     /*
1167      * PlainTime strings that are of similar form to PlainMonthDay and
1168      * PlainYearMonth strings, but are not ambiguous due to components that
1169      * aren't valid as months or days.
1170      */
1171     plainTimeStringsUnambiguous() {
1172       return [
1173         "2021-13",          // 13 is not a month
1174         "202113",           //   ditto
1175         "2021-13[-13:00]",  //   ditto
1176         "202113[-13:00]",   //   ditto
1177         "0000-00",          // 0 is not a month
1178         "000000",           //   ditto
1179         "0000-00[UTC]",     //   ditto
1180         "000000[UTC]",      //   ditto
1181         "1314",             // 13 is not a month
1182         "13-14",            //   ditto
1183         "1232",             // 32 is not a day
1184         "0230",             // 30 is not a day in February
1185         "0631",             // 31 is not a day in June
1186         "0000",             // 0 is neither a month nor a day
1187         "00-00",            //   ditto
1188       ];
1189     },
1191     /*
1192      * PlainYearMonth-like strings that are not valid.
1193      */
1194     plainYearMonthStringsInvalid() {
1195       return [
1196         "2020-13",
1197         "1976-11[u-ca=gregory]",
1198         "1976-11[u-ca=hebrew]",
1199         "1976-11[U-CA=iso8601]",
1200         "1976-11[u-CA=iso8601]",
1201         "1976-11[FOO=bar]",
1202       ];
1203     },
1205     /*
1206      * PlainYearMonth-like strings that are valid and should produce November
1207      * 1976 in the ISO 8601 calendar.
1208      */
1209     plainYearMonthStringsValid() {
1210       return [
1211         "1976-11",
1212         "1976-11-10",
1213         "1976-11-01T09:00:00+00:00",
1214         "1976-11-01T00:00:00+05:00",
1215         "197611",
1216         "+00197611",
1217         "1976-11-18T15:23:30.1-02:00",
1218         "1976-11-18T152330.1+00:00",
1219         "19761118T15:23:30.1+00:00",
1220         "1976-11-18T15:23:30.1+0000",
1221         "1976-11-18T152330.1+0000",
1222         "19761118T15:23:30.1+0000",
1223         "19761118T152330.1+00:00",
1224         "19761118T152330.1+0000",
1225         "+001976-11-18T152330.1+00:00",
1226         "+0019761118T15:23:30.1+00:00",
1227         "+001976-11-18T15:23:30.1+0000",
1228         "+001976-11-18T152330.1+0000",
1229         "+0019761118T15:23:30.1+0000",
1230         "+0019761118T152330.1+00:00",
1231         "+0019761118T152330.1+0000",
1232         "1976-11-18T15:23",
1233         "1976-11-18T15",
1234         "1976-11-18",
1235       ];
1236     },
1238     /*
1239      * PlainYearMonth-like strings that are valid and should produce November of
1240      * the ISO year -9999.
1241      */
1242     plainYearMonthStringsValidNegativeYear() {
1243       return [
1244         "-009999-11",
1245       ];
1246     },
1247   }