Temporal: Add tests for order of observable operations in *FromFields methods (#3568)

This adds tests for https://github.com/tc39/proposal-temporal/pull/2203
which was a normative change that reached consensus in the June 2022 TC39
plenary meeting.

Co-authored-by: Ms2ger <Ms2ger@gmail.com>
This commit is contained in:
Philip Chimento 2022-06-14 15:46:16 +02:00 committed by GitHub
parent 697784363f
commit 0e86baf6f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 537 additions and 0 deletions

View File

@ -0,0 +1,45 @@
// 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.calendar.prototype.datefromfields
description: Errors due to missing properties on fields object are thrown in the correct order
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Calendar("iso8601");
const missingDay = {
get year() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
get month() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
get monthCode() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
};
assert.throws(TypeError, () => instance.dateFromFields(missingDay), "day should be checked before year and month");
let getMonth = false;
let getMonthCode = false;
const missingYearAndMonth = {
day: 1,
get month() {
getMonth = true;
},
get monthCode() {
getMonthCode = true;
},
};
assert.throws(TypeError, () => instance.dateFromFields(missingYearAndMonth), "year should be checked after fetching but before resolving the month");
assert(getMonth, "year is fetched after month");
assert(getMonthCode, "year is fetched after monthCode");
const missingMonth = {
day: 1,
year: 2000,
};
assert.throws(TypeError, () => instance.dateFromFields(missingMonth), "month should be resolved last");

View File

@ -0,0 +1,68 @@
// 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.calendar.prototype.datefromfields
description: Properties on objects passed to dateFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.day",
"get fields.day.valueOf",
"call fields.day.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.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("iso8601");
const fields = {
year: 1.7,
month: 1.7,
monthCode: "M01",
day: 1.7,
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.dateFromFields(arg1, arg2);
TemporalHelpers.assertPlainDate(result, 1, 1, "M01", 1, "date result");
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");

View File

@ -0,0 +1,42 @@
// 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.calendar.prototype.monthdayfromfields
description: Errors due to missing properties on fields object are thrown in the correct order
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Calendar("iso8601");
const missingDay = {
get year() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
get month() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
get monthCode() {
TemporalHelpers.assertUnreachable("day should be checked first");
},
};
assert.throws(TypeError, () => instance.monthDayFromFields(missingDay), "day should be checked before year and month");
let getMonthCode = false;
let getYear = false;
const monthWithoutYear = {
day: 1,
month: 5,
get monthCode() {
getMonthCode = true;
},
get year() {
getYear = true;
},
};
assert.throws(TypeError, () => instance.monthDayFromFields(monthWithoutYear), "year/month should be checked after fetching but before resolving the month code");
assert(getMonthCode, "year/month is checked after fetching monthCode");
assert(getYear, "year/month is fetched after fetching month");
assert.throws(TypeError, () => instance.monthDayFromFields({ day: 1 }), "month should be resolved last");

View File

@ -0,0 +1,68 @@
// 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.calendar.prototype.monthdayfromfields
description: Properties on objects passed to monthDayFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.day",
"get fields.day.valueOf",
"call fields.day.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.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("iso8601");
const fields = {
year: 1.7,
month: 1.7,
monthCode: "M01",
day: 1.7,
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.monthDayFromFields(arg1, arg2);
TemporalHelpers.assertPlainMonthDay(result, "M01", 1, "monthDay result");
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");

View File

@ -0,0 +1,26 @@
// 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.calendar.prototype.yearmonthfromfields
description: Errors due to missing properties on fields object are thrown in the correct order
features: [Temporal]
---*/
const instance = new Temporal.Calendar("iso8601");
let getMonth = false;
let getMonthCode = false;
const missingYearAndMonth = {
get month() {
getMonth = true;
},
get monthCode() {
getMonthCode = true;
},
};
assert.throws(TypeError, () => instance.yearMonthFromFields(missingYearAndMonth), "year should be checked after fetching but before resolving the month");
assert(getMonth, "year is fetched after month");
assert(getMonthCode, "year is fetched after monthCode");
assert.throws(TypeError, () => instance.yearMonthFromFields({ year: 2000 }), "month should be resolved last");

View File

@ -0,0 +1,64 @@
// 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.calendar.prototype.yearmonthfromfields
description: Properties on objects passed to yearMonthFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.month",
"get fields.month.valueOf",
"call fields.month.valueOf",
"get fields.monthCode",
"get fields.monthCode.toString",
"call fields.monthCode.toString",
"get fields.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("iso8601");
const fields = {
year: 1.7,
month: 1.7,
monthCode: "M01",
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.yearMonthFromFields(arg1, arg2);
TemporalHelpers.assertPlainYearMonth(result, 1, 1, "M01", "yearMonth result");
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");

View File

@ -0,0 +1,76 @@
// 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.calendar.prototype.datefromfields
description: Properties on objects passed to dateFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.day",
"get fields.day.valueOf",
"call fields.day.valueOf",
"get fields.era",
"get fields.era.toString",
"call fields.era.toString",
"get fields.eraYear",
"get fields.eraYear.valueOf",
"call fields.eraYear.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.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("gregory");
const fields = {
era: "ce",
eraYear: 1.7,
year: 1.7,
month: 1.7,
monthCode: "M01",
day: 1.7,
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.dateFromFields(arg1, arg2);
TemporalHelpers.assertPlainDate(result, 1, 1, "M01", 1, "date result", "ce", 1);
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");

View File

@ -0,0 +1,76 @@
// 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.calendar.prototype.monthdayfromfields
description: Properties on objects passed to monthDayFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.day",
"get fields.day.valueOf",
"call fields.day.valueOf",
"get fields.era",
"get fields.era.toString",
"call fields.era.toString",
"get fields.eraYear",
"get fields.eraYear.valueOf",
"call fields.eraYear.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.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("gregory");
const fields = {
era: "ce",
eraYear: 1.7,
year: 1.7,
month: 1.7,
monthCode: "M01",
day: 1.7,
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.monthDayFromFields(arg1, arg2);
TemporalHelpers.assertPlainMonthDay(result, "M01", 1, "monthDay result");
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");

View File

@ -0,0 +1,72 @@
// 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.calendar.prototype.yearmonthfromfields
description: Properties on objects passed to yearMonthFromFields() are accessed in the correct order
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"get options.overflow",
"get options.overflow.toString",
"call options.overflow.toString",
"get fields.era",
"get fields.era.toString",
"call fields.era.toString",
"get fields.eraYear",
"get fields.eraYear.valueOf",
"call fields.eraYear.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.year",
"get fields.year.valueOf",
"call fields.year.valueOf",
];
const actual = [];
const instance = new Temporal.Calendar("gregory");
const fields = {
era: "ce",
eraYear: 1.7,
year: 1.7,
month: 1.7,
monthCode: "M01",
};
const arg1 = new Proxy(fields, {
get(target, key) {
actual.push(`get fields.${key}`);
if (key === "calendar") return instance;
const result = target[key];
return TemporalHelpers.toPrimitiveObserver(actual, result, `fields.${key}`);
},
has(target, key) {
actual.push(`has fields.${key}`);
return key in target;
},
});
const options = {
overflow: "reject",
};
const arg2 = new Proxy(options, {
get(target, key) {
actual.push(`get options.${key}`);
return TemporalHelpers.toPrimitiveObserver(actual, target[key], `options.${key}`);
},
has(target, key) {
actual.push(`has options.${key}`);
return key in target;
},
});
const result = instance.yearMonthFromFields(arg1, arg2);
TemporalHelpers.assertPlainYearMonth(result, 1, 1, "M01", "yearMonth result", "ce", 1);
assert.sameValue(result.calendar, instance, "calendar result");
assert.compareArray(actual, expected, "order of operations");