diff --git a/platforms/react-native/sample/src/App.tsx b/platforms/react-native/sample/src/App.tsx index 3a3067175..6e0917103 100644 --- a/platforms/react-native/sample/src/App.tsx +++ b/platforms/react-native/sample/src/App.tsx @@ -48,6 +48,7 @@ import { parseCartBootstrapLink, type CartBootstrapLink, } from './linking/cartBootstrap'; +import {e2eTestIds} from './e2e/testIds'; const log = createDebugLogger('ENV'); @@ -247,7 +248,7 @@ function CartIcon({onPress}: {onPress: () => void}) { const theme = useTheme(); return ( - + ); @@ -509,14 +510,14 @@ function Routes() { return ( + testID={linkingReady ? e2eTestIds.appReady : undefined}> @@ -524,7 +525,7 @@ function Routes() { name="Cart" component={CartScreen} options={{ - tabBarButtonTestID: 'cart-tab', + tabBarButtonTestID: e2eTestIds.tabs.cart, tabBarIcon: createNavigationIcon('shopping-bag'), tabBarBadge: totalQuantity > 0 ? totalQuantity : undefined, }} @@ -534,7 +535,7 @@ function Routes() { component={AccountStackScreen} options={{ headerShown: false, - tabBarButtonTestID: 'account-tab', + tabBarButtonTestID: e2eTestIds.tabs.account, tabBarIcon: createNavigationIcon('user'), }} /> @@ -542,7 +543,7 @@ function Routes() { name="Settings" component={SettingsScreen} options={{ - tabBarButtonTestID: 'settings-tab', + tabBarButtonTestID: e2eTestIds.tabs.settings, tabBarIcon: createNavigationIcon('cog'), }} /> diff --git a/platforms/react-native/sample/src/e2e/testIds.ts b/platforms/react-native/sample/src/e2e/testIds.ts new file mode 100644 index 000000000..6830a0e2e --- /dev/null +++ b/platforms/react-native/sample/src/e2e/testIds.ts @@ -0,0 +1,51 @@ +function kebabCase(value: string) { + return value + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase(); +} + +export const e2eTestIds = { + appReady: 'checkout-kit-sample-ready', + tabs: { + catalog: 'catalog-tab', + cart: 'cart-tab', + account: 'account-tab', + settings: 'settings-tab', + }, + catalog: { + headerCartIcon: 'header-cart-icon', + productGridItem: (index: number) => `product-${index}-grid-item`, + }, + productDetails: { + addToCartButton: 'add-to-cart-button', + }, + cart: { + emptyMessage: 'cart-empty-message', + checkoutButton: 'checkout-button', + }, + settings: { + screen: 'settings-screen', + section: (section: string) => `settings-section-${kebabCase(section)}`, + buyerIdentityOption: (mode: string) => + `settings-buyer-identity-option-${kebabCase(mode)}`, + themeOption: (scheme: string) => + `settings-theme-option-${kebabCase(scheme)}`, + applePayStyleOption: (style: string) => + `settings-apple-pay-style-option-${kebabCase(style)}`, + buyerIdentityDetails: 'settings-buyer-identity-details', + buyerIdentitySignInLink: 'settings-buyer-identity-sign-in-link', + buyerIdentityChangeUserLink: 'settings-buyer-identity-change-user-link', + }, + account: { + screen: 'account-screen', + loading: 'account-loading', + signedInView: 'account-signed-in-view', + signedOutView: 'account-signed-out-view', + email: 'account-email', + signInButton: 'account-sign-in-button', + signOutButton: 'account-sign-out-button', + loginProcessing: 'account-login-processing', + loginWebView: 'account-login-webview', + }, +} as const; diff --git a/platforms/react-native/sample/src/screens/AccountScreen.tsx b/platforms/react-native/sample/src/screens/AccountScreen.tsx index c7d52c42b..1334a9910 100644 --- a/platforms/react-native/sample/src/screens/AccountScreen.tsx +++ b/platforms/react-native/sample/src/screens/AccountScreen.tsx @@ -13,6 +13,7 @@ import type {AccountStackParamList} from '../App'; import type {Colors} from '../context/Theme'; import {useTheme} from '../context/Theme'; import {useAuth} from '../context/Auth'; +import {e2eTestIds} from '../e2e/testIds'; type Props = NativeStackScreenProps; @@ -23,7 +24,7 @@ function AccountScreen({navigation}: Props) { if (isLoading) { return ( - + ); @@ -51,15 +52,22 @@ function AuthenticatedView({ const {logout} = useAuth(); return ( - - + + Signed In - {email && {email}} + {email && ( + + {email} + + )} Your checkout will be pre-filled with your account information. - + Sign Out @@ -75,8 +83,8 @@ function UnauthenticatedView({ onSignIn: () => void; }) { return ( - - + + Sign in to your account @@ -87,7 +95,10 @@ function UnauthenticatedView({ • Pre-filled shipping details • Order history and tracking - + Sign In diff --git a/platforms/react-native/sample/src/screens/CartScreen.tsx b/platforms/react-native/sample/src/screens/CartScreen.tsx index cec402c40..66aa9ec20 100644 --- a/platforms/react-native/sample/src/screens/CartScreen.tsx +++ b/platforms/react-native/sample/src/screens/CartScreen.tsx @@ -30,6 +30,7 @@ import { useShopifyEventHandlers, useShopifyProtocolEventHandlers, } from '../hooks/useCheckoutEventHandlers'; +import {e2eTestIds} from '../e2e/testIds'; function CartScreen(): React.JSX.Element { const ShopifyCheckout = useShopifyCheckout(); @@ -139,7 +140,7 @@ function CartScreen(): React.JSX.Element { return ( - + Your cart is empty. @@ -204,7 +205,7 @@ function CartScreen(): React.JSX.Element { /> diff --git a/platforms/react-native/sample/src/screens/CatalogScreen.tsx b/platforms/react-native/sample/src/screens/CatalogScreen.tsx index f2464181a..ce9764fed 100644 --- a/platforms/react-native/sample/src/screens/CatalogScreen.tsx +++ b/platforms/react-native/sample/src/screens/CatalogScreen.tsx @@ -19,6 +19,7 @@ import {useCart} from '../context/Cart'; import type {NativeStackScreenProps} from '@react-navigation/native-stack'; import type {RootStackParamList} from '../App'; import {currency} from '../utils'; +import {e2eTestIds} from '../e2e/testIds'; type Props = NativeStackScreenProps; @@ -64,7 +65,7 @@ function CatalogScreen({navigation}: Props) { { navigation.navigate('ProductDetails', { product: node, diff --git a/platforms/react-native/sample/src/screens/LoginScreen.tsx b/platforms/react-native/sample/src/screens/LoginScreen.tsx index 3fc34d602..75802d97e 100644 --- a/platforms/react-native/sample/src/screens/LoginScreen.tsx +++ b/platforms/react-native/sample/src/screens/LoginScreen.tsx @@ -13,6 +13,7 @@ import { } from '../auth/customerAccountManager'; import type {Colors} from '../context/Theme'; import {useTheme} from '../context/Theme'; +import {e2eTestIds} from '../e2e/testIds'; type Props = NativeStackScreenProps; @@ -66,7 +67,7 @@ function LoginScreen({navigation}: Props) { if (isProcessing) { return ( - + ); @@ -75,6 +76,7 @@ function LoginScreen({navigation}: Props) { return ( ; @@ -114,15 +115,14 @@ function ProductDetails({ )} variant?.id && onAddToCart(variant.id)}> {loading ? ( ) : ( - + Add to cart )} diff --git a/platforms/react-native/sample/src/screens/SettingsScreen.tsx b/platforms/react-native/sample/src/screens/SettingsScreen.tsx index 24d4eba15..c6d6e496b 100644 --- a/platforms/react-native/sample/src/screens/SettingsScreen.tsx +++ b/platforms/react-native/sample/src/screens/SettingsScreen.tsx @@ -23,6 +23,7 @@ import { BuyerIdentityMode, BuyerIdentityModeDisplayNames, } from '../auth/types'; +import {e2eTestIds} from '../e2e/testIds'; enum SectionType { SingleSelect = 'single-select', @@ -51,6 +52,7 @@ function isTextItem(item: any): item is TextItem { } interface SectionData { + id: string; title: string; footer?: string; data: readonly (SingleSelectItem | TextItem)[]; @@ -188,22 +190,26 @@ function SettingsScreen() { const sections: SectionData[] = useMemo( () => [ { + id: 'authentication', title: 'Authentication', footer: 'Prefills buyer identity at checkout. Changing this setting will clear your cart.', data: buyerIdentityOptions, }, { + id: 'theme', title: 'Theme', data: themeOptions, }, { + id: 'apple-pay-style', title: 'Apple Pay Style', footer: 'Configures the visual style of the Apple Pay button.', data: applePayStyleOptions, }, { + id: 'versions', title: 'Versions', data: informationalItems, }, @@ -219,6 +225,7 @@ function SettingsScreen() { return ( item.title} renderItem={({item, section}) => { @@ -232,6 +239,7 @@ function SettingsScreen() { handler(item)} /> ); @@ -243,8 +251,10 @@ function SettingsScreen() { return null; }} - renderSectionHeader={({section: {title}}) => ( - + renderSectionHeader={({section: {id, title}}) => ( + {title} )} @@ -279,6 +289,7 @@ function SettingsScreen() { interface SelectItemProps { item: SingleSelectItem; styles: ReturnType; + testID: string; onPress: () => void; } @@ -287,9 +298,9 @@ interface TextItemProps { styles: ReturnType; } -function SelectItem({item, styles, onPress}: SelectItemProps) { +function SelectItem({item, styles, testID, onPress}: SelectItemProps) { return ( - + {item.title} {item.selected && } @@ -306,6 +317,19 @@ function TextItem({item, styles}: TextItemProps) { ); } +function selectItemTestID(sectionId: string, item: SingleSelectItem) { + switch (sectionId) { + case 'authentication': + return e2eTestIds.settings.buyerIdentityOption(String(item.value)); + case 'theme': + return e2eTestIds.settings.themeOption(String(item.value)); + case 'apple-pay-style': + return e2eTestIds.settings.applePayStyleOption(String(item.value)); + default: + return e2eTestIds.settings.section(`${sectionId}-${item.value}`); + } +} + interface BuyerIdentityDetailsProps { mode: BuyerIdentityMode; isAuthenticated: boolean; @@ -328,7 +352,9 @@ function BuyerIdentityDetails({ return null; case BuyerIdentityMode.Hardcoded: return ( - + Populates Cart Buyer Identity with values from .env @@ -337,7 +363,9 @@ function BuyerIdentityDetails({ case BuyerIdentityMode.CustomerAccount: if (authenticated) { return ( - + Changing Buyer Identity will log you out. @@ -345,7 +373,9 @@ function BuyerIdentityDetails({ User: {email ?? 'Unknown'} - navigation.navigate('Account' as never)}> + navigation.navigate('Account' as never)}> Change user @@ -358,8 +388,11 @@ function BuyerIdentityDetails({ ); } return ( - + navigation.navigate('Account' as never)}> Sign in on the