Temporal: Tests for calendar-specific mergeFields

In https://github.com/tc39/proposal-temporal/pull/2474, which achieved
consensus at the TC39 plenary meeting of 2023-01-31, the implementation-
defined steps for Temporal.Calendar.prototype.mergeFields had their
language tightened, to better specify what implementations must do.

This adds tests covering the new spec language, and moves one related test
out of staging.
This commit is contained in:
Philip Chimento 2023-02-02 16:20:05 -08:00 committed by Philip Chimento
parent 6d7925a2d2
commit a29788dd5d
6 changed files with 207 additions and 10 deletions

View File

@ -3,7 +3,7 @@
/*---
esid: sec-temporal.calendar.prototype.mergefields
description: Non-object arguments are converted with ToObject and merge their [[OwnPropertyKeys]]
description: Non-object arguments are converted with ToObject and merge their [[OwnPropertyKeys]] onto a new object
includes: [compareArray.js]
features: [Temporal]
---*/
@ -18,23 +18,23 @@ assert.throws(TypeError, () => calendar.mergeFields({}, null));
const boolResult = calendar.mergeFields(true, false);
assert.compareArray(Object.keys(boolResult), [], "Boolean objects have no own property keys");
assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
const numResult = calendar.mergeFields(3, 4);
assert.compareArray(Object.keys(numResult), [], "Number objects have no own property keys");
assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
const strResult = calendar.mergeFields("abc", "de");
assert.compareArray(Object.keys(strResult), ["0", "1", "2"], "String objects have integer indices as own property keys");
assert.sameValue(strResult["0"], "d");
assert.sameValue(strResult["1"], "e");
assert.sameValue(strResult["2"], "c");
assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
assert.sameValue(Object.getPrototypeOf(boolResult), null, "null-prototype object returned");
const symResult = calendar.mergeFields(Symbol("foo"), Symbol("bar"));
assert.compareArray(Object.keys(symResult), [], "Symbol objects have no own property keys");
assert.sameValue(Object.getPrototypeOf(symResult), Object.prototype, "plain object returned");
assert.sameValue(Object.getPrototypeOf(symResult), null, "null-prototype object returned");
const bigintResult = calendar.mergeFields(3n, 4n);
assert.compareArray(Object.keys(bigintResult), [], "BigInt objects have no own property keys");
assert.sameValue(Object.getPrototypeOf(bigintResult), Object.prototype, "plain object returned");
assert.sameValue(Object.getPrototypeOf(bigintResult), null, "null-prototype object returned");

View File

@ -9,6 +9,7 @@ includes: [compareArray.js, temporalHelpers.js]
---*/
const expected = [
// CopyDataProperties on fields
"ownKeys fields",
"getOwnPropertyDescriptor fields.year",
"get fields.year",
@ -18,6 +19,7 @@ const expected = [
"get fields.day",
"getOwnPropertyDescriptor fields.extra",
"get fields.extra",
// CopyDataProperties on additionalFields
"ownKeys additionalFields",
"getOwnPropertyDescriptor additionalFields.3",
"get additionalFields.3",

View File

@ -0,0 +1,105 @@
// 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.calendar.prototype.mergefields
description: Calendar-specific mutually exclusive keys in mergeFields
features: [Temporal]
---*/
function assertEntriesEqual(actual, expectedEntries, message) {
const names = Object.getOwnPropertyNames(actual);
const symbols = Object.getOwnPropertySymbols(actual);
const actualKeys = names.concat(symbols);
assert.sameValue(
actualKeys.length,
expectedEntries.length,
`${message}: expected object to have ${expectedEntries.length} properties, not ${actualKeys.length}:`
);
for (var index = 0; index < actualKeys.length; index++) {
const actualKey = actualKeys[index];
const expectedKey = expectedEntries[index][0];
const expectedValue = expectedEntries[index][1];
assert.sameValue(actualKey, expectedKey, `${message}: key ${index}:`);
assert.sameValue(actual[actualKey], expectedValue, `${message}: value ${index}:`);
}
}
const instance = new Temporal.Calendar("gregory");
const fullFields = {
era: "ce",
eraYear: 1981,
year: 1981,
month: 12,
monthCode: "M12",
day: 15,
};
assertEntriesEqual(instance.mergeFields(fullFields, { era: "bce", eraYear: 1 }), [
["month", 12],
["monthCode", "M12"],
["day", 15],
["era", "bce"],
["eraYear", 1],
], "era and eraYear together exclude year");
assertEntriesEqual(instance.mergeFields(fullFields, { year: -2 }), [
["month", 12],
["monthCode", "M12"],
["day", 15],
["year", -2],
], "year excludes era and eraYear");
assertEntriesEqual(instance.mergeFields(fullFields, { month: 5 }), [
["era", "ce"],
["eraYear", 1981],
["year", 1981],
["day", 15],
["month", 5],
], "month excludes monthCode");
assertEntriesEqual(instance.mergeFields(fullFields, { monthCode: "M05" }), [
["era", "ce"],
["eraYear", 1981],
["year", 1981],
["day", 15],
["monthCode", "M05"],
], "monthCode excludes month");
// Specific test cases, of mergeFields on information that is not complete
// enough to construct a PlainDate from, as discussed in
// https://github.com/tc39/proposal-temporal/issues/2407:
assertEntriesEqual(instance.mergeFields({ day: 25, monthCode: "M12", year: 1997, era: "bce" }, { eraYear: 1 }), [
["day", 25],
["monthCode", "M12"],
["eraYear", 1],
], "eraYear excludes year and era");
assertEntriesEqual(instance.mergeFields({ day: 25, monthCode: "M12", era: "bce" }, { eraYear: 1, year: 1997 }), [
["day", 25],
["monthCode", "M12"],
["eraYear", 1],
["year", 1997],
], "eraYear and year both exclude era");
assertEntriesEqual(instance.mergeFields({ day: 25, monthCode: "M12", eraYear: 1 }, { era: "bce", year: 1997 }), [
["day", 25],
["monthCode", "M12"],
["era", "bce"],
["year", 1997],
], "era and year both exclude eraYear");
assertEntriesEqual(instance.mergeFields({ day: 25, monthCode: "M12", year: 1997, eraYear: 1 }, { era: "bce" }), [
["day", 25],
["monthCode", "M12"],
["era", "bce"],
], "era excludes year and eraYear");
assertEntriesEqual(instance.mergeFields({ day: 25, monthCode: "M12", year: 1997 }, { eraYear: 1, year: 2 }), [
["day", 25],
["monthCode", "M12"],
["eraYear", 1],
["year", 2],
], "eraYear excludes year and era, year overwritten");

View File

@ -0,0 +1,77 @@
// 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.calendar.prototype.mergefields
description: Calendar-specific mutually exclusive keys in mergeFields
features: [Temporal]
---*/
function assertEntriesEqual(actual, expectedEntries, message) {
const names = Object.getOwnPropertyNames(actual);
const symbols = Object.getOwnPropertySymbols(actual);
const actualKeys = names.concat(symbols);
assert.sameValue(
actualKeys.length,
expectedEntries.length,
`${message}: expected object to have ${expectedEntries.length} properties, not ${actualKeys.length}:`
);
for (var index = 0; index < actualKeys.length; index++) {
const actualKey = actualKeys[index];
const expectedKey = expectedEntries[index][0];
const expectedValue = expectedEntries[index][1];
assert.sameValue(actualKey, expectedKey, `${message}: key ${index}:`);
assert.sameValue(actual[actualKey], expectedValue, `${message}: value ${index}:`);
}
}
const instance = new Temporal.Calendar("japanese");
const lastDayOfShowaFields = { era: "showa", eraYear: 64, year: 1989, month: 1, monthCode: "M01", day: 7 };
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { day: 10 }), [
["year", 1989],
["month", 1],
["monthCode", "M01"],
["day", 10],
], "day excludes era and eraYear");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { month: 2 }), [
["year", 1989],
["day", 7],
["month", 2],
], "month excludes monthCode, era, and eraYear");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { monthCode: "M03" }), [
["year", 1989],
["day", 7],
["monthCode", "M03"],
], "monthCode excludes month, era, and eraYear");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { year: 1988 }), [
["month", 1],
["monthCode", "M01"],
["day", 7],
["year", 1988],
], "year excludes era and eraYear (within same era)");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { year: 1990 }), [
["month", 1],
["monthCode", "M01"],
["day", 7],
["year", 1990],
], "year excludes era and eraYear (in a different era)");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { eraYear: 1 }), [
["month", 1],
["monthCode", "M01"],
["day", 7],
["eraYear", 1],
], "eraYear excludes year and era");
assertEntriesEqual(instance.mergeFields(lastDayOfShowaFields, { era: "heisei" }), [
["month", 1],
["monthCode", "M01"],
["day", 7],
["era", "heisei"],
], "era excludes year and eraYear");

View File

@ -0,0 +1,17 @@
// 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.plaindate.prototype.with
description: Behaviour when property bag forms a date out of bounds of the current era
features: [Temporal]
---*/
// Last day of Showa era
const instance = new Temporal.PlainDate(1989, 1, 7, "japanese");
const result1 = instance.with({ day: 10 });
assert.notSameValue(result1.era, instance.era, "resulting day should have crossed an era boundary");
const result2 = instance.with({ month: 2 });
assert.notSameValue(result2.era, instance.era, "resulting month should have crossed an era boundary");

View File

@ -116,7 +116,3 @@ assert.throws(RangeError, () => Temporal.PlainDate.from({
day: 1,
calendar: "japanese"
}));
// `with` doesn't crash when constraining dates out of bounds of the current era
var date = Temporal.PlainDate.from("1989-01-07").withCalendar(Temporal.Calendar.from("japanese")).with({ day: 10 });
assert.sameValue(`${ date }`, "1989-01-10[u-ca=japanese]");