diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with | |
parent | Initial commit. (diff) | |
download | firefox-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/ZonedDateTime/prototype/with')
49 files changed, 1639 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js new file mode 100644 index 0000000000..e7935824fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js @@ -0,0 +1,65 @@ +// |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.zoneddatetime.prototype.with +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-addtime step 8: + 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-adddatetime step 1: + 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-builtintimezonegetinstantfor step 13.a: + a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*). + sec-temporal-interpretisodatetimeoffset steps 4–10: + 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then + a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_). + ... + ... + 6. Assert: _offset_ is *"prefer"* or *"reject"*. + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + ... + 9. If _offset_ is *"reject"*, throw a *RangeError* exception. + 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_). + sec-temporal.zoneddatetime.prototype.with step 26: + 26. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], _dateTimeResult_.[[Month]], _dateTimeResult_.[[Day]], _dateTimeResult_.[[Hour]], _dateTimeResult_.[[Minute]], _dateTimeResult_.[[Second]], _dateTimeResult_.[[Millisecond]], _dateTimeResult_.[[Microsecond]], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const shiftInstant = new Temporal.Instant(3661_001_001_001n); +const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); +const datetime1 = new Temporal.ZonedDateTime(3661_001_001_000n, tz1); + +// This code path is encountered if offset is `ignore` or `prefer`, +// disambiguation is `earlier` and the shift is a spring-forward change +datetime1.with({ nanosecond: 1 }, { offset: "ignore", disambiguation: "earlier" }); + +const expected = [ + "1970-01-01T01:01:01.001001001", + "1970-01-01T01:01:01.001000999", +]; +assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected); + +const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); +const datetime2 = new Temporal.ZonedDateTime(3661_001_001_000n, tz2); + +datetime2.with({ nanosecond: 1 }, { offset: "prefer", disambiguation: "earlier" }); + +assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js new file mode 100644 index 0000000000..089d357535 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const with_ = Temporal.ZonedDateTime.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.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => with_.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..29b963ecf8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ day: 5 }); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..0d3f1edc2f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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 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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ year: 2001 }); + +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/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..d40d49f7fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.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.zoneddatetime.prototype.with +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ year: 2001 }); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js new file mode 100644 index 0000000000..c18d00211d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +description: > + Tests that Temporal.ZonedDateTime.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.ZonedDateTime.prototype.with), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.with), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.with), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.with.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js new file mode 100644 index 0000000000..7993c23c19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.with step 9: + 9. 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 = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.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/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..35d0c27f9f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +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.ZonedDateTime(0n, "UTC", calendar); +instance.with({ 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/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js new file mode 100644 index 0000000000..7f1e187cbf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(TypeError, () => instance.with({ year: 2005 }), "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/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..6a75d5ce22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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.ZonedDateTime(0n, "UTC", calendar); +instance.with({ 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/ZonedDateTime/prototype/with/calendar-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js new file mode 100644 index 0000000000..2daa8e28ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.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.zoneddatetime.prototype.with +description: > + The options argument is copied and the copy is passed to + Calendar#dateFromFields. +features: [Temporal] +---*/ + +const options = { + extra: "property", +}; +let calledDateFromFields = 0; +class Calendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateFromFields(fields, optionsArg) { + ++calledDateFromFields; + assert.notSameValue(optionsArg, options, "should pass copied options object"); + assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object"); + assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype"); + return super.dateFromFields(fields, optionsArg); + } +}; +const calendar = new Calendar(); +const datetime = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const result = datetime.with({ year: 1971 }, options); +assert.sameValue(result.epochNanoseconds, 365n * 86400_000_000_000n, "year changed from 1970 to 1971") +assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..640cd8d689 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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 zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js new file mode 100644 index 0000000000..dc159e00f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js @@ -0,0 +1,61 @@ +// |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.plaindatetime.prototype.with +description: The object returned from mergeFields() is copied before being passed to dateFromFields(). +info: | + sec-temporal.plaindatetime.prototype.with steps 18–19 and 23: + 18. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_). + 19. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"timeZone"* »). + 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields step 2: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get day", + "get day.valueOf", + "call day.valueOf", + "get hour", + "get hour.valueOf", + "call hour.valueOf", + "get microsecond", + "get microsecond.valueOf", + "call microsecond.valueOf", + "get millisecond", + "get millisecond.valueOf", + "call millisecond.valueOf", + "get minute", + "get minute.valueOf", + "call minute.valueOf", + "get month", + "get month.valueOf", + "call month.valueOf", + "get monthCode", + "get monthCode.toString", + "call monthCode.toString", + "get nanosecond", + "get nanosecond.valueOf", + "call nanosecond.valueOf", + "get offset", + "get offset.toString", + "call offset.toString", + "get second", + "get second.valueOf", + "call second.valueOf", + "get year", + "get year.valueOf", + "call year.valueOf", +]; + +const calendar = TemporalHelpers.calendarMergeFieldsGetters(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.with({ year: 2022 }); + +assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js new file mode 100644 index 0000000000..6afaf10645 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js @@ -0,0 +1,52 @@ +// |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.zoneddatetime.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_). +features: [Temporal] +---*/ + +const d1 = new Temporal.ZonedDateTime(1_000_000_000_000_000_789n, "UTC"); + +const d2 = d1.with({ day: 1, hour: 10, year: undefined }); + +assert.sameValue(d2.year, 2001, + "only the properties that are present and defined in the plain object are copied (year value)" +); + +assert.sameValue(d2.month, 9, + "only the properties that are present and defined in the plain object are copied (month value)" +); + +assert.sameValue(d2.day, 1, + "only the properties that are present and defined in the plain object are copied (day value)" +); + +assert.sameValue(d2.hour, 10, + "only the properties that are present and defined in the plain object are copied (hour value)" +); +assert.sameValue(d2.minute, 46, + "only the properties that are present and defined in the plain object are copied (minute value)" +); +assert.sameValue(d2.second, 40, + "only the properties that are present and defined in the plain object are copied (second value)" +); +assert.sameValue(d2.millisecond, 0, + "only the properties that are present and defined in the plain object are copied (millisecond value)" +); +assert.sameValue(d2.microsecond, 0, + "only the properties that are present and defined in the plain object are copied (microsecond value)" +); +assert.sameValue(d2.nanosecond, 789, + "only the properties that are present and defined in the plain object are copied (nanosecond value)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js new file mode 100644 index 0000000000..8cf415e441 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.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.zoneddatetime.protoype.with +description: RangeError thrown when disambiguation 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-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => datetime.with({ hour: 2 }, { disambiguation: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js new file mode 100644 index 0000000000..e81165eeb7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.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.zoneddatetime.prototype.with +description: Fallback value for disambiguation option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const springForwardDatetime = new Temporal.ZonedDateTime(954702001_000_000_000n, timeZone); +const fallBackDatetime = new Temporal.ZonedDateTime(972849601_000_000_000n, timeZone); +const offset = "ignore"; + +[ + [springForwardDatetime, { hour: 2, minute: 30 }, 954671401_000_000_000n], + [fallBackDatetime, { hour: 1, minute: 30 }, 972808201_000_000_000n], +].forEach(([datetime, fields, expected]) => { + const explicit = datetime.with(fields, { offset, disambiguation: undefined }); + assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible"); + const implicit = datetime.with(fields, { offset }); + assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js new file mode 100644 index 0000000000..90358a2752 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.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.zoneddatetime.prototype.with +description: Type conversions for disambiguation option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible", + (disambiguation) => datetime.with({ hour: 2 }, { disambiguation }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js new file mode 100644 index 0000000000..bcf2046f1c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['nanosecond'], ['second'], ['year'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + + assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..67a199f72f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,61 @@ +// |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.zoneddatetime.prototype.with +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); + +for (const disambiguation of ["earlier", "later", "compatible"]) { + const timeZone = new SkippedDateTime(); + const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); + + instance.with({ day: 1 }, { disambiguation }); + + assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..2dc6beff59 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].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/ZonedDateTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js new file mode 100644 index 0000000000..89ac918633 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with +description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.with, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js new file mode 100644 index 0000000000..09ebb9c90b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError when ZonedDateTime at minimum instant and an explicit +1h offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(-86_40000_00000_00000_00000n, "UTC"); + +let temporalZonedDateTimeLike = { + offset: "+01", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js new file mode 100644 index 0000000000..cfff0818fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.with, "name", { + value: "with", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js new file mode 100644 index 0000000000..6487ca2e44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +description: > + Temporal.ZonedDateTime.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.ZonedDateTime.prototype.with(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.with), false, + "isConstructor(Temporal.ZonedDateTime.prototype.with)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js new file mode 100644 index 0000000000..d099f8857a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.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.zoneddatetime.protoype.with +description: RangeError thrown when offset 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-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => datetime.with({ hour: 2 }, { offset: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js new file mode 100644 index 0000000000..cc48282502 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.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.zoneddatetime.prototype.with +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const offsetOptions = ['use', 'prefer', 'ignore', 'reject']; + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +offsetOptions.forEach((offsetOption) => { + badOffsets.forEach((offset) => { + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => instance.with({ offset }, { offset: offsetOption }), + `"${offset} is not a valid offset string (with ${offsetOption} offset option)`, + ); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js new file mode 100644 index 0000000000..f48b9c750e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-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.zoneddatetime.prototype.with +description: Fallback value for offset option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("-03:30"); + +const datetime = new Temporal.ZonedDateTime(1572757201_000_000_000n, timeZone); +const explicit = datetime.with({ minute: 31 }, { offset: undefined }); +assert.sameValue(explicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer"); +const implicit = datetime.with({ minute: 31 }, {}); +assert.sameValue(implicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js new file mode 100644 index 0000000000..af450155ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.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.zoneddatetime.prototype.with +description: Type conversions for offset option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("offset", "prefer", + (offset) => datetime.with({ hour: 2 }, { offset }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js new file mode 100644 index 0000000000..9674f868a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/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.zoneddatetime.prototype.with +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.with({ day: 5 }, {}); +assert.sameValue( + result1.epochNanoseconds, 345600000000000n, "UTC", + "options may be an empty plain object" +); + +const result2 = instance.with({ day: 5 }, () => {}); +assert.sameValue( + result2.epochNanoseconds, 345600000000000n, "UTC", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js new file mode 100644 index 0000000000..e648410e0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.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.zoneddatetime.prototype.with +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(949494896_987_654_321n, "UTC"); +const fields = { day: 31 }; + +const explicit = datetime.with(fields, undefined); +assert.sameValue(explicit.month, 2, "default overflow is constrain"); +assert.sameValue(explicit.day, 29, "default overflow is constrain"); + +const implicit = datetime.with(fields); +assert.sameValue(implicit.month, 2, "default overflow is constrain"); +assert.sameValue(implicit.day, 29, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js new file mode 100644 index 0000000000..94ea786d19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +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.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.with({ day: 5 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js new file mode 100644 index 0000000000..18387ceb9a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js @@ -0,0 +1,177 @@ +// |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.zoneddatetime.prototype.with +description: Properties on objects 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.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.extra", + "get options.extra", + // lookup + "get this.calendar.dateFromFields", + "get this.calendar.fields", + "get this.calendar.mergeFields", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetOffsetNanosecondsFor on receiver + "call this.timeZone.getOffsetNanosecondsFor", + // CalendarFields + "call this.calendar.fields", + // PrepareTemporalFields on receiver + "get this.calendar.day", + "call this.calendar.day", + "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.day", + "get fields.day.valueOf", + "call fields.day.valueOf", + "get fields.hour", + "get fields.hour.valueOf", + "call fields.hour.valueOf", + "get fields.microsecond", + "get fields.microsecond.valueOf", + "call fields.microsecond.valueOf", + "get fields.millisecond", + "get fields.millisecond.valueOf", + "call fields.millisecond.valueOf", + "get fields.minute", + "get fields.minute.valueOf", + "call fields.minute.valueOf", + "get fields.month", + "get fields.month.valueOf", + "call fields.month.valueOf", + "get fields.monthCode", + "get fields.monthCode.toString", + "call fields.monthCode.toString", + "get fields.nanosecond", + "get fields.nanosecond.valueOf", + "call fields.nanosecond.valueOf", + "get fields.offset", + "get fields.offset.toString", + "call fields.offset.toString", + "get fields.second", + "get fields.second.valueOf", + "call fields.second.valueOf", + "get fields.year", + "get fields.year.valueOf", + "call fields.year.valueOf", + // CalendarMergeFields + "call this.calendar.mergeFields", + // InterpretTemporalDateTimeFields + "get options.disambiguation.toString", + "call options.disambiguation.toString", + "get options.offset.toString", + "call options.offset.toString", + "get options.overflow.toString", + "call options.overflow.toString", + "call this.calendar.dateFromFields", + // InterpretISODateTimeOffset + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, 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", + day: 1.7, + hour: 1.7, + minute: 1.7, + second: 1.7, + millisecond: 1.7, + microsecond: 1.7, + nanosecond: 1.7, + offset: "+00:00", +}, "fields"); + +const options = TemporalHelpers.propertyBagObserver(actual, { + overflow: "constrain", + disambiguation: "compatible", + offset: "prefer", + extra: "property", +}, "options"); + +instance.with(fields, options); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); + +const dstInstance = new Temporal.ZonedDateTime(37800_000_000_000n /* 1970-01-01T02:30-08:00 */, dstTimeZoneObserver, calendar); +actual.splice(0); // clear calls that happened in constructor + +const fallBackFields = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", // ignored +}, "fields"); +dstInstance.with(fallBackFields, options); +assert.compareArray(actual, expected.concat([ + // extra call in InterpretISODateTimeOffset + "call this.timeZone.getOffsetNanosecondsFor", +]), "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +const springForwardFields = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 4, + monthCode: "M04", + day: 2, + hour: 2, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", // ignored +}, "fields"); +dstInstance.with(springForwardFields, options); +assert.compareArray(actual, expected.concat([ + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js new file mode 100644 index 0000000000..8e2be9a65f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => datetime.with({ minute: 45 }, { overflow }), + `invalid overflow ("${overflow}")` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js new file mode 100644 index 0000000000..17e7dd007f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.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.zoneddatetime.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-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const explicit = datetime.with({ second: 67 }, { overflow: undefined }); +assert.sameValue(explicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain"); +const implicit = datetime.with({ second: 67 }, {}); +assert.sameValue(implicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js new file mode 100644 index 0000000000..1ce7632251 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.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.plaindatetime.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-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +// See TemporalHelpers.checkStringOptionWrongType(); this code path has +// different expectations for observable calls + +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: null }), "null"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: true }), "true"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: false }), "false"); +assert.throws(TypeError, () => datetime.with({ second: 41 }, { overflow: Symbol() }), "symbol"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2 }), "number"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object"); + +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). +const expected = [ + "get overflow.toString", + "call overflow.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); +const result = datetime.with({ second: 41 }, { overflow: observer }); +assert.sameValue(result.epochNanoseconds, 1_000_000_001_987_654_321n, "object with toString"); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js new file mode 100644 index 0000000000..510a4ed819 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.prototype.with +description: The "with" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.with, + "function", + "`typeof ZonedDateTime.prototype.with` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "with", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js new file mode 100644 index 0000000000..e7eca2b136 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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.zoneddatetime.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 zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..0977701e40 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.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.zoneddatetime.prototype.with +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.prototype.with step 23: + 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); +const newDatetime = datetime.with({ year: 2022 }); + +assert.sameValue(newDatetime.hour, 1, "hour value"); +assert.sameValue(newDatetime.minute, 46, "minute value"); +assert.sameValue(newDatetime.second, 40, "second value"); +assert.sameValue(newDatetime.millisecond, 987, "millisecond value"); +assert.sameValue(newDatetime.microsecond, 654, "microsecond value"); +assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js new file mode 100644 index 0000000000..c47d2634eb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js @@ -0,0 +1,59 @@ +// |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.zoneddatetime.protoype.with +description: > + TypeError thrown when the offset field of the argument or the object returned + from mergeFields is broken +info: | + 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + 8. Append *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"nanosecond"*, *"offset"*, and *"second"* to _fieldNames_. + 9. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"offset"* »). + 10. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~). + 11. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_). + 12. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"offset"* »). +features: [Temporal] +---*/ + +class ObservedCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.resetCalls(); + } + + toString() { + return "observed-calendar"; + } + + mergeFields(original, additional) { + this.mergeFieldsCalled++; + const result = super.mergeFields(original, additional); + result.offset = Symbol("can't convert to string"); + return result; + } + + resetCalls() { + this.mergeFieldsCalled = 0; + } +} + +const calendar = new ObservedCalendar(); +const dateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + +// Test throw in step 10 + +assert.throws(TypeError, () => dateTime.with({ offset: Symbol("can't convert to string") }), "conversion failure on ZonedDateTime-like"); +assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not be called"); + +calendar.resetCalls(); + +// Test throw in step 12 (before sabotaging the ZonedDateTime instance) + +assert.throws(TypeError, () => dateTime.with({ year: 2002 }), "conversion failure on sabotaged return value from mergeFields"); +assert.sameValue(calendar.mergeFieldsCalled, 1, "calendar.mergeFields was called once"); + +calendar.resetCalls(); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/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/ZonedDateTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js new file mode 100644 index 0000000000..7435848f8d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.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.zoneddatetime.prototype.with +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "with", + [{ year: 2000 }], + (result) => { + assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result"); + assert.sameValue(result.year, 2000, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..abcf246b03 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.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.zoneddatetime.prototype.with +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..49db5085a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,39 @@ +// |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.zoneddatetime.prototype.with +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.with({ day: 27 }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no disambiguation case)` + ); + + const badTimeZone = { + id: "Etc/Bad", + getPossibleInstantsFor() { return []; }, + getOffsetNanosecondsFor: notCallable, + }; + const badDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, badTimeZone); + assert.throws( + TypeError, + () => badDateTime.with({ day: 27 }, { offset: "ignore" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=ignore and no possible instants case)` + ); + assert.throws( + TypeError, + () => badDateTime.with({ day: 27 }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no possible instants case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..cec9c2b6e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.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.zoneddatetime.prototype.with +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7d1d89673b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-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.zoneddatetime.prototype.with +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..c24bad7373 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.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.zoneddatetime.prototype.with +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], [...], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2005-09-09T01:46:40", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.with({ year: 2005 }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js new file mode 100644 index 0000000000..f4fbeff05b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset and an explicit offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js new file mode 100644 index 0000000000..fe7a12d195 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike)); + +reportCompare(0, 0); |