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.
7 This defines helper objects and functions for testing Temporal.
8 defines: [TemporalHelpers]
9 features: [Symbol.species, Symbol.iterator, Temporal]
12 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
14 function formatPropertyName(propertyKey, objectName = "") {
15 switch (typeof propertyKey) {
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}]`;
22 return `${objectName}[Symbol('${propertyKey.description}')]`
25 if (propertyKey !== String(Number(propertyKey))) {
26 if (ASCII_IDENTIFIER.test(propertyKey)) {
27 return objectName ? `${objectName}.${propertyKey}` : propertyKey;
29 return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
33 // integer or string integer-index
34 return `${objectName}[${propertyKey}]`;
38 const SKIP_SYMBOL = Symbol("Skip");
40 var TemporalHelpers = {
42 * Codes and maximum lengths of months in the ISO 8601 calendar.
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 }
60 * List of known calendar eras and their possible aliases.
62 * https://tc39.es/proposal-intl-era-monthcode/#table-eras
66 { era: "buddhist", aliases: ["be"] },
73 { era: "coptic-inverse" },
79 { era: "ethiopic", aliases: ["incar"] },
80 { era: "ethioaa", aliases: ["ethiopic-amete-alem", "mundi"] },
83 { era: "ethioaa", aliases: ["ethiopic-amete-alem", "mundi"] },
86 { era: "gregory", aliases: ["ce", "ad"] },
87 { era: "gregory-inverse", aliases: ["bc", "bce"] },
90 { era: "hebrew", aliases: ["am"] },
93 { era: "indian", aliases: ["saka"] },
96 { era: "islamic", aliases: ["ah"] },
99 { era: "islamic-civil", aliases: ["islamicc", "ah"] },
102 { era: "islamic-rgsa", aliases: ["ah"] },
105 { era: "islamic-tbla", aliases: ["ah"] },
107 "islamic-umalqura": [
108 { era: "islamic-umalqura", aliases: ["ah"] },
112 { era: "japanese", aliases: ["gregory", "ad", "ce"] },
113 { era: "japanese-inverse", aliases: ["gregory-inverse", "bc", "bce"] },
120 { era: "persian", aliases: ["ap"] },
123 { era: "roc", aliases: ["minguo"] },
124 { era: "roc-inverse", aliases: ["before-roc"] },
129 * Return the canonical era code.
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);
138 assert(Object.hasOwn(TemporalHelpers.CalendarEras, calendarId));
140 if (eraName === undefined) {
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)) {
150 throw new Test262Error(`Unsupported era name: ${eraName}`);
154 * assertDuration(duration, years, ..., nanoseconds[, description]):
156 * Shorthand for asserting that each field of a Temporal.Duration is equal to
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`);
175 * assertDateDuration(duration, years, months, weeks, days, [, description]):
177 * Shorthand for asserting that each date field of a Temporal.Duration is
178 * equal to an expected value.
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:`);
196 * assertDurationsEqual(actual, expected[, description]):
198 * Shorthand for asserting that each field of a Temporal.Duration is equal to
199 * the corresponding field in another Temporal.Duration.
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);
208 * assertInstantsEqual(actual, expected[, description]):
210 * Shorthand for asserting that two Temporal.Instants are of the correct type
211 * and equal according to their equals() methods.
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`);
221 * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
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.)
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`);
232 TemporalHelpers.canonicalizeCalendarEra(date.calendarId, date.era),
233 TemporalHelpers.canonicalizeCalendarEra(date.calendarId, era),
234 `${prefix}era result:`
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:`);
244 * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
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.)
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`);
255 TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, datetime.era),
256 TemporalHelpers.canonicalizeCalendarEra(datetime.calendarId, era),
257 `${prefix}era result:`
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:`);
273 * assertPlainDateTimesEqual(actual, expected[, description]):
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.
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`);
287 `${prefix}calendar same value:`
292 * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
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().)
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:`);
309 * assertPlainTime(time, hour, ..., nanosecond[, description]):
311 * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
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:`);
326 * assertPlainTimesEqual(actual, expected[, description]):
328 * Shorthand for asserting that two Temporal.PlainTimes are of the correct
329 * type and equal according to their equals() methods.
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`);
339 * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
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.)
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`);
350 TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, yearMonth.era),
351 TemporalHelpers.canonicalizeCalendarEra(yearMonth.calendarId, era),
352 `${prefix}era result:`
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:`);
363 * assertZonedDateTimesEqual(actual, expected[, description]):
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.
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:`);
378 `${prefix}calendar same value:`
383 * assertUnreachable(description):
385 * Helper for asserting that code is not executed.
387 assertUnreachable(description) {
388 let message = "This code should not be executed";
390 message = `${message}: ${description}`;
392 throw new Test262Error(message);
396 * checkPlainDateTimeConversionFastPath(func):
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.
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.
406 checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
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, {
416 actual.push(`get ${formatPropertyName(property)}`);
417 const value = prototypeDescrs[property].get.call(this);
420 actual.push(`toString ${formatPropertyName(property)}`);
421 return value.toString();
424 actual.push(`valueOf ${formatPropertyName(property)}`);
431 Object.defineProperty(datetime, "calendar", {
433 actual.push("get calendar");
439 assert.compareArray(actual, expected, `${message}: property getters not called`);
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
451 checkPluralUnitsAccepted(func, validSingularUnits) {
460 millisecond: 'milliseconds',
461 microsecond: 'microseconds',
462 nanosecond: 'nanoseconds',
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);
480 assert.sameValue(pluralValue, singularValue);
486 * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
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()
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");
512 "get roundingIncrement.valueOf",
513 "call roundingIncrement.valueOf",
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");
523 * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
525 * Checks the type handling of a string option, of which there are several in
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.
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");
552 `get ${propertyName}.toString`,
553 `call ${propertyName}.toString`,
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");
563 * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
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.
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.
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);
590 * Checks that replacing the 'constructor' property of the instance with
591 * various primitive values does not affect the returned new instance.
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);
604 check("test", "string");
605 check(Symbol(), "Symbol");
611 * Checks that replacing the 'constructor' property of the subclass with
612 * undefined does not affect the returned new instance.
614 checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
617 class MySubclass extends construct {
620 super(...constructArgs);
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);
636 * Checks that making the 'constructor' property of the instance throw when
637 * called does not affect the returned new instance.
639 checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
640 function CustomError() {}
641 const instance = new construct(...constructArgs);
642 Object.defineProperty(instance, "constructor", {
644 throw new CustomError();
647 const result = instance[method](...methodArgs);
648 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
649 resultAssertions(result);
653 * Checks that when subclassing, the subclass constructor is not called by
654 * the method under test.
656 checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
659 class MySubclass extends construct {
662 super(...constructArgs);
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);
676 * Check that the constructor's @@species property is ignored when it's a
677 * constructor that returns a non-object value.
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() {
687 const result = instance[method](...methodArgs);
688 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
689 resultAssertions(result);
692 check(undefined, "undefined");
695 check("test", "string");
696 check(Symbol(), "Symbol");
699 check({}, "plain object");
703 * Check that the constructor's @@species property is ignored when it's not a
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,
712 const result = instance[method](...methodArgs);
713 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
714 resultAssertions(result);
718 check("test", "string");
719 check(Symbol(), "Symbol");
722 check({}, "plain object");
726 * Check that the constructor's @@species property is ignored when it's null.
728 checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
731 class MySubclass extends construct {
734 super(...constructArgs);
738 const instance = new MySubclass();
739 assert.sameValue(called, 1);
741 MySubclass.prototype.constructor = {
742 [Symbol.species]: null,
745 const result = instance[method](...methodArgs);
746 assert.sameValue(called, 1);
747 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
748 resultAssertions(result);
752 * Check that the constructor's @@species property is ignored when it's
755 checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
758 class MySubclass extends construct {
761 super(...constructArgs);
765 const instance = new MySubclass();
766 assert.sameValue(called, 1);
768 MySubclass.prototype.constructor = {
769 [Symbol.species]: undefined,
772 const result = instance[method](...methodArgs);
773 assert.sameValue(called, 1);
774 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
775 resultAssertions(result);
779 * Check that the constructor's @@species property is ignored when it throws,
780 * i.e. it is not called at all.
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();
792 const result = instance[method](...methodArgs);
793 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
797 * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
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
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.
809 checkSubclassingIgnoredStatic(...args) {
810 this.checkStaticInvalidReceiver(...args);
811 this.checkStaticReceiverNotCalled(...args);
812 this.checkThisValueNotCalled(...args);
816 * Check that calling the static method with a receiver that's not callable,
817 * still calls the intrinsic constructor.
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);
826 check(undefined, "undefined");
829 check("test", "string");
830 check(Symbol(), "symbol");
833 check({}, "Non-callable object");
837 * Check that calling the static method with a receiver that returns a value
838 * that's not callable, still calls the intrinsic constructor.
840 checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
841 function check(value, description) {
842 const receiver = function () {
845 const result = construct[method].apply(receiver, methodArgs);
846 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
847 resultAssertions(result);
850 check(undefined, "undefined");
853 check("test", "string");
854 check(Symbol(), "symbol");
857 check({}, "Non-callable object");
861 * Check that the receiver isn't called.
863 checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
866 class MySubclass extends construct {
867 constructor(...args) {
873 const result = MySubclass[method](...methodArgs);
874 assert.sameValue(called, false);
875 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
876 resultAssertions(result);
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.
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) => {
895 Object.defineProperty(temporalObject, "calendar", {
897 actual.push("get calendar");
902 func(temporalObject);
903 assert.compareArray(actual, expected, "calendar getter not called");
907 checkToTemporalInstantFastPath(func) {
911 const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
912 Object.defineProperty(datetime, 'toString', {
914 actual.push("get toString");
915 return function (options) {
916 actual.push("call toString");
917 return Temporal.ZonedDateTime.prototype.toString.call(this, options);
923 assert.compareArray(actual, expected, "toString not called");
926 checkToTemporalPlainDateTimeFastPath(func) {
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, {
935 actual.push(`get ${formatPropertyName(property)}`);
936 const value = prototypeDescrs[property].get.call(this);
937 return TemporalHelpers.toPrimitiveObserver(actual, value, property);
941 ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
942 Object.defineProperty(date, property, {
944 actual.push(`get ${formatPropertyName(property)}`);
949 Object.defineProperty(date, "calendar", {
951 actual.push("get calendar");
957 assert.compareArray(actual, expected, "property getters not called");
961 * observeProperty(calls, object, propertyName, value):
963 * Defines an own property @object.@propertyName with value @value, that
964 * will log any calls to its accessors to the array @calls.
966 observeProperty(calls, object, propertyName, value, objectName = "") {
967 Object.defineProperty(object, propertyName, {
969 calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
973 calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
979 * observeMethod(calls, object, propertyName, value):
981 * Defines an own property @object.@propertyName with value @value, that
982 * will log any calls of @value to the array @calls.
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);
993 * Used for substituteMethod to indicate default behavior instead of a
996 SUBSTITUTE_SKIP: SKIP_SYMBOL,
999 * substituteMethod(object, propertyName, values):
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
1009 substituteMethod(object, propertyName, values) {
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) {
1017 return method.apply(object, arguments);
1019 return values[calls++];
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
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.
1038 propertyBagObserver(calls, propertyBag, objectName, skipToPrimitive) {
1039 return new Proxy(propertyBag, {
1041 calls.push(`ownKeys ${objectName}`);
1042 return Reflect.ownKeys(target);
1044 getOwnPropertyDescriptor(target, key) {
1045 calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1046 return Reflect.getOwnPropertyDescriptor(target, key);
1048 get(target, key, receiver) {
1049 calls.push(`get ${formatPropertyName(key, objectName)}`);
1050 const result = Reflect.get(target, key, receiver);
1051 if (result === undefined) {
1054 if ((result !== null && typeof result === "object") || typeof result === "function") {
1057 if (skipToPrimitive && skipToPrimitive.indexOf(key) >= 0) {
1060 return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1063 calls.push(`has ${formatPropertyName(key, objectName)}`);
1064 return Reflect.has(target, key);
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.
1074 toPrimitiveObserver(calls, primitiveValue, propertyName) {
1077 calls.push(`get ${propertyName}.valueOf`);
1078 return function () {
1079 calls.push(`call ${propertyName}.valueOf`);
1080 return primitiveValue;
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();
1095 * An object containing further methods that return arrays of ISO strings, for
1100 * PlainMonthDay strings that are not valid.
1102 plainMonthDayStringsInvalid() {
1105 "11-18[u-ca=gregory]",
1106 "11-18[u-ca=hebrew]",
1107 "11-18[U-CA=iso8601]",
1108 "11-18[u-CA=iso8601]",
1114 * PlainMonthDay strings that are valid and that should produce October 1st.
1116 plainMonthDayStringsValid() {
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",
1145 * PlainTime strings that may be mistaken for PlainMonthDay or
1146 * PlainYearMonth strings, and so require a time designator.
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
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);
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.
1171 plainTimeStringsUnambiguous() {
1173 "2021-13", // 13 is not a month
1175 "2021-13[-13:00]", // ditto
1176 "202113[-13:00]", // ditto
1177 "0000-00", // 0 is not a month
1179 "0000-00[UTC]", // ditto
1180 "000000[UTC]", // ditto
1181 "1314", // 13 is not a month
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
1192 * PlainYearMonth-like strings that are not valid.
1194 plainYearMonthStringsInvalid() {
1197 "1976-11[u-ca=gregory]",
1198 "1976-11[u-ca=hebrew]",
1199 "1976-11[U-CA=iso8601]",
1200 "1976-11[u-CA=iso8601]",
1206 * PlainYearMonth-like strings that are valid and should produce November
1207 * 1976 in the ISO 8601 calendar.
1209 plainYearMonthStringsValid() {
1213 "1976-11-01T09:00:00+00:00",
1214 "1976-11-01T00:00:00+05:00",
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",
1239 * PlainYearMonth-like strings that are valid and should produce November of
1240 * the ISO year -9999.
1242 plainYearMonthStringsValidNegativeYear() {