summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/PlainYearMonth
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/built-ins/Temporal/PlainYearMonth
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/PlainYearMonth')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js47
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js13
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js80
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js44
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js164
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js183
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js55
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js192
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js75
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js58
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js164
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js38
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js190
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js353
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js34
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js48
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js50
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js56
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js193
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js33
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js45
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js15
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js14
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js35
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js18
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js74
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js31
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js16
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js54
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js21
624 files changed, 17970 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js
new file mode 100644
index 0000000000..89df253b7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: PlainYearMonth constructor works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(1976, 11);
+assert.sameValue(typeof ym, "object");
+TemporalHelpers.assertPlainYearMonth(ym, 1976, 11, "M11");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js
new file mode 100644
index 0000000000..ec3397b9a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/builtin.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Tests that Temporal.PlainYearMonth meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainYearMonth.prototype,
+ "object", "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js
new file mode 100644
index 0000000000..bfc3f7c978
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-always.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: If calendar name is to be emitted, include additional reference info
+features: [Temporal]
+---*/
+
+const pym = new Temporal.PlainYearMonth(2019, 10, "iso8601", 31);
+
+assert.sameValue(
+ pym.toString({ calendarName: 'always' }),
+ "2019-10-31[u-ca=iso8601]",
+ "emit year-month-day if calendarName = 'always' (four-argument constructor)"
+);
+
+const anotherPYM = Temporal.PlainYearMonth.from("2019-10-31"); // 31 will get dropped
+
+assert.sameValue(
+ anotherPYM.toString({ calendarName: 'always' }),
+ "2019-10-01[u-ca=iso8601]",
+ "emit fallback day if calendarName = 'always' (static from)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js
new file mode 100644
index 0000000000..d3465f7957
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Calendar names are case-insensitive
+features: [Temporal]
+---*/
+
+const arg = "iSo8601";
+
+const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
new file mode 100644
index 0000000000..b5c69e0d04
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if the calendar argument is invalid
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 1970, "year"),
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "month"),
+ "local",
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "day")
+];
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js
new file mode 100644
index 0000000000..dc11586ee5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: A number is not allowed to be a calendar
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ -19761118,
+ 19761118,
+ 1234567890,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => new Temporal.PlainYearMonth(2000, 5, arg, 1),
+ "A number is not a valid ISO string for Calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js
new file mode 100644
index 0000000000..3d8417dd83
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.constructor
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const arg = "iso8601";
+
+const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
new file mode 100644
index 0000000000..501c0b44a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal-totemporalcalendar step 1.b:
+ b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const plainMonthDay = new Temporal.PlainMonthDay(5, 2);
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 5);
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => {
+ const actual = [];
+ const expected = [];
+
+ const calendar = arg.getISOFields().calendar;
+
+ Object.defineProperty(arg, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ const result = new Temporal.PlainYearMonth(2000, 5, arg, 1);
+ assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar");
+
+ assert.compareArray(actual, expected, "calendar getter not called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
new file mode 100644
index 0000000000..317f57a426
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2000, 5];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.calendarId, "iso8601");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.calendarId, "iso8601");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js
new file mode 100644
index 0000000000..1bbf456ce8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or object for Calendar
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => new Temporal.PlainYearMonth(2000, 5, arg, 1),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => new Temporal.PlainYearMonth(2000, 5, arg, 1), `${description} is not a valid object and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..1ac34ed0c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js
new file mode 100644
index 0000000000..4f98ccb77c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-cast.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: compare() casts its arguments
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+
+assert.sameValue(Temporal.PlainYearMonth.compare({ year: 1994, month: 11 }, jun13), -1, "one object");
+assert.sameValue(Temporal.PlainYearMonth.compare("1994-11", jun13), -1, "one string");
+assert.throws(TypeError, () => Temporal.PlainYearMonth.compare({ year: 1994 }, jun13), "one missing property");
+
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, { year: 2013, month: 6 }), -1, "two object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, "2013-06"), -1, "two string");
+assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(nov94, { year: 2013 }), "two missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js
new file mode 100644
index 0000000000..6381187105
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-number.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const arg = 201906;
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth (first argument)`
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth (first argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..c37f433a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)");
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..47e6492289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(
+ result1,
+ 0,
+ "leap second is a valid ISO string for calendar (first argument)"
+);
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(
+ result2,
+ 0,
+ "leap second is a valid ISO string for calendar (second argument)"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..9db6ff73d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ "A number is not a valid ISO string for calendar (first argument)"
+ );
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ "A number is not a valid ISO string for calendar (second argument)"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..d757e80b1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-string.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6));
+assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`);
+
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg);
+assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`);
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..bed5c45623
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof calendar === "string" ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6ecb7deff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ "reject minus zero as extended year (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ "reject minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..8b6f2f790c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..cf8b20edca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject unknown annotation with critical flag: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject unknown annotation with critical flag: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..3375a84db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth (second argument)`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js
new file mode 100644
index 0000000000..26c2f8b2c5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const arg2 = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, arg2), `"${arg}" is invalid (first argument)`);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg2, arg), `"${arg}" is invalid (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..d29e88d68b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-calendar.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject more than one calendar annotation if any critical: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject more than one calendar annotation if any critical: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..fecfb4bc40
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-multiple-time-zone.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `reject more than one time zone annotation: ${arg} (first argument)`
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `reject more than one time zone annotation: ${arg} (second argument)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js
new file mode 100644
index 0000000000..8307aacff7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const yearMonth = new Temporal.PlainYearMonth(2019, 12);
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(arg, yearMonth),
+ 0,
+ `variant time separators (${description}), first argument`
+ );
+
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(yearMonth, arg),
+ 0,
+ `variant time separators (${description}), second argument`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..e2af75b112
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-time-zone-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..eb501cfc7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.compare(arg, arg);
+
+ assert.sameValue(
+ result,
+ 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..2b3687fc43
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string-with-utc-designator.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const yearMonth = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, yearMonth),
+ "String with UTC designator should not be valid as a PlainYearMonth (first argument)"
+ );
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(yearMonth, arg),
+ "String with UTC designator should not be valid as a PlainYearMonth (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js
new file mode 100644
index 0000000000..3cd77be14c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-string.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validStrings = TemporalHelpers.ISO.plainYearMonthStringsValid().concat(TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear());
+
+for (const arg of validStrings) {
+ assert.sameValue(
+ Temporal.PlainYearMonth.compare(arg, arg),
+ 0,
+ `"${arg}" is a valid PlainYearMonth string`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js
new file mode 100644
index 0000000000..1f6ae998ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/argument-wrong-type.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)),
+ `${description} does not convert to a valid ISO string (first argument)`
+ );
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg),
+ `${description} does not convert to a valid ISO string (second argument)`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)), `${description} is not a valid property bag and does not convert to a string (first argument)`);
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg), `${description} is not a valid property bag and does not convert to a string (second argument)`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js
new file mode 100644
index 0000000000..704b0177e7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/basic.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Basic tests for compare()
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const nov94bis = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, nov94), 0, "same object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, nov94bis), 0, "different object");
+assert.sameValue(Temporal.PlainYearMonth.compare(nov94, jun13), -1, "before");
+assert.sameValue(Temporal.PlainYearMonth.compare(jun13, nov94), 1, "after");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js
new file mode 100644
index 0000000000..bef5d9ad97
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Tests that Temporal.PlainYearMonth.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..4591181208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg1 = { year: 2000, month: 5, calendar };
+const arg2 = new Temporal.PlainYearMonth(2019, 6);
+
+Temporal.PlainYearMonth.compare(arg1, arg2);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar (first argument)");
+
+calendar.yearMonthFromFieldsCallCount = 0;
+
+Temporal.PlainYearMonth.compare(arg2, arg1);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
new file mode 100644
index 0000000000..a4e08ca12f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: calendar1 },
+ { year: 2001, month: 6, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
new file mode 100644
index 0000000000..60b3f7026d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: temporalObject },
+ { year: 2001, month: 6, calendar: temporalObject },
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..ee9fe4aae1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+Temporal.PlainYearMonth.compare({ year: 2000, month: 5, calendar }, { year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js
new file mode 100644
index 0000000000..46bab4df8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-calendar.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: compare() does not take the calendar into account
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this._id = id;
+ }
+ toString() {
+ return this._id;
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("a"), 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("b"), 1);
+assert.sameValue(Temporal.PlainYearMonth.compare(ym1, ym2), 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js
new file mode 100644
index 0000000000..91d93770c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/compare-reference-day.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: compare() takes the reference day into account
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, iso, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, iso, 2);
+assert.sameValue(Temporal.PlainYearMonth.compare(ym1, ym2), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..ae2f23fafb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..ae032c2fef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/duplicate-calendar-fields.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js
new file mode 100644
index 0000000000..38dd3eb88e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/exhaustive.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Tests for compare() with each possible outcome
+features: [Temporal]
+---*/
+
+const cal1 = "iso8601";
+const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601");
+
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(1987, 5, cal2)),
+ 1,
+ "year >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 12, cal1), new Temporal.PlainYearMonth(2048, 12, cal2)),
+ -1,
+ "year <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(2000, 3, cal2)),
+ 1,
+ "month >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 4, cal1), new Temporal.PlainYearMonth(1981, 12, cal2)),
+ -1,
+ "month <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1, 30), new Temporal.PlainYearMonth(2000, 5, cal2, 14)),
+ 1,
+ "reference day >"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(1981, 4, cal1, 1), new Temporal.PlainYearMonth(1981, 4, cal2, 12)),
+ -1,
+ "reference day <"
+);
+assert.sameValue(
+ Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2000, 5, cal1), new Temporal.PlainYearMonth(2000, 5, cal2)),
+ 0,
+ "="
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6d55f794e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js
new file mode 100644
index 0000000000..ab971deb60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/leap-second.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Leap second is a valid ISO string for PlainYearMonth
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2016, 12));
+assert.sameValue(result1, 0, "leap second is a valid ISO string for PlainYearMonth (first argument)");
+const result2 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2016, 12), arg);
+assert.sameValue(result2, 0, "leap second is a valid ISO string for PlainYearMonth (second argument)");
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2016, 12));
+assert.sameValue(result3, 0, "second: 60 is ignored in property bag for PlainYearMonth (first argument)");
+const result4 = Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2016, 12), arg);
+assert.sameValue(result4, 0, "second: 60 is ignored in property bag for PlainYearMonth (second argument)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js
new file mode 100644
index 0000000000..dedfbae35d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js
new file mode 100644
index 0000000000..a76b7a59e8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.name is "compare".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js
new file mode 100644
index 0000000000..d83397671c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.compare), false,
+ "isConstructor(Temporal.PlainYearMonth.compare)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js
new file mode 100644
index 0000000000..a8a01f21da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: The "compare" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.compare,
+ "function",
+ "`typeof PlainYearMonth.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..d7757e3a18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.compare
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(arg, new Temporal.PlainYearMonth(2019, 6)));
+assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(new Temporal.PlainYearMonth(2019, 6), arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
new file mode 100644
index 0000000000..95f9927c41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersYearMonth extends Temporal.PlainYearMonth {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersYearMonth(2000, 5);
+const two = new AvoidGettersYearMonth(2006, 3);
+assert.sameValue(Temporal.PlainYearMonth.compare(one, two), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js
new file mode 100644
index 0000000000..50486522de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/compare/year-zero.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Negative zero, as an extended year, fails
+esid: sec-temporal.plainyearmonth.compare
+features: [Temporal]
+---*/
+
+const ok = new Temporal.PlainYearMonth(2000, 5);
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(arg, ok),
+ "Cannot use minus zero as extended year (first argument)"
+ );
+
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.compare(ok, arg),
+ "Cannot use minus zero as extended year (second argument)"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js
new file mode 100644
index 0000000000..7b94464b55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/constructor.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainYearMonth(1970, 1));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..84532a349e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+Temporal.PlainYearMonth.from(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js
new file mode 100644
index 0000000000..d4e9bb0eae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-number.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js
new file mode 100644
index 0000000000..9c63069bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-object.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: An object argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 2019, monthCode: "M11" }),
+ 2019, 11, "M11", "Only monthCode");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 2019, month: 11 }),
+ 2019, 11, "M11", "Only month");
+assert.throws(RangeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019, month: 11, monthCode: "M12" }),
+ "Mismatch between month and monthCode");
+
+const monthDayItem = { year: 2019, month: 11, get day() { throw new Test262Error("should not read the day property") } };
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from(monthDayItem),
+ 2019, 11, "M11", "month with day");
+
+const monthCodeDayItem = { year: 2019, monthCode: "M11", get day() { throw new Test262Error("should not read the day property") } };
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from(monthCodeDayItem),
+ 2019, 11, "M11", "monthCode with day");
+
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({}),
+ "No properties");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019 }),
+ "Only year");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: 2019, months: 6 }),
+ "Year and plural 'months'");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ month: 6 }),
+ "Only month");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ monthCode: "M06" }),
+ "Only monthCode");
+assert.throws(TypeError,
+ () => Temporal.PlainYearMonth.from({ year: undefined, month: 6 }),
+ "year explicit undefined");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 1976, month: 11, months: 12 }),
+ 1976, 11, "M11", "Plural property ignored");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js
new file mode 100644
index 0000000000..05becd1028
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plaindate.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A PlainYearMonth argument is cloned
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+const plainYearMonth = Temporal.PlainYearMonth.from(plainDate);
+TemporalHelpers.assertPlainYearMonth(plainYearMonth, 1976, 11, "M11");
+const fields = plainYearMonth.getISOFields();
+assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+assert.sameValue(fields.isoDay, 1, "isoDay");
+assert.sameValue(fields.isoMonth, 11, "isoMonth");
+assert.sameValue(fields.isoYear, 1976, "isoYear");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js
new file mode 100644
index 0000000000..23d8832f2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-plainyearmonth.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A PlainYearMonth object is copied, not returned directly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const orig = new Temporal.PlainYearMonth(2000, 5, undefined, 7);
+const result = Temporal.PlainYearMonth.from(orig);
+
+TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2000, 5, "M05",
+ "PlainYearMonth is copied",
+ /* era = */ undefined, /* eraYear = */ undefined, /* isoDay = */ 7
+);
+
+assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "Calendar is copied");
+
+assert.notSameValue(
+ result,
+ orig,
+ "When a PlainYearMonth is given, the returned value is not the original PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..1b09db947a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(result, 2019, 6, "M06", "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..22b71d7e9c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 6, "M06",
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..83ccb1205b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..89c9e2fd19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(result, 2019, 6, "M06", `Calendar created from string "${calendar}"`);
+assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..043b3d9d48
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..9d039d2365
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..833cb89b30
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..ff157f3086
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..f1eb91f5f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js
new file mode 100644
index 0000000000..4466c79b3d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-invalid.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(input, { overflow: "reject" }));
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(input, { overflow: "constrain" }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..1cf41e7de7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..87c47ab87d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js
new file mode 100644
index 0000000000..964481e3c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-separators.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..cf078a1a26
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-time-zone-annotation.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js
new file mode 100644
index 0000000000..248ccdca42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-trailing-junk.js
@@ -0,0 +1,13 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: RangeError thrown if a string with trailing junk is used as a PlainYearMonth
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from("1976-11junk"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..982ae9ad9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+tests.forEach(([arg, description]) => {
+ const result = Temporal.PlainYearMonth.from(arg);
+
+ TemporalHelpers.assertPlainYearMonth(
+ result,
+ 2019, 12, "M12",
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..4841b03596
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string-with-utc-designator.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js
new file mode 100644
index 0000000000..59b9f44fbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-string.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const plainYearMonth = Temporal.PlainYearMonth.from(input);
+ TemporalHelpers.assertPlainYearMonth(plainYearMonth, 1976, 11, "M11");
+ const fields = plainYearMonth.getISOFields();
+ assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+ assert.sameValue(fields.isoDay, 1, "isoDay");
+ assert.sameValue(fields.isoMonth, 11, "isoMonth");
+ assert.sameValue(fields.isoYear, 1976, "isoYear");
+}
+
+for (const input of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const plainYearMonth = Temporal.PlainYearMonth.from(input);
+ TemporalHelpers.assertPlainYearMonth(plainYearMonth, -9999, 11, "M11");
+ const fields = plainYearMonth.getISOFields();
+ assert.sameValue(fields.calendar, "iso8601", "calendar slot should store a string");
+ assert.sameValue(fields.isoDay, 1, "isoDay");
+ assert.sameValue(fields.isoMonth, 11, "isoMonth");
+ assert.sameValue(fields.isoYear, -9999, "isoYear");
+ assert.sameValue(plainYearMonth.toString(), "-009999-11");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js
new file mode 100644
index 0000000000..de5a5590f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/argument-wrong-type.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js
new file mode 100644
index 0000000000..233cebcb81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/builtin.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Tests that Temporal.PlainYearMonth.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.from.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..e019e44b5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const arg = { year: 2000, month: 5, calendar };
+Temporal.PlainYearMonth.from(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
new file mode 100644
index 0000000000..7f20933b28
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
new file mode 100644
index 0000000000..eb863f5961
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar: temporalObject });
+ assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..83c2be28c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..066993f13a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..c8e260b3b1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js
new file mode 100644
index 0000000000..cf9eae3c06
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/leap-second.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let arg = "2016-12-31T23:59:60";
+
+const result1 = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result1,
+ 2016, 12, "M12",
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+const result2 = Temporal.PlainYearMonth.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainYearMonth(
+ result2,
+ 2016, 12, "M12",
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+
+const result3 = Temporal.PlainYearMonth.from(arg);
+TemporalHelpers.assertPlainYearMonth(
+ result3,
+ 2016, 12, "M12",
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+const result4 = Temporal.PlainYearMonth.from(arg, { overflow: "reject" });
+TemporalHelpers.assertPlainYearMonth(
+ result4,
+ 2016, 12, "M12",
+ "second: 60 is ignored in property bag for PlainYearMonth even with overflow: reject"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js
new file mode 100644
index 0000000000..f652f2ef2e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js
new file mode 100644
index 0000000000..2cc1f868bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/limits.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: PlainYearMonth.from enforces the supported range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+["reject", "constrain"].forEach((overflow) => {
+ [{ year: -271821, month: 3 }, { year: 275760, month: 10 }, "-271821-03", "+275760-10"].forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(value, { overflow }),
+ `${JSON.stringify(value)} with ${overflow}`);
+ });
+});
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: -271821, month: 4 }),
+ -271821, 4, "M04", "min object");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from({ year: 275760, month: 9 }),
+ 275760, 9, "M09", "max object");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("-271821-04"),
+ -271821, 4, "M04", "min string");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("+275760-09"),
+ 275760, 9, "M09", "max string");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js
new file mode 100644
index 0000000000..1442021f14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.name is "from".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js
new file mode 100644
index 0000000000..df8fdb4b08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.from), false,
+ "isConstructor(Temporal.PlainYearMonth.from)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js
new file mode 100644
index 0000000000..bb5d83b298
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-primitive.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: overflow property is extracted with string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+const result = Temporal.PlainYearMonth.from("2021-05", options);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainYearMonth(result, 2021, 5, "M05");
+
+actual.splice(0); // empty it for the next check
+const failureExpected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+assert.throws(TypeError, () => Temporal.PlainYearMonth.from(7, options));
+assert.compareArray(actual, failureExpected, "Failing call");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js
new file mode 100644
index 0000000000..567cc6b653
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/observable-get-overflow-argument-string-invalid.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: overflow property is extracted with ISO-invalid string argument.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+];
+
+let actual = [];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from("2020-13", options));
+assert.compareArray(actual, expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js
new file mode 100644
index 0000000000..71a0c690bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-invalid.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const items = [
+ { year: 2000, month: 11 },
+ "2000-11",
+ new Temporal.PlainYearMonth(2000, 11),
+];
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+
+for (const item of items) {
+ for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from(item, badOptions));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js
new file mode 100644
index 0000000000..e836ed81bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.from
+description: Empty object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainYearMonth(
+ Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, {}), 2021, 1, "M01",
+ "options may be an empty plain object"
+);
+
+TemporalHelpers.assertPlainYearMonth(
+ Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, () => {}), 2021, 1, "M01",
+ "options may be an empty function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js
new file mode 100644
index 0000000000..1a4d3c9afa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13 };
+
+const explicit = Temporal.PlainYearMonth.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainYearMonth.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js
new file mode 100644
index 0000000000..5b937ec8b7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/options-wrong-type.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+for (const value of badOptions) {
+ assert.throws(TypeError, () => Temporal.PlainYearMonth.from({ year: 2021, monthCode: "M01" }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
new file mode 100644
index 0000000000..7d9082ffa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
@@ -0,0 +1,80 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // GetTemporalCalendarSlotValueWithISODefault
+ "get fields.calendar",
+ "has fields.calendar.dateAdd",
+ "has fields.calendar.dateFromFields",
+ "has fields.calendar.dateUntil",
+ "has fields.calendar.day",
+ "has fields.calendar.dayOfWeek",
+ "has fields.calendar.dayOfYear",
+ "has fields.calendar.daysInMonth",
+ "has fields.calendar.daysInWeek",
+ "has fields.calendar.daysInYear",
+ "has fields.calendar.fields",
+ "has fields.calendar.id",
+ "has fields.calendar.inLeapYear",
+ "has fields.calendar.mergeFields",
+ "has fields.calendar.month",
+ "has fields.calendar.monthCode",
+ "has fields.calendar.monthDayFromFields",
+ "has fields.calendar.monthsInYear",
+ "has fields.calendar.weekOfYear",
+ "has fields.calendar.year",
+ "has fields.calendar.yearMonthFromFields",
+ "has fields.calendar.yearOfWeek",
+ // lookup
+ "get fields.calendar.fields",
+ "get fields.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call fields.calendar.fields",
+ // PrepareTemporalFields
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarYearMonthFromFields
+ "call fields.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"),
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+Temporal.PlainYearMonth.from(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js
new file mode 100644
index 0000000000..7d27364aa0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-constrain.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Reject value for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const propertyBag = { year: 2000, month: 13 };
+const plainYearMonth = Temporal.PlainYearMonth.from(propertyBag, { overflow: "constrain" });
+TemporalHelpers.assertPlainYearMonth(plainYearMonth, 2000, 12, "M12", "default overflow is constrain");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
new file mode 100644
index 0000000000..b47d26ae14
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const value of validValues) {
+ for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(value, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js
new file mode 100644
index 0000000000..204f834311
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-reject.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Reject value for overflow option
+features: [Temporal]
+---*/
+
+const bad = { year: 2019, month: 13 };
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(bad, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
new file mode 100644
index 0000000000..1ead004463
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ "2000-05",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainYearMonth.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainYearMonth(explicit, 2000, 5, "M05", "overflow is ignored");
+ const implicit = Temporal.PlainYearMonth.from(value, {});
+ TemporalHelpers.assertPlainYearMonth(implicit, 2000, 5, "M05", "overflow is ignored");
+ const lambda = Temporal.PlainYearMonth.from(value, () => {});
+ TemporalHelpers.assertPlainYearMonth(lambda, 2000, 5, "M05", "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13 };
+const explicit = Temporal.PlainYearMonth.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = Temporal.PlainYearMonth.from(propertyBag, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
+const lambda = Temporal.PlainYearMonth.from(propertyBag, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2000, 12, "M12", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
new file mode 100644
index 0000000000..17564481ba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainYearMonth.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05", descr),
+));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js
new file mode 100644
index 0000000000..1ca65fe582
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: The "from" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.from,
+ "function",
+ "`typeof PlainYearMonth.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..eedf95b18b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.from
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+
+assert.throws(RangeError, () => Temporal.PlainYearMonth.from(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js
new file mode 100644
index 0000000000..807269128f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainYearMonth,
+ "from",
+ ["2000-05"],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js
new file mode 100644
index 0000000000..07ffa27808
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/from/year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => Temporal.PlainYearMonth.from(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..ee276ac931
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js
new file mode 100644
index 0000000000..06d322c288
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js
new file mode 100644
index 0000000000..92f711a442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/limits.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Limits for the PlainYearMonth constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(-271821, 3), "min");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 10), "max");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(-271821, 4),
+ -271821, 4, "M04", "min");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(-271821, 4, "iso8601", 18),
+ -271821, 4, "M04", "min with referenceISODay",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 18);
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(275760, 9),
+ 275760, 9, "M09", "max");
+TemporalHelpers.assertPlainYearMonth(new Temporal.PlainYearMonth(275760, 9, "iso8601", 14),
+ 275760, 9, "M09", "max with referenceISODay",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js
new file mode 100644
index 0000000000..0c0f0d973e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/missing-arguments.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js
new file mode 100644
index 0000000000..fec362e0a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth.name is "PlainYearMonth"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth, "name", {
+ value: "PlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..3bfb7a66f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,44 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is -Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(-Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(-Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js
new file mode 100644
index 0000000000..fc3afdc33d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: The "PlainYearMonth" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth,
+ "function",
+ "`typeof PlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal, "PlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js
new file mode 100644
index 0000000000..7189fa8806
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const maxCases = [
+ ["P273790Y8M12DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 12, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M12DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 12, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285714W2DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285714, days: 2, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000000DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000000, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000023H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000023, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000001439M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000001439, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640000086399.999999999S", "string with max seconds"],
+ [{ seconds: 8640000086399, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainYearMonth(result, 275760, 9, "M09", `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M42DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -42, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M42DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -42, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285718W5DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285718, days: -5, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000031DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000031, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000767H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000767, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000046079M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000046079, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640002764799.999999999S", "string with min seconds"],
+ [{ seconds: -8640002764799, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.add(arg);
+ TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js
new file mode 100644
index 0000000000..57fd0409c0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-object.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: A Duration object is supported as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const diff = Temporal.Duration.from("P18Y7M");
+TemporalHelpers.assertPlainYearMonth(nov94.add(diff), 2013, 6, "M06");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..be8dff7e3e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js
new file mode 100644
index 0000000000..ca3bc50ed5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+assert.throws(
+ TypeError,
+ () => instance.add({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.add({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js
new file mode 100644
index 0000000000..e7fe44f217
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Using lower units in add() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ days: 1 }, 2019, 11, "M11"],
+ [{ days: 29 }, 2019, 11, "M11"],
+ [{ hours: 1 }, 2019, 11, "M11"],
+ [{ minutes: 1 }, 2019, 11, "M11"],
+ [{ seconds: 1 }, 2019, 11, "M11"],
+ [{ milliseconds: 1 }, 2019, 11, "M11"],
+ [{ microseconds: 1 }, 2019, 11, "M11"],
+ [{ nanoseconds: 1 }, 2019, 11, "M11"],
+ [{ days: 30 }, 2019, 12, "M12"],
+ [{ days: 31 }, 2019, 12, "M12"],
+ [{ days: 60 }, 2019, 12, "M12"],
+ [{ days: 61 }, 2020, 1, "M01"],
+ [{ hours: 720 }, 2019, 12, "M12"],
+ [{ minutes: 43200 }, 2019, 12, "M12"],
+ [{ seconds: 2592000 }, 2019, 12, "M12"],
+ [{ milliseconds: 2592000_000 }, 2019, 12, "M12"],
+ [{ microseconds: 2592000_000_000 }, 2019, 12, "M12"],
+ [{ nanoseconds: 2592000_000_000_000 }, 2019, 12, "M12"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js
new file mode 100644
index 0000000000..100bd0924c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.add({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
new file mode 100644
index 0000000000..3543c747f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(TypeError, () => instance.add(undefined), "undefined");
+assert.throws(TypeError, () => instance.add(null), "null");
+assert.throws(TypeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.add(7), "number");
+assert.throws(TypeError, () => instance.add(7n), "bigint");
+assert.throws(TypeError, () => instance.add([]), "array");
+assert.throws(TypeError, () => instance.add(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js
new file mode 100644
index 0000000000..1ea7154439
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-object.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Passing an object to add() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ months: 2 }, 2020, 1, "M01"],
+ [{ years: 1 }, 2020, 11, "M11"],
+ [{ months: -2 }, 2019, 9, "M09"],
+ [{ years: -1 }, 2018, 11, "M11"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.add(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js
new file mode 100644
index 0000000000..47fdacb939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.add(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..525a26d87f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
new file mode 100644
index 0000000000..f7f1095695
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.add("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js
new file mode 100644
index 0000000000..7122227f4d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const add = Temporal.PlainYearMonth.prototype.add;
+
+assert.sameValue(typeof add, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => add.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => add.apply(null, args), "null");
+assert.throws(TypeError, () => add.apply(true, args), "true");
+assert.throws(TypeError, () => add.apply("", args), "empty string");
+assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => add.apply(1, args), "1");
+assert.throws(TypeError, () => add.apply({}, args), "plain object");
+assert.throws(TypeError, () => add.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => add.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..97ad7c2fa1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.add({ years: 5, months: 2 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4aa25749dc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.add(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js
new file mode 100644
index 0000000000..9fc7f42ab9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
new file mode 100644
index 0000000000..a0e16c38bf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { extra: 5 }, "options");
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
+assert.compareArray(actual, expected, "extra field options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
new file mode 100644
index 0000000000..1fc4f0632c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+ // Temporal.Calendar.prototype.yearMonthFromFields (toPrimitiveObserver copied but not options object)
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 10 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2001, 1, "M01");
+assert.compareArray(actual, expected, "copied options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..272afc1e73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Duration addition to PlainYearMonth calls Calendar.dateAdd the right number of times
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: 5}), 1983, 4, 'M04', "Adding 5 weeks to March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with positive add");
+
+calendar.dateAddCallCount = 0;
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: -5}), 1983, 2, 'M02', "Adding -5 weeks to March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with negative add");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js
new file mode 100644
index 0000000000..38f18e0d1b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ TemporalHelpers.assertPlainDate(plainDate, 2000, 3, "M03", 1, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 10 });
+TemporalHelpers.assertPlainYearMonth(result, 2001, 1, "M01");
+assert.sameValue(calls, 1, "should have called dateAdd");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js
new file mode 100644
index 0000000000..a69ce9aa7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js
@@ -0,0 +1,164 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Calls calendar's dateFromFields method to obtain a start date for the
+ operation, based on the sign of the duration
+info: |
+ 8. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»).
+ 9. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0).
+ 10. If _sign_ < 0, then
+ a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_).
+ b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_).
+ 11. Else,
+ a. Let _day_ be 1.
+ 12. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_).
+ 13. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*).
+includes: [deepEqual.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCalls = [];
+ }
+ year(date) {
+ // years in this calendar start and end on the same day as ISO 8601 years
+ return date.getISOFields().isoYear;
+ }
+ month(date) {
+ // this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6
+ const { isoYear, isoMonth, isoDay } = date.getISOFields();
+ const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay);
+ return Math.floor((isoDate.dayOfYear - 1) / 36) + 1;
+ }
+ monthCode(date) {
+ return "M" + this.month(date).toString().padStart(2, "0");
+ }
+ day(date) {
+ return (date.dayOfYear - 1) % 36 + 1;
+ }
+ daysInMonth(date) {
+ if (this.month(date) < 11) return 36;
+ return this.daysInYear(date) - 360;
+ }
+ _dateFromFieldsImpl({ year, month, monthCode, day }) {
+ if (year === undefined) throw new TypeError("year required");
+ if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required");
+ if (month !== undefined && month < 1) throw new RangeError("month < 1");
+ if (day === undefined) throw new TypeError("day required");
+
+ if (monthCode !== undefined) {
+ const numberPart = +(monthCode.slice(1));
+ if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode");
+ if (month === undefined) {
+ month = numberPart;
+ } else if (month !== numberPart) {
+ throw new RangeError("month and monthCode must match");
+ }
+ }
+
+ const isoDayOfYear = (month - 1) * 36 + day;
+ return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this);
+ }
+ dateFromFields(...args) {
+ this.dateFromFieldsCalls.push(args);
+ return this._dateFromFieldsImpl(...args);
+ }
+ yearMonthFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ dateAdd(date, duration, options) {
+ const {isoYear, isoMonth, isoDay} = date.getISOFields();
+ let {years, months, weeks, days} = duration;
+ let iter = new Temporal.PlainDate(isoYear + years, isoMonth, isoDay, "iso8601");
+ const monthsDays = months * 36;
+ if (iter.dayOfYear + monthsDays > iter.daysInYear || iter.dayOfYear + monthsDays < 1)
+ throw new Error("complicated addition not implemented in this test");
+ return iter.add({ weeks, days: monthsDays + days }).withCalendar(this);
+ }
+ toString() {
+ return "thirty-six";
+ }
+}
+
+const calendar = new CustomCalendar();
+const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar });
+const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35);
+const oneMonth = new Temporal.Duration(0, 0, 0, 36);
+
+// Reference ISO dates in the custom calendar:
+// M01 = 01-01
+// M02 = 02-06
+// M03 = 03-14
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(lessThanOneMonth),
+ 2022, 2, "M02",
+ "adding positive less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when adding positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(oneMonth),
+ 2022, 3, "M03",
+ "adding positive one month's worth of days yields the following month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when adding positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(lessThanOneMonth.negated()),
+ 2022, 2, "M02",
+ "adding negative less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when adding negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.add(oneMonth.negated()),
+ 2022, 1, "M01",
+ "adding negative one month's worth of days yields the previous month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 1
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when adding negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
new file mode 100644
index 0000000000..57d5f3549e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.add step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.add({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0fdeb5d83c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.add(new Temporal.Duration(1));
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..9daccc5f77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-yearmonthfromfields-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype object
+ as the options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2019, 6, calendar);
+const argument = new Temporal.Duration(1, 1);
+instance.add(argument);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..f92a871207
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.add({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js
new file mode 100644
index 0000000000..e5af2a5d7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/custom-daysInMonth-irrelevant.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Addition of a negative duration to a PlainYearMonth is not influenced by the implementation of daysInMonth()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(ym, ...args) {
+ return 15;
+ }
+}
+
+const customCalendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(2023, 3, customCalendar);
+
+TemporalHelpers.assertPlainYearMonth(instance.add({days: -30}), 2023, 3, 'M03', "Adding -30 days from calendar reimplementing daysinMonth()")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..8c26670fdc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.add({days: 123}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js
new file mode 100644
index 0000000000..58fd4b9fe3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/end-of-month-out-of-range.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: RangeError thrown when adding negative duration and end of month is out of range
+features: [Temporal]
+info: |
+ AddDurationToOrSubtractDurationFromPlainYearMonth:
+ 12. If _sign_ &lt; 0, then
+ a. Let _oneMonthDuration_ be ! CreateTemporalDuration(0, 1, 0, 0, 0, 0, 0, 0, 0, 0).
+ b. Let _nextMonth_ be ? CalendarDateAdd(_calendar_, _intermediateDate_, _oneMonthDuration_, *undefined*, _dateAdd_).
+ c. Let _endOfMonthISO_ be ! AddISODate(_nextMonth_.[[ISOYear]], _nextMonth_.[[ISOMonth]], _nextMonth_.[[ISODay]], 0, 0, 0, -1, *"constrain"*).
+ d. Let _endOfMonth_ be ? CreateTemporalDate(_endOfMonthISO_.[[Year]], _endOfMonthISO_.[[Month]], _endOfMonthISO_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const duration = new Temporal.Duration(0, 0, 0, -1);
+
+// Calendar addition result is out of range
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 9).add(duration), "Addition of 1 month to receiver out of range");
+
+// Calendar addition succeeds, but subtracting 1 day gives out of range result
+const cal = new class extends Temporal.Calendar {
+ dateAdd() {
+ return new Temporal.PlainDate(-271821, 4, 19);
+ }
+}("iso8601");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(2000, 1, cal).add(duration), "Subtraction of 1 day from next month out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..263c8f9532
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js
new file mode 100644
index 0000000000..7fd1c9d437
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js
new file mode 100644
index 0000000000..190bb5bbda
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/limits.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: RangeError thrown when going out of range
+features: [Temporal]
+---*/
+
+const max = Temporal.PlainYearMonth.from("+275760-09");
+for (const overflow of ["reject", "constrain"]) {
+ assert.throws(RangeError, () => max.add({ months: 1 }, { overflow }), overflow);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js
new file mode 100644
index 0000000000..9ab1999987
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/month-length.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: add() takes month length into account
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 27 }),
+ 2019, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").add({ days: 28 }),
+ 2019, 3, "M03");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 28 }),
+ 2020, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").add({ days: 29 }),
+ 2020, 3, "M03");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 29 }),
+ 2019, 11, "M11");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").add({ days: 30 }),
+ 2019, 12, "M12");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 30 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").add({ days: 31 }),
+ 2020, 2, "M02");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js
new file mode 100644
index 0000000000..eb28c6ec22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..6af1f2bf62
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..c9291564a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js
new file mode 100644
index 0000000000..f547f9b87f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Temporal.PlainYearMonth.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.add), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.add)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js
new file mode 100644
index 0000000000..49d0259b77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-invalid.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Invalid options throw
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => ym.add({ years: 1 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js
new file mode 100644
index 0000000000..b1e9c17962
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.add({ months: 1 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2019, 11, "M11",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.add({ months: 1 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2019, 11, "M11",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
new file mode 100644
index 0000000000..1dc3954e38
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called = true;
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 1, calendar);
+const duration = { months: 1 };
+
+yearmonth.add(duration, undefined);
+yearmonth.add(duration);
+
+assert(calendar.called);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js
new file mode 100644
index 0000000000..ec5796ece6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.add({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
new file mode 100644
index 0000000000..f1be5cbd2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
@@ -0,0 +1,183 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // CalendarDateAdd
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.add(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // SnapshotOwnProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
new file mode 100644
index 0000000000..5ee0755a73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.add(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js
new file mode 100644
index 0000000000..be130752f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.add() actually ignores the
+// overflow option. There is no addition in the ISO calendar that we could test
+// which would actually show a difference between the 'constrain' and 'reject'
+// values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2001, 6, "M06", "default overflow is constrain");
+const implicit = yearmonth.add(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2001, 6, "M06", "default overflow is constrain");
+const lambda = yearmonth.add(duration, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2001, 6, "M06", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js
new file mode 100644
index 0000000000..8a228a78b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.add(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: 2 }), "number");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.add(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 2001, 6, "M06", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js
new file mode 100644
index 0000000000..ef7c146f3c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: The "add" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.add,
+ "function",
+ "`typeof PlainYearMonth.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..de323c1214
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.add({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js
new file mode 100644
index 0000000000..2afd7da12a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "add",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 6, "M06"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js
new file mode 100644
index 0000000000..0d9b9175de
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.calendarid
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const calendarId = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "calendarId").get;
+
+assert.sameValue(typeof calendarId, "function");
+
+assert.throws(TypeError, () => calendarId.call(undefined), "undefined");
+assert.throws(TypeError, () => calendarId.call(null), "null");
+assert.throws(TypeError, () => calendarId.call(true), "true");
+assert.throws(TypeError, () => calendarId.call(""), "empty string");
+assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol");
+assert.throws(TypeError, () => calendarId.call(1), "1");
+assert.throws(TypeError, () => calendarId.call({}), "plain object");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => calendarId.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..40e1330c6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.calendarid
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.calendarId;
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js
new file mode 100644
index 0000000000..ec83805f4c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.calendarid
+description: The "calendarId" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "calendarId");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/calendarId/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js
new file mode 100644
index 0000000000..5584c1d064
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/constructor.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.constructor
+description: Test for Temporal.PlainYearMonth.prototype.constructor.
+info: The initial value of Temporal.PlainYearMonth.prototype.constructor is %Temporal.PlainYearMonth%.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "constructor", {
+ value: Temporal.PlainYearMonth,
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js
new file mode 100644
index 0000000000..1a39263196
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/basic.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: daysInMonth works
+features: [Temporal]
+---*/
+
+const tests = [
+ [new Temporal.PlainYearMonth(1976, 2), 29],
+ [new Temporal.PlainYearMonth(1976, 11), 30],
+ [new Temporal.PlainYearMonth(1976, 12), 31],
+ [new Temporal.PlainYearMonth(1977, 2), 28],
+];
+for (const [plainYearMonth, expected] of tests) {
+ assert.sameValue(plainYearMonth.daysInMonth, expected, `${expected} days in the month of ${plainYearMonth}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js
new file mode 100644
index 0000000000..4f62df5ae4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInMonth").get;
+
+assert.sameValue(typeof daysInMonth, "function");
+
+assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInMonth.call(null), "null");
+assert.throws(TypeError, () => daysInMonth.call(true), "true");
+assert.throws(TypeError, () => daysInMonth.call(""), "empty string");
+assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInMonth.call(1), "1");
+assert.throws(TypeError, () => daysInMonth.call({}), "plain object");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => daysInMonth.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a15e38458f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.daysinmonth
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInMonth should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.daysInMonth;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js
new file mode 100644
index 0000000000..6dec04d593
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Custom calendar tests for daysInMonth().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "daysInMonth arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.daysInMonth;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 0000000000..65a27466d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js
new file mode 100644
index 0000000000..bbdfb954a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: Validate result returned from calendar daysInMonth() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInMonth() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js
new file mode 100644
index 0000000000..d5454b3721
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/basic.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: daysInYear works
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainYearMonth(1976, 11)).daysInYear, 366, "leap year");
+assert.sameValue((new Temporal.PlainYearMonth(1977, 11)).daysInYear, 365, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js
new file mode 100644
index 0000000000..c8f726278e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const daysInYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInYear").get;
+
+assert.sameValue(typeof daysInYear, "function");
+
+assert.throws(TypeError, () => daysInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => daysInYear.call(null), "null");
+assert.throws(TypeError, () => daysInYear.call(true), "true");
+assert.throws(TypeError, () => daysInYear.call(""), "empty string");
+assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => daysInYear.call(1), "1");
+assert.throws(TypeError, () => daysInYear.call({}), "plain object");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => daysInYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..80be46b506
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.daysinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("daysInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.daysInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js
new file mode 100644
index 0000000000..fbe58f3918
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: Custom calendar tests for daysInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "daysInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.daysInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js
new file mode 100644
index 0000000000..c9d5ae7d5a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..f600bdec79
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: Validate result returned from calendar daysInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ daysInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..88039009aa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.equals(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js
new file mode 100644
index 0000000000..b14f01000b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-cast.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: equals() casts its argument
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+
+assert.sameValue(nov94.equals({ year: 2013, month: 6 }), false, "object");
+assert.sameValue(nov94.equals({ year: 1994, month: 11 }), true, "object");
+assert.sameValue(nov94.equals("2013-06"), false, "string");
+assert.sameValue(nov94.equals("1994-11"), true, "string");
+assert.throws(TypeError, () => nov94.equals({ year: 2013 }), "missing property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js
new file mode 100644
index 0000000000..abcf713fd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..785f769b50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: The calendar name is case-insensitive
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..1b405e75e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Leap second is a valid ISO string for a calendar in a property bag
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(
+ result,
+ true,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..d0c0c6570a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.equals(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..1f6ce3ef8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-string.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: A calendar ID is valid input for Calendar
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.equals(arg);
+assert.sameValue(result, true, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..5ee7390dad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..0e652fd79b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..5ba6126ca4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..9d108c2a70
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..b24b89be0b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js
@@ -0,0 +1,55 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js
new file mode 100644
index 0000000000..dd4584f445
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.equals(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..e2abd6b0ef
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..76e3ea2a8a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js
new file mode 100644
index 0000000000..5eb55c5eed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-separators.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Time separator in string argument can vary
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..797b6ceaf1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-time-zone-annotation.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..ce906672c3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Various forms of unknown annotation
+features: [Temporal]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.equals(arg);
+
+ assert.sameValue(
+ result,
+ true,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..7a5ffda315
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js
new file mode 100644
index 0000000000..2a3cae1340
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.equals(arg);
+ assert.sameValue(result, true, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.equals(arg);
+ assert.sameValue(result, true, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js
new file mode 100644
index 0000000000..1ea064cb25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.equals(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js
new file mode 100644
index 0000000000..11e1fdfeba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/basic.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const nov94 = Temporal.PlainYearMonth.from("1994-11");
+const nov94bis = Temporal.PlainYearMonth.from("1994-11");
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+assert.sameValue(nov94.equals(nov94), true, "same object");
+assert.sameValue(nov94.equals(nov94bis), true, "different object");
+assert.sameValue(nov94.equals(jun13), false, "different year-months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js
new file mode 100644
index 0000000000..5245e85687
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const equals = Temporal.PlainYearMonth.prototype.equals;
+
+assert.sameValue(typeof equals, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+assert.throws(TypeError, () => equals.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => equals.apply(null, args), "null");
+assert.throws(TypeError, () => equals.apply(true, args), "true");
+assert.throws(TypeError, () => equals.apply("", args), "empty string");
+assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => equals.apply(1, args), "1");
+assert.throws(TypeError, () => equals.apply({}, args), "plain object");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => equals.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5e517712ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.equals(new Temporal.PlainYearMonth(2000, 5));
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js
new file mode 100644
index 0000000000..13b8448d6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..02d8d9ee6a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.equals(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 0000000000..d82f9bd6d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.equals({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 0000000000..358f74051f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.equals({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..b9dfa79a80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.equals({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js
new file mode 100644
index 0000000000..b16fab695c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-calendar.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: equals() takes the calendar into account
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+class CustomCalendar extends Temporal.Calendar {
+ constructor(id) {
+ super("iso8601");
+ this._id = id;
+ }
+ get id() {
+ actual.push(this._id);
+ return this._id;
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("should not call toString");
+ }
+}
+
+const sharedCalendar = new CustomCalendar("a");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, sharedCalendar, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, sharedCalendar, 1);
+assert.sameValue(ym1.equals(ym2), true);
+assert.compareArray(actual, [], "should not call toString if objects are equal");
+
+const ym3 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("b"), 1);
+const ym4 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("c"), 2);
+assert.sameValue(ym3.equals(ym4), false);
+assert.compareArray(actual, [], "should not call toString if ISO dates differ");
+
+const ym5 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("d"), 1);
+const ym6 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("e"), 1);
+assert.sameValue(ym5.equals(ym6), false);
+assert.compareArray(actual, ["d", "e"], "order of operations");
+
+actual.splice(0); // empty it for the next check
+const ym7 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("f"), 1);
+const ym8 = new Temporal.PlainYearMonth(2000, 1, new CustomCalendar("f"), 1);
+assert.sameValue(ym7.equals(ym8), true);
+assert.compareArray(actual, ["f", "f"], "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js
new file mode 100644
index 0000000000..12a1ebcfe6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/compare-reference-day.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: equals() takes the reference day into account
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const ym1 = new Temporal.PlainYearMonth(2000, 1, iso, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, iso, 2);
+assert.sameValue(ym1.equals(ym2), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..a86af5a09d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..028b5de48e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.equals(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..51b7eb7e73
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js
new file mode 100644
index 0000000000..ecfb5aa8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/leap-second.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Leap second is a valid ISO string for PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.equals(arg);
+assert.sameValue(
+ result1,
+ true,
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.equals(arg);
+assert.sameValue(
+ result2,
+ true,
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js
new file mode 100644
index 0000000000..844a0f96da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js
new file mode 100644
index 0000000000..686b5adae1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js
new file mode 100644
index 0000000000..77c8d7e6eb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Temporal.PlainYearMonth.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.equals), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.equals)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
new file mode 100644
index 0000000000..8da8c28acc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: The "equals" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.equals,
+ "function",
+ "`typeof PlainYearMonth.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cf77b12b86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.equals(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js
new file mode 100644
index 0000000000..a7d728148c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/use-internal-slots.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: equals() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersYearMonth extends Temporal.PlainYearMonth {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersYearMonth(2000, 5);
+const two = new AvoidGettersYearMonth(2006, 3);
+assert.sameValue(one.equals(two), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js
new file mode 100644
index 0000000000..97c0979390
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/equals/year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.equals(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js
new file mode 100644
index 0000000000..e98b91f378
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getCalendar = Temporal.PlainYearMonth.prototype.getCalendar;
+
+assert.sameValue(typeof getCalendar, "function");
+
+assert.throws(TypeError, () => getCalendar.call(undefined), "undefined");
+assert.throws(TypeError, () => getCalendar.call(null), "null");
+assert.throws(TypeError, () => getCalendar.call(true), "true");
+assert.throws(TypeError, () => getCalendar.call(""), "empty string");
+assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getCalendar.call(1), "1");
+assert.throws(TypeError, () => getCalendar.call({}), "plain object");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => getCalendar.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js
new file mode 100644
index 0000000000..dd5e96de16
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.getCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.getCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.getCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.getCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.getCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js
new file mode 100644
index 0000000000..197adcd4d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: Temporal.PlainYearMonth.prototype.getCalendar.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getCalendar, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js
new file mode 100644
index 0000000000..80d117d82e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: Temporal.PlainYearMonth.prototype.getCalendar.name is "getCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getCalendar, "name", {
+ value: "getCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js
new file mode 100644
index 0000000000..4da964f6cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: >
+ Temporal.PlainYearMonth.prototype.getCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.getCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.getCalendar), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.getCalendar)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js
new file mode 100644
index 0000000000..76161c39d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getcalendar
+description: The "getCalendar" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.getCalendar,
+ "function",
+ "`typeof PlainYearMonth.prototype.getCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "getCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getCalendar/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js
new file mode 100644
index 0000000000..c8b335d42a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const getISOFields = Temporal.PlainYearMonth.prototype.getISOFields;
+
+assert.sameValue(typeof getISOFields, "function");
+
+assert.throws(TypeError, () => getISOFields.call(undefined), "undefined");
+assert.throws(TypeError, () => getISOFields.call(null), "null");
+assert.throws(TypeError, () => getISOFields.call(true), "true");
+assert.throws(TypeError, () => getISOFields.call(""), "empty string");
+assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol");
+assert.throws(TypeError, () => getISOFields.call(1), "1");
+assert.throws(TypeError, () => getISOFields.call({}), "plain object");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => getISOFields.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js
new file mode 100644
index 0000000000..802f49025f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js
new file mode 100644
index 0000000000..66cdc86229
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/custom.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: getISOFields does not call into user code.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarThrowEverything();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const result = instance.getISOFields();
+
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 1, "isoDay result");
+assert.sameValue(result.calendar, calendar, "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js
new file mode 100644
index 0000000000..e655c4af18
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+
+const result = ym.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 1, "isoDay result");
+assert.sameValue(result.calendar, "iso8601", "calendar result");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 0000000000..cc86065908
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 0000000000..0134ca0cbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js
new file mode 100644
index 0000000000..ce4b0d448f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js
new file mode 100644
index 0000000000..5294ab200d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 0000000000..c1b408b939
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: >
+ Temporal.PlainYearMonth.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.getISOFields)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
new file mode 100644
index 0000000000..654a834e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.getISOFields,
+ "function",
+ "`typeof PlainYearMonth.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js
new file mode 100644
index 0000000000..7f6bdda8ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prototype.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Correct prototype on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const result = instance.getISOFields();
+assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js
new file mode 100644
index 0000000000..3797a79e4b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: Basic test for inLeapYear
+features: [Temporal]
+---*/
+
+assert.sameValue((new Temporal.PlainYearMonth(1976, 11)).inLeapYear,
+ true, "leap year");
+assert.sameValue((new Temporal.PlainYearMonth(1977, 11)).inLeapYear,
+ false, "non-leap year");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js
new file mode 100644
index 0000000000..d89a21bde5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "inLeapYear").get;
+
+assert.sameValue(typeof inLeapYear, "function");
+
+assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined");
+assert.throws(TypeError, () => inLeapYear.call(null), "null");
+assert.throws(TypeError, () => inLeapYear.call(true), "true");
+assert.throws(TypeError, () => inLeapYear.call(""), "empty string");
+assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => inLeapYear.call(1), "1");
+assert.throws(TypeError, () => inLeapYear.call({}), "plain object");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => inLeapYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..a826d38570
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.inleapyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear");
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("inLeapYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.inLeapYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js
new file mode 100644
index 0000000000..f131227eac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: Custom calendar tests for inLeapYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ inLeapYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "inLeapYear arguments");
+ return true;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.inLeapYear;
+assert.sameValue(result, true, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 0000000000..ab83da7706
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js
new file mode 100644
index 0000000000..dbcb59baec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/validate-calendar-value.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: Validate result returned from calendar inLeapYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [0, TypeError],
+ [-0, TypeError],
+ [42, TypeError],
+ [7.1, TypeError],
+ [NaN, TypeError],
+ [Infinity, TypeError],
+ [-Infinity, TypeError],
+ ["", TypeError],
+ ["a string", TypeError],
+ ["0", TypeError],
+ [Symbol("foo"), TypeError],
+ [0n, TypeError],
+ [42n, TypeError],
+ [{}, TypeError],
+ [{valueOf() { return false; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`);
+});
+
+const preservedResults = [
+ true,
+ false,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ inLeapYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js
new file mode 100644
index 0000000000..5f69bd865d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const month = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "month").get;
+
+assert.sameValue(typeof month, "function");
+
+assert.throws(TypeError, () => month.call(undefined), "undefined");
+assert.throws(TypeError, () => month.call(null), "null");
+assert.throws(TypeError, () => month.call(true), "true");
+assert.throws(TypeError, () => month.call(""), "empty string");
+assert.throws(TypeError, () => month.call(Symbol()), "symbol");
+assert.throws(TypeError, () => month.call(1), "1");
+assert.throws(TypeError, () => month.call({}), "plain object");
+assert.throws(TypeError, () => month.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => month.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..5750040a72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.month
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month");
+Object.defineProperty(Temporal.Calendar.prototype, "month", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("month should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.month;
+
+Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js
new file mode 100644
index 0000000000..64afcd70f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: Custom calendar tests for month().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ month(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "month arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.month;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js
new file mode 100644
index 0000000000..b4f16e85ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: The "month" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js
new file mode 100644
index 0000000000..2beafb2476
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/month/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: Validate result returned from calendar month() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ month() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js
new file mode 100644
index 0000000000..d6b41374a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthcode
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthCode = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthCode").get;
+
+assert.sameValue(typeof monthCode, "function");
+
+assert.throws(TypeError, () => monthCode.call(undefined), "undefined");
+assert.throws(TypeError, () => monthCode.call(null), "null");
+assert.throws(TypeError, () => monthCode.call(true), "true");
+assert.throws(TypeError, () => monthCode.call(""), "empty string");
+assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthCode.call(1), "1");
+assert.throws(TypeError, () => monthCode.call({}), "plain object");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => monthCode.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..d3db8ede45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.monthcode
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode");
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthCode should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.monthCode;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js
new file mode 100644
index 0000000000..bb7255b0ee
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthcode
+description: Custom calendar tests for monthCode().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthCode(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "monthCode arguments");
+ return "M01";
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.monthCode;
+assert.sameValue(result, "M01", "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js
new file mode 100644
index 0000000000..00fd595d89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js
new file mode 100644
index 0000000000..49326950bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthCode/validate-calendar-value.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthcode
+description: Validate result returned from calendar monthCode() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Symbol("foo"), TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, TypeError],
+ [{toString() { return "M01"; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthCode() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js
new file mode 100644
index 0000000000..9b6c340271
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/basic.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: monthsInYear works
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(1976, 11);
+assert.sameValue(ym.monthsInYear, 12);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js
new file mode 100644
index 0000000000..a96c7b5a5f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthsInYear").get;
+
+assert.sameValue(typeof monthsInYear, "function");
+
+assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined");
+assert.throws(TypeError, () => monthsInYear.call(null), "null");
+assert.throws(TypeError, () => monthsInYear.call(true), "true");
+assert.throws(TypeError, () => monthsInYear.call(""), "empty string");
+assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol");
+assert.throws(TypeError, () => monthsInYear.call(1), "1");
+assert.throws(TypeError, () => monthsInYear.call({}), "plain object");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => monthsInYear.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..1ecaeffa7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.monthsinyear
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear");
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("monthsInYear should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.monthsInYear;
+
+Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js
new file mode 100644
index 0000000000..db48e8f44f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Custom calendar tests for monthsInYear().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthsInYear(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "monthsInYear arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.monthsInYear;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 0000000000..b877378c7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js
new file mode 100644
index 0000000000..a06c23ef68
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/validate-calendar-value.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: Validate result returned from calendar monthsInYear() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [null, TypeError],
+ [false, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [NaN, RangeError],
+ [-7, RangeError],
+ [-0.1, RangeError],
+ ["string", TypeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [{}, TypeError],
+ [true, TypeError],
+ [7.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ monthsInYear() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js
new file mode 100644
index 0000000000..3444d7fc57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/prop-desc.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plainyearmonth-prototype
+description: The "prototype" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal.PlainYearMonth.prototype, "object");
+assert.notSameValue(Temporal.PlainYearMonth.prototype, null);
+
+verifyProperty(Temporal.PlainYearMonth, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..7a611af37f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.since(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js
new file mode 100644
index 0000000000..22f10a717e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-casting.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Calls to PYM.since cast arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = jun13.since(nov94);
+
+TemporalHelpers.assertDurationsEqual(jun13.since({ year: 1994, month: 11 }), diff, 'Casts object argument');
+TemporalHelpers.assertDurationsEqual(jun13.since('1994-11'), diff, 'Casts string argument');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js
new file mode 100644
index 0000000000..a156c0e661
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..0c2882525b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..4c9b860cff
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..08a1caa46b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..94f7794ff5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.since(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..6f2564adcd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..6c0b70f03b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..49771507ed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..5ecbd63ffa
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..fc41f5a947
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js
new file mode 100644
index 0000000000..8c2f08fa7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.since(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..f2ee8748ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..24999eface
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..266a179849
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..683d085094
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..cdc91aed7d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..1ee9e2d49b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js
new file mode 100644
index 0000000000..efc5c95b3f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..b2951d004b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js
new file mode 100644
index 0000000000..4627a2af88
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/arguments-missing-throws.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Calls to PYM.since throw when missing required arguments.
+features: [Temporal]
+---*/
+
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+
+assert.throws(TypeError, () => jun13.since({ year: 1994 }), 'Throws when missing required month');
+assert.throws(TypeError, () => jun13.since({ month: 11 }), 'Throws when missing required year');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js
new file mode 100644
index 0000000000..5b9adf908e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainYearMonth.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+assert.throws(TypeError, () => since.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => since.apply(null, args), "null");
+assert.throws(TypeError, () => since.apply(true, args), "true");
+assert.throws(TypeError, () => since.apply("", args), "empty string");
+assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => since.apply(1, args), "1");
+assert.throws(TypeError, () => since.apply({}, args), "plain object");
+assert.throws(TypeError, () => since.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => since.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..2b862b8a7b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.since({ year: 2005, month: 3 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..9332585b8c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.since(new Temporal.PlainYearMonth(1999, 4));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js
new file mode 100644
index 0000000000..c6d3d8a7c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..9ee3ebe408
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1970, 1, calendar);
+instance.since(new Temporal.PlainYearMonth(2000, 5, calendar), { smallestUnit: "year" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..75f16cfa45
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.since(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..af2e53d56a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since({ year: 2000, month: 6, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..bfa120aab8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const argument = new Temporal.PlainYearMonth(2022, 6, calendar);
+instance.since(argument);
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..5c4df0383d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.since steps 21–22:
+ 21. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 22. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 0000000000..9404d314da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.since step 14:
+ 14. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.since({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..db22d52738
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since(new Temporal.PlainYearMonth(2019, 2));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
new file mode 100644
index 0000000000..4bcf60f6ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.since({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..e1e6c19be5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.since({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..7bb6258ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..54cb1ae302
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.since(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..e8aa44d442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js
new file mode 100644
index 0000000000..dbcc180f93
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-auto.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: auto value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "auto" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "auto" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js
new file mode 100644
index 0000000000..72b038c0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-disallowed-units.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Since throws on to0-small largestUnit
+features: [Temporal, arrow-function]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+[
+ 'weeks',
+ 'days',
+ 'hours',
+ 'minutes',
+ 'seconds',
+ 'milliseconds',
+ 'microseconds',
+ 'nanoseconds'
+].forEach((largestUnit) => {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),`throws on disallowed or invalid largestUnit: ${largestUnit}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..29cf4602f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js
new file mode 100644
index 0000000000..61ce3f3567
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-months.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: months value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "months" }),
+ 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "months" }),
+ 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..e47e7c6112
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..6b212d9377
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const units = ["years", "months"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..8abe071470
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: undefined }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: undefined }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier, {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier, () => {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, () => {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..17711d11e9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js
new file mode 100644
index 0000000000..b5d8cea58a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-years.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: years value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(later.since(earlier, { largestUnit: "years" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (pos)");
+TemporalHelpers.assertDuration(earlier.since(later, { largestUnit: "years" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js
new file mode 100644
index 0000000000..1ad065f65a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js
new file mode 100644
index 0000000000..2883b71ed8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js
new file mode 100644
index 0000000000..9db026f10b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/mixed-calendar-invalid.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Mixed calendars throw as invalid
+features: [Temporal]
+---*/
+
+class customCal extends Temporal.Calendar {
+ constructor () {
+ super('iso8601');
+ }
+
+ get id() {
+ return "I am a secret cal.";
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new customCal());
+
+assert.throws(RangeError, () => ym1.since(ym2), 'since throws with different calendars');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js
new file mode 100644
index 0000000000..29a276f95f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..69770b793b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Temporal.PlainYearMonth.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.since), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..822d4570a1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-invalid.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Verify that invalid options are handled correctly.
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainYearMonth(2020, 2);
+const feb21 = new Temporal.PlainYearMonth(2021, 2);
+
+[
+ null,
+ 1,
+ 'hello',
+ true,
+ Symbol('foo'),
+ 1n
+].forEach((badOption) =>
+ assert.throws(TypeError, () => feb21.since(feb20, badOption), `${String(badOption)} throws TypeError`)
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js
new file mode 100644
index 0000000000..dacf123949
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.since(new Temporal.PlainYearMonth(1976, 11), {});
+TemporalHelpers.assertDuration(
+ result1, 42, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainYearMonth(1976, 11), () => {});
+TemporalHelpers.assertDuration(
+ result2, 42, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..29fc58b30a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+TemporalHelpers.assertDuration(later.since(earlier, undefined),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later, undefined),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(later.since(earlier),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(earlier.since(later),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..5a166db02a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainYearMonth(1976, 11), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..d5ca77eff3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
@@ -0,0 +1,192 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Properties on objects passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expectedMinimal = [
+ // ToTemporalYearMonth
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.fields",
+ "get other.calendar.yearMonthFromFields",
+ "call other.calendar.fields",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.yearMonthFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+
+const expected = expectedMinimal.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.dateUntil",
+ "get this.calendar.fields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields / CalendarDateFromFields (receiver)
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ "call this.calendar.dateFromFields",
+ // PrepareTemporalFields / CalendarDateFromFields (argument)
+ "get other.calendar.monthCode",
+ "call other.calendar.monthCode",
+ "get other.calendar.year",
+ "call other.calendar.year",
+ "call this.calendar.dateFromFields",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]);
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, ownCalendar, 1);
+
+const otherYearMonthPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "months", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// code path that skips RoundDuration:
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 1 }));
+assert.compareArray(actual, expected, "order of operations with no rounding");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.since(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateUntil", // 7.o
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.s not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
+const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.o not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.since(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 10.c
+ "call this.calendar.dateAdd", // 10.e
+ "call this.calendar.dateAdd", // 10.k MoveRelativeDate
+ // (10.n.iii MoveRelativeDate not called because weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..252d36870d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: The "since" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.since,
+ "function",
+ "`typeof PlainYearMonth.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..9a2d96724b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..89a4645edd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2022, 1);
+const later = new Temporal.PlainYearMonth(2023, 12);
+const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -12 months balances to -2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js
new file mode 100644
index 0000000000..c91f5bab6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/rounding-zero-year-month-length.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const ym1 = new Temporal.PlainYearMonth(1970, 1, cal);
+const ym2 = new Temporal.PlainYearMonth(1971, 1, cal);
+
+assert.throws(RangeError, () => ym1.since(ym2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => ym1.since(ym2, { smallestUnit: "months", roundingIncrement: 2 }), "zero month length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js
new file mode 100644
index 0000000000..4baad1a8f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-as-expected.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Since rounding increments work as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const laterSinceYear = later.since(earlier, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" });
+TemporalHelpers.assertDuration(laterSinceYear,
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of years");
+
+const laterSinceMixed = later.since(earlier, { smallestUnit: "months", roundingIncrement: 5 });
+TemporalHelpers.assertDuration(laterSinceMixed,
+ /* years = */ 2, /* months = */ 5, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of months mixed with years");
+
+const laterSinceMonth = later.since(earlier, { largestUnit: "months", smallestUnit: "months", roundingIncrement: 10 });
+TemporalHelpers.assertDuration(laterSinceMonth,
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of pure months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..b6fd705a95
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..b0a7983f1a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+// Cannot test the upper bound of 1e9 + 0.5 here, because the duration is
+// rounded relative to the receiver PlainYearMonth, and 1e9 months is outside of
+// the PlainYearMonth range.
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..2a75b13429
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..b0aee1cfb8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..5760d55317
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..1a0df86da5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..dda697ab6d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-expand.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..43295a8cb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-floor.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..c1c743a1ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..1d264b755a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..e68173a741
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..cdd03d23af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..55b3f2a109
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..f4b95b6588
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..a8b5763120
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..b87ea94053
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..d31c4709c2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..ff7ea1df01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..147d14b4cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..a041e0b4d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..d25b44a89c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js
new file mode 100644
index 0000000000..f5d8a8396a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/symmetry.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Since observes symmetry with until
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = jun13.since(nov94);
+
+TemporalHelpers.assertDurationsEqual(diff, nov94.until(jun13), 'Since is inverse of until');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js
new file mode 100644
index 0000000000..5558dc3927
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/since/year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js
new file mode 100644
index 0000000000..2396a974c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Maximum allowed duration
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const maxCases = [
+ ["P273790Y8M42DT23H59M59.999999999S", "string with max years"],
+ [{ years: 273790, months: 8, days: 42, nanoseconds: 86399999999999 }, "property bag with max years"],
+ ["P3285488M42DT23H59M59.999999999S", "string with max months"],
+ [{ months: 3285488, days: 42, nanoseconds: 86399999999999 }, "property bag with max months"],
+ ["P14285718W5DT23H59M59.999999999S", "string with max weeks"],
+ [{ weeks: 14285718, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"],
+ ["P100000031DT23H59M59.999999999S", "string with max days"],
+ [{ days: 100000031, nanoseconds: 86399999999999 }, "property bag with max days"],
+ ["PT2400000767H59M59.999999999S", "string with max hours"],
+ [{ hours: 2400000767, nanoseconds: 3599999999999 }, "property bag with max hours"],
+ ["PT144000046079M59.999999999S", "string with max minutes"],
+ [{ minutes: 144000046079, nanoseconds: 59999999999 }, "property bag with max minutes"],
+ ["PT8640002764799.999999999S", "string with max seconds"],
+ [{ seconds: 8640002764799, nanoseconds: 999999999 }, "property bag with max seconds"],
+];
+
+for (const [arg, descr] of maxCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`);
+}
+
+const minCases = [
+ ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"],
+ [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"],
+ ["-P3285488M12DT23H59M59.999999999S", "string with min months"],
+ [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"],
+ ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"],
+ [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"],
+ ["-P100000000DT23H59M59.999999999S", "string with min days"],
+ [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"],
+ ["-PT2400000023H59M59.999999999S", "string with min hours"],
+ [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"],
+ ["-PT144000001439M59.999999999S", "string with min minutes"],
+ [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"],
+ ["-PT8640000086399.999999999S", "string with min seconds"],
+ [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"],
+];
+
+for (const [arg, descr] of minCases) {
+ const result = instance.subtract(arg);
+ TemporalHelpers.assertPlainYearMonth(result, 275760, 9, "M09", `operation succeeds with ${descr}`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js
new file mode 100644
index 0000000000..3d72383b35
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: A Duration object is supported as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const jun13 = Temporal.PlainYearMonth.from("2013-06");
+const diff = Temporal.Duration.from("P18Y7M");
+TemporalHelpers.assertPlainYearMonth(jun13.subtract(diff), 1994, 11, "M11");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js
new file mode 100644
index 0000000000..d0977de87a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js
@@ -0,0 +1,75 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Duration-like argument that is out of range
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1970, 1);
+
+const cases = [
+ // 2^32 = 4294967296
+ ["P4294967296Y", "string with years > max"],
+ [{ years: 4294967296 }, "property bag with years > max"],
+ ["-P4294967296Y", "string with years < min"],
+ [{ years: -4294967296 }, "property bag with years < min"],
+ ["P4294967296M", "string with months > max"],
+ [{ months: 4294967296 }, "property bag with months > max"],
+ ["-P4294967296M", "string with months < min"],
+ [{ months: -4294967296 }, "property bag with months < min"],
+ ["P4294967296W", "string with weeks > max"],
+ [{ weeks: 4294967296 }, "property bag with weeks > max"],
+ ["-P4294967296W", "string with weeks < min"],
+ [{ weeks: -4294967296 }, "property bag with weeks < min"],
+
+ // ceil(max safe integer / 86400) = 104249991375
+ ["P104249991375D", "string with days > max"],
+ [{ days: 104249991375 }, "property bag with days > max"],
+ ["P104249991374DT24H", "string where hours balance into days > max"],
+ [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"],
+ ["-P104249991375D", "string with days < min"],
+ [{ days: -104249991375 }, "property bag with days < min"],
+ ["-P104249991374DT24H", "string where hours balance into days < min"],
+ [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"],
+
+ // ceil(max safe integer / 3600) = 2501999792984
+ ["PT2501999792984H", "string with hours > max"],
+ [{ hours: 2501999792984 }, "property bag with hours > max"],
+ ["PT2501999792983H60M", "string where minutes balance into hours > max"],
+ [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"],
+ ["-PT2501999792984H", "string with hours < min"],
+ [{ hours: -2501999792984 }, "property bag with hours < min"],
+ ["-PT2501999792983H60M", "string where minutes balance into hours < min"],
+ [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"],
+
+ // ceil(max safe integer / 60) = 150119987579017
+ ["PT150119987579017M", "string with minutes > max"],
+ [{ minutes: 150119987579017 }, "property bag with minutes > max"],
+ ["PT150119987579016M60S", "string where seconds balance into minutes > max"],
+ [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"],
+ ["-PT150119987579017M", "string with minutes < min"],
+ [{ minutes: -150119987579017 }, "property bag with minutes < min"],
+ ["-PT150119987579016M60S", "string where seconds balance into minutes < min"],
+ [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"],
+
+ // 2^53 = 9007199254740992
+ ["PT9007199254740992S", "string with seconds > max"],
+ [{ seconds: 9007199254740992 }, "property bag with seconds > max"],
+ [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"],
+ [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"],
+ ["-PT9007199254740992S", "string with seconds < min"],
+ [{ seconds: -9007199254740992 }, "property bag with seconds < min"],
+ [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"],
+ [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"],
+];
+
+for (const [arg, descr] of cases) {
+ assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js
new file mode 100644
index 0000000000..21ff0edfc5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: temporalDurationLike object must contain at least one correctly spelled property
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({}),
+ "Throws TypeError if no property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ nonsense: true }),
+ "Throws TypeError if no recognized property is present"
+);
+
+assert.throws(
+ TypeError,
+ () => instance.subtract({ sign: 1 }),
+ "Sign property is not recognized"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js
new file mode 100644
index 0000000000..ea3156580b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Using lower units in subtract() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ days: 1 }, 2019, 11, "M11"],
+ [{ hours: 1 }, 2019, 11, "M11"],
+ [{ minutes: 1 }, 2019, 11, "M11"],
+ [{ seconds: 1 }, 2019, 11, "M11"],
+ [{ milliseconds: 1 }, 2019, 11, "M11"],
+ [{ microseconds: 1 }, 2019, 11, "M11"],
+ [{ nanoseconds: 1 }, 2019, 11, "M11"],
+ [{ days: 29 }, 2019, 11, "M11"],
+ [{ days: 30 }, 2019, 10, "M10"],
+ [{ days: 60 }, 2019, 10, "M10"],
+ [{ days: 61 }, 2019, 9, "M09"],
+ [{ hours: 720 }, 2019, 10, "M10"],
+ [{ minutes: 43200 }, 2019, 10, "M10"],
+ [{ seconds: 2592000 }, 2019, 10, "M10"],
+ [{ milliseconds: 2592000_000 }, 2019, 10, "M10"],
+ [{ microseconds: 2592000_000_000 }, 2019, 10, "M10"],
+ [{ nanoseconds: 2592000_000_000_000 }, 2019, 10, "M10"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js
new file mode 100644
index 0000000000..f66b8abada
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Positive and negative values in the temporalDurationLike argument are not acceptable
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+["constrain", "reject"].forEach((overflow) => {
+ assert.throws(
+ RangeError,
+ () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
+ `mixed positive and negative values always throw (overflow = "${overflow}")`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
new file mode 100644
index 0000000000..f9460b024d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(TypeError, () => instance.subtract(undefined), "undefined");
+assert.throws(TypeError, () => instance.subtract(null), "null");
+assert.throws(TypeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(TypeError, () => instance.subtract(7), "number");
+assert.throws(TypeError, () => instance.subtract(7n), "bigint");
+assert.throws(TypeError, () => instance.subtract([]), "array");
+assert.throws(TypeError, () => instance.subtract(() => {}), "function");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js
new file mode 100644
index 0000000000..8b7393da1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Passing an object to subtract() works
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+const tests = [
+ [{ months: 2 }, 2019, 9, "M09"],
+ [{ years: 1 }, 2018, 11, "M11"],
+ [{ months: -2 }, 2020, 1, "M01"],
+ [{ years: -1 }, 2020, 11, "M11"],
+];
+
+for (const [argument, ...expected] of tests) {
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain");
+ TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject");
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js
new file mode 100644
index 0000000000..779b7dbd08
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Singular properties in the property bag are always ignored
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[
+ { year: 1 },
+ { month: 2 },
+ { week: 3 },
+ { day: 4 },
+ { hour: 5 },
+ { minute: 6 },
+ { second: 7 },
+ { millisecond: 8 },
+ { microsecond: 9 },
+ { nanosecond: 10 },
+].forEach((badObject) => {
+ assert.throws(TypeError, () => instance.subtract(badObject),
+ "Throw TypeError if temporalDurationLike is not valid");
+});
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 0000000000..b3e7c9a74c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
new file mode 100644
index 0000000000..deb2820797
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.subtract("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 2, "M02");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js
new file mode 100644
index 0000000000..badb8f195c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const subtract = Temporal.PlainYearMonth.prototype.subtract;
+
+assert.sameValue(typeof subtract, "function");
+
+const args = [new Temporal.Duration(5)];
+
+assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => subtract.apply(null, args), "null");
+assert.throws(TypeError, () => subtract.apply(true, args), "true");
+assert.throws(TypeError, () => subtract.apply("", args), "empty string");
+assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => subtract.apply(1, args), "1");
+assert.throws(TypeError, () => subtract.apply({}, args), "plain object");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..97b965d7d3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.subtract({ years: 5, months: 2 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..6b2834cf80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd");
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.subtract(new Temporal.Duration(1));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js
new file mode 100644
index 0000000000..e69c799d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
new file mode 100644
index 0000000000..b636036de0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { extra: 5 }, "options");
+
+let dateAddCalls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ dateAddCalls++;
+ if (dateAddCalls == 2)
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "extra field options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
new file mode 100644
index 0000000000..49e74d46b8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
@@ -0,0 +1,58 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // Temporal.Calendar.prototype.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // overwriting property in custom calendar dateAdd
+ "getOwnPropertyDescriptor options.overflow",
+ // Temporal.Calendar.prototype.yearMonthFromFields (toPrimitiveObserver copied but not options object)
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+let dateAddCalls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ dateAddCalls++;
+ if (dateAddCalls == 2)
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 9 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "copied options object order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..c788dd00cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Duration subtraction from PlainYearMonth calls Calendar.dateAdd the right number of times
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: 5}), 1983, 2, 'M02', "Removing 5 weeks from March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with positive subtract");
+
+calendar.dateAddCallCount = 0;
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: -5}), 1983, 4, 'M04', "Removing -5 weeks from March in is8601 calendar")
+assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with negative subtract");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js
new file mode 100644
index 0000000000..72455866bc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: PlainYearMonth.prototype.subtract should call dateAdd with the appropriate values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(plainDate, duration, options) {
+ ++calls;
+ if (calls == 2) {
+ TemporalHelpers.assertPlainDate(plainDate, 2000, 3, "M03", 31, "plainDate argument");
+ TemporalHelpers.assertDuration(duration, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument");
+ assert.sameValue(typeof options, "object", "options argument: type");
+ assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype");
+ }
+ return super.dateAdd(plainDate, duration, options);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 10 });
+TemporalHelpers.assertPlainYearMonth(result, 1999, 5, "M05");
+assert.sameValue(calls, 2, "should have called dateAdd 2 times");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js
new file mode 100644
index 0000000000..b83c3ccd36
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js
@@ -0,0 +1,164 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Calls calendar's dateFromFields method to obtain a start date for the
+ operation, based on the sign of the duration
+info: |
+ 9. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»).
+ 10. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0).
+ 11. If _sign_ < 0, then
+ a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_).
+ b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_).
+ 12. Else,
+ a. Let _day_ be 1.
+ 13. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_).
+ 14. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*).
+includes: [deepEqual.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCalls = [];
+ }
+ year(date) {
+ // years in this calendar start and end on the same day as ISO 8601 years
+ return date.getISOFields().isoYear;
+ }
+ month(date) {
+ // this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6
+ const { isoYear, isoMonth, isoDay } = date.getISOFields();
+ const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay);
+ return Math.floor((isoDate.dayOfYear - 1) / 36) + 1;
+ }
+ monthCode(date) {
+ return "M" + this.month(date).toString().padStart(2, "0");
+ }
+ day(date) {
+ return (date.dayOfYear - 1) % 36 + 1;
+ }
+ daysInMonth(date) {
+ if (this.month(date) < 11) return 36;
+ return this.daysInYear(date) - 360;
+ }
+ _dateFromFieldsImpl({ year, month, monthCode, day }) {
+ if (year === undefined) throw new TypeError("year required");
+ if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required");
+ if (month !== undefined && month < 1) throw new RangeError("month < 1");
+ if (day === undefined) throw new TypeError("day required");
+
+ if (monthCode !== undefined) {
+ const numberPart = +(monthCode.slice(1));
+ if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode");
+ if (month === undefined) {
+ month = numberPart;
+ } else if (month !== numberPart) {
+ throw new RangeError("month and monthCode must match");
+ }
+ }
+
+ const isoDayOfYear = (month - 1) * 36 + day;
+ return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this);
+ }
+ dateFromFields(...args) {
+ this.dateFromFieldsCalls.push(args);
+ return this._dateFromFieldsImpl(...args);
+ }
+ yearMonthFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ dateAdd(date, duration, options) {
+ const {isoYear, isoMonth, isoDay} = date.getISOFields();
+ let {years, months, weeks, days} = duration;
+ let iter = new Temporal.PlainDate(isoYear + years, isoMonth, isoDay, "iso8601");
+ const monthsDays = months * 36;
+ if (iter.dayOfYear + monthsDays > iter.daysInYear || iter.dayOfYear + monthsDays < 1)
+ throw new Error("complicated addition not implemented in this test");
+ return iter.add({ weeks, days: monthsDays + days }).withCalendar(this);
+ }
+ toString() {
+ return "thirty-six";
+ }
+}
+
+const calendar = new CustomCalendar();
+const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar });
+const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35);
+const oneMonth = new Temporal.Duration(0, 0, 0, 36);
+
+// Reference ISO dates in the custom calendar:
+// M01 = 01-01
+// M02 = 02-06
+// M03 = 03-14
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(lessThanOneMonth),
+ 2022, 2, "M02",
+ "subtracting positive less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when subtracting positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(oneMonth),
+ 2022, 1, "M01",
+ "subtracting positive one month's worth of days yields the previous month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 1
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[1][0],
+ { year: 2022, monthCode: "M02", day: 36 },
+ "last day of month 2 passed to dateFromFields when subtracting positive duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(lessThanOneMonth.negated()),
+ 2022, 2, "M02",
+ "subtracting negative less than one month's worth of days yields the same month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when subtracting negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+calendar.dateFromFieldsCalls = [];
+TemporalHelpers.assertPlainYearMonth(
+ month2.subtract(oneMonth.negated()),
+ 2022, 3, "M03",
+ "subtracting negative one month's worth of days yields the following month",
+ /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14
+);
+assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called");
+assert.deepEqual(
+ calendar.dateFromFieldsCalls[0][0],
+ { year: 2022, monthCode: "M02", day: 1 },
+ "first day of month 2 passed to dateFromFields when subtracting negative duration"
+);
+assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
new file mode 100644
index 0000000000..0c5ca99437
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.subtract step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.subtract({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..897d365a8f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.subtract(new Temporal.Duration(1));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called twice on the calendar");
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..b3ad583339
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype object
+ as the options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2019, 6, calendar);
+const argument = new Temporal.Duration(1, 1);
+instance.subtract(argument);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..5ee587243a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.subtract({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js
new file mode 100644
index 0000000000..d611f5e50c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Subtraction of positive duration to a PlainYearMonth is not influenced by the implementation of daysInMonth()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ daysInMonth(ym, ...args) {
+ return 15;
+ }
+}
+
+const customCalendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(2023, 3, customCalendar);
+
+TemporalHelpers.assertPlainYearMonth(instance.subtract({days: 30}), 2023, 3, 'M03', "Subtracting 30 days from calendar reimplementing daysinMonth()")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..9a56dc9ee7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.subtract({days: 123}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js
new file mode 100644
index 0000000000..947f07589f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: RangeError thrown when subtracting positive duration and end of month is out of range
+features: [Temporal]
+info: |
+ AddDurationToOrSubtractDurationFromPlainYearMonth:
+ 12. If _sign_ &lt; 0, then
+ a. Let _oneMonthDuration_ be ! CreateTemporalDuration(0, 1, 0, 0, 0, 0, 0, 0, 0, 0).
+ b. Let _nextMonth_ be ? CalendarDateAdd(_calendar_, _intermediateDate_, _oneMonthDuration_, *undefined*, _dateAdd_).
+ c. Let _endOfMonthISO_ be ! AddISODate(_nextMonth_.[[ISOYear]], _nextMonth_.[[ISOMonth]], _nextMonth_.[[ISODay]], 0, 0, 0, -1, *"constrain"*).
+ d. Let _endOfMonth_ be ? CreateTemporalDate(_endOfMonthISO_.[[Year]], _endOfMonthISO_.[[Month]], _endOfMonthISO_.[[Day]], _calendar_).
+---*/
+
+// Based on a test case by André Bargull <andre.bargull@gmail.com>
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// Calendar addition result is out of range
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 9).subtract(duration), "Addition of 1 month to receiver out of range");
+
+// Calendar addition succeeds, but subtracting 1 day gives out of range result
+const cal = new class extends Temporal.Calendar {
+ dateAdd() {
+ return new Temporal.PlainDate(-271821, 4, 19);
+ }
+}("iso8601");
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(2000, 1, cal).subtract(duration), "Subtraction of 1 day from next month out of range");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..d529ef21c1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js
new file mode 100644
index 0000000000..7b0ef46739
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js
new file mode 100644
index 0000000000..6489ed09a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: RangeError thrown when going out of range
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainYearMonth.from("-271821-04");
+for (const overflow of ["reject", "constrain"]) {
+ assert.throws(RangeError, () => min.subtract({ months: 1 }, { overflow }), overflow);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js
new file mode 100644
index 0000000000..ab18f8ef9a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: subtract() takes month length into account
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 27 }),
+ 2019, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 28 }),
+ 2019, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 28 }),
+ 2020, 2, "M02");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 29 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 29 }),
+ 2019, 11, "M11");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 30 }),
+ 2019, 10, "M10");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 30 }),
+ 2020, 1, "M01");
+TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 31 }),
+ 2019, 12, "M12");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js
new file mode 100644
index 0000000000..fe1133c53d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..5412bb0f7e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 0000000000..e5073101a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js
new file mode 100644
index 0000000000..14d79bcdd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Temporal.PlainYearMonth.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.subtract), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.subtract)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js
new file mode 100644
index 0000000000..73622a2eb3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Invalid options throw
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-11");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => ym.subtract({ years: 1 }, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js
new file mode 100644
index 0000000000..7c8aa9302d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.subtract({ months: 1 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2019, 9, "M09",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.subtract({ months: 1 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2019, 9, "M09",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
new file mode 100644
index 0000000000..fa37762e41
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.called = 0;
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called += 1;
+ if (this.called == 2)
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 3, calendar);
+const duration = { months: 1 };
+
+yearmonth.subtract(duration, undefined);
+assert.sameValue(calendar.called, 2, "dateAdd should have been called twice");
+
+calendar.called = 0;
+yearmonth.subtract(duration);
+assert.sameValue(calendar.called, 2, "dateAdd should have been called twice");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js
new file mode 100644
index 0000000000..cec4d21c4e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.subtract({ months: 1 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
new file mode 100644
index 0000000000..c12b7bd1a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
@@ -0,0 +1,190 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.months.valueOf",
+ "call fields.months.valueOf",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.weeks.valueOf",
+ "call fields.weeks.valueOf",
+ "get fields.years",
+ "get fields.years.valueOf",
+ "call fields.years.valueOf",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // calculate last day of month
+ "call this.calendar.dateFromFields",
+ "call this.calendar.dateAdd",
+ "call this.calendar.day",
+ "call this.calendar.dateFromFields",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // CalendarDateAdd
+ "call this.calendar.dateAdd",
+ // inside Calendar.p.dateAdd
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options");
+
+instance.subtract(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+ // ToTemporalDuration
+ "get fields.days",
+ "get fields.days.valueOf",
+ "call fields.days.valueOf",
+ "get fields.hours",
+ "get fields.hours.valueOf",
+ "call fields.hours.valueOf",
+ "get fields.microseconds",
+ "get fields.microseconds.valueOf",
+ "call fields.microseconds.valueOf",
+ "get fields.milliseconds",
+ "get fields.milliseconds.valueOf",
+ "call fields.milliseconds.valueOf",
+ "get fields.minutes",
+ "get fields.minutes.valueOf",
+ "call fields.minutes.valueOf",
+ "get fields.months",
+ "get fields.nanoseconds",
+ "get fields.nanoseconds.valueOf",
+ "call fields.nanoseconds.valueOf",
+ "get fields.seconds",
+ "get fields.seconds.valueOf",
+ "call fields.seconds.valueOf",
+ "get fields.weeks",
+ "get fields.years",
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.day",
+ "get this.calendar.fields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarDateFromFields
+ "call this.calendar.dateFromFields",
+ // calculate last day of month
+ "call this.calendar.dateAdd",
+ "call this.calendar.day",
+ "call this.calendar.dateFromFields",
+ // SnapshotOwnProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ // AddDate
+ "get options.overflow",
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+ // PrepareTemporalFields on added date
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 0000000000..96af1e7847
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.subtract(duration, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js
new file mode 100644
index 0000000000..f87903ef12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.subtract() actually ignores the
+// overflow option. There is no subtraction in the ISO calendar that we could
+// test which would actually show a difference between the 'constrain' and
+// 'reject' values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 1999, 4, "M04", "default overflow is constrain");
+const implicit = yearmonth.subtract(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 1999, 4, "M04", "default overflow is constrain");
+const lambda = yearmonth.subtract(duration, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 1999, 4, "M04", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 0000000000..30a23df9cf
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.subtract(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2 }), "number");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.subtract(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 1999, 4, "M04", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js
new file mode 100644
index 0000000000..ed4a5ff063
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: The "subtract" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.subtract,
+ "function",
+ "`typeof PlainYearMonth.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..5660520393
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.subtract({days: 123}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js
new file mode 100644
index 0000000000..346758ebd5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js
@@ -0,0 +1,353 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 0000000000..14e0e703f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "subtract",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 4, "M04"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js
new file mode 100644
index 0000000000..01c34dadba
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toJSON = Temporal.PlainYearMonth.prototype.toJSON;
+
+assert.sameValue(typeof toJSON, "function");
+
+assert.throws(TypeError, () => toJSON.call(undefined), "undefined");
+assert.throws(TypeError, () => toJSON.call(null), "null");
+assert.throws(TypeError, () => toJSON.call(true), "true");
+assert.throws(TypeError, () => toJSON.call(""), "empty string");
+assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toJSON.call(1), "1");
+assert.throws(TypeError, () => toJSON.call({}), "plain object");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toJSON.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..4468fb65e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toJSON();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js
new file mode 100644
index 0000000000..b33d4a9b59
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js
new file mode 100644
index 0000000000..89b7904e89
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js
new file mode 100644
index 0000000000..9e761d32a9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 0000000000..213d7d5e7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: >
+ Temporal.PlainYearMonth.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toJSON)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js
new file mode 100644
index 0000000000..326cedaeab
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: The "toJSON" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toJSON,
+ "function",
+ "`typeof PlainYearMonth.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js
new file mode 100644
index 0000000000..c988038991
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toJSON/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainYearMonth(-100000, 12);
+assert.sameValue(instance.toJSON(), "-100000-12", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-10000, 4);
+assert.sameValue(instance.toJSON(), "-010000-04", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-9999, 6);
+assert.sameValue(instance.toJSON(), "-009999-06", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1000, 8);
+assert.sameValue(instance.toJSON(), "-001000-08", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-999, 10);
+assert.sameValue(instance.toJSON(), "-000999-10", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1, 8);
+assert.sameValue(instance.toJSON(), "-000001-08", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(0, 6);
+assert.sameValue(instance.toJSON(), "0000-06", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1, 4);
+assert.sameValue(instance.toJSON(), "0001-04", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(999, 2);
+assert.sameValue(instance.toJSON(), "0999-02", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1000, 1);
+assert.sameValue(instance.toJSON(), "1000-01", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(9999, 4);
+assert.sameValue(instance.toJSON(), "9999-04", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(10000, 6);
+assert.sameValue(instance.toJSON(), "+010000-06", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(100000, 8);
+assert.sameValue(instance.toJSON(), "+100000-08", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js
new file mode 100644
index 0000000000..3effa5b45c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toLocaleString = Temporal.PlainYearMonth.prototype.toLocaleString;
+
+assert.sameValue(typeof toLocaleString, "function");
+
+assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined");
+assert.throws(TypeError, () => toLocaleString.call(null), "null");
+assert.throws(TypeError, () => toLocaleString.call(true), "true");
+assert.throws(TypeError, () => toLocaleString.call(""), "empty string");
+assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toLocaleString.call(1), "1");
+assert.throws(TypeError, () => toLocaleString.call({}), "plain object");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toLocaleString.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..ed40d63388
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toLocaleString(undefined, { calendar: "iso8601" });
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js
new file mode 100644
index 0000000000..ae655de729
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js
new file mode 100644
index 0000000000..ff34afa4b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js
new file mode 100644
index 0000000000..eceae6dcd1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 0000000000..7cab6ecebe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: >
+ Temporal.PlainYearMonth.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 0000000000..9d35419db3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toLocaleString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
new file mode 100644
index 0000000000..e97cf63771
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Throws a TypeError if the argument is not an Object, before any other observable actions
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[null, undefined, true, 3.1416, "a string", Symbol("symbol"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarThrowEverything();
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => plainYearMonth.toPlainDate(primitive));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js
new file mode 100644
index 0000000000..904e36750c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/basic.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Basic check for toPlainDate()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2002-01");
+TemporalHelpers.assertPlainDate(ym.toPlainDate({ day: 22 }), 2002, 1, "M01", 22);
+assert.throws(TypeError, () => ym.toPlainDate({ something: "nothing" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js
new file mode 100644
index 0000000000..40902ba67c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toPlainDate = Temporal.PlainYearMonth.prototype.toPlainDate;
+
+assert.sameValue(typeof toPlainDate, "function");
+
+const args = [{ day: 7 }];
+
+assert.throws(TypeError, () => toPlainDate.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => toPlainDate.apply(null, args), "null");
+assert.throws(TypeError, () => toPlainDate.apply(true, args), "true");
+assert.throws(TypeError, () => toPlainDate.apply("", args), "empty string");
+assert.throws(TypeError, () => toPlainDate.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => toPlainDate.apply(1, args), "1");
+assert.throws(TypeError, () => toPlainDate.apply({}, args), "plain object");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toPlainDate.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..470d1d697e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.toPlainDate({ day: 5 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..f271fe42cc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toPlainDate({ day: 12 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js
new file mode 100644
index 0000000000..4017d9b961
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
new file mode 100644
index 0000000000..76bfe5946d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate step 5:
+ 5. Let _receiverFieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal.plainyearmonth.prototype.toplaindate step 7:
+ 7. Let _inputFieldNames_ be ? CalendarFields(_calendar_, « *"day"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "day",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 15 });
+
+assert.sameValue(calendar.fieldsCallCount, 2, "fields() method called twice");
+assert.compareArray(calendar.fieldsCalledWith[0], expected1, "fields() method called first time with correct args");
+assert.compareArray(calendar.fieldsCalledWith[1], expected2, "fields() method called second time with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole first iterable");
+assert(calendar.iteratorExhausted[1], "iterated through the whole second iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..0830c5b67c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.toPlainDate({ day: 24 });
+assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..60f61d7a64
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.toPlainDate({ day: 2 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..5b420c1d69
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.toPlainDate({ day: 24 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..0bf6ede55d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
new file mode 100644
index 0000000000..e786b86e42
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
@@ -0,0 +1,34 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate steps 9 and 11:
+ 9. Let _mergedFields_ be ? CalendarMergeFields(_calendar_, _fields_, _inputFields_).
+ 11. Set _mergedFields_ to ? PrepareTemporalFields(_mergedFields_, _mergedFieldNames_, «»).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 2 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js
new file mode 100644
index 0000000000..6cc15dadd2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/default-overflow-behaviour.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: A nonexistent resulting date is constrained to an existing date
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const febCommonYear = new Temporal.PlainYearMonth(2023, 2);
+const result = febCommonYear.toPlainDate({ day: 29 });
+// 2023-02-29 does not exist because 2023 is a common year
+TemporalHelpers.assertPlainDate(result, 2023, 2, "M02", 28, "2023-02 + 29 = 2023-02-28");
+
+const juneAnyYear = new Temporal.PlainYearMonth(1998, 6);
+const result2 = juneAnyYear.toPlainDate({ day: 31 });
+// 06-31 does not exist in any year
+TemporalHelpers.assertPlainDate(result2, 1998, 6, "M06", 30, "1998-06 + 31 = 1998-06-31");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..13243c741f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year'], ['day']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..327232c113
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ day: inf }), `day property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "day");
+ assert.throws(RangeError, () => instance.toPlainDate({ day: obj }));
+ assert.compareArray(calls, ["get day.valueOf", "call day.valueOf"], "it fails after fetching the primitive value");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js
new file mode 100644
index 0000000000..27fab0fa9e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js
new file mode 100644
index 0000000000..78fc602c1e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/limits.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Throws a RangeError if the resulting PlainDate is out of range
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const min = Temporal.PlainYearMonth.from("-271821-04");
+assert.throws(RangeError, () => min.toPlainDate({ day: 18 }), "min");
+TemporalHelpers.assertPlainDate(min.toPlainDate({ day: 19 }),
+ -271821, 4, "M04", 19, "min");
+
+const max = Temporal.PlainYearMonth.from("+275760-09");
+assert.throws(RangeError, () => max.toPlainDate({ day: 14 }), "max");
+TemporalHelpers.assertPlainDate(max.toPlainDate({ day: 13 }),
+ 275760, 9, "M09", 13, "max");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js
new file mode 100644
index 0000000000..b40f6b1e63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 0000000000..9c6fe3cc19
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Temporal.PlainYearMonth.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 0000000000..4cbf1717e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toPlainDate,
+ "function",
+ "`typeof PlainYearMonth.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cd560586d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.toPlainDate({day: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js
new file mode 100644
index 0000000000..aea563d4a2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const toString = Temporal.PlainYearMonth.prototype.toString;
+
+assert.sameValue(typeof toString, "function");
+
+assert.throws(TypeError, () => toString.call(undefined), "undefined");
+assert.throws(TypeError, () => toString.call(null), "null");
+assert.throws(TypeError, () => toString.call(true), "true");
+assert.throws(TypeError, () => toString.call(""), "empty string");
+assert.throws(TypeError, () => toString.call(Symbol()), "symbol");
+assert.throws(TypeError, () => toString.call(1), "1");
+assert.throws(TypeError, () => toString.call({}), "plain object");
+assert.throws(TypeError, () => toString.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => toString.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..c10fcdbef1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+Object.defineProperty(Temporal.Calendar.prototype, "id", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("id should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.toString();
+
+Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js
new file mode 100644
index 0000000000..54557224d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js
new file mode 100644
index 0000000000..60e9b1fd9b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendar-tostring.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: Number of observable 'toString' calls on the calendar for each value of calendarName
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calls;
+const customCalendar = {
+ get id() {
+ ++calls;
+ return "custom";
+ },
+ toString() {
+ TemporalHelpers.assertUnreachable('toString should not be called');
+ },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, customCalendar);
+[
+ ["always", "2000-05-01[u-ca=custom]", 1],
+ ["auto", "2000-05-01[u-ca=custom]", 1],
+ ["critical", "2000-05-01[!u-ca=custom]", 1],
+ ["never", "2000-05-01", 1],
+ [undefined, "2000-05-01[u-ca=custom]", 1],
+].forEach(([calendarName, expectedResult, expectedCalls]) => {
+ calls = 0;
+ const result = yearmonth.toString({ calendarName });
+ assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`);
+ assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js
new file mode 100644
index 0000000000..5736e96050
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-always.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-01[u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-01[u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "always" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = always`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js
new file mode 100644
index 0000000000..30c4f108f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = auto`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js
new file mode 100644
index 0000000000..108bbe3a60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ If calendarName is "calendar", the calendar ID should be included and prefixed
+ with "!".
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05-01[!u-ca=iso8601]", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[!u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05-01[!u-ca=iso8601]", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[!u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[!u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "critical" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = critical`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 0000000000..9a940adaf5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"];
+
+for (const calendarName of invalidValues) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.toString({ calendarName }),
+ `${calendarName} is an invalid value for calendarName option`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js
new file mode 100644
index 0000000000..c73c8ec551
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: "never" });
+ assert.sameValue(result, expected, `${description} calendar for calendarName = never`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
new file mode 100644
index 0000000000..af3159efb9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendarMethods = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+const tests = [
+ [[], "2000-05", "built-in ISO"],
+ [[{ id: "custom", ...calendarMethods }], "2000-05-01[u-ca=custom]", "custom"],
+ [[{ id: "iso8601", ...calendarMethods }], "2000-05", "custom with iso8601 id"],
+ [[{ id: "ISO8601", ...calendarMethods }], "2000-05-01[u-ca=ISO8601]", "custom with caps id"],
+ [[{ id: "\u0131so8601", ...calendarMethods }], "2000-05-01[u-ca=\u0131so8601]", "custom with dotless i id"],
+];
+
+for (const [args, expected, description] of tests) {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, ...args);
+ const result = yearmonth.toString({ calendarName: undefined });
+ assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`);
+ // See options-object.js for {} and options-undefined.js for absent options arg
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 0000000000..7370fa065f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ id: "custom",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => yearmonth.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-01[u-ca=custom]", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js
new file mode 100644
index 0000000000..cd1685cbce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js
new file mode 100644
index 0000000000..1a70a1464f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js
new file mode 100644
index 0000000000..6e1e7ddf03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ Temporal.PlainYearMonth.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toString)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js
new file mode 100644
index 0000000000..e4d36a0f37
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-object.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Empty or a function object may be used as options
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.toString({});
+assert.sameValue(
+ result1, "2019-10",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.toString(() => {});
+assert.sameValue(
+ result2, "2019-10",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
new file mode 100644
index 0000000000..13b358188c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "custom",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+const yearmonth1 = new Temporal.PlainYearMonth(2000, 5);
+const yearmonth2 = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+[
+ [yearmonth1, "2000-05"],
+ [yearmonth2, "2000-05-01[u-ca=custom]"],
+].forEach(([yearmonth, expected]) => {
+ const explicit = yearmonth.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = yearmonth.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js
new file mode 100644
index 0000000000..0c698aa19e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.toString(value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js
new file mode 100644
index 0000000000..3dd5392f8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Properties on an object passed to toString() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get options.calendarName",
+ "get options.calendarName.toString",
+ "call options.calendarName.toString",
+ "get this.calendar.id",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ calendarName: "auto",
+}, "options");
+
+instance.toString(options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js
new file mode 100644
index 0000000000..c741e7538b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: The "toString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js
new file mode 100644
index 0000000000..f9c1876054
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toString/year-format.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Verify that the year is appropriately formatted as 4 or 6 digits
+features: [Temporal]
+---*/
+
+let instance = new Temporal.PlainYearMonth(-100000, 12);
+assert.sameValue(instance.toString(), "-100000-12", "large negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-10000, 4);
+assert.sameValue(instance.toString(), "-010000-04", "smallest 5-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-9999, 6);
+assert.sameValue(instance.toString(), "-009999-06", "largest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1000, 8);
+assert.sameValue(instance.toString(), "-001000-08", "smallest 4-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-999, 10);
+assert.sameValue(instance.toString(), "-000999-10", "largest 3-digit negative year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(-1, 8);
+assert.sameValue(instance.toString(), "-000001-08", "year -1 formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(0, 6);
+assert.sameValue(instance.toString(), "0000-06", "year 0 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1, 4);
+assert.sameValue(instance.toString(), "0001-04", "year 1 formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(999, 2);
+assert.sameValue(instance.toString(), "0999-02", "largest 3-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(1000, 1);
+assert.sameValue(instance.toString(), "1000-01", "smallest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(9999, 4);
+assert.sameValue(instance.toString(), "9999-04", "largest 4-digit positive year formatted as 4-digit");
+
+instance = new Temporal.PlainYearMonth(10000, 6);
+assert.sameValue(instance.toString(), "+010000-06", "smallest 5-digit positive year formatted as 6-digit");
+
+instance = new Temporal.PlainYearMonth(100000, 8);
+assert.sameValue(instance.toString(), "+100000-08", "large positive year formatted as 6-digit");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js
new file mode 100644
index 0000000000..12c51f56d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype, Symbol.toStringTag, {
+ value: "Temporal.PlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/toStringTag/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..071b4b5311
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-builtin-calendar-no-array-iteration.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calling the method with a property bag argument with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar: "iso8601" };
+instance.until(arg);
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js
new file mode 100644
index 0000000000..1762b1bf2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-casting.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Calls to PYM.until cast arguments.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const nov94 = new Temporal.PlainYearMonth(1994, 11);
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+const diff = nov94.until(jun13);
+
+TemporalHelpers.assertDurationsEqual(nov94.until({ year: 2013, month: 6 }), diff, "Casts object argument");
+TemporalHelpers.assertDurationsEqual(nov94.until("2013-06"), diff, "Casts string argument");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js
new file mode 100644
index 0000000000..765d9b3208
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: A number is invalid in place of an ISO string for Temporal.PlainYearMonth
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const numbers = [
+ 1,
+ 201906,
+ -201906,
+ 1234567,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ `A number (${arg}) is not a valid ISO string for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js
new file mode 100644
index 0000000000..607c1a84d6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: The calendar name is case-insensitive
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "IsO8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js
new file mode 100644
index 0000000000..4293ad5c60
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Leap second is a valid ISO string for a calendar in a property bag
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "2016-12-31T23:59:60";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for calendar"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js
new file mode 100644
index 0000000000..363d158d7a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: A number as calendar in a property bag is not accepted
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(1976, 11, 18);
+
+const numbers = [
+ 1,
+ 19970327,
+ -19970327,
+ 1234567890,
+];
+for (const calendar of numbers) {
+ const arg = { year: 2019, monthCode: "M06", calendar };
+ assert.throws(
+ TypeError,
+ () => instance.until(arg),
+ "Numbers cannot be used as a calendar"
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js
new file mode 100644
index 0000000000..2254ed6c2b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-string.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: A calendar ID is valid input for Calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 6);
+
+const calendar = "iso8601";
+
+const arg = { year: 2019, monthCode: "M06", calendar };
+const result = instance.until(arg);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js
new file mode 100644
index 0000000000..b575729f01
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Appropriate error thrown when a calendar property from a property bag cannot
+ be converted to a calendar object or string
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [calendar, description] of primitiveTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(
+ typeof calendar === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object that doesn't implement the protocol"],
+ [new Temporal.TimeZone("UTC"), "time zone instance"],
+ [Temporal.Calendar, "Temporal.Calendar, object"],
+ [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields()
+];
+
+for (const [calendar, description] of typeErrorTests) {
+ const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js
new file mode 100644
index 0000000000..71400ef76d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-10-31",
+ "-000000-10-31T17:45",
+ "-000000-10-31T17:45Z",
+ "-000000-10-31T17:45+01:00",
+ "-000000-10-31T17:45+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..a354acab55
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[u-ca=iso8601]", "without time zone"],
+ ["2019-12-15T15:23[UTC][u-ca=iso8601]", "with time zone"],
+ ["2019-12-15T15:23[!u-ca=iso8601]", "with ! and no time zone"],
+ ["2019-12-15T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..64cb1aa2c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..0f07321125
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js
@@ -0,0 +1,56 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+const validStrings = [
+ "2019-12[Africa/Abidjan]",
+ "2019-12[!Africa/Abidjan]",
+ "2019-12[u-ca=iso8601]",
+ "2019-12[Africa/Abidjan][u-ca=iso8601]",
+ "2019-12-15T00+00:00",
+ "2019-12-15T00+00:00[UTC]",
+ "2019-12-15T00+00:00[!UTC]",
+ "2019-12-15T00-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainYearMonth`
+ );
+}
+
+const invalidStrings = [
+ "2022-09[u-ca=hebrew]",
+ "2022-09Z",
+ "2022-09+01:00",
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `"${arg}" UTC offset without time is not valid for PlainYearMonth`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js
new file mode 100644
index 0000000000..9a744d64fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-invalid.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: An invalid ISO string is never supported
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsInvalid()) {
+ assert.throws(RangeError, () => instance.until(arg), `"${arg}" is not a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..2b54b2263f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..29a0d1c74c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-multiple-time-zone.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js
new file mode 100644
index 0000000000..40e31b9143
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-separators.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23", "uppercase T"],
+ ["2019-12-15t15:23", "lowercase T"],
+ ["2019-12-15 15:23", "space between date and time"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..f1c5a1b4fd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-time-zone-annotation.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[Asia/Kolkata]", "named, with no offset"],
+ ["2019-12-15T15:23[!Europe/Vienna]", "named, with ! and no offset"],
+ ["2019-12-15T15:23[+00:00]", "numeric, with no offset"],
+ ["2019-12-15T15:23[!-02:30]", "numeric, with ! and no offset"],
+ ["2019-12-15T15:23+00:00[UTC]", "named, with offset"],
+ ["2019-12-15T15:23+00:00[!Africa/Abidjan]", "named, with offset and !"],
+ ["2019-12-15T15:23+00:00[+01:00]", "numeric, with offset"],
+ ["2019-12-15T15:23+00:00[!-08:00]", "numeric, with offset and !"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..c31afa03a8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["2019-12-15T15:23[foo=bar]", "alone"],
+ ["2019-12-15T15:23[UTC][foo=bar]", "with time zone"],
+ ["2019-12-15T15:23[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["2019-12-15T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["2019-12-15T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 12);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.until(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..f45f28a365
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-with-utc-designator.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown if a string with UTC designator is used as a PlainYearMonth
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "String with UTC designator should not be valid as a PlainYearMonth"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js
new file mode 100644
index 0000000000..e0cf058da2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: A string argument is parsed into a PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(1976, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValid()) {
+ const result = instance.until(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+const instanceNegativeYear = new Temporal.PlainYearMonth(-9999, 11);
+for (const arg of TemporalHelpers.ISO.plainYearMonthStringsValidNegativeYear()) {
+ const result = instanceNegativeYear.until(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `"${arg}" is a valid PlainYearMonth string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js
new file mode 100644
index 0000000000..65dde25fd0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainYearMonth
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.until(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainYearMonth, "Temporal.PlainYearMonth, object"],
+ [Temporal.PlainYearMonth.prototype, "Temporal.PlainYearMonth.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js
new file mode 100644
index 0000000000..f7ae571fbd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/arguments-missing-throws.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Calls to PYM.until throw when missing required arguments.
+features: [Temporal]
+---*/
+
+const jun13 = new Temporal.PlainYearMonth(2013, 6);
+
+assert.throws(TypeError, () => jun13.until({ year: 1994 }), 'Throws when missing required month');
+assert.throws(TypeError, () => jun13.until({ month: 11 }), 'Throws when missing required year');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js
new file mode 100644
index 0000000000..4fa0323443
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const until = Temporal.PlainYearMonth.prototype.until;
+
+assert.sameValue(typeof until, "function");
+
+const args = [new Temporal.PlainYearMonth(2022, 6)];
+
+assert.throws(TypeError, () => until.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => until.apply(null, args), "null");
+assert.throws(TypeError, () => until.apply(true, args), "true");
+assert.throws(TypeError, () => until.apply("", args), "empty string");
+assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => until.apply(1, args), "1");
+assert.throws(TypeError, () => until.apply({}, args), "plain object");
+assert.throws(TypeError, () => until.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => until.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..15839e5969
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.until({ year: 2070, month: 7 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..da8a43e025
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil");
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.until(new Temporal.PlainYearMonth(2001, 6));
+
+Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js
new file mode 100644
index 0000000000..3ce2efa436
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
new file mode 100644
index 0000000000..0442eba080
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateadd-called-with-plaindate-instance.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ relativeTo parameters that are not ZonedDateTime or undefined, are always
+ converted to PlainDate for observable calendar calls
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
+const instance = new Temporal.PlainYearMonth(1970, 1, calendar);
+instance.until(new Temporal.PlainYearMonth(2000, 5, calendar), { smallestUnit: "year" });
+assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..532ef791a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const arg = { year: 2000, month: 5, calendar };
+instance.until(arg);
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the property bag's calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..5e362ee8ad
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-datefromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calendar.dateFromFields method is called with undefined as the options value
+ when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until({ year: 2000, month: 6, day: 3, calendar });
+assert.sameValue(calendar.dateFromFieldsCallCount, 2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
new file mode 100644
index 0000000000..aa70bb2d12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calendar.dateUntil method is called with a null-prototype object as the
+ options value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+const argument = new Temporal.PlainYearMonth(2022, 6, calendar);
+instance.until(argument);
+assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 0000000000..3d969c6ad6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.until steps 20–21:
+ 20. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 21. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 0000000000..bc123e6c33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.until step 13:
+ 13. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.until({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..d95e655a47
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calendar.dateFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until(new Temporal.PlainYearMonth(2019, 2));
+assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
new file mode 100644
index 0000000000..7e5adc104d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.until({ year: 2005, month: 6, calendar: temporalObject });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js
new file mode 100644
index 0000000000..50658a49d0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-yearmonthfromfields-called-with-options-undefined.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Calendar.yearMonthFromFields method is called with undefined as the options
+ value when call originates internally
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
+let instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.until({ year: 2000, month: 6, calendar });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..85e8b20103
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/constructor-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..5d3fafbaf7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/duplicate-calendar-fields.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['month'], ['monthCode'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar };
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+ assert.throws(RangeError, () => instance.until(arg));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..f738387d6e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js
new file mode 100644
index 0000000000..afbff2bee6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-auto.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: auto value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "auto" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "auto" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "auto largestUnit is year (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js
new file mode 100644
index 0000000000..c55e4c4c77
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-disallowed-units.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Until throws on to0-small largestUnit
+features: [Temporal, arrow-function]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+[
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach((largestUnit) => {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `throws on disallowed or invalid largestUnit: ${largestUnit}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 0000000000..02ab42568f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js
new file mode 100644
index 0000000000..711dd53b1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-months.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: months value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "months" }),
+ 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "months" }),
+ 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is months (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..d84a89e5f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..e5f3042e75
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const units = ["years", "months"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
new file mode 100644
index 0000000000..6a8ae1753f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: undefined }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: undefined }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later, {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later, () => {}),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, () => {}),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (arrow function, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 0000000000..cc87b1025f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js
new file mode 100644
index 0000000000..249eb4b32b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-years.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: years value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.assertDuration(earlier.until(later, { largestUnit: "years" }),
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (pos)");
+TemporalHelpers.assertDuration(later.until(earlier, { largestUnit: "years" }),
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, "largestUnit is years (neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js
new file mode 100644
index 0000000000..46b09c470c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Leap second is a valid ISO string for PlainYearMonth
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2016, 12);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainYearMonth"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.until(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainYearMonth"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js
new file mode 100644
index 0000000000..883f548bfc
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js
new file mode 100644
index 0000000000..c3e00f3d99
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/mixed-calendar-invalid.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Mixed calendars throw as invalid
+features: [Temporal]
+---*/
+
+class customCal extends Temporal.Calendar {
+ constructor () {
+ super('iso8601');
+ }
+
+ get id() {
+ return "I am a secret cal.";
+ }
+}
+
+const ym1 = new Temporal.PlainYearMonth(2000, 1);
+const ym2 = new Temporal.PlainYearMonth(2000, 1, new customCal());
+
+assert.throws(RangeError, () => ym1.until(ym2), 'until throws with different calendars');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js
new file mode 100644
index 0000000000..191af91c8d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js
new file mode 100644
index 0000000000..be0a4a674e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Temporal.PlainYearMonth.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.until), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.until)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js
new file mode 100644
index 0000000000..c29a89d3ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-invalid.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Verify that invalid options are handled correctly.
+features: [Temporal]
+---*/
+
+const feb20 = new Temporal.PlainYearMonth(2020, 2);
+const feb21 = new Temporal.PlainYearMonth(2021, 2);
+
+[
+ null,
+ 1,
+ "hello",
+ true,
+ Symbol("foo"),
+ 1n
+].forEach((badOption) =>
+ assert.throws(TypeError, () => feb20.until(feb21, badOption), `${String(badOption)} throws TypeError`)
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js
new file mode 100644
index 0000000000..ba9bd522ac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.until(new Temporal.PlainYearMonth(1976, 11), {});
+TemporalHelpers.assertDuration(
+ result1, -42, -11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.until(new Temporal.PlainYearMonth(1976, 11), () => {});
+TemporalHelpers.assertDuration(
+ result2, -42, -11, 0, 0, 0, 0, 0, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js
new file mode 100644
index 0000000000..293aeffcbe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Verify that undefined options are handled correctly.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+TemporalHelpers.assertDuration(earlier.until(later, undefined),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier, undefined),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (explicit, neg)");
+
+TemporalHelpers.assertDuration(earlier.until(later),
+ 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, pos)");
+TemporalHelpers.assertDuration(later.until(earlier),
+ -2, -7, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year (implicit, neg)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js
new file mode 100644
index 0000000000..d8d91f94f3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.until(new Temporal.PlainYearMonth(1976, 11), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
new file mode 100644
index 0000000000..bbc4f29916
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
@@ -0,0 +1,193 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Properties on objects passed to until() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expectedMinimal = [
+ // ToTemporalYearMonth
+ "get other.calendar",
+ "has other.calendar.dateAdd",
+ "has other.calendar.dateFromFields",
+ "has other.calendar.dateUntil",
+ "has other.calendar.day",
+ "has other.calendar.dayOfWeek",
+ "has other.calendar.dayOfYear",
+ "has other.calendar.daysInMonth",
+ "has other.calendar.daysInWeek",
+ "has other.calendar.daysInYear",
+ "has other.calendar.fields",
+ "has other.calendar.id",
+ "has other.calendar.inLeapYear",
+ "has other.calendar.mergeFields",
+ "has other.calendar.month",
+ "has other.calendar.monthCode",
+ "has other.calendar.monthDayFromFields",
+ "has other.calendar.monthsInYear",
+ "has other.calendar.weekOfYear",
+ "has other.calendar.year",
+ "has other.calendar.yearMonthFromFields",
+ "has other.calendar.yearOfWeek",
+ "get other.calendar.fields",
+ "get other.calendar.yearMonthFromFields",
+ "call other.calendar.fields",
+ "get other.month",
+ "get other.month.valueOf",
+ "call other.month.valueOf",
+ "get other.monthCode",
+ "get other.monthCode.toString",
+ "call other.monthCode.toString",
+ "get other.year",
+ "get other.year.valueOf",
+ "call other.year.valueOf",
+ "call other.calendar.yearMonthFromFields",
+ // CalendarEquals
+ "get this.calendar.id",
+ "get other.calendar.id",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+
+const expected = expectedMinimal.concat([
+ // lookup
+ "get this.calendar.dateAdd",
+ "get this.calendar.dateFromFields",
+ "get this.calendar.dateUntil",
+ "get this.calendar.fields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields / CalendarDateFromFields (receiver)
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ "call this.calendar.dateFromFields",
+ // PrepareTemporalFields / CalendarDateFromFields (argument)
+ "get other.calendar.monthCode",
+ "call other.calendar.monthCode",
+ "get other.calendar.year",
+ "call other.calendar.year",
+ "call this.calendar.dateFromFields",
+ // CalendarDateUntil
+ "call this.calendar.dateUntil",
+]);
+const actual = [];
+
+const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, ownCalendar, 1);
+
+const otherYearMonthPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 6,
+ monthCode: "M06",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+function createOptionsObserver({ smallestUnit = "months", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) {
+ return TemporalHelpers.propertyBagObserver(actual, {
+ // order is significant, due to iterating through properties in order to
+ // copy them to an internal null-prototype object:
+ roundingIncrement,
+ roundingMode,
+ largestUnit,
+ smallestUnit,
+ additional: "property",
+ }, "options");
+}
+
+// clear any observable things that happened while constructing the objects
+actual.splice(0);
+
+// code path that skips RoundDuration:
+instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 1 }));
+assert.compareArray(actual, expected, "order of operations with no rounding");
+actual.splice(0); // clear
+
+// short-circuit for identical objects:
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2000,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+
+instance.until(identicalPropertyBag, createOptionsObserver());
+assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateUntil", // 7.o
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.s not called because other units can't add up to >1 year at this point)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
+const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
+ year: 2001,
+ month: 5,
+ monthCode: "M05",
+ calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
+}, "other");
+const expectedOpsForYearRoundingSameMonth = expected.concat([
+ "call this.calendar.dateAdd", // 7.e
+ "call this.calendar.dateAdd", // 7.g
+ "call this.calendar.dateAdd", // 7.y MoveRelativeDate
+ // (7.o not called because months and weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 9.c
+ "call this.calendar.dateUntil" // 9.d
+]);
+instance.until(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
+assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest month:
+const expectedOpsForMonthRounding = expected.concat([
+ // RoundDuration
+ "call this.calendar.dateAdd", // 10.c
+ "call this.calendar.dateAdd", // 10.e
+ "call this.calendar.dateAdd", // 10.k MoveRelativeDate
+ // (10.n.iii MoveRelativeDate not called because weeks == 0)
+ // BalanceDateDurationRelative
+ "call this.calendar.dateAdd", // 10.d
+ "call this.calendar.dateUntil" // 10.e
+]);
+instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
+assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js
new file mode 100644
index 0000000000..ca70d809b2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: The "until" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.until,
+ "function",
+ "`typeof PlainYearMonth.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..cebfe1a96b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/proto-in-calendar-fields.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar};
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+assert.throws(RangeError, () => instance.until(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..f570eed198
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2022, 1);
+const later = new Temporal.PlainYearMonth(2023, 12);
+const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 12 months balances to 2 years");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js
new file mode 100644
index 0000000000..b714ddca6f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/rounding-zero-year-month-length.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ A malicious calendar resulting in a year, month, or week length of zero is
+ handled correctly
+info: |
+ RoundDuration
+ 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception.
+ ...
+ 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception.
+ ...
+ 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const cal = new class extends Temporal.Calendar {
+ dateAdd(date, duration, options) {
+ // Called several times, last call sets oneYear/Month/WeekDays to 0
+ return new Temporal.PlainDate(1970, 1, 1);
+ }
+}("iso8601");
+
+const ym1 = new Temporal.PlainYearMonth(1970, 1, cal);
+const ym2 = new Temporal.PlainYearMonth(1971, 1, cal);
+
+assert.throws(RangeError, () => ym1.until(ym2, { smallestUnit: "years" }), "zero year length handled correctly");
+assert.throws(RangeError, () => ym1.until(ym2, { smallestUnit: "months", roundingIncrement: 2 }), "zero month length handled correctly");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js
new file mode 100644
index 0000000000..e9430e2d33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-as-expected.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Until rounding increments work as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const laterSinceYear = earlier.until(later, { smallestUnit: "years", roundingIncrement: 4, roundingMode: "halfExpand" });
+TemporalHelpers.assertDuration(laterSinceYear,
+ /* years = */ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of years");
+
+const laterSinceMixed = earlier.until(later, { smallestUnit: "months", roundingIncrement: 5 });
+TemporalHelpers.assertDuration(laterSinceMixed,
+ /* years = */ 2, /* months = */ 5, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of months mixed with years");
+
+const laterSinceMonth = earlier.until(later, { largestUnit: "months", smallestUnit: "months", roundingIncrement: 10 });
+TemporalHelpers.assertDuration(laterSinceMonth,
+ 0, /* months = */ 30, 0, 0, 0, 0, 0, 0, 0, 0, "rounds to an increment of pure months");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js
new file mode 100644
index 0000000000..d1f0aa1100
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..743e0b0c87
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2");
+// Cannot test the upper bound of 1e9 + 0.5 here, because the duration is
+// rounded relative to the receiver PlainYearMonth, and 1e9 months is outside of
+// the PlainYearMonth range.
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..e2540546a7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(ℝ(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 0000000000..8179e49442
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..cca7ee7078
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js
new file mode 100644
index 0000000000..baa45e6263
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-ceil.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js
new file mode 100644
index 0000000000..24a911a328
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-expand.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js
new file mode 100644
index 0000000000..dbc6cc1343
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-floor.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..d07eebdc1c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfCeil.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js
new file mode 100644
index 0000000000..9ec8af04ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfEven.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..989b59e5ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfExpand.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..2bd83d6f2f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfFloor.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..bed39ed9e4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-halfTrunc.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [3], [-3]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..8e80a0c598
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js
new file mode 100644
index 0000000000..06dd0e1964
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-trunc.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2019, 1);
+const later = new Temporal.PlainYearMonth(2021, 9);
+
+const expected = [
+ ["years", [2], [-2]],
+ ["months", [2, 8], [-2, -8]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ earlier.until(later, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ later.until(earlier, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js
new file mode 100644
index 0000000000..1c73dfc3ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..4f2b3b3d8e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..00a17e14d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const badValues = [
+ "era",
+ "eraYear",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+ "month\0",
+ "YEAR",
+ "eras",
+ "eraYears",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+ "months\0",
+ "YEARS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..ae5c26042b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js
new file mode 100644
index 0000000000..d2f70d5b1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..fddccb6ae7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js
new file mode 100644
index 0000000000..20ce83cdb0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/until/year-zero.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-06",
+ "-000000-06-24",
+ "-000000-06-24T15:43:27",
+ "-000000-06-24T15:43:27+01:00",
+ "-000000-06-24T15:43:27+00:00[UTC]",
+];
+const instance = new Temporal.PlainYearMonth(2000, 5);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.until(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js
new file mode 100644
index 0000000000..6ce4e91289
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/basic.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainYearMonth = Temporal.PlainYearMonth.from("1963-02");
+const plainYearMonth2 = Temporal.PlainYearMonth.from("1963-02");
+
+assert.throws(TypeError, () => plainYearMonth.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainYearMonth < plainYearMonth, "<");
+assert.throws(TypeError, () => plainYearMonth <= plainYearMonth, "<=");
+assert.throws(TypeError, () => plainYearMonth > plainYearMonth, ">");
+assert.throws(TypeError, () => plainYearMonth >= plainYearMonth, ">=");
+assert.sameValue(plainYearMonth === plainYearMonth, true, "===");
+assert.sameValue(plainYearMonth === plainYearMonth2, false, "===");
+assert.sameValue(plainYearMonth !== plainYearMonth, false, "!==");
+assert.sameValue(plainYearMonth !== plainYearMonth2, true, "!==");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js
new file mode 100644
index 0000000000..c566f848c8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const valueOf = Temporal.PlainYearMonth.prototype.valueOf;
+
+assert.sameValue(typeof valueOf, "function");
+
+assert.throws(TypeError, () => valueOf.call(undefined), "undefined");
+assert.throws(TypeError, () => valueOf.call(null), "null");
+assert.throws(TypeError, () => valueOf.call(true), "true");
+assert.throws(TypeError, () => valueOf.call(""), "empty string");
+assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol");
+assert.throws(TypeError, () => valueOf.call(1), "1");
+assert.throws(TypeError, () => valueOf.call({}), "plain object");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => valueOf.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js
new file mode 100644
index 0000000000..12d63be064
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js
new file mode 100644
index 0000000000..bf0edd10ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js
new file mode 100644
index 0000000000..44179950c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 0000000000..11d2048fe9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: >
+ Temporal.PlainYearMonth.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.valueOf)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js
new file mode 100644
index 0000000000..142e78030d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: The "valueOf" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.valueOf,
+ "function",
+ "`typeof PlainYearMonth.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/valueOf/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js
new file mode 100644
index 0000000000..a51a817c86
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-calendar-field.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Throw if the argument has a calendar field
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({ year: 2021, calendar: "iso8601" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js
new file mode 100644
index 0000000000..fa12608d71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-missing-fields.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: TypeError thrown when argument doesn't contain any of the supported properties
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({}), "No properties");
+assert.throws(TypeError, () => ym.with({ months: 12 }), "Only plural 'months' property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js
new file mode 100644
index 0000000000..1c502ac58f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/argument-timezone-field.js
@@ -0,0 +1,14 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Throw if the argument has a timeZone field
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+assert.throws(TypeError, () => ym.with({ year: 2021, timeZone: "UTC" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js
new file mode 100644
index 0000000000..23e536c3ec
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/basic.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Basic tests for with
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const ym = Temporal.PlainYearMonth.from("2019-10");
+
+TemporalHelpers.assertPlainYearMonth(ym.with({ year: 2020 }), 2020, 10, "M10", "year");
+TemporalHelpers.assertPlainYearMonth(ym.with({ month: 9 }), 2019, 9, "M09", "month");
+TemporalHelpers.assertPlainYearMonth(ym.with({ monthCode: "M09" }), 2019, 9, "M09", "monthCode");
+
+assert.throws(RangeError, () => ym.with({ month: 9, monthCode: "M10" }), "month/monthCode mismatch");
+TemporalHelpers.assertPlainYearMonth(ym.with({ month: 1, years: 2020 }), 2019, 1, "M01", "plural 'years'");
+
+const withDay = ym.with({ year: 2019, get day() { throw new Test262Error("should not read the day property") } });
+TemporalHelpers.assertPlainYearMonth(withDay, 2019, 10, "M10", "day property");
+assert.sameValue(withDay.getISOFields().isoDay, 1);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js
new file mode 100644
index 0000000000..cf57a3a840
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const with_ = Temporal.PlainYearMonth.prototype.with;
+
+assert.sameValue(typeof with_, "function");
+
+const args = [{ year: 2022 }];
+
+assert.throws(TypeError, () => with_.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => with_.apply(null, args), "null");
+assert.throws(TypeError, () => with_.apply(true, args), "true");
+assert.throws(TypeError, () => with_.apply("", args), "empty string");
+assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => with_.apply(1, args), "1");
+assert.throws(TypeError, () => with_.apply({}, args), "plain object");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => with_.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js
new file mode 100644
index 0000000000..68911e8e03
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-array-iteration.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable array iteration when getting the calendar fields.
+features: [Temporal]
+---*/
+
+const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
+Array.prototype[Symbol.iterator] = function arrayIterator() {
+ throw new Test262Error("Array should not be iterated");
+}
+
+const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601");
+instance.with({ month: 4 });
+
+Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..61550c948d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields");
+Object.defineProperty(Temporal.Calendar.prototype, "fields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("fields should not be looked up");
+ },
+});
+const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields");
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be looked up");
+ },
+});
+const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields");
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.with({ year: 2001 });
+
+Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal);
+Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js
new file mode 100644
index 0000000000..f4352832ce
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js
new file mode 100644
index 0000000000..c7ed6aa754
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {
+ extra: "property",
+};
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1] is a copy of options");
+ assert.sameValue(args[1].extra, "property", "All properties are copied");
+ assert.sameValue(Object.getPrototypeOf(args[1]), null, "Copy has null prototype");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.with({ month: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 0000000000..cddf4d51e6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..4cc5cd6348
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Calendar.yearMonthFromFields method is called with a null-prototype fields object
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.with({ year: 2019 });
+assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 0000000000..142b9f4be0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.yearMonthFromFieldsCallCount, 0, "yearMonthFromFields() never called");
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
new file mode 100644
index 0000000000..23f00d76a3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Calendar.mergeFields method is called with null-prototype fields objects
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution();
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+instance.with({ year: 2019 });
+assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js
new file mode 100644
index 0000000000..11b0ae41f6
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/constructor-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.with({month: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 0000000000..795e2f4778
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialYearMonth_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2004 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js
new file mode 100644
index 0000000000..ada7ba2178
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/copy-properties-not-undefined.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: PreparePartialTemporalFields copies only defined properties of source object
+info: |
+ 4. For each value _property_ of _fieldNames_, do
+ a. Let _value_ be ? Get(_fields_, _property_).
+ b. If _value_ is not *undefined*, then
+ ...
+ iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainYearMonth = new Temporal.PlainYearMonth(2001, 9);
+
+TemporalHelpers.assertPlainYearMonth(plainYearMonth.with({ month: 11, year: undefined }),
+ 2001, 11, "M11",
+ "only the properties that are present and defined in the plain object are copied"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js
new file mode 100644
index 0000000000..4b11d1d737
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/duplicate-calendar-fields.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['month'], ['year']]) {
+ const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
+ const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+ assert.throws(RangeError, () => ym.with({month: 1}));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 0000000000..1b056a39c4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js
new file mode 100644
index 0000000000..7f8f2278ca
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js
new file mode 100644
index 0000000000..052ec04c0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js
new file mode 100644
index 0000000000..7c8699125d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Temporal.PlainYearMonth.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.with), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.with)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js
new file mode 100644
index 0000000000..bc0a3991e3
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+
+const result1 = instance.with({ year: 2020 }, {});
+TemporalHelpers.assertPlainYearMonth(
+ result1, 2020, 10, "M10",
+ "options may be an empty plain object"
+);
+
+const result2 = instance.with({ year: 2020 }, () => {});
+TemporalHelpers.assertPlainYearMonth(
+ result2, 2020, 10, "M10",
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js
new file mode 100644
index 0000000000..5544585218
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 2);
+const fields = { month: 13 };
+
+const explicit = yearmonth.with(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = yearmonth.with(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js
new file mode 100644
index 0000000000..9c4072a17a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "2021-01",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainYearMonth(2019, 10);
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.with({ year: 2020 }, value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
new file mode 100644
index 0000000000..de2a5849d4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
@@ -0,0 +1,74 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // RejectObjectWithCalendarOrTimeZone
+ "get fields.calendar",
+ "get fields.timeZone",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.overflow",
+ "get options.overflow",
+ "getOwnPropertyDescriptor options.extra",
+ "get options.extra",
+ // lookup
+ "get this.calendar.fields",
+ "get this.calendar.mergeFields",
+ "get this.calendar.yearMonthFromFields",
+ // CalendarFields
+ "call this.calendar.fields",
+ // PrepareTemporalFields on receiver
+ "get this.calendar.month",
+ "call this.calendar.month",
+ "get this.calendar.monthCode",
+ "call this.calendar.monthCode",
+ "get this.calendar.year",
+ "call this.calendar.year",
+ // PrepareTemporalFields on argument
+ "get fields.month",
+ "get fields.month.valueOf",
+ "call fields.month.valueOf",
+ "get fields.monthCode",
+ "get fields.monthCode.toString",
+ "call fields.monthCode.toString",
+ "get fields.year",
+ "get fields.year.valueOf",
+ "call fields.year.valueOf",
+ // CalendarMergeFields
+ "call this.calendar.mergeFields",
+ // CalendarYearMonthFromFields
+ "call this.calendar.yearMonthFromFields",
+ // inside Calendar.p.yearMonthFromFields
+ "get options.overflow.toString",
+ "call options.overflow.toString",
+];
+const actual = [];
+
+const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
+const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+// clear observable operations that occurred during the constructor call
+actual.splice(0);
+
+const fields = TemporalHelpers.propertyBagObserver(actual, {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+}, "fields");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ overflow: "constrain",
+ extra: "property",
+}, "options");
+
+instance.with(fields, options);
+assert.compareArray(actual, expected, "order of operations");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
new file mode 100644
index 0000000000..0b0fe17833
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+
+const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"];
+for (const overflow of badOverflows) {
+ assert.throws(
+ RangeError,
+ () => yearmonth.with({ month: 8 }, { overflow }),
+ `invalid overflow ("${overflow}")`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
new file mode 100644
index 0000000000..a4060fe96a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const explicit = yearmonth.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = yearmonth.with({ month: 15 }, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
+const lambda = yearmonth.with({ month: 15 }, () => {});
+TemporalHelpers.assertPlainYearMonth(lambda, 2000, 12, "M12", "default overflow is constrain");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js
new file mode 100644
index 0000000000..6003269773
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => yearmonth.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08", descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js
new file mode 100644
index 0000000000..4fb0f4541b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: The "with" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.with,
+ "function",
+ "`typeof PlainYearMonth.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js
new file mode 100644
index 0000000000..b24f66a0f0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/proto-in-calendar-fields.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
+const ym = new Temporal.PlainYearMonth(2023, 5, calendar);
+
+assert.throws(RangeError, () => ym.with({month: 1}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js
new file mode 100644
index 0000000000..1a7e7899da
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "with",
+ [{ month: 11 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 11, "M11"),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js
new file mode 100644
index 0000000000..7edf70a33b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/branding.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const year = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "year").get;
+
+assert.sameValue(typeof year, "function");
+
+assert.throws(TypeError, () => year.call(undefined), "undefined");
+assert.throws(TypeError, () => year.call(null), "null");
+assert.throws(TypeError, () => year.call(true), "true");
+assert.throws(TypeError, () => year.call(""), "empty string");
+assert.throws(TypeError, () => year.call(Symbol()), "symbol");
+assert.throws(TypeError, () => year.call(1), "1");
+assert.throws(TypeError, () => year.call({}), "plain object");
+assert.throws(TypeError, () => year.call(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => year.call(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js
new file mode 100644
index 0000000000..94ffaec418
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/builtin-calendar-no-observable-calls.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.year
+description: >
+ Calling the method on an instance constructed with a builtin calendar causes
+ no observable lookups or calls to calendar methods.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year");
+Object.defineProperty(Temporal.Calendar.prototype, "year", {
+ configurable: true,
+ enumerable: false,
+ get() {
+ TemporalHelpers.assertUnreachable("year should not be looked up");
+ },
+});
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1);
+instance.year;
+
+Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js
new file mode 100644
index 0000000000..35d879d280
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/custom.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: Custom calendar tests for year().
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+let calls = 0;
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ year(...args) {
+ ++calls;
+ assert.compareArray(args, [instance], "year arguments");
+ return 7;
+ }
+}
+
+const calendar = new CustomCalendar();
+const instance = new Temporal.PlainYearMonth(1830, 8, calendar);
+const result = instance.year;
+assert.sameValue(result, 7, "result");
+assert.sameValue(calls, 1, "calls");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js
new file mode 100644
index 0000000000..01acf1b0ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: The "year" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js
new file mode 100644
index 0000000000..81ee25a1af
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/year/validate-calendar-value.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: Validate result returned from calendar year() method
+features: [Temporal]
+---*/
+
+const badResults = [
+ [undefined, TypeError],
+ [Infinity, RangeError],
+ [-Infinity, RangeError],
+ [Symbol("foo"), TypeError],
+ [7n, TypeError],
+ [NaN, RangeError],
+ ["string", TypeError],
+ [{}, TypeError],
+ [null, TypeError],
+ [true, TypeError],
+ [false, TypeError],
+ [7.1, RangeError],
+ [-0.1, RangeError],
+ ["7", TypeError],
+ ["7.5", TypeError],
+ [{valueOf() { return 7; }}, TypeError],
+];
+
+badResults.forEach(([result, error]) => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`);
+});
+
+const preservedResults = [
+ -7,
+];
+
+preservedResults.forEach(result => {
+ const calendar = new class extends Temporal.Calendar {
+ year() {
+ return result;
+ }
+ }("iso8601");
+ const instance = new Temporal.PlainYearMonth(1981, 12, calendar);
+ assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
new file mode 100644
index 0000000000..57b7d16f12
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: referenceISODay argument defaults to 1 if not given
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const args = [2000, 5, calendar];
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/shell.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js
new file mode 100644
index 0000000000..6b7dc1d850
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/subclass.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Test for Temporal.PlainYearMonth subclassing.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomPlainYearMonth extends Temporal.PlainYearMonth {
+}
+
+const instance = new CustomPlainYearMonth(2000, 5);
+TemporalHelpers.assertPlainYearMonth(instance, 2000, 5, "M05");
+assert.sameValue(Object.getPrototypeOf(instance), CustomPlainYearMonth.prototype, "Instance of CustomPlainYearMonth");
+assert(instance instanceof CustomPlainYearMonth, "Instance of CustomPlainYearMonth");
+assert(instance instanceof Temporal.PlainYearMonth, "Instance of Temporal.PlainYearMonth");
+
+reportCompare(0, 0);