瀏覽代碼

LibJS: Implement Temporal.PlainMonthDay.prototype.with/equals

Timothy Flynn 7 月之前
父節點
當前提交
5389acc231

+ 35 - 0
Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp

@@ -17,6 +17,7 @@
 #include <LibJS/Runtime/Temporal/Instant.h>
 #include <LibJS/Runtime/Temporal/Instant.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainDateTime.h>
 #include <LibJS/Runtime/Temporal/PlainDateTime.h>
+#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
 #include <LibJS/Runtime/Temporal/TimeZone.h>
 #include <LibJS/Runtime/Temporal/TimeZone.h>
 
 
 namespace JS::Temporal {
 namespace JS::Temporal {
@@ -451,6 +452,40 @@ Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit unit)
     }
     }
 }
 }
 
 
+// 13.23 IsPartialTemporalObject ( value ), https://tc39.es/proposal-temporal/#sec-temporal-ispartialtemporalobject
+ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
+{
+    // 1. If value is not an Object, return false.
+    if (!value.is_object())
+        return false;
+
+    auto const& object = value.as_object();
+
+    // 2. If value has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],
+    //    [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal
+    //    slot, return false.
+    // FIXME: Add the other types as we define them.
+    if (is<PlainMonthDay>(object))
+        return false;
+
+    // 3. Let calendarProperty be ? Get(value, "calendar").
+    auto calendar_property = TRY(object.get(vm.names.calendar));
+
+    // 4. If calendarProperty is not undefined, return false.
+    if (!calendar_property.is_undefined())
+        return false;
+
+    // 5. Let timeZoneProperty be ? Get(value, "timeZone").
+    auto time_zone_property = TRY(object.get(vm.names.timeZone));
+
+    // 6. If timeZoneProperty is not undefined, return false.
+    if (!time_zone_property.is_undefined())
+        return false;
+
+    // 7. Return true.
+    return true;
+}
+
 // 13.24 FormatFractionalSeconds ( subSecondNanoseconds, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatfractionalseconds
 // 13.24 FormatFractionalSeconds ( subSecondNanoseconds, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatfractionalseconds
 String format_fractional_seconds(u64 sub_second_nanoseconds, Precision precision)
 String format_fractional_seconds(u64 sub_second_nanoseconds, Precision precision)
 {
 {

+ 1 - 0
Libraries/LibJS/Runtime/Temporal/AbstractOperations.h

@@ -162,6 +162,7 @@ bool is_calendar_unit(Unit);
 UnitCategory temporal_unit_category(Unit);
 UnitCategory temporal_unit_category(Unit);
 RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
 RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
 Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
 Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
+ThrowCompletionOr<bool> is_partial_temporal_object(VM&, Value);
 String format_fractional_seconds(u64, Precision);
 String format_fractional_seconds(u64, Precision);
 String format_time_string(u8 hour, u8 minute, u8 second, u16 sub_second_nanoseconds, SecondsStringPrecision::Precision, Optional<TimeStyle> = {});
 String format_time_string(u8 hour, u8 minute, u8 second, u16 sub_second_nanoseconds, SecondsStringPrecision::Precision, Optional<TimeStyle> = {});
 UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);
 UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);

+ 109 - 0
Libraries/LibJS/Runtime/Temporal/Calendar.cpp

@@ -243,6 +243,73 @@ ThrowCompletionOr<CalendarFields> prepare_calendar_fields(VM& vm, StringView cal
     return result;
     return result;
 }
 }
 
 
+// 12.2.4 CalendarFieldKeysPresent ( fields ), https://tc39.es/proposal-temporal/#sec-temporal-calendarfieldkeyspresent
+Vector<CalendarField> calendar_field_keys_present(CalendarFields const& fields)
+{
+    // 1. Let list be « ».
+    Vector<CalendarField> list;
+
+    auto handle_field = [&](auto enumeration_key, auto const& value) {
+        // a. Let value be fields' field whose name is given in the Field Name column of the row.
+        // b. Let enumerationKey be the value in the Enumeration Key column of the row.
+        // c. If value is not unset, append enumerationKey to list.
+        if (value.has_value())
+            list.append(enumeration_key);
+    };
+
+    // 2. For each row of Table 19, except the header row, do
+#define __JS_ENUMERATE(enumeration, field_name, property_key, conversion) \
+    handle_field(enumeration, fields.field_name);
+    JS_ENUMERATE_CALENDAR_FIELDS
+#undef __JS_ENUMERATE
+
+    // 3. Return list.
+    return list;
+}
+
+// 12.2.5 CalendarMergeFields ( calendar, fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmergefields
+CalendarFields calendar_merge_fields(StringView calendar, CalendarFields const& fields, CalendarFields const& additional_fields)
+{
+    // 1. Let additionalKeys be CalendarFieldKeysPresent(additionalFields).
+    auto additional_keys = calendar_field_keys_present(additional_fields);
+
+    // 2. Let overriddenKeys be CalendarFieldKeysToIgnore(calendar, additionalKeys).
+    auto overridden_keys = calendar_field_keys_to_ignore(calendar, additional_keys);
+
+    // 3. Let merged be a Calendar Fields Record with all fields set to unset.
+    auto merged = CalendarFields::unset();
+
+    // 4. Let fieldsKeys be CalendarFieldKeysPresent(fields).
+    auto fields_keys = calendar_field_keys_present(fields);
+
+    auto merge_field = [&](auto key, auto& merged_field, auto const& fields_field, auto const& additional_fields_field) {
+        // a. Let key be the value in the Enumeration Key column of the row.
+
+        // b. If fieldsKeys contains key and overriddenKeys does not contain key, then
+        if (fields_keys.contains_slow(key) && !overridden_keys.contains_slow(key)) {
+            // i. Let propValue be fields' field whose name is given in the Field Name column of the row.
+            // ii. Set merged's field whose name is given in the Field Name column of the row to propValue.
+            merged_field = fields_field;
+        }
+
+        // c. If additionalKeys contains key, then
+        if (additional_keys.contains_slow(key)) {
+            // i. Let propValue be additionalFields' field whose name is given in the Field Name column of the row.
+            // ii. Set merged's field whose name is given in the Field Name column of the row to propValue.
+            merged_field = additional_fields_field;
+        }
+    };
+
+    // 5. For each row of Table 19, except the header row, do
+#define __JS_ENUMERATE(enumeration, field_name, property_key, conversion) \
+    merge_field(enumeration, merged.field_name, fields.field_name, additional_fields.field_name);
+    JS_ENUMERATE_CALENDAR_FIELDS
+#undef __JS_ENUMERATE
+
+    // 6. Return merged.
+    return merged;
+}
+
 // 12.2.8 ToTemporalCalendarIdentifier ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendaridentifier
 // 12.2.8 ToTemporalCalendarIdentifier ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendaridentifier
 ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal_calendar_like)
 ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal_calendar_like)
 {
 {
@@ -328,6 +395,15 @@ String format_calendar_annotation(StringView id, ShowCalendar show_calendar)
     return MUST(String::formatted("[{}u-ca={}]", flag, id));
     return MUST(String::formatted("[{}u-ca={}]", flag, id));
 }
 }
 
 
+// 12.2.14 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals
+bool calendar_equals(StringView one, StringView two)
+{
+    // 1. If CanonicalizeUValue("ca", one) is CanonicalizeUValue("ca", two), return true.
+    // 2. Return false.
+    return Unicode::canonicalize_unicode_extension_values("ca"sv, one)
+        == Unicode::canonicalize_unicode_extension_values("ca"sv, two);
+}
+
 // 12.2.15 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
 // 12.2.15 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
 u8 iso_days_in_month(double year, double month)
 u8 iso_days_in_month(double year, double month)
 {
 {
@@ -532,6 +608,39 @@ Vector<CalendarField> calendar_extra_fields(StringView calendar, CalendarFieldLi
     return {};
     return {};
 }
 }
 
 
+// 12.2.23 CalendarFieldKeysToIgnore ( calendar, keys ), https://tc39.es/proposal-temporal/#sec-temporal-calendarfieldkeystoignore
+Vector<CalendarField> calendar_field_keys_to_ignore(StringView calendar, ReadonlySpan<CalendarField> keys)
+{
+    // 1. If calendar is "iso8601", then
+    if (calendar == "iso8601"sv) {
+        // a. Let ignoredKeys be an empty List.
+        Vector<CalendarField> ignored_keys;
+
+        // b. For each element key of keys, do
+        for (auto key : keys) {
+            // i. Append key to ignoredKeys.
+            ignored_keys.append(key);
+
+            // ii. If key is MONTH, append MONTH-CODE to ignoredKeys.
+            if (key == CalendarField::Month)
+                ignored_keys.append(CalendarField::MonthCode);
+            // iii. Else if key is MONTH-CODE, append MONTH to ignoredKeys.
+            else if (key == CalendarField::MonthCode)
+                ignored_keys.append(CalendarField::Month);
+        }
+
+        // c. NOTE: While ignoredKeys can have duplicate elements, this is not intended to be meaningful. This specification
+        //    only checks whether particular keys are or are not members of the list.
+
+        // d. Return ignoredKeys.
+        return ignored_keys;
+    }
+
+    // 2. Return an implementation-defined List as described below.
+    // FIXME: Return keys for an ISO8601 calendar for now.
+    return calendar_field_keys_to_ignore("iso8601"sv, keys);
+}
+
 // 12.2.24 CalendarResolveFields ( calendar, fields, type ), https://tc39.es/proposal-temporal/#sec-temporal-calendarresolvefields
 // 12.2.24 CalendarResolveFields ( calendar, fields, type ), https://tc39.es/proposal-temporal/#sec-temporal-calendarresolvefields
 ThrowCompletionOr<void> calendar_resolve_fields(VM& vm, StringView calendar, CalendarFields& fields, DateType type)
 ThrowCompletionOr<void> calendar_resolve_fields(VM& vm, StringView calendar, CalendarFields& fields, DateType type)
 {
 {

+ 4 - 0
Libraries/LibJS/Runtime/Temporal/Calendar.h

@@ -101,15 +101,19 @@ Vector<String> const& available_calendars();
 ThrowCompletionOr<CalendarFields> prepare_calendar_fields(VM&, StringView calendar, Object const& fields, CalendarFieldList calendar_field_names, CalendarFieldList non_calendar_field_names, CalendarFieldListOrPartial required_field_names);
 ThrowCompletionOr<CalendarFields> prepare_calendar_fields(VM&, StringView calendar, Object const& fields, CalendarFieldList calendar_field_names, CalendarFieldList non_calendar_field_names, CalendarFieldListOrPartial required_field_names);
 ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM&, StringView calendar, CalendarFields, Overflow);
 ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM&, StringView calendar, CalendarFields, Overflow);
 String format_calendar_annotation(StringView id, ShowCalendar);
 String format_calendar_annotation(StringView id, ShowCalendar);
+bool calendar_equals(StringView one, StringView two);
 u8 iso_days_in_month(double year, double month);
 u8 iso_days_in_month(double year, double month);
 YearWeek iso_week_of_year(ISODate const&);
 YearWeek iso_week_of_year(ISODate const&);
 u16 iso_day_of_year(ISODate const&);
 u16 iso_day_of_year(ISODate const&);
 u8 iso_day_of_week(ISODate const&);
 u8 iso_day_of_week(ISODate const&);
+Vector<CalendarField> calendar_field_keys_present(CalendarFields const&);
+CalendarFields calendar_merge_fields(StringView calendar, CalendarFields const& fields, CalendarFields const& additional_fields);
 ThrowCompletionOr<String> to_temporal_calendar_identifier(VM&, Value temporal_calendar_like);
 ThrowCompletionOr<String> to_temporal_calendar_identifier(VM&, Value temporal_calendar_like);
 ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&, Object const& item);
 ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&, Object const& item);
 ThrowCompletionOr<ISODate> calendar_month_day_to_iso_reference_date(VM&, StringView calendar, CalendarFields const&, Overflow);
 ThrowCompletionOr<ISODate> calendar_month_day_to_iso_reference_date(VM&, StringView calendar, CalendarFields const&, Overflow);
 CalendarDate calendar_iso_to_date(StringView calendar, ISODate const&);
 CalendarDate calendar_iso_to_date(StringView calendar, ISODate const&);
 Vector<CalendarField> calendar_extra_fields(StringView calendar, CalendarFieldList);
 Vector<CalendarField> calendar_extra_fields(StringView calendar, CalendarFieldList);
+Vector<CalendarField> calendar_field_keys_to_ignore(StringView calendar, ReadonlySpan<CalendarField>);
 ThrowCompletionOr<void> calendar_resolve_fields(VM&, StringView calendar, CalendarFields&, DateType);
 ThrowCompletionOr<void> calendar_resolve_fields(VM&, StringView calendar, CalendarFields&, DateType);
 
 
 }
 }

+ 31 - 0
Libraries/LibJS/Runtime/Temporal/PlainDate.cpp

@@ -113,4 +113,35 @@ bool iso_date_within_limits(ISODate iso_date)
     return iso_date_time_within_limits(iso_date_time);
     return iso_date_time_within_limits(iso_date_time);
 }
 }
 
 
+// 3.5.12 CompareISODate ( isoDate1, isoDate2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate
+i8 compare_iso_date(ISODate iso_date1, ISODate iso_date2)
+{
+    // 1. If isoDate1.[[Year]] > isoDate2.[[Year]], return 1.
+    if (iso_date1.year > iso_date2.year)
+        return 1;
+
+    // 2. If isoDate1.[[Year]] < isoDate2.[[Year]], return -1.
+    if (iso_date1.year < iso_date2.year)
+        return -1;
+
+    // 3. If isoDate1.[[Month]] > isoDate2.[[Month]], return 1.
+    if (iso_date1.month > iso_date2.month)
+        return 1;
+
+    // 4. If isoDate1.[[Month]] < isoDate2.[[Month]], return -1.
+    if (iso_date1.month < iso_date2.month)
+        return -1;
+
+    // 5. If isoDate1.[[Day]] > isoDate2.[[Day]], return 1.
+    if (iso_date1.day > iso_date2.day)
+        return 1;
+
+    // 6. If isoDate1.[[Day]] < isoDate2.[[Day]], return -1.
+    if (iso_date1.day < iso_date2.day)
+        return -1;
+
+    // 7. Return 0.
+    return 0;
+}
+
 }
 }

+ 1 - 0
Libraries/LibJS/Runtime/Temporal/PlainDate.h

@@ -27,5 +27,6 @@ ThrowCompletionOr<ISODate> regulate_iso_date(VM& vm, double year, double month,
 bool is_valid_iso_date(double year, double month, double day);
 bool is_valid_iso_date(double year, double month, double day);
 String pad_iso_year(i32 year);
 String pad_iso_year(i32 year);
 bool iso_date_within_limits(ISODate);
 bool iso_date_within_limits(ISODate);
+i8 compare_iso_date(ISODate, ISODate);
 
 
 }
 }

+ 60 - 0
Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.cpp

@@ -6,6 +6,7 @@
  */
  */
 
 
 #include <LibJS/Runtime/Temporal/Calendar.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
+#include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
 #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
 #include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
 #include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
 #include <LibJS/Runtime/VM.h>
 #include <LibJS/Runtime/VM.h>
@@ -34,6 +35,8 @@ void PlainMonthDayPrototype::initialize(Realm& realm)
     define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable);
     define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable);
 
 
     u8 attr = Attribute::Writable | Attribute::Configurable;
     u8 attr = Attribute::Writable | Attribute::Configurable;
+    define_native_function(realm, vm.names.with, with, 1, attr);
+    define_native_function(realm, vm.names.equals, equals, 1, attr);
     define_native_function(realm, vm.names.toString, to_string, 0, attr);
     define_native_function(realm, vm.names.toString, to_string, 0, attr);
     define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
     define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
     define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
     define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
@@ -72,6 +75,63 @@ JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::day_getter)
     return calendar_iso_to_date(month_day->calendar(), month_day->iso_date()).day;
     return calendar_iso_to_date(month_day->calendar(), month_day->iso_date()).day;
 }
 }
 
 
+// 10.3.6 Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.with
+JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::with)
+{
+    auto temporal_month_day_like = vm.argument(0);
+    auto options = vm.argument(1);
+
+    // 1. Let monthDay be the this value.
+    // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
+    auto month_day = TRY(typed_this_object(vm));
+
+    // 3. If ? IsPartialTemporalObject(temporalMonthDayLike) is false, throw a TypeError exception.
+    if (!TRY(is_partial_temporal_object(vm, temporal_month_day_like)))
+        return vm.throw_completion<TypeError>(ErrorType::TemporalObjectMustBePartialTemporalObject);
+
+    // 4. Let calendar be monthDay.[[Calendar]].
+    auto const& calendar = month_day->calendar();
+
+    // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], MONTH-DAY).
+    auto fields = iso_date_to_fields(calendar, month_day->iso_date(), DateType::MonthDay);
+
+    // 6. Let partialMonthDay be ? PrepareCalendarFields(calendar, temporalMonthDayLike, « YEAR, MONTH, MONTH-CODE, DAY », « », PARTIAL).
+    auto partial_month_day = TRY(prepare_calendar_fields(vm, calendar, temporal_month_day_like.as_object(), { { CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day } }, {}, Partial {}));
+
+    // 7. Set fields to CalendarMergeFields(calendar, fields, partialMonthDay).
+    fields = calendar_merge_fields(calendar, fields, partial_month_day);
+
+    // 8. Let resolvedOptions be ? GetOptionsObject(options).
+    auto resolved_options = TRY(get_options_object(vm, options));
+
+    // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
+    auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
+
+    // 10. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, overflow).
+    auto iso_date = TRY(calendar_month_day_from_fields(vm, calendar, fields, overflow));
+
+    // 11. Return ! CreateTemporalMonthDay(isoDate, calendar).
+    return MUST(create_temporal_month_day(vm, iso_date, calendar));
+}
+
+// 10.3.7 Temporal.PlainMonthDay.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.equals
+JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::equals)
+{
+    // 1. Let monthDay be the this value.
+    // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
+    auto month_day = TRY(typed_this_object(vm));
+
+    // 3. Set other to ? ToTemporalMonthDay(other).
+    auto other = TRY(to_temporal_month_day(vm, vm.argument(0)));
+
+    // 4. If CompareISODate(monthDay.[[ISODate]], other.[[ISODate]]) ≠ 0, return false.
+    if (compare_iso_date(month_day->iso_date(), other->iso_date()) != 0)
+        return false;
+
+    // 5. Return CalendarEquals(monthDay.[[Calendar]], other.[[Calendar]]).
+    return calendar_equals(month_day->calendar(), other->calendar());
+}
+
 // 10.3.8 Temporal.PlainMonthDay.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tostring
 // 10.3.8 Temporal.PlainMonthDay.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tostring
 JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_string)
 JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_string)
 {
 {

+ 2 - 0
Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.h

@@ -26,6 +26,8 @@ private:
     JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
     JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
     JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
     JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
     JS_DECLARE_NATIVE_FUNCTION(day_getter);
     JS_DECLARE_NATIVE_FUNCTION(day_getter);
+    JS_DECLARE_NATIVE_FUNCTION(with);
+    JS_DECLARE_NATIVE_FUNCTION(equals);
     JS_DECLARE_NATIVE_FUNCTION(to_string);
     JS_DECLARE_NATIVE_FUNCTION(to_string);
     JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
     JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
     JS_DECLARE_NATIVE_FUNCTION(to_json);
     JS_DECLARE_NATIVE_FUNCTION(to_json);

+ 14 - 0
Libraries/LibJS/Tests/builtins/Temporal/PlainMonthDay/PlainMonthDay.prototype.equals.js

@@ -0,0 +1,14 @@
+describe("correct behavior", () => {
+    test("length is 1", () => {
+        expect(Temporal.PlainMonthDay.prototype.equals).toHaveLength(1);
+    });
+
+    test("basic functionality", () => {
+        const firstPlainMonthDay = new Temporal.PlainMonthDay(2, 1, "iso8601");
+        const secondPlainMonthDay = new Temporal.PlainMonthDay(1, 1, "iso8601");
+        expect(firstPlainMonthDay.equals(firstPlainMonthDay)).toBeTrue();
+        expect(secondPlainMonthDay.equals(secondPlainMonthDay)).toBeTrue();
+        expect(firstPlainMonthDay.equals(secondPlainMonthDay)).toBeFalse();
+        expect(secondPlainMonthDay.equals(firstPlainMonthDay)).toBeFalse();
+    });
+});

+ 57 - 0
Libraries/LibJS/Tests/builtins/Temporal/PlainMonthDay/PlainMonthDay.prototype.with.js

@@ -0,0 +1,57 @@
+describe("correct behavior", () => {
+    test("length is 1", () => {
+        expect(Temporal.PlainMonthDay.prototype.with).toHaveLength(1);
+    });
+
+    test("basic functionality", () => {
+        const plainMonthDay = new Temporal.PlainMonthDay(1, 1);
+        const values = [
+            [{ monthCode: "M07" }, new Temporal.PlainMonthDay(7, 1)],
+            [{ monthCode: "M07", day: 6 }, new Temporal.PlainMonthDay(7, 6)],
+            [{ year: 0, month: 7, day: 6 }, new Temporal.PlainMonthDay(7, 6)],
+        ];
+        for (const [arg, expected] of values) {
+            expect(plainMonthDay.with(arg).equals(expected)).toBeTrue();
+        }
+
+        // Supplying the same values doesn't change the month/day, but still creates a new object
+        const plainMonthDayLike = {
+            month: plainMonthDay.month,
+            day: plainMonthDay.day,
+        };
+        expect(plainMonthDay.with(plainMonthDayLike)).not.toBe(plainMonthDay);
+        expect(plainMonthDay.with(plainMonthDayLike).equals(plainMonthDay)).toBeTrue();
+    });
+});
+
+describe("errors", () => {
+    test("this value must be a Temporal.PlainMonthDay object", () => {
+        expect(() => {
+            Temporal.PlainMonthDay.prototype.with.call("foo");
+        }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay");
+    });
+
+    test("argument must be an object", () => {
+        expect(() => {
+            new Temporal.PlainMonthDay(1, 1).with("foo");
+        }).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
+        expect(() => {
+            new Temporal.PlainMonthDay(1, 1).with(42);
+        }).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
+    });
+
+    test("argument must have at least one Temporal property", () => {
+        expect(() => {
+            new Temporal.PlainMonthDay(1, 1).with({});
+        }).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
+    });
+
+    test("argument must not have 'calendar' or 'timeZone'", () => {
+        expect(() => {
+            new Temporal.PlainMonthDay(1, 1).with({ calendar: {} });
+        }).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
+        expect(() => {
+            new Temporal.PlainMonthDay(1, 1).with({ timeZone: {} });
+        }).toThrowWithMessage(TypeError, "Object must be a partial Temporal object");
+    });
+});