Skip to content

Commit

Permalink
Bring existing toString options tests in sync with each other
Browse files Browse the repository at this point in the history
Of the toString() methods that have options for printing a time with
seconds and fractional seconds, PlainTime seems to have the most
comprehensive set of tests. Bring all the others (Duration, Instant,
PlainDateTime, and ZonedDateTime) in sync with PlainTime, and edit the
PlainTime ones where necessary to include improvements from the others.

Tests:
  - fractionalseconddigits-invalid-string.js: copy and expand on
    PlainTime's more comprehensive set of invalid strings. Add assertion
    message. Fix front matter.
  - fractionalseconddigits-non-integer.js: Fix front matter.
  - fractionalseconddigits-out-of-range.js: make sure infinity is tested.
    Add assertion messages. Fix front matter.
  - fractionalseconddigits-undefined.js: copy PlainTime's more
    comprehensive test with whole minutes, whole seconds, and subseconds.
    Copy PlainTime's test of an empty function object. Add more
    descriptive variable names and assertion messages. Fix front matter.
  - fractionalseconddigits-wrong-type.js: inline and delete TemporalHelper
    used here; it was only good for this test anyway. Improve assertion
    messages.
  - smallestunit-valid-units.js: copy PlainTime's test with a second value
    with zero seconds even. Refactor repetitive tests into a loop. Copy
    the invalid unit "era" from the Instant test. Add assertion messages.
  • Loading branch information
ptomato authored and Ms2ger committed Apr 13, 2022
1 parent 2c880bf commit b4c0aed
Show file tree
Hide file tree
Showing 30 changed files with 439 additions and 146 deletions.
33 changes: 0 additions & 33 deletions harness/temporalHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,39 +238,6 @@ var TemporalHelpers = {
});
},

/*
* checkFractionalSecondDigitsOptionWrongType(temporalObject):
*
* Checks the string-or-number type handling of the fractionalSecondDigits
* option to the various types' toString() methods. temporalObject is an
* instance of the Temporal type under test.
*/
checkFractionalSecondDigitsOptionWrongType(temporalObject) {
// null is not a number, and converts to the string "null", which is an invalid string value
assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: null }), "null");
// Booleans are not numbers, and convert to the strings "true" or "false", which are invalid
assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: true }), "true");
assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: false }), "false");
// Symbols are not numbers and cannot convert to strings
assert.throws(TypeError, () => temporalObject.toString({ fractionalSecondDigits: Symbol() }), "symbol");
// BigInts are not numbers and convert to strings which are invalid
assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: 2n }), "bigint");

// Objects are not numbers and prefer their toString() methods when converting to a string
assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: {} }), "plain object");

const toStringExpected = temporalObject.toString({ fractionalSecondDigits: 'auto' });
const expected = [
"get fractionalSecondDigits.toString",
"call fractionalSecondDigits.toString",
];
const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
const result = temporalObject.toString({ fractionalSecondDigits: observer });
assert.sameValue(result, toStringExpected, "object with toString");
assert.compareArray(actual, expected, "order of operations");
},

/*
* checkPlainDateTimeConversionFastPath(func):
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ features: [Temporal]

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);

for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos"]) {
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits }));
for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits }),
`"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ features: [Temporal]

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);

assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -Infinity }));
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -1 }));
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 10 }));
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: Infinity }));
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -Infinity }),
"−∞ is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -1 }),
"−1 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 10 }),
"10 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: Infinity }),
"∞ is out of range for fractionalSecondDigits");
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,21 @@ info: |
features: [Temporal]
---*/

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7);
const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650);

const explicit = duration.toString({ fractionalSecondDigits: undefined });
assert.sameValue(explicit, "P1Y2M3W4DT5H6M7.98765S", "default fractionalSecondDigits is auto");
const tests = [
[wholeSeconds, "P1Y2M3W4DT5H6M7S"],
[subSeconds, "P1Y2M3W4DT5H6M7.98765S"],
];

const implicit = duration.toString({});
assert.sameValue(implicit, "P1Y2M3W4DT5H6M7.98765S", "default fractionalSecondDigits is auto");
for (const [duration, expected] of tests) {
const explicit = duration.toString({ fractionalSecondDigits: undefined });
assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");

const implicit = duration.toString({});
assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");

const lambda = duration.toString(() => {});
assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,26 @@ features: [Temporal]
---*/

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(duration);

assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: null }),
"null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: true }),
"true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: false }),
"false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
assert.throws(TypeError, () => duration.toString({ fractionalSecondDigits: Symbol() }),
"symbols are not numbers and cannot convert to strings");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 2n }),
"bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: {} }),
"plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");

const expected = [
"get fractionalSecondDigits.toString",
"call fractionalSecondDigits.toString",
];
const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
const result = duration.toString({ fractionalSecondDigits: observer });
assert.sameValue(result, "P1Y2M3W4DT5H6M7.98765S", "object with toString uses toString return value");
assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,37 @@ features: [Temporal]

const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);

assert.sameValue(duration.toString({ smallestUnit: "second" }), "P1Y2M3W4DT5H6M7S");
assert.sameValue(duration.toString({ smallestUnit: "millisecond" }), "P1Y2M3W4DT5H6M7.987S");
assert.sameValue(duration.toString({ smallestUnit: "microsecond" }), "P1Y2M3W4DT5H6M7.987654S");
assert.sameValue(duration.toString({ smallestUnit: "nanosecond" }), "P1Y2M3W4DT5H6M7.987654321S");
function test(instance, expectations, description) {
for (const [smallestUnit, expectedResult] of expectations) {
assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
`${description} with smallestUnit "${smallestUnit}"`);
}
}

test(
duration,
[
["seconds", "P1Y2M3W4DT5H6M7S"],
["milliseconds", "P1Y2M3W4DT5H6M7.987S"],
["microseconds", "P1Y2M3W4DT5H6M7.987654S"],
["nanoseconds", "P1Y2M3W4DT5H6M7.987654321S"],
],
"subseconds toString"
);

test(
new Temporal.Duration(1, 2, 3, 4, 5, 6, 7),
[
["seconds", "P1Y2M3W4DT5H6M7S"],
["milliseconds", "P1Y2M3W4DT5H6M7.000S"],
["microseconds", "P1Y2M3W4DT5H6M7.000000S"],
["nanoseconds", "P1Y2M3W4DT5H6M7.000000000S"],
],
"whole seconds toString"
);

const notValid = [
"era",
"year",
"month",
"week",
Expand All @@ -24,5 +49,6 @@ const notValid = [
];

notValid.forEach((smallestUnit) => {
assert.throws(RangeError, () => duration.toString({ smallestUnit }), smallestUnit);
assert.throws(RangeError, () => duration.toString({ smallestUnit }),
`"${smallestUnit}" is not a valid unit for the smallestUnit option`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.instant.prototype.tostring step 6:
6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

const instant = new Temporal.Instant(1_000_000_000_987_650_000n);

assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: "other string" }));
for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits }),
`"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.instant.prototype.tostring step 6:
6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.instant.prototype.tostring step 6:
6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

const instant = new Temporal.Instant(1_000_000_000_987_650_000n);

assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -1 }));
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 10 }));
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -Infinity }));
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: Infinity }));
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -Infinity }),
"−∞ is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -1 }),
"−1 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 10 }),
"10 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: Infinity }),
"∞ is out of range for fractionalSecondDigits");
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,31 @@ info: |
sec-getoption step 3:
3. If _value_ is *undefined*, return _fallback_.
sec-getstringornumberoption step 2:
2. Let _value_ be ? GetOption(_options_, _property_, *"stringOrNumber"*, *undefined*, _fallback_).
2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.instant.prototype.tostring step 6:
6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
const zeroSeconds = new Temporal.Instant(0n);
const wholeSeconds = new Temporal.Instant(30_000_000_000n);
const subSeconds = new Temporal.Instant(30_123_400_000n);

const explicit = instant.toString({ fractionalSecondDigits: undefined });
assert.sameValue(explicit, "2001-09-09T01:46:40.98765Z", "default fractionalSecondDigits is auto");
const tests = [
[zeroSeconds, "1970-01-01T00:00:00Z"],
[wholeSeconds, "1970-01-01T00:00:30Z"],
[subSeconds, "1970-01-01T00:00:30.1234Z"],
];

const implicit = instant.toString({});
assert.sameValue(implicit, "2001-09-09T01:46:40.98765Z", "default fractionalSecondDigits is auto");
for (const [instant, expected] of tests) {
const explicit = instant.toString({ fractionalSecondDigits: undefined });
assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)");

const implicit = instant.toString({});
assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)");

const lambda = instant.toString(() => {});
assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)");
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,26 @@ features: [Temporal]
---*/

const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(instant);

assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: null }),
"null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: true }),
"true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: false }),
"false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits");
assert.throws(TypeError, () => instant.toString({ fractionalSecondDigits: Symbol() }),
"symbols are not numbers and cannot convert to strings");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 2n }),
"bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits");
assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: {} }),
"plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits");

const expected = [
"get fractionalSecondDigits.toString",
"call fractionalSecondDigits.toString",
];
const actual = [];
const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
const result = instant.toString({ fractionalSecondDigits: observer });
assert.sameValue(result, "2001-09-09T01:46:40.98765Z", "object with toString uses toString return value");
assert.compareArray(actual, expected, "object with toString calls toString and not valueOf");
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,36 @@ features: [Temporal]

const instant = new Temporal.Instant(1_000_000_000_123_456_789n);

assert.sameValue(instant.toString({ smallestUnit: "minute" }), "2001-09-09T01:46Z");
assert.sameValue(instant.toString({ smallestUnit: "second" }), "2001-09-09T01:46:40Z");
assert.sameValue(instant.toString({ smallestUnit: "millisecond" }), "2001-09-09T01:46:40.123Z");
assert.sameValue(instant.toString({ smallestUnit: "microsecond" }), "2001-09-09T01:46:40.123456Z");
assert.sameValue(instant.toString({ smallestUnit: "nanosecond" }), "2001-09-09T01:46:40.123456789Z");
function test(instance, expectations, description) {
for (const [smallestUnit, expectedResult] of expectations) {
assert.sameValue(instance.toString({ smallestUnit }), expectedResult,
`${description} with smallestUnit "${smallestUnit}"`);
}
}

test(
instant,
[
["minute", "2001-09-09T01:46Z"],
["second", "2001-09-09T01:46:40Z"],
["millisecond", "2001-09-09T01:46:40.123Z"],
["microsecond", "2001-09-09T01:46:40.123456Z"],
["nanosecond", "2001-09-09T01:46:40.123456789Z"],
],
"subseconds toString"
);

test(
new Temporal.Instant(999_999_960_000_000_000n),
[
["minute", "2001-09-09T01:46Z"],
["second", "2001-09-09T01:46:00Z"],
["millisecond", "2001-09-09T01:46:00.000Z"],
["microsecond", "2001-09-09T01:46:00.000000Z"],
["nanosecond", "2001-09-09T01:46:00.000000000Z"],
],
"whole minutes toString"
);

const notValid = [
"era",
Expand All @@ -25,5 +50,6 @@ const notValid = [
];

notValid.forEach((smallestUnit) => {
assert.throws(RangeError, () => instant.toString({ smallestUnit }), smallestUnit);
assert.throws(RangeError, () => instant.toString({ smallestUnit }),
`"${smallestUnit}" is not a valid unit for the smallestUnit option`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.plaindatetime.prototype.tostring step 4:
4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);

assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: "other string" }));
for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) {
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits }),
`"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.plaindatetime.prototype.tostring step 4:
4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ info: |
sec-temporal-tosecondsstringprecision step 9:
9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
sec-temporal.plaindatetime.prototype.tostring step 4:
4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
features: [Temporal]
---*/

const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);

assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }));
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }));
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -Infinity }),
"−∞ is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }),
"−1 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }),
"10 is out of range for fractionalSecondDigits");
assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: Infinity }),
"∞ is out of range for fractionalSecondDigits");
Loading

0 comments on commit b4c0aed

Please sign in to comment.