Skip to content
Permalink
Browse files
[Payment Request] upstream new features
https://bugs.webkit.org/show_bug.cgi?id=241389
<rdar://problem/94581707>

Reviewed by Tim Horton.

* Source/WTF/wtf/PlatformHave.h:
* Source/WTF/wtf/PlatformEnableCocoa.h:
Add new flags.

* Source/WebCore/Modules/applepay/ApplePayPaymentTiming.idl:
* Source/WebCore/Modules/applepay/ApplePayPaymentTiming.h:
* Source/WebCore/Modules/applepay/ApplePayLineItem.idl:
* Source/WebCore/Modules/applepay/ApplePayLineItem.h:
(WebCore::ApplePayLineItem::encode const):
(WebCore::ApplePayLineItem::decode):
* Source/WebCore/Modules/applepay/PaymentSummaryItems.h:
* Source/WebCore/Modules/applepay/cocoa/PaymentSummaryItemsCocoa.mm:
(WebCore::platformAutomaticReloadSummaryItem): Added.
(WebCore::platformSummaryItem):
Add a new line item type for automatic reload (i.e. "charge when balance drops low enough").

* Source/WebCore/Modules/applepay/ApplePayRecurringPaymentRequest.idl: Added.
* Source/WebCore/Modules/applepay/ApplePayRecurringPaymentRequest.h: Added.
(WebCore::ApplePayRecurringPaymentRequest::encode const):
(WebCore::ApplePayRecurringPaymentRequest::decode):
* Source/WebKit/Shared/ApplePay/RecurringPaymentRequest.h: Added.
* Source/WebKit/Shared/ApplePay/cocoa/RecurringPaymentRequestCocoa.mm: Added.
(WebKit::platformRecurringPaymentRequest):
Add additional details for configuring a recurring payment.

* Source/WebCore/Modules/applepay/ApplePayAutomaticReloadPaymentRequest.idl: Added.
* Source/WebCore/Modules/applepay/ApplePayAutomaticReloadPaymentRequest.h: Added.
(WebCore::ApplePayAutomaticReloadPaymentRequest::encode const):
(WebCore::ApplePayAutomaticReloadPaymentRequest::decode):
* Source/WebKit/Shared/ApplePay/AutomaticReloadPaymentRequest.h: Added.
* Source/WebKit/Shared/ApplePay/cocoa/AutomaticReloadPaymentRequestCocoa.mm: Added.
(WebKit::platformAutomaticReloadPaymentRequest):
Add additional details for configuring an automatic reload payment.

* Source/WebCore/Modules/applepay/ApplePayPaymentTokenContext.idl: Added.
* Source/WebCore/Modules/applepay/ApplePayPaymentTokenContext.h: Added.
(WebCore::ApplePayPaymentTokenContext::encode const):
(WebCore::ApplePayPaymentTokenContext::decode):
* Source/WebKit/Shared/ApplePay/PaymentTokenContext.h: Added.
* Source/WebKit/Shared/ApplePay/cocoa/PaymentTokenContextCocoa.mm: Added.
(WebKit::toDecimalNumber):
(WebKit::platformPaymentTokenContext):
(WebKit::platformPaymentTokenContexts):
Add additional details for configuring a multi-merchant payment (i.e. booking a vacation online
often aggregates multiple separate things (e.g. car, flights, hotel, etc.) into a single purchase).

* Source/WebCore/Modules/applepay/ApplePayPaymentOrderDetails.idl: Added.
* Source/WebCore/Modules/applepay/ApplePayPaymentOrderDetails.h: Added.
(WebCore::ApplePayPaymentOrderDetails::encode const):
(WebCore::ApplePayPaymentOrderDetails::decode):
Add additional details after a payment is complete for saving order details to iOS/macOS Wallet.

* Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl:
* Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h:
* Source/WebKit/Shared/Cocoa/WebCoreArgumentCodersCocoa.mm:
(IPC::ArgumentCoder<WebCore::ApplePaySessionPaymentRequest>::encode):
(IPC::ArgumentCoder<WebCore::ApplePaySessionPaymentRequest>::decode):
* Source/WebCore/Modules/applepay/ApplePaySession.cpp:
(WebCore::convertAndValidate):
(WebCore::merge): Deleted.
* Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:
(WebCore::ApplePayPaymentHandler::show):
(WebCore::ApplePayPaymentHandler::computeShippingMethods const):
(WebCore::validate):
(WebCore::ApplePayPaymentHandler::shippingAddressUpdated):
(WebCore::ApplePayPaymentHandler::shippingOptionUpdated):
(WebCore::ApplePayPaymentHandler::paymentMethodUpdated):
(WebCore::convertAndValidate):
(WebCore::ApplePayPaymentHandler::complete):
(WebCore::merge): Deleted.
* Source/WebCore/Modules/applepay/ApplePaySessionPaymentRequest.h:
(WebCore::ApplePaySessionPaymentRequest::recurringPaymentRequest const): Added.
(WebCore::ApplePaySessionPaymentRequest::setRecurringPaymentRequest): Added.
(WebCore::ApplePaySessionPaymentRequest::automaticReloadPaymentRequest const): Added.
(WebCore::ApplePaySessionPaymentRequest::setAutomaticReloadPaymentRequest): Added.
(WebCore::ApplePaySessionPaymentRequest::multiTokenContexts const): Added.
(WebCore::ApplePaySessionPaymentRequest::setMultiTokenContexts): Added.
* Source/WebCore/Modules/applepay/ApplePayDetailsUpdateBase.idl:
* Source/WebCore/Modules/applepay/ApplePayDetailsUpdateBase.h:
(WebCore::ApplePayDetailsUpdateBase::encode const):
(WebCore::ApplePayDetailsUpdateBase::decodeBase):
* Source/WebCore/Modules/applepay/paymentrequest/ApplePayModifier.h:
* Source/WebCore/Modules/applepay/paymentrequest/ApplePayModifier.idl:
* Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizationResult.idl:
* Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizationResult.h:
(WebCore::ApplePayPaymentAuthorizationResult::encode const):
(WebCore::ApplePayPaymentAuthorizationResult::decode):
* Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentCompleteDetails.idl:
* Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentCompleteDetails.h:
(WebCore::ApplePayPaymentCompleteDetails::encode const):
(WebCore::ApplePayPaymentCompleteDetails::decode):
* Source/WebKit/Platform/cocoa/PaymentAuthorizationPresenter.mm:
(WebKit::PaymentAuthorizationPresenter::completePaymentMethodSelection):
(WebKit::PaymentAuthorizationPresenter::completePaymentSession):
(WebKit::PaymentAuthorizationPresenter::completeShippingContactSelection):
(WebKit::PaymentAuthorizationPresenter::completeShippingMethodSelection):
(WebKit::PaymentAuthorizationPresenter::completeCouponCodeChange):
(WebKit::merge): Deleted.
* Source/WebKit/Shared/ApplePay/cocoa/WebPaymentCoordinatorProxyCocoa.mm:
(WebKit::WebPaymentCoordinatorProxy::platformPaymentRequest):
(WebKit::merge): Deleted.
* Source/WebKit/Platform/cocoa/WKPaymentAuthorizationDelegate.h:
* Source/WebKit/Platform/cocoa/WKPaymentAuthorizationDelegate.mm:
(-[WKPaymentAuthorizationDelegate completePaymentSession:errors:orderDetails:]): Added.
Pipe the above from the WebProcess to the UIProcess (to PassKit).

* Source/WebCore/PAL/pal/cocoa/PassKitSoftLink.h:
* Source/WebCore/PAL/pal/cocoa/PassKitSoftLink.mm:
* Source/WebCore/PAL/pal/spi/cocoa/PassKitSPI.h:
Handle new PassKit symbols for the above.

* Source/WebCore/Modules/applepay/cocoa/PaymentAPIVersionCocoa.mm:
(WebCore::PaymentAPIVersion::current):
Use new flags for the new version.

* Source/WebCore/DerivedSources.make:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/PlatformMac.cmake:
* Source/WebCore/SourcesCocoa.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebKit/SourcesCocoa.txt:
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:

* Source/WebCore/testing/MockPaymentCoordinator.h:
* Source/WebCore/testing/MockPaymentCoordinator.idl:
* Source/WebCore/testing/MockPaymentCoordinator.cpp:
(WebCore::MockPaymentCoordinator::showPaymentUI):
(WebCore::MockPaymentCoordinator::completeShippingMethodSelection):
(WebCore::MockPaymentCoordinator::completeShippingContactSelection):
(WebCore::MockPaymentCoordinator::completePaymentMethodSelection):
(WebCore::MockPaymentCoordinator::completeCouponCodeChange):
(WebCore::MockPaymentCoordinator::merge): Deleted.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-automaticReloadPaymentRequest.https.html: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-automaticReloadPaymentRequest.https-expected.txt: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-multiTokenContexts.https.html: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-multiTokenContexts.https-expected.txt: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-recurringPaymentRequest.https.html: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayModifier-recurringPaymentRequest.https-expected.txt: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayPaymentCompleteDetails-orderDetails.https.html: Added.
* LayoutTests/http/tests/paymentrequest/ApplePayPaymentCompleteDetails-orderDetails.https-expected.txt: Added.
* LayoutTests/platform/ios-wk2/TestExpectations:
* LayoutTests/platform/mac-wk2/TestExpectations:

* Source/WebKit/Shared/mac/PasteboardTypes.mm:
* Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm:
Drive-by: Unified sources fixes.

Canonical link: https://commits.webkit.org/251449@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295443 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
dcrousso committed Jun 10, 2022
1 parent b469094 commit ab957893362d9d34a90c4f8d4497f9e941bc1c07
Show file tree
Hide file tree
Showing 68 changed files with 2,222 additions and 250 deletions.
@@ -0,0 +1,26 @@

PASS The default value of `automaticReloadPaymentRequest` should be `null` if `data` is not provided as part of the request.
PASS The default value of `automaticReloadPaymentRequest` should be `null` if `data` is not provided as part of an update.
PASS Should not error if optional properties are not provided as part of the request.
PASS Should not error if optional properties are not provided as part of an update.
PASS Should propagate all data as part of the request.
PASS Should propagate all data as part of an update.
PASS Should error when `automaticReloadBilling` is missing `paymentTiming` as part of the request.
PASS Should error when `automaticReloadBilling` is missing `paymentTiming` as part of an update.
PASS Should error when `automaticReloadBilling` has an invalid `paymentTiming` as part of the request.
PASS Should error when `automaticReloadBilling` has an invalid `paymentTiming` as part of an update.
PASS Should error when `automaticReloadBilling` is missing `label` as part of the request.
PASS Should error when `automaticReloadBilling` is missing `label` as part of an update.
PASS Should error when `automaticReloadBilling` is missing `amount` as part of the request.
PASS Should error when `automaticReloadBilling` is missing `amount` as part of an update.
PASS Should error when `automaticReloadBilling` has an invalid `amount` as part of the request.
PASS Should error when `automaticReloadBilling` has an invalid `amount` as part of an update.
PASS Should error when `automaticReloadBilling` is missing `automaticReloadPaymentThresholdAmount` as part of the request.
PASS Should error when `automaticReloadBilling` is missing `automaticReloadPaymentThresholdAmount` as part of an update.
PASS Should error when `automaticReloadBilling` has an invalid `automaticReloadPaymentThresholdAmount` as part of the request.
PASS Should error when `automaticReloadBilling` has an invalid `automaticReloadPaymentThresholdAmount` as part of an update.
PASS Should error when `managementURL` is not a valid URL as part of the request.
PASS Should error when `managementURL` is not a valid URL as part of an update.
PASS Should error when `tokenNotificationURL` is not a valid URL as part of the request.
PASS Should error when `tokenNotificationURL` is not a valid URL as part of an update.

@@ -0,0 +1,162 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Tests for providing `automaticReloadPaymentRequest` 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 });

const paymentDescription = "paymentDescription";
const automaticReloadBilling = { label: "automaticReloadBilling", amount: "5", type: "final", paymentTiming: "automaticReload", automaticReloadPaymentThresholdAmount: "10", recurringPaymentIntervalUnit: "month", recurringPaymentIntervalCount: 1 };
const billingAgreement = "billingAgreement";
const managementURL = "http://managementURL.com";
const tokenNotificationURL = "http://tokenNotificationURL.com";

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

function testValidAutomaticReloadPaymentRequest(description, {initialAutomaticReloadPaymentRequest, expectedAutomaticReloadPaymentRequest}) {
user_activation_test(async (test) => {
let detailsInit = validPaymentDetails();
if (initialAutomaticReloadPaymentRequest !== undefined)
detailsInit.modifiers = modifiersWithData({ automaticReloadPaymentRequest: initialAutomaticReloadPaymentRequest });

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 actualAutomaticReloadPaymentRequest = internals.mockPaymentCoordinator.automaticReloadPaymentRequest;
if (actualAutomaticReloadPaymentRequest)
assert_object_equals(actualAutomaticReloadPaymentRequest, expectedAutomaticReloadPaymentRequest, "check that the `automaticReloadPaymentRequest` matches");
else
assert_equals(actualAutomaticReloadPaymentRequest, expectedAutomaticReloadPaymentRequest, "check that the `automaticReloadPaymentRequest` 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 (initialAutomaticReloadPaymentRequest !== undefined)
detailsUpdate.modifiers = modifiersWithData({ automaticReloadPaymentRequest: initialAutomaticReloadPaymentRequest });
event.updateWith(detailsUpdate);
internals.mockPaymentCoordinator.acceptPayment();
});

let response = await request.show();

let actualAutomaticReloadPaymentRequest = internals.mockPaymentCoordinator.automaticReloadPaymentRequest;
if (actualAutomaticReloadPaymentRequest)
assert_object_equals(actualAutomaticReloadPaymentRequest, expectedAutomaticReloadPaymentRequest, "check that the `automaticReloadPaymentRequest` matches");
else
assert_equals(actualAutomaticReloadPaymentRequest, expectedAutomaticReloadPaymentRequest, "check that the `automaticReloadPaymentRequest` matches");

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

testValidAutomaticReloadPaymentRequest("The default value of `automaticReloadPaymentRequest` should be `null` if `data` is not provided", {
initialAutomaticReloadPaymentRequest: undefined,
expectedAutomaticReloadPaymentRequest: null,
});

testValidAutomaticReloadPaymentRequest("Should not error if optional properties are not provided", {
initialAutomaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, managementURL },
expectedAutomaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, managementURL },
});
testValidAutomaticReloadPaymentRequest("Should propagate all data", {
initialAutomaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, billingAgreement, managementURL, tokenNotificationURL },
expectedAutomaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, billingAgreement, managementURL, tokenNotificationURL },
});

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

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({ automaticReloadPaymentRequest }) });
});

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.");
}

testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` is missing `paymentTiming`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { } , managementURL },
expectedErrorSubstring: "'automaticReloadBilling' must be an 'automaticReload' line item",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` has an invalid `paymentTiming`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "immediate" } , managementURL },
expectedErrorSubstring: "'automaticReloadBilling' must be an 'automaticReload' line item",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` is missing `label`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "automaticReload" } , managementURL },
expectedErrorSubstring: "Missing label for 'automaticReloadBilling'",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` is missing `amount`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "automaticReload", label: "automaticReloadBillingBad" } , managementURL },
expectedErrorSubstring: "not a valid amount",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` has an invalid `amount`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "automaticReload", label: "automaticReloadBillingBad", amount: "invalid" } , managementURL },
expectedErrorSubstring: "not a valid amount",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` is missing `automaticReloadPaymentThresholdAmount`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "automaticReload", label: "automaticReloadBillingBad", amount: "42" } , managementURL },
expectedErrorSubstring: "not a valid automaticReloadPaymentThresholdAmount",
});
testInvalidAutomaticReloadPaymentRequest("Should error when `automaticReloadBilling` has an invalid `automaticReloadPaymentThresholdAmount`", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling: { paymentTiming: "automaticReload", label: "automaticReloadBillingBad", amount: "42", automaticReloadPaymentThresholdAmount: "invalid" } , managementURL },
expectedErrorSubstring: "not a valid automaticReloadPaymentThresholdAmount",
});

testInvalidAutomaticReloadPaymentRequest("Should error when `managementURL` is not a valid URL", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, managementURL: 42 },
expectedErrorSubstring: "not a valid URL",
});

testInvalidAutomaticReloadPaymentRequest("Should error when `tokenNotificationURL` is not a valid URL", {
automaticReloadPaymentRequest: { paymentDescription, automaticReloadBilling, managementURL, tokenNotificationURL: 42 },
expectedErrorSubstring: "not a valid URL",
});

done();
</script>

@@ -0,0 +1,12 @@

PASS The default value of `multiTokenContexts` should be `null` if `data` is not provided as part of the request.
PASS The default value of `multiTokenContexts` should be `null` if `data` is not provided as part of an update.
PASS The value of `multiTokenContexts` should be empty if `data` is provided as `[]` as part of the request.
PASS The value of `multiTokenContexts` should be empty if `data` is provided as `[]` as part of an update.
PASS Should not error if optional properties are not provided as part of the request.
PASS Should not error if optional properties are not provided as part of an update.
PASS Should propagate all data as part of the request.
PASS Should propagate all data as part of an update.
PASS Should error when an `amount` in `multiTokenContexts` is not a valid amount as part of the request.
PASS Should error when an `amount` in `multiTokenContexts` is not a valid amount as part of an update.

@@ -0,0 +1,132 @@
<!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 });

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>

0 comments on commit ab95789

Please sign in to comment.