Add tests for Temporal.now.plainDateTime (#3037)

* Temporal.now.plainDateTime: import tests from prop

* Add required metadata

* Correct invalid test

Ensure the error is thrown due to the invocation of the provided method.
Add a separate test to verify how the method is invoked.

* Remove duplicated assertions

* Improve coverage

* Rewrite test to focus on Calendar parameter

The observable interactions with the "timeZone" parameter are verified
by another test which is named for that purpose.

* Remove non-standard test

This test's title suggests that it was intended to verify the behavior
when the "calendar" parameter was undefined. The expected behavior in
that case depends on the presence of a builtin calendar named
"undefined." Test262 cannot definitively assert the presence or absence
of such a calendar.

In contrast to the title, the test body actually uses the calendar name
"japanese."  Test262 cannot definitively assert the presence or absence
of such a calendar.
This commit is contained in:
jugglinmike 2021-07-16 09:25:55 -04:00 committed by GitHub
parent afe217b318
commit 162e8be99f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 716 additions and 0 deletions

View File

@ -0,0 +1,82 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
This defines helper objects and functions for testing Temporal.
defines: [TemporalHelpers]
features: [Symbol.species, Symbol.iterator, Temporal]
---*/
var TemporalHelpers = {
/*
* Check that any calendar-carrying Temporal object has its [[Calendar]]
* internal slot read by ToTemporalCalendar, and does not fetch the calendar
* by calling getters.
* The custom calendar object is passed in to func() so that it can do its
* own additional assertions involving the calendar if necessary. (Sometimes
* there is nothing to assert as the calendar isn't stored anywhere that can
* be asserted about.)
*/
checkToTemporalCalendarFastPath(func) {
class CalendarFastPathCheck extends Temporal.Calendar {
constructor() {
super("iso8601");
}
toString() {
return "fast-path-check";
}
}
const calendar = new CalendarFastPathCheck();
const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
const actual = [];
const expected = [];
Object.defineProperty(temporalObject, "calendar", {
get() {
actual.push("get calendar");
return calendar;
},
});
func(temporalObject, calendar);
assert.compareArray(actual, expected, "calendar getter not called");
});
},
/*
* specificOffsetTimeZone():
*
* This returns an instance of a custom time zone class, which returns a
* specific custom value from its getOffsetNanosecondsFrom() method. This is
* for the purpose of testing the validation of what this method returns.
*
* It also returns an empty array from getPossibleInstantsFor(), so as to
* trigger calls to getOffsetNanosecondsFor() when used from the
* BuiltinTimeZoneGetInstantFor operation.
*/
specificOffsetTimeZone(offsetValue) {
class SpecificOffsetTimeZone extends Temporal.TimeZone {
constructor(offsetValue) {
super("UTC");
this._offsetValue = offsetValue;
}
getOffsetNanosecondsFor() {
return this._offsetValue;
}
getPossibleInstantsFor() {
return [];
}
}
return new SpecificOffsetTimeZone(offsetValue);
},
};

View File

@ -0,0 +1,46 @@
// 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.now.plaindatetime
description: Behavior when provided calendar value is a function
includes: [compareArray.js]
features: [Temporal]
---*/
const actual = [];
const expected = [
"has timeZone.timeZone",
"get timeZone.getOffsetNanosecondsFor",
"call timeZone.getOffsetNanosecondsFor",
];
const calendar = function() {};
const timeZone = new Proxy({
getOffsetNanosecondsFor(instant) {
actual.push("call timeZone.getOffsetNanosecondsFor");
return -Number(instant.epochNanoseconds % 86400_000_000_000n);
},
}, {
has(target, property) {
actual.push(`has timeZone.${property}`);
return property in target;
},
get(target, property) {
actual.push(`get timeZone.${property}`);
return target[property];
},
});
Object.defineProperty(Temporal.Calendar, "from", {
get() {
actual.push("get Temporal.Calendar.from");
return undefined;
},
});
const result = Temporal.now.plainDateTime(calendar, timeZone);
for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
assert.sameValue(result[property], 0, property);
}
assert.compareArray(actual, expected);

View File

@ -0,0 +1,20 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by invoking "toString" property
features: [Temporal]
---*/
var calendar = {
calendar: {
calendar: true,
toString: function() {
throw new Test262Error();
},
}
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime(calendar);
});

View File

@ -0,0 +1,17 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by retrieving value of "calendar" property
features: [Temporal]
---*/
var calendar = {
get calendar() {
throw new Test262Error();
},
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime(calendar);
});

View File

@ -0,0 +1,20 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by checking presence of "calendar" property
features: [Temporal]
---*/
var calendar = new Proxy({}, {
has: function(target, property) {
if (property === 'calendar') {
throw new Test262Error();
}
},
});
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime(calendar);
});

View File

@ -0,0 +1,21 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by checking presence of nested "calendar" property
features: [Temporal]
---*/
var calendar = {
calendar: new Proxy({}, {
has: function(target, property) {
if (property === 'calendar') {
throw new Test262Error();
}
},
})
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime(calendar);
});

View File

@ -0,0 +1,61 @@
// 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.now.plaindatetime
description: Observable interactions with the provided calendar-like object
includes: [compareArray.js]
features: [Temporal]
---*/
const actual = [];
const expected = [
'has calendar.calendar',
'get calendar.calendar',
'has nestedCalendar.calendar',
'get nestedCalendar.Symbol(Symbol.toPrimitive)',
'get nestedCalendar.toString',
'call nestedCalendar.toString'
];
const nestedCalendar = new Proxy({
toString: function() {
actual.push('call nestedCalendar.toString');
return 'iso8601';
}
}, {
has(target, property) {
actual.push(`has nestedCalendar.${String(property)}`);
return property in target;
},
get(target, property) {
actual.push(`get nestedCalendar.${String(property)}`);
return target[property];
},
});
const calendar = new Proxy({
calendar: nestedCalendar,
toString: function() {
actual.push('call calendar.toString');
return 'iso8601';
},
}, {
has(target, property) {
actual.push(`has calendar.${String(property)}`);
return property in target;
},
get(target, property) {
actual.push(`get calendar.${String(property)}`);
return target[property];
},
});
Object.defineProperty(Temporal.Calendar, 'from', {
get() {
actual.push('get Temporal.Calendar.from');
return undefined;
},
});
Temporal.now.plainDateTime(calendar);
assert.compareArray(actual, expected);

View File

@ -0,0 +1,22 @@
// 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.now.plaindatetime
description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
info: |
sec-temporal.now.plaindatetime step 1:
1. Return ? SystemDateTime(_temporalTimeZoneLike_, _calendar_).
sec-temporal-systemdatetime step 3:
3. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
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.now.plainDateTime(temporalObject);
assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
});

View File

@ -0,0 +1,9 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Temporal.now.plainDateTime is extensible.
features: [Temporal]
---*/
assert(Object.isExtensible(Temporal.now.plainDateTime));

View File

@ -0,0 +1,25 @@
// 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.now.plaindatetime
description: The `length` property of Temporal.now.plainDateTime
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.now.plainDateTime, "length", {
value: 1,
writable: false,
enumerable: false,
configurable: true,
});

View File

@ -0,0 +1,16 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plainDateTime
description: Temporal.now.plainDateTime.name is "plainDateTime".
includes: [propertyHelper.js]
features: [Temporal]
---*/
assert.sameValue(Temporal.now.plainDateTime.name, 'plainDateTime');
verifyProperty(Temporal.now.plainDateTime, 'name', {
enumerable: false,
writable: false,
configurable: true
});

View File

@ -0,0 +1,14 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Temporal.now.plainDateTime does not implement [[Construct]]
includes: [isConstructor.js]
features: [Reflect.construct, Temporal]
---*/
assert.sameValue(isConstructor(Temporal.now.plainDateTime), false, 'isConstructor(Temporal.now.plainDateTime) must return false');
assert.throws(TypeError, () => {
new Temporal.now.plainDateTime();
}, '`new Temporal.now.plainDateTime()` throws TypeError');

View File

@ -0,0 +1,14 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: The "plainDateTime" property of Temporal.now
includes: [propertyHelper.js]
features: [Temporal]
---*/
verifyProperty(Temporal.now, 'plainDateTime', {
enumerable: false,
writable: true,
configurable: true
});

View File

@ -0,0 +1,20 @@
// 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.now.plaindatetime
description: Return value describes the start of a day
features: [Temporal]
---*/
const calendar = Temporal.Calendar.from("iso8601");
const timeZone = {
getOffsetNanosecondsFor(instant) {
return -Number(instant.epochNanoseconds % 86400_000_000_000n);
}
};
const result = Temporal.now.plainDateTime(calendar, timeZone);
for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
assert.sameValue(result[property], 0, property);
}

View File

@ -0,0 +1,29 @@
// 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.now.plaindatetime
description: Functions when time zone argument is omitted
includes: [compareArray.js]
features: [Temporal]
---*/
const actual = [];
const expected = [];
Object.defineProperty(Temporal.TimeZone, "from", {
get() {
actual.push("get Temporal.TimeZone.from");
return undefined;
},
});
const resultExplicit = Temporal.now.plainDateTime("iso8601", undefined);
assert(resultExplicit instanceof Temporal.PlainDateTime);
assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");
const resultImplicit = Temporal.now.plainDateTime("iso8601");
assert(resultImplicit instanceof Temporal.PlainDateTime);
assert.compareArray(actual, expected, "Temporal.TimeZone.from should not be called");

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Correctly invokes `getOffsetNanosecondsFor` method of TimeZone-like objects
features: [Temporal]
---*/
var calls = [];
var timeZone = {
getOffsetNanosecondsFor: function() {
calls.push({
args: arguments,
this: this
});
return 0;
},
};
Temporal.now.plainDateTime('iso8601', timeZone);
assert.sameValue(calls.length, 1, 'call count');
assert.sameValue(calls[0].args.length, 1, 'arguments');
assert(calls[0].args[0] instanceof Temporal.Instant);
assert.sameValue(calls[0].this, timeZone);

View File

@ -0,0 +1,15 @@
// 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.now.plaindatetime
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].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
assert.throws(RangeError, () => Temporal.now.plainDateTime("iso8601", timeZone));
});

View File

@ -0,0 +1,15 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Rejects when `getOffsetNanosecondsFor` property is not a method
features: [Temporal]
---*/
var timeZone = {
getOffsetNanosecondsFor: 7
};
assert.throws(TypeError, function() {
Temporal.now.plainDateTime('iso8601', timeZone);
});

View File

@ -0,0 +1,33 @@
// 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.now.plaindatetime
description: Rejects non-numeric nanosecond values reported by TimeZone-like object
features: [Temporal]
---*/
const invalidValues = [
undefined,
null,
true,
"2020-01-01T12:45:36",
Symbol(),
2n,
{},
Temporal.PlainDateTime,
Temporal.PlainDateTime.prototype,
];
for (const dateTime of invalidValues) {
let callCount = 0;
const timeZone = {
getOffsetNanosecondsFor(instant, calendar) {
callCount += 1;
return dateTime;
},
};
assert.throws(TypeError, () => Temporal.now.plainDateTime("iso8601", timeZone));
assert.sameValue(callCount, 1, 'Invoked `getOffsetNanosecondsFor`');
}

View File

@ -0,0 +1,15 @@
// 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.now.plaindatetime
description: RangeError thrown if time zone reports an offset that is out of range
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
assert.throws(RangeError, () => Temporal.now.plainDateTime("iso8601", timeZone));
});

View File

@ -0,0 +1,17 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error when accessing `getOffsetNanosecondsFor` property throws
features: [Temporal]
---*/
var timeZone = {
get getOffsetNanosecondsFor() {
throw new Test262Error();
}
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime('iso8601', timeZone);
});

View File

@ -0,0 +1,17 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error when `getOffsetNanosecondsFor` throws
features: [Temporal]
---*/
var timeZone = {
getOffsetNanosecondsFor() {
throw new Test262Error();
}
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime('iso8601', timeZone);
});

View File

@ -0,0 +1,24 @@
// 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.now.plaindatetime
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);
assert.throws(TypeError, () => Temporal.now.plainDateTime("iso8601", timeZone));
});

View File

@ -0,0 +1,20 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by invoking "toString" property
features: [Temporal]
---*/
var timeZone = {
timeZone: {
timeZone: true,
toString: function() {
throw new Test262Error();
},
}
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime("iso8601", timeZone);
});

View File

@ -0,0 +1,17 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by retrieving value of "timeZone" property
features: [Temporal]
---*/
var timeZone = {
get timeZone() {
throw new Test262Error();
},
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime("iso8601", timeZone);
});

View File

@ -0,0 +1,21 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by checking presence of nested "timeZone" property
features: [Temporal]
---*/
var timeZone = {
timeZone: new Proxy({}, {
has: function(target, property) {
if (property === 'timeZone') {
throw new Test262Error();
}
},
})
};
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime("iso8601", timeZone);
});

View File

@ -0,0 +1,19 @@
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.now.plaindatetime
description: Forwards error thrown by checking presence of "timeZone" property
features: [Temporal]
---*/
var timeZone = new Proxy({}, {
has: function(target, property) {
if (property === 'timeZone') {
throw new Test262Error();
}
},
});
assert.throws(Test262Error, function() {
Temporal.now.plainDateTime("iso8601", timeZone);
});

View File

@ -0,0 +1,62 @@
// 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.now.plaindatetime
description: Observable interactions with the provided timezone-like object
includes: [compareArray.js]
features: [Temporal]
---*/
const actual = [];
const expected = [
"has timeZone.timeZone",
"get timeZone.timeZone",
"has nestedTimeZone.timeZone",
"get nestedTimeZone.getOffsetNanosecondsFor",
"call nestedTimeZone.getOffsetNanosecondsFor",
];
const nestedTimeZone = new Proxy({
getOffsetNanosecondsFor(instant) {
actual.push("call nestedTimeZone.getOffsetNanosecondsFor");
assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
return -Number(instant.epochNanoseconds % 86400_000_000_000n);
},
}, {
has(target, property) {
actual.push(`has nestedTimeZone.${String(property)}`);
return property in target;
},
get(target, property) {
actual.push(`get nestedTimeZone.${String(property)}`);
return target[property];
},
});
const timeZone = new Proxy({
timeZone: nestedTimeZone,
getOffsetNanosecondsFor(instant) {
actual.push("call timeZone.getOffsetNanosecondsFor");
assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
return -Number(instant.epochNanoseconds % 86400_000_000_000n);
},
}, {
has(target, property) {
actual.push(`has timeZone.${property}`);
return property in target;
},
get(target, property) {
actual.push(`get timeZone.${property}`);
return target[property];
},
});
Object.defineProperty(Temporal.TimeZone, "from", {
get() {
actual.push("get Temporal.TimeZone.from");
return undefined;
},
});
Temporal.now.plainDateTime("iso8601", timeZone);
assert.compareArray(actual, expected);