From b9813ed99e4e0cd24819a6cb2488c43e340d8749 Mon Sep 17 00:00:00 2001 From: Westin Wrzesinski Date: Mon, 25 May 2026 16:32:08 -0500 Subject: [PATCH] chore: add embedded checkout protocol parity tests Add shared TypeScript constants and types for the embedded checkout protocol methods, delegations, and snapshot version, and export them from the protocol package. --- .../checkoutkit/CheckoutProtocolTest.kt | 39 +++++++++- .../tests/protocol-parity.test.ts | 64 +++++++++++++++ .../DescriptorTests.swift | 30 ++++++++ .../typescript/src/embedded-checkout.ts | 77 +++++++++++++++++++ protocol/languages/typescript/src/index.ts | 24 ++++++ 5 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 platforms/react-native/modules/@shopify/checkout-kit-react-native/tests/protocol-parity.test.ts create mode 100644 protocol/languages/typescript/src/embedded-checkout.ts diff --git a/platforms/android/lib/src/test/java/com/shopify/checkoutkit/CheckoutProtocolTest.kt b/platforms/android/lib/src/test/java/com/shopify/checkoutkit/CheckoutProtocolTest.kt index 7fdc459a..c74472fb 100644 --- a/platforms/android/lib/src/test/java/com/shopify/checkoutkit/CheckoutProtocolTest.kt +++ b/platforms/android/lib/src/test/java/com/shopify/checkoutkit/CheckoutProtocolTest.kt @@ -12,6 +12,27 @@ class CheckoutProtocolTest { // region NotificationDescriptor + @Test + fun `public notification descriptors match supported embedded checkout methods`() { + assertThat( + listOf( + CheckoutProtocol.start.method, + CheckoutProtocol.complete.method, + CheckoutProtocol.error.method, + CheckoutProtocol.lineItemsChange.method, + CheckoutProtocol.totalsChange.method, + CheckoutProtocol.messagesChange.method, + ) + ).containsExactly( + "ec.start", + "ec.complete", + "ec.error", + "ec.line_items.change", + "ec.totals.change", + "ec.messages.change", + ) + } + @Test fun `start descriptor has correct method`() { assertThat(CheckoutProtocol.start.method).isEqualTo("ec.start") @@ -32,6 +53,11 @@ class CheckoutProtocolTest { assertThat(CheckoutProtocol.lineItemsChange.method).isEqualTo("ec.line_items.change") } + @Test + fun `totalsChange descriptor has correct method`() { + assertThat(CheckoutProtocol.totalsChange.method).isEqualTo("ec.totals.change") + } + @Test fun `buyerChange descriptor has correct method`() { assertThat(CheckoutProtocol.buyerChange.method).isEqualTo("ec.buyer.change") @@ -44,6 +70,15 @@ class CheckoutProtocolTest { // endregion + // region DelegationDescriptor + + @Test + fun `windowOpen descriptor has correct method`() { + assertThat(CheckoutProtocol.windowOpen.method).isEqualTo("ec.window.open_request") + } + + // endregion + // region process — notification dispatch @Test @@ -175,8 +210,8 @@ class CheckoutProtocolTest { // region specVersion @Test - fun `specVersion is non-empty`() { - assertThat(CheckoutProtocol.specVersion).isNotEmpty() + fun `specVersion matches embedded checkout snapshot`() { + assertThat(CheckoutProtocol.specVersion).isEqualTo("2026-04-08") } // endregion diff --git a/platforms/react-native/modules/@shopify/checkout-kit-react-native/tests/protocol-parity.test.ts b/platforms/react-native/modules/@shopify/checkout-kit-react-native/tests/protocol-parity.test.ts new file mode 100644 index 00000000..50c9919f --- /dev/null +++ b/platforms/react-native/modules/@shopify/checkout-kit-react-native/tests/protocol-parity.test.ts @@ -0,0 +1,64 @@ +/* +MIT License + +Copyright 2023 - Present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +import type { + Checkout, + EmbeddedCheckoutPublicNotificationMethod, + ErrorResponse, +} from '@shopify/checkout-kit-protocol'; + +describe('protocol type parity', () => { + it('can type check React Native against generated protocol models', () => { + const method: EmbeddedCheckoutPublicNotificationMethod = 'ec.start'; + const checkout: Checkout = { + id: 'chk_1', + currency: 'USD', + status: 'incomplete', + lineItems: [], + links: [], + totals: [], + ucp: { + paymentHandlers: {}, + version: '2026-04-08', + }, + }; + const error: ErrorResponse = { + ucp: { + version: '2026-04-08', + status: 'error', + }, + messages: [ + { + type: 'error', + code: 'unknown_error', + content: 'Something went wrong.', + severity: 'unrecoverable', + }, + ], + }; + + expect(method).toBe('ec.start'); + expect(checkout.id).toBe('chk_1'); + expect(error.ucp.status).toBe('error'); + }); +}); diff --git a/protocol/languages/swift/Tests/ShopifyCheckoutProtocolTests/DescriptorTests.swift b/protocol/languages/swift/Tests/ShopifyCheckoutProtocolTests/DescriptorTests.swift index 01ba7586..c47a7132 100644 --- a/protocol/languages/swift/Tests/ShopifyCheckoutProtocolTests/DescriptorTests.swift +++ b/protocol/languages/swift/Tests/ShopifyCheckoutProtocolTests/DescriptorTests.swift @@ -12,6 +12,24 @@ struct DescriptorTests { @Suite("Notifications") struct Notifications { + @Test func publicNotificationMethods() { + #expect([ + CheckoutProtocol.start.method, + CheckoutProtocol.complete.method, + CheckoutProtocol.error.method, + CheckoutProtocol.lineItemsChange.method, + CheckoutProtocol.totalsChange.method, + CheckoutProtocol.messagesChange.method, + ] == [ + "ec.start", + "ec.complete", + "ec.error", + "ec.line_items.change", + "ec.totals.change", + "ec.messages.change", + ]) + } + @Test func startMethod() { #expect(CheckoutProtocol.start.method == "ec.start") } @@ -40,4 +58,16 @@ struct DescriptorTests { #expect(CheckoutProtocol.error.method == "ec.error") } } + + @Suite("Delegations") + struct Delegations { + @Test func defaultDelegations() { + #expect(CheckoutProtocol.defaultDelegations == ["window.open"]) + } + + @Test func windowOpenDescriptor() { + #expect(CheckoutProtocol.windowOpen.method == "ec.window.open_request") + #expect(CheckoutProtocol.windowOpen.delegation == "window.open") + } + } } diff --git a/protocol/languages/typescript/src/embedded-checkout.ts b/protocol/languages/typescript/src/embedded-checkout.ts new file mode 100644 index 00000000..74c64dcc --- /dev/null +++ b/protocol/languages/typescript/src/embedded-checkout.ts @@ -0,0 +1,77 @@ +/* +MIT License + +Copyright 2023 - Present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +import type {Checkout, ErrorResponse} from './generated/Models'; + +export const CHECKOUT_PROTOCOL_VERSION = '2026-04-08' as const; + +export const EMBEDDED_CHECKOUT_PUBLIC_NOTIFICATION_METHODS = [ + 'ec.start', + 'ec.complete', + 'ec.error', + 'ec.line_items.change', + 'ec.totals.change', + 'ec.messages.change', +] as const; + +export const EMBEDDED_CHECKOUT_INTERNAL_NOTIFICATION_METHODS = [ + 'ec.buyer.change', +] as const; + +export const EMBEDDED_CHECKOUT_DELEGATIONS = ['window.open'] as const; + +export const EMBEDDED_CHECKOUT_DELEGATION_METHODS = [ + 'ec.window.open_request', +] as const; + +export type EmbeddedCheckoutPublicNotificationMethod = + (typeof EMBEDDED_CHECKOUT_PUBLIC_NOTIFICATION_METHODS)[number]; + +export type EmbeddedCheckoutInternalNotificationMethod = + (typeof EMBEDDED_CHECKOUT_INTERNAL_NOTIFICATION_METHODS)[number]; + +export type EmbeddedCheckoutNotificationMethod = + | EmbeddedCheckoutPublicNotificationMethod + | EmbeddedCheckoutInternalNotificationMethod; + +export type EmbeddedCheckoutDelegation = + (typeof EMBEDDED_CHECKOUT_DELEGATIONS)[number]; + +export type EmbeddedCheckoutDelegationMethod = + (typeof EMBEDDED_CHECKOUT_DELEGATION_METHODS)[number]; + +export interface EmbeddedCheckoutReadyParams { + delegate?: string[]; +} + +export interface EmbeddedCheckoutCheckoutParams { + checkout: Checkout; +} + +export interface EmbeddedCheckoutErrorParams { + error: ErrorResponse; +} + +export interface EmbeddedCheckoutWindowOpenParams { + url: string; +} diff --git a/protocol/languages/typescript/src/index.ts b/protocol/languages/typescript/src/index.ts index 34cfa43a..d615070b 100644 --- a/protocol/languages/typescript/src/index.ts +++ b/protocol/languages/typescript/src/index.ts @@ -1 +1,25 @@ +/* +MIT License + +Copyright 2023 - Present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +export * from './embedded-checkout'; export type * from './generated/Models';