Temporal: Add TemporalHelpers.calendarObserver

Similar to the previous commits with property bags and time zones, there
are also some existing tests that use a Proxy to test the order of
observable operations which involve user code
passed in as part of a Temporal.TimeZone object. I am going to write
several more tests that do this, as well. This seems like a good thing to
put into TemporalHelpers, where it can be implemented consistently so that
we don't get discrepancies in which operations are tracked, or bugs due to
a Symbol-valued property.

Updates existing tests to use this helper.
This commit is contained in:
Philip Chimento 2022-09-20 12:45:59 -07:00 committed by Ms2ger
parent 12f919e45d
commit ae52931aae
3 changed files with 105 additions and 62 deletions

View File

@ -1381,6 +1381,97 @@ var TemporalHelpers = {
}); });
}, },
/*
* calendarObserver:
* A custom calendar that behaves exactly like the ISO 8601 calendar but
* tracks calls to any of its methods, and Get/Has operations on its
* properties, by appending messages to an array. This is for the purpose of
* testing order of operations that are observable from user code.
* objectName is used in the log.
*/
calendarObserver(calls, objectName, methodOverrides = {}) {
const iso8601 = new Temporal.Calendar("iso8601");
const trackingMethods = {
dateFromFields(...args) {
calls.push(`call ${objectName}.dateFromFields`);
if ('dateFromFields' in methodOverrides) {
const value = methodOverrides.dateFromFields;
return typeof value === "function" ? value(...args) : value;
}
const originalResult = iso8601.dateFromFields(...args);
// Replace the calendar in the result with the call-tracking calendar
const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
// Remove the HasProperty check resulting from the above constructor call
assert.sameValue(calls.pop(), `has ${objectName}.calendar`);
return result;
},
yearMonthFromFields(...args) {
calls.push(`call ${objectName}.yearMonthFromFields`);
if ('yearMonthFromFields' in methodOverrides) {
const value = methodOverrides.yearMonthFromFields;
return typeof value === "function" ? value(...args) : value;
}
const originalResult = iso8601.yearMonthFromFields(...args);
// Replace the calendar in the result with the call-tracking calendar
const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
// Remove the HasProperty check resulting from the above constructor call
assert.sameValue(calls.pop(), `has ${objectName}.calendar`);
return result;
},
monthDayFromFields(...args) {
calls.push(`call ${objectName}.monthDayFromFields`);
if ('monthDayFromFields' in methodOverrides) {
const value = methodOverrides.monthDayFromFields;
return typeof value === "function" ? value(...args) : value;
}
const originalResult = iso8601.monthDayFromFields(...args);
// Replace the calendar in the result with the call-tracking calendar
const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
// Remove the HasProperty check resulting from the above constructor call
assert.sameValue(calls.pop(), `has ${objectName}.calendar`);
return result;
},
dateAdd(...args) {
calls.push(`call ${objectName}.dateAdd`);
if ('dateAdd' in methodOverrides) {
const value = methodOverrides.dateAdd;
return typeof value === "function" ? value(...args) : value;
}
const originalResult = iso8601.dateAdd(...args);
const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
// Remove the HasProperty check resulting from the above constructor call
assert.sameValue(calls.pop(), `has ${objectName}.calendar`);
return result;
}
};
// Automatically generate the other methods that don't need any custom code
["toString", "dateUntil", "era", "eraYear", "year", "month", "monthCode", "day", "fields", "mergeFields"].forEach((methodName) => {
trackingMethods[methodName] = function (...args) {
actual.push(`call ${formatPropertyName(methodName, objectName)}`);
if (methodName in methodOverrides) {
const value = methodOverrides[methodName];
return typeof value === "function" ? value(...args) : value;
}
return iso8601[methodName](...args);
};
});
return new Proxy(trackingMethods, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
actual.push(`get ${formatPropertyName(key, objectName)}`);
return result;
},
has(target, key) {
actual.push(`has ${formatPropertyName(key, objectName)}`);
return Reflect.has(target, key);
},
});
},
/* /*
* A custom calendar that does not allow any of its methods to be called, for * A custom calendar that does not allow any of its methods to be called, for
* the purpose of asserting that a particular operation does not call into * the purpose of asserting that a particular operation does not call into

View File

@ -4,7 +4,7 @@
/*--- /*---
esid: sec-temporal.now.plaindatetime esid: sec-temporal.now.plaindatetime
description: Observable interactions with the provided calendar-like object description: Observable interactions with the provided calendar-like object
includes: [compareArray.js] includes: [compareArray.js, temporalHelpers.js]
features: [Proxy, Temporal] features: [Proxy, Temporal]
---*/ ---*/
@ -18,41 +18,17 @@ const expectedWith = [
'has calendar.calendar', 'has calendar.calendar',
'get calendar.calendar', 'get calendar.calendar',
'has nestedCalendar.calendar', 'has nestedCalendar.calendar',
'get nestedCalendar.Symbol(Symbol.toPrimitive)', 'get nestedCalendar[Symbol.toPrimitive]',
'get nestedCalendar.toString', 'get nestedCalendar.toString',
'call nestedCalendar.toString' 'call nestedCalendar.toString'
]; ];
const nestedCalendar = new Proxy({ const nestedCalendar = TemporalHelpers.calendarObserver(actual, "nestedCalendar", {
toString: function() { toString: "iso8601",
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({ const calendar = TemporalHelpers.calendarObserver(actual, "calendar", {
calendar: nestedCalendar, toString: "iso8601",
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];
},
}); });
calendar.calendar = nestedCalendar;
Object.defineProperty(Temporal.Calendar, 'from', { Object.defineProperty(Temporal.Calendar, 'from', {
get() { get() {

View File

@ -4,7 +4,7 @@
/*--- /*---
esid: sec-temporal.now.zoneddatetime esid: sec-temporal.now.zoneddatetime
description: Observable interactions with the provided calendar-like object description: Observable interactions with the provided calendar-like object
includes: [compareArray.js] includes: [compareArray.js, temporalHelpers.js]
features: [Proxy, Temporal] features: [Proxy, Temporal]
---*/ ---*/
@ -18,41 +18,17 @@ const expectedWith = [
'has calendar.calendar', 'has calendar.calendar',
'get calendar.calendar', 'get calendar.calendar',
'has nestedCalendar.calendar', 'has nestedCalendar.calendar',
'get nestedCalendar.Symbol(Symbol.toPrimitive)', 'get nestedCalendar[Symbol.toPrimitive]',
'get nestedCalendar.toString', 'get nestedCalendar.toString',
'call nestedCalendar.toString' 'call nestedCalendar.toString'
]; ];
const nestedCalendar = new Proxy({ const nestedCalendar = TemporalHelpers.calendarObserver(actual, "nestedCalendar", {
toString: function() { toString: "iso8601",
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({ const calendar = TemporalHelpers.calendarObserver(actual, "calendar", {
calendar: nestedCalendar, toString: "iso8601",
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];
},
}); });
calendar.calendar = nestedCalendar;
Object.defineProperty(Temporal.Calendar, 'from', { Object.defineProperty(Temporal.Calendar, 'from', {
get() { get() {