<!DOCTYPE html>
<meta charset="utf-8">
<title>Tests for providing `multiTokenContexts` as part of `ApplePayModifier`.</title>
<script src="/js-test-resources/ui-helper.js"></script>
<script src="/resources/payment-request.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
setup({ explicit_done: true, explicit_timeout: true });

test((test) => {
    const method = validPaymentMethod();
    method.data.features = ["paymentRequestMultiTokenContexts"];
    new PaymentRequest([method], validPaymentDetails());
}, "Should have a feature for `multiTokenContexts`.");

const merchantIdentifier = "merchantIdentifier";
const externalIdentifier = "externalIdentifier";
const merchantName = "merchantName";
const merchantDomain = "merchantDomain";
const amount = "42";

function modifiersWithData(data) {
    return [ { supportedMethods: 'https://apple.com/apple-pay', data } ];
}

function testValidMultiCokenContexts(description, {initialMultiCokenContexts, expectedMultiCokenContexts}) {
    user_activation_test(async (test) => {
        let detailsInit = validPaymentDetails();
        if (initialMultiCokenContexts !== undefined)
            detailsInit.modifiers = modifiersWithData({ multiTokenContexts: initialMultiCokenContexts });

        let request = new PaymentRequest([validPaymentMethod()], detailsInit);
        request.addEventListener("merchantvalidation", (event) => {
            event.complete({ });
        });
        request.addEventListener("shippingaddresschange", (event) => {
            event.updateWith({ });
            internals.mockPaymentCoordinator.acceptPayment();
        });

        let response = await request.show();

        let actualMultiCokenContexts = internals.mockPaymentCoordinator.multiTokenContexts;
        if (actualMultiCokenContexts)
            assert_object_equals(actualMultiCokenContexts, expectedMultiCokenContexts, "check that the `multiTokenContexts` matches");
        else
            assert_equals(actualMultiCokenContexts, expectedMultiCokenContexts, "check that the `multiTokenContexts` matches");

        await response.complete("success");
    }, description + " as part of the request.");

    user_activation_test(async (test) => {
        let request = new PaymentRequest([validPaymentMethod()], validPaymentDetails());
        request.addEventListener("merchantvalidation", (event) => {
            event.complete({ });
        });
        request.addEventListener("shippingaddresschange", (event) => {
            let detailsUpdate = { };
            if (initialMultiCokenContexts !== undefined)
                detailsUpdate.modifiers = modifiersWithData({ multiTokenContexts: initialMultiCokenContexts });
            event.updateWith(detailsUpdate);
            internals.mockPaymentCoordinator.acceptPayment();
        });

        let response = await request.show();

        let actualMultiCokenContexts = internals.mockPaymentCoordinator.multiTokenContexts;
        if (actualMultiCokenContexts)
            assert_object_equals(actualMultiCokenContexts, expectedMultiCokenContexts, "check that the `multiTokenContexts` matches");
        else
            assert_equals(actualMultiCokenContexts, expectedMultiCokenContexts, "check that the `multiTokenContexts` matches");

        await response.complete("success");
    }, description + " as part of an update.");
}

testValidMultiCokenContexts("The default value of `multiTokenContexts` should be `null` if `data` is not provided", {
    initialMultiCokenContexts: undefined,
    expectedMultiCokenContexts: null,
});
testValidMultiCokenContexts("The value of `multiTokenContexts` should be empty if `data` is provided as `[]`", {
    initialMultiCokenContexts: [ ],
    expectedMultiCokenContexts: [ ],
});

testValidMultiCokenContexts("Should not error if optional properties are not provided", {
    initialMultiCokenContexts: [ { merchantIdentifier, externalIdentifier, merchantName, amount } ],
    expectedMultiCokenContexts: [ { merchantIdentifier, externalIdentifier, merchantName, amount } ],
});
testValidMultiCokenContexts("Should propagate all data", {
    initialMultiCokenContexts: [ { merchantIdentifier, externalIdentifier, merchantName, merchantDomain, amount } ],
    expectedMultiCokenContexts: [ { merchantIdentifier, externalIdentifier, merchantName, merchantDomain, amount } ],
});

function testInvalidMultiCokenContexts(description, {multiTokenContexts, expectedErrorSubstring}) {
    user_activation_test(async (test) => {
        let detailsInit = validPaymentDetails();
        detailsInit.modifiers = modifiersWithData({ multiTokenContexts });

        let request = new PaymentRequest([validPaymentMethod()], detailsInit);
        try {
            let response = await request.show();
            internals.mockPaymentCoordinator.cancelPayment();
            assert_true(false, "should error about " + expectedErrorSubstring);
        } catch (e) {
            assert_true(e.message.includes(expectedErrorSubstring), "should error about " + expectedErrorSubstring);
        }
    }, description + " as part of the request.");

    user_activation_test(async (test) => {
        let request = new PaymentRequest([validPaymentMethod()], validPaymentDetails());
        request.addEventListener("merchantvalidation", (event) => {
            event.complete({ });
        });
        request.addEventListener("shippingaddresschange", (event) => {
            event.updateWith({ modifiers: modifiersWithData({ multiTokenContexts }) });
        });

        try {
            let response = await request.show();
            internals.mockPaymentCoordinator.cancelPayment();
            assert_true(false, "should error about " + expectedErrorSubstring);
        } catch (e) {
            assert_true(e.message.includes(expectedErrorSubstring), "should error about " + expectedErrorSubstring);
        }
    }, description + " as part of an update.");
}

testInvalidMultiCokenContexts("Should error when an `amount` in `multiTokenContexts` is not a valid amount", {
    multiTokenContexts: [ { merchantIdentifier, externalIdentifier, merchantName, amount: "invalid" } ],
    expectedErrorSubstring: "not a valid amount",
});

done();
</script>

