diff --git a/__mocks__/react-native.ts b/__mocks__/react-native.ts index 34cb204a..69210799 100644 --- a/__mocks__/react-native.ts +++ b/__mocks__/react-native.ts @@ -39,6 +39,8 @@ const requireNativeComponent = (..._args: any[]) => { }); }; +const codegenNativeComponent = requireNativeComponent; + const StyleSheet = { flatten: jest.fn(style => style), }; @@ -47,6 +49,7 @@ const exampleConfig = {preloading: true}; const ShopifyCheckoutSheetKit = { version: '0.7.0', + getConstants: jest.fn(() => ({version: '0.7.0'})), preload: jest.fn(), present: jest.fn(), dismiss: jest.fn(), @@ -58,6 +61,8 @@ const ShopifyCheckoutSheetKit = { initiateGeolocationRequest: jest.fn(), configureAcceleratedCheckouts: jest.fn(), isAcceleratedCheckoutAvailable: jest.fn(), + addListener: jest.fn(), + removeListeners: jest.fn(), }; // CommonJS export for Jest manual mock resolution @@ -68,6 +73,15 @@ module.exports = { }, NativeEventEmitter: jest.fn(() => createMockEmitter()), requireNativeComponent, + codegenNativeComponent, + TurboModuleRegistry: { + getEnforcing: jest.fn((name: string) => { + if (name === 'ShopifyCheckoutSheetKit') { + return ShopifyCheckoutSheetKit; + } + return null; + }), + }, NativeModules: { ShopifyCheckoutSheetKit: { ...ShopifyCheckoutSheetKit, diff --git a/modules/@shopify/checkout-sheet-kit/package.json b/modules/@shopify/checkout-sheet-kit/package.json index 3ddaf0de..da6bd81f 100644 --- a/modules/@shopify/checkout-sheet-kit/package.json +++ b/modules/@shopify/checkout-sheet-kit/package.json @@ -54,6 +54,14 @@ "react-native-builder-bob": "^0.23.2", "typescript": "^5.9.2" }, + "codegenConfig": { + "name": "RNShopifyCheckoutSheetKitSpec", + "type": "all", + "jsSrcsDir": "src/specs", + "android": { + "javaPackageName": "com.shopify.checkoutsheetkit" + } + }, "react-native-builder-bob": { "source": "src", "output": "lib", diff --git a/modules/@shopify/checkout-sheet-kit/package.snapshot.json b/modules/@shopify/checkout-sheet-kit/package.snapshot.json index 782ccbaa..8cddcc9f 100644 --- a/modules/@shopify/checkout-sheet-kit/package.snapshot.json +++ b/modules/@shopify/checkout-sheet-kit/package.snapshot.json @@ -30,6 +30,10 @@ "lib/commonjs/index.js.map", "lib/commonjs/pixels.d.js", "lib/commonjs/pixels.d.js.map", + "lib/commonjs/specs/NativeShopifyCheckoutSheetKit.js", + "lib/commonjs/specs/NativeShopifyCheckoutSheetKit.js.map", + "lib/commonjs/specs/RCTAcceleratedCheckoutButtonsNativeComponent.js", + "lib/commonjs/specs/RCTAcceleratedCheckoutButtonsNativeComponent.js.map", "lib/module/components/AcceleratedCheckoutButtons.js", "lib/module/components/AcceleratedCheckoutButtons.js.map", "lib/module/context.js", @@ -44,12 +48,20 @@ "lib/module/index.js.map", "lib/module/pixels.d.js", "lib/module/pixels.d.js.map", + "lib/module/specs/NativeShopifyCheckoutSheetKit.js", + "lib/module/specs/NativeShopifyCheckoutSheetKit.js.map", + "lib/module/specs/RCTAcceleratedCheckoutButtonsNativeComponent.js", + "lib/module/specs/RCTAcceleratedCheckoutButtonsNativeComponent.js.map", "lib/typescript/src/components/AcceleratedCheckoutButtons.d.ts", "lib/typescript/src/components/AcceleratedCheckoutButtons.d.ts.map", "lib/typescript/src/context.d.ts", "lib/typescript/src/context.d.ts.map", "lib/typescript/src/index.d.ts", "lib/typescript/src/index.d.ts.map", + "lib/typescript/src/specs/NativeShopifyCheckoutSheetKit.d.ts", + "lib/typescript/src/specs/NativeShopifyCheckoutSheetKit.d.ts.map", + "lib/typescript/src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.d.ts", + "lib/typescript/src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.d.ts.map", "package.json", "src/components/AcceleratedCheckoutButtons.tsx", "src/context.tsx", @@ -57,5 +69,7 @@ "src/events.d.ts", "src/index.d.ts", "src/index.ts", - "src/pixels.d.ts" + "src/pixels.d.ts", + "src/specs/NativeShopifyCheckoutSheetKit.ts", + "src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.ts" ] diff --git a/modules/@shopify/checkout-sheet-kit/src/components/AcceleratedCheckoutButtons.tsx b/modules/@shopify/checkout-sheet-kit/src/components/AcceleratedCheckoutButtons.tsx index ab8971f7..c52ad100 100644 --- a/modules/@shopify/checkout-sheet-kit/src/components/AcceleratedCheckoutButtons.tsx +++ b/modules/@shopify/checkout-sheet-kit/src/components/AcceleratedCheckoutButtons.tsx @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO */ import React, {useCallback, useMemo, useState} from 'react'; -import {requireNativeComponent, Platform} from 'react-native'; +import {codegenNativeComponent, Platform} from 'react-native'; import type {ViewStyle} from 'react-native'; import type { AcceleratedCheckoutWallet, @@ -178,7 +178,7 @@ interface NativeAcceleratedCheckoutButtonsProps { } const RCTAcceleratedCheckoutButtons = - requireNativeComponent( + codegenNativeComponent( 'RCTAcceleratedCheckoutButtons', ); diff --git a/modules/@shopify/checkout-sheet-kit/src/index.ts b/modules/@shopify/checkout-sheet-kit/src/index.ts index 41bf2a5c..5e5bc8f1 100644 --- a/modules/@shopify/checkout-sheet-kit/src/index.ts +++ b/modules/@shopify/checkout-sheet-kit/src/index.ts @@ -21,17 +21,13 @@ 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 { - NativeModules, - NativeEventEmitter, - PermissionsAndroid, - Platform, -} from 'react-native'; +import {NativeEventEmitter, PermissionsAndroid, Platform} from 'react-native'; import type { EmitterSubscription, EventSubscription, PermissionStatus, } from 'react-native'; +import RNShopifyCheckoutSheetKit from './specs/NativeShopifyCheckoutSheetKit'; import {ShopifyCheckoutSheetProvider, useShopifyCheckoutSheet} from './context'; import {ApplePayContactField, ColorScheme, LogLevel} from './index.d'; import type { @@ -64,15 +60,6 @@ import type { RenderStateChangeEvent, } from './components/AcceleratedCheckoutButtons'; -const RNShopifyCheckoutSheetKit = NativeModules.ShopifyCheckoutSheetKit; - -if (!('ShopifyCheckoutSheetKit' in NativeModules)) { - throw new Error(` - "@shopify/checkout-sheet-kit" is not correctly linked. - - If you are building for iOS, make sure to run "pod install" first and restart the metro server.`); -} - const defaultFeatures: Features = { handleGeolocationRequests: true, }; @@ -114,7 +101,8 @@ class ShopifyCheckoutSheet implements ShopifyCheckoutSheetKit { } } - public readonly version: string = RNShopifyCheckoutSheetKit.version; + public readonly version: string = + RNShopifyCheckoutSheetKit.getConstants().version; /** * Dismisses the currently displayed checkout sheet @@ -151,7 +139,7 @@ class ShopifyCheckoutSheet implements ShopifyCheckoutSheetKit { * @returns Promise containing the current Configuration */ public async getConfig(): Promise { - return RNShopifyCheckoutSheetKit.getConfig(); + return RNShopifyCheckoutSheetKit.getConfig() as Promise; } /** diff --git a/modules/@shopify/checkout-sheet-kit/src/specs/NativeShopifyCheckoutSheetKit.ts b/modules/@shopify/checkout-sheet-kit/src/specs/NativeShopifyCheckoutSheetKit.ts new file mode 100644 index 00000000..89855ea5 --- /dev/null +++ b/modules/@shopify/checkout-sheet-kit/src/specs/NativeShopifyCheckoutSheetKit.ts @@ -0,0 +1,101 @@ +/* +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 {TurboModule} from 'react-native'; +import {TurboModuleRegistry} from 'react-native'; + +type IosColorsSpec = { + tintColor?: string; + backgroundColor?: string; + closeButtonColor?: string; +}; + +type AndroidColorsBaseSpec = { + progressIndicator?: string; + backgroundColor?: string; + headerBackgroundColor?: string; + headerTextColor?: string; + closeButtonColor?: string; +}; + +type AndroidColorsSpec = { + progressIndicator?: string; + backgroundColor?: string; + headerBackgroundColor?: string; + headerTextColor?: string; + closeButtonColor?: string; + light?: AndroidColorsBaseSpec; + dark?: AndroidColorsBaseSpec; +}; + +type ColorsSpec = { + ios?: IosColorsSpec; + android?: AndroidColorsSpec; +}; + +type ConfigurationSpec = { + preloading?: boolean; + title?: string; + colorScheme?: string; + logLevel?: string; + colors?: ColorsSpec; +}; + +type ConfigurationResultSpec = { + preloading: boolean; + colorScheme: string; + logLevel: string; + title?: string; + tintColor?: string; + backgroundColor?: string; + closeButtonColor?: string; +}; + +export interface Spec extends TurboModule { + present(checkoutUrl: string): void; + preload(checkoutUrl: string): void; + dismiss(): void; + invalidateCache(): void; + setConfig(configuration: ConfigurationSpec): void; + getConfig(): Promise; + configureAcceleratedCheckouts( + storefrontDomain: string, + storefrontAccessToken: string, + customerEmail: string | null, + customerPhoneNumber: string | null, + customerAccessToken: string | null, + applePayMerchantIdentifier: string | null, + applyPayContactFields: string[], + supportedShippingCountries: string[], + ): Promise; + isAcceleratedCheckoutAvailable(): Promise; + isApplePayAvailable(): Promise; + initiateGeolocationRequest(allow: boolean): void; + addListener(eventName: string): void; + removeListeners(count: number): void; + getConstants(): {version: string}; +} + +export default TurboModuleRegistry.getEnforcing( + 'ShopifyCheckoutSheetKit', +); diff --git a/modules/@shopify/checkout-sheet-kit/src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.ts b/modules/@shopify/checkout-sheet-kit/src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.ts new file mode 100644 index 00000000..9af49d0d --- /dev/null +++ b/modules/@shopify/checkout-sheet-kit/src/specs/RCTAcceleratedCheckoutButtonsNativeComponent.ts @@ -0,0 +1,81 @@ +/* +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 {codegenNativeComponent, type ViewProps} from 'react-native'; +import type { + BubblingEventHandler, + DirectEventHandler, + Double, + Float, + // eslint-disable-next-line @react-native/no-deep-imports -- codegen parser requires these type names to be imported directly (not via aliases) so it can match them statically during AST traversal +} from 'react-native/Libraries/Types/CodegenTypes'; + +type FailEvent = Readonly<{ + __typename: string; + message: string; + code?: string; + recoverable?: boolean; +}>; + +type CompleteEvent = Readonly<{ + orderDetails: Readonly<{ + id: string; + email?: string; + phone?: string; + }>; +}>; + +type RenderStateChangeEvent = Readonly<{ + state: string; + reason?: string; +}>; + +type ClickLinkEvent = Readonly<{url: string}>; +type SizeChangeEvent = Readonly<{height: Double}>; + +type CheckoutIdentifierSpec = Readonly<{ + cartId?: string; + variantId?: string; + quantity?: Double; +}>; + +interface NativeProps extends ViewProps { + checkoutIdentifier: CheckoutIdentifierSpec; + cornerRadius?: Float; + wallets?: ReadonlyArray; + applePayLabel?: string; + onFail?: BubblingEventHandler; + onComplete?: BubblingEventHandler; + onCancel?: BubblingEventHandler; + onRenderStateChange?: BubblingEventHandler; + onWebPixelEvent?: BubblingEventHandler>; + onClickLink?: BubblingEventHandler; + onSizeChange?: DirectEventHandler; + onShouldRecoverFromError?: DirectEventHandler< + Readonly<{recoverable: boolean}> + >; +} + +export default codegenNativeComponent( + 'RCTAcceleratedCheckoutButtons', +); diff --git a/modules/@shopify/checkout-sheet-kit/tests/index.test.ts b/modules/@shopify/checkout-sheet-kit/tests/index.test.ts index 450f925c..da69755e 100644 --- a/modules/@shopify/checkout-sheet-kit/tests/index.test.ts +++ b/modules/@shopify/checkout-sheet-kit/tests/index.test.ts @@ -20,7 +20,11 @@ import { type AcceleratedCheckoutConfiguration, } from '../src'; import type {ApplePayContactField} from '../src/index.d'; -import {NativeModules, PermissionsAndroid, Platform} from 'react-native'; +import {TurboModuleRegistry, PermissionsAndroid, Platform} from 'react-native'; + +const NativeModule = TurboModuleRegistry.getEnforcing( + 'ShopifyCheckoutSheetKit', +) as any; const checkoutUrl = 'https://shopify.com/checkout'; const config: Configuration = { @@ -64,13 +68,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventEmitter = ShopifyCheckoutSheet.eventEmitter; afterEach(() => { - NativeModules.ShopifyCheckoutSheetKit.setConfig.mockReset(); - NativeModules.ShopifyCheckoutSheetKit.eventEmitter.addListener.mockClear(); - NativeModules.ShopifyCheckoutSheetKit.eventEmitter.removeAllListeners.mockClear(); - - // Clear mock listeners - NativeModules._listeners = []; - + NativeModule.setConfig.mockReset(); jest.clearAllMocks(); }); @@ -78,14 +76,14 @@ describe('ShopifyCheckoutSheetKit', () => { it('calls `setConfig` with the specified config on instantiation', () => { new ShopifyCheckoutSheet(config); expect( - NativeModules.ShopifyCheckoutSheetKit.setConfig, + NativeModule.setConfig, ).toHaveBeenCalledWith(config); }); it('does not call `setConfig` if no config was specified on instantiation', () => { new ShopifyCheckoutSheet(); expect( - NativeModules.ShopifyCheckoutSheetKit.setConfig, + NativeModule.setConfig, ).not.toHaveBeenCalled(); }); }); @@ -95,10 +93,10 @@ describe('ShopifyCheckoutSheetKit', () => { const instance = new ShopifyCheckoutSheet(); instance.setConfig(config); expect( - NativeModules.ShopifyCheckoutSheetKit.setConfig, + NativeModule.setConfig, ).toHaveBeenCalledTimes(1); expect( - NativeModules.ShopifyCheckoutSheetKit.setConfig, + NativeModule.setConfig, ).toHaveBeenCalledWith(config); }); @@ -110,7 +108,7 @@ describe('ShopifyCheckoutSheetKit', () => { }; instance.setConfig(configWithLogLevel); expect( - NativeModules.ShopifyCheckoutSheetKit.setConfig, + NativeModule.setConfig, ).toHaveBeenCalledWith(configWithLogLevel); }); }); @@ -120,10 +118,10 @@ describe('ShopifyCheckoutSheetKit', () => { const instance = new ShopifyCheckoutSheet(); instance.preload(checkoutUrl); expect( - NativeModules.ShopifyCheckoutSheetKit.preload, + NativeModule.preload, ).toHaveBeenCalledTimes(1); expect( - NativeModules.ShopifyCheckoutSheetKit.preload, + NativeModule.preload, ).toHaveBeenCalledWith(checkoutUrl); }); }); @@ -133,7 +131,7 @@ describe('ShopifyCheckoutSheetKit', () => { const instance = new ShopifyCheckoutSheet(); instance.invalidate(); expect( - NativeModules.ShopifyCheckoutSheetKit.invalidateCache, + NativeModule.invalidateCache, ).toHaveBeenCalledTimes(1); }); }); @@ -143,10 +141,10 @@ describe('ShopifyCheckoutSheetKit', () => { const instance = new ShopifyCheckoutSheet(); instance.present(checkoutUrl); expect( - NativeModules.ShopifyCheckoutSheetKit.present, + NativeModule.present, ).toHaveBeenCalledTimes(1); expect( - NativeModules.ShopifyCheckoutSheetKit.present, + NativeModule.present, ).toHaveBeenCalledWith(checkoutUrl); }); }); @@ -156,7 +154,7 @@ describe('ShopifyCheckoutSheetKit', () => { const instance = new ShopifyCheckoutSheet(); instance.dismiss(); expect( - NativeModules.ShopifyCheckoutSheetKit.dismiss, + NativeModule.dismiss, ).toHaveBeenCalledTimes(1); }); }); @@ -168,7 +166,7 @@ describe('ShopifyCheckoutSheetKit', () => { preloading: true, }); expect( - NativeModules.ShopifyCheckoutSheetKit.getConfig, + NativeModule.getConfig, ).toHaveBeenCalledTimes(1); }); }); @@ -191,7 +189,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'pixel'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -214,7 +212,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'pixel'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -242,7 +240,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'pixel'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -271,7 +269,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'pixel'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -295,7 +293,7 @@ describe('ShopifyCheckoutSheetKit', () => { throw new Error('Callback error'); }); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -325,7 +323,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'completed'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -345,7 +343,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'completed'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -363,7 +361,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'completed'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -436,7 +434,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'error'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -459,7 +457,7 @@ describe('ShopifyCheckoutSheetKit', () => { const eventName = 'error'; const callback = jest.fn(); instance.addEventListener(eventName, callback); - NativeModules.ShopifyCheckoutSheetKit.addEventListener( + NativeModule.addEventListener( eventName, callback, ); @@ -553,7 +551,7 @@ describe('ShopifyCheckoutSheetKit', () => { 'android.permission.ACCESS_FINE_LOCATION', ]); expect( - NativeModules.ShopifyCheckoutSheetKit.initiateGeolocationRequest, + NativeModule.initiateGeolocationRequest, ).toHaveBeenCalledWith(true); }); @@ -578,7 +576,7 @@ describe('ShopifyCheckoutSheetKit', () => { 'android.permission.ACCESS_FINE_LOCATION', ]); expect( - NativeModules.ShopifyCheckoutSheetKit.initiateGeolocationRequest, + NativeModule.initiateGeolocationRequest, ).toHaveBeenCalledWith(false); }); @@ -623,7 +621,7 @@ describe('ShopifyCheckoutSheetKit', () => { await emitGeolocationRequest(); expect( - NativeModules.ShopifyCheckoutSheetKit.initiateGeolocationRequest, + NativeModule.initiateGeolocationRequest, ).not.toHaveBeenCalled(); }); @@ -722,14 +720,14 @@ describe('ShopifyCheckoutSheetKit', () => { beforeEach(() => { Platform.OS = 'ios'; Platform.Version = '17.0'; - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts.mockReset(); - NativeModules.ShopifyCheckoutSheetKit.isAcceleratedCheckoutAvailable.mockReset(); + NativeModule.configureAcceleratedCheckouts.mockReset(); + NativeModule.isAcceleratedCheckoutAvailable.mockReset(); }); describe('configureAcceleratedCheckouts', () => { it('calls native configureAcceleratedCheckouts with correct parameters on iOS', async () => { const instance = new ShopifyCheckoutSheet(); - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts.mockResolvedValue( + NativeModule.configureAcceleratedCheckouts.mockResolvedValue( true, ); @@ -738,7 +736,7 @@ describe('ShopifyCheckoutSheetKit', () => { expect(result).toBe(true); expect( - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts, + NativeModule.configureAcceleratedCheckouts, ).toHaveBeenCalledWith( 'test-shop.myshopify.com', 'shpat_test_token', @@ -757,14 +755,14 @@ describe('ShopifyCheckoutSheetKit', () => { storefrontDomain: 'test-shop.myshopify.com', storefrontAccessToken: 'shpat_test_token', }; - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts.mockResolvedValue( + NativeModule.configureAcceleratedCheckouts.mockResolvedValue( true, ); await instance.configureAcceleratedCheckouts(minimalConfig); expect( - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts, + NativeModule.configureAcceleratedCheckouts, ).toHaveBeenCalledWith( 'test-shop.myshopify.com', 'shpat_test_token', @@ -786,7 +784,7 @@ describe('ShopifyCheckoutSheetKit', () => { expect(result).toBe(false); expect( - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts, + NativeModule.configureAcceleratedCheckouts, ).not.toHaveBeenCalled(); }); @@ -881,7 +879,7 @@ describe('ShopifyCheckoutSheetKit', () => { storefrontDomain: 'test-shop.myshopify.com', storefrontAccessToken: 'shpat_test_token', }; - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts.mockResolvedValue( + NativeModule.configureAcceleratedCheckouts.mockResolvedValue( true, ); @@ -930,7 +928,7 @@ describe('ShopifyCheckoutSheetKit', () => { }); expect( - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts, + NativeModule.configureAcceleratedCheckouts, ).toHaveBeenCalledWith( 'test-shop.myshopify.com', 'shpat_test_token', @@ -958,7 +956,7 @@ describe('ShopifyCheckoutSheetKit', () => { }); expect( - NativeModules.ShopifyCheckoutSheetKit.configureAcceleratedCheckouts, + NativeModule.configureAcceleratedCheckouts, ).toHaveBeenCalledWith( 'test-shop.myshopify.com', 'shpat_test_token', @@ -975,7 +973,7 @@ describe('ShopifyCheckoutSheetKit', () => { describe('isAcceleratedCheckoutAvailable', () => { it('calls native isAcceleratedCheckoutAvailable on iOS', async () => { const instance = new ShopifyCheckoutSheet(); - NativeModules.ShopifyCheckoutSheetKit.isAcceleratedCheckoutAvailable.mockResolvedValue( + NativeModule.isAcceleratedCheckoutAvailable.mockResolvedValue( true, ); @@ -983,7 +981,7 @@ describe('ShopifyCheckoutSheetKit', () => { expect(result).toBe(true); expect( - NativeModules.ShopifyCheckoutSheetKit.isAcceleratedCheckoutAvailable, + NativeModule.isAcceleratedCheckoutAvailable, ).toHaveBeenCalledTimes(1); }); @@ -995,7 +993,7 @@ describe('ShopifyCheckoutSheetKit', () => { expect(result).toBe(false); expect( - NativeModules.ShopifyCheckoutSheetKit.isAcceleratedCheckoutAvailable, + NativeModule.isAcceleratedCheckoutAvailable, ).not.toHaveBeenCalled(); }); }); diff --git a/modules/@shopify/checkout-sheet-kit/tests/linking.test.ts b/modules/@shopify/checkout-sheet-kit/tests/linking.test.ts index b2587f05..f83795f8 100644 --- a/modules/@shopify/checkout-sheet-kit/tests/linking.test.ts +++ b/modules/@shopify/checkout-sheet-kit/tests/linking.test.ts @@ -1,31 +1,22 @@ -/** - * Test for native module linking error - */ - -// Mock NativeModules without ShopifyCheckoutSheetKit jest.mock('react-native', () => ({ - NativeModules: { - // Intentionally empty to trigger linking error - }, + NativeModules: {}, NativeEventEmitter: jest.fn(), Platform: { OS: 'ios', }, - requireNativeComponent: jest.fn().mockImplementation(() => { - const mockComponent = (props: any) => { - // Use React.createElement with plain object instead - const mockReact = jest.requireActual('react'); - return mockReact.createElement('View', props); - }; - return mockComponent; - }), + TurboModuleRegistry: { + getEnforcing: jest.fn((name: string) => { + throw new Error( + `TurboModuleRegistry.getEnforcing(...): '${name}' could not be found.`, + ); + }), + }, })); describe('Native Module Linking', () => { it('throws error when native module is not linked', () => { expect(() => { - // This will trigger the linking check require('../src/index'); - }).toThrow('@shopify/checkout-sheet-kit" is not correctly linked.'); + }).toThrow('ShopifyCheckoutSheetKit'); }); }); diff --git a/sample/android/settings.gradle b/sample/android/settings.gradle index f49ac411..53360013 100644 --- a/sample/android/settings.gradle +++ b/sample/android/settings.gradle @@ -1,4 +1,11 @@ -pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") } +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } + includeBuild("../../node_modules/@react-native/gradle-plugin") +} plugins { id("com.facebook.react.settings") } extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } diff --git a/sample/package.json b/sample/package.json index 3c70b08d..206396f1 100644 --- a/sample/package.json +++ b/sample/package.json @@ -10,8 +10,8 @@ "release:android": "sh ./scripts/release_android", "build:ios": "sh ./scripts/build_ios", "lint": "yarn typecheck && eslint .", - "ios": "react-native run-ios --simulator 'iPhone 15 Pro'", - "start": "react-native start -- --simulator 'iPhone 15 Pro' --reset-cache", + "ios": "react-native run-ios", + "start": "react-native start -- --reset-cache", "typecheck": "tsc --noEmit", "test:ios": "sh ./scripts/test_ios", "test:android": "sh ./scripts/test_android"