Skip to content

Commit 6953b71

Browse files
committed
Add Swift iOS checkout e2e flow
1 parent 7e1c78e commit 6953b71

8 files changed

Lines changed: 212 additions & 9 deletions

File tree

e2e/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ terminal.
5151
| Platform | From | Command |
5252
| ------------------ | ------------------------------- | ------------------ |
5353
| React Native, iOS | `platforms/react-native/` | `pnpm e2e:ios` |
54-
| Swift, iOS | TBD | TBD |
54+
| Swift, iOS | `platforms/swift/` | `./Scripts/e2e_maestro_ios` |
5555
| Android (native) | TBD | TBD |
5656
| RN, Android | `platforms/react-native/` | `pnpm e2e:android` |
5757

e2e/swift/checkout-completion.yaml

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
appId: com.shopify.example.MobileBuyIntegration
2+
name: Checkout completes
3+
tags:
4+
- swift
5+
- ios
6+
- checkout
7+
8+
# Override these for store-specific product and shipping-address data.
9+
env:
10+
PRODUCT_INDEX: ${PRODUCT_INDEX || "1"}
11+
COUNTRY: ${COUNTRY || "United States"}
12+
ADDRESS_LINE1: ${ADDRESS_LINE1 || "350 5th Ave"}
13+
CITY: ${CITY || "New York"}
14+
POSTAL_CODE: ${POSTAL_CODE || "10118"}
15+
POSTAL_FIELD: ${POSTAL_FIELD || "ZIP code"}
16+
---
17+
# Timeout tiers:
18+
# 10000 - in-app interactions (taps, animations)
19+
# 30000 - checkout step transitions (network)
20+
# 60000 - cold starts, first checkout paint, final confirmation
21+
22+
# Product and cart
23+
- launchApp:
24+
clearState: true
25+
arguments:
26+
AppleLocale: en_US
27+
AppleLanguages: "(en)"
28+
- extendedWaitUntil:
29+
visible:
30+
id: product-0-grid-item
31+
timeout: 60000
32+
- scrollUntilVisible:
33+
element:
34+
id: product-${PRODUCT_INDEX}-grid-item
35+
direction: DOWN
36+
timeout: 10000
37+
centerElement: true
38+
- tapOn:
39+
id: product-${PRODUCT_INDEX}-grid-item
40+
- extendedWaitUntil:
41+
visible:
42+
id: add-to-cart-button
43+
timeout: 10000
44+
- tapOn:
45+
id: add-to-cart-button
46+
enabled: true
47+
- extendedWaitUntil:
48+
visible: "^Added$"
49+
timeout: 30000
50+
- tapOn:
51+
id: product-sheet-close-button
52+
- waitForAnimationToEnd:
53+
timeout: 10000
54+
- tapOn:
55+
id: cart-tab
56+
- extendedWaitUntil:
57+
visible:
58+
id: checkout-button
59+
timeout: 30000
60+
- tapOn:
61+
id: checkout-button
62+
enabled: true
63+
64+
# Contact
65+
- extendedWaitUntil:
66+
visible:
67+
text: "^Email( or mobile phone number)?$"
68+
timeout: 60000
69+
- tapOn:
70+
text: "^Email( or mobile phone number)?$"
71+
index: -1
72+
- inputText: "maestro.e2e@shopify.com"
73+
- tapOn: "selected"
74+
- tapOn:
75+
text: "^First name( \\(optional\\))?$"
76+
index: -1
77+
- inputText: "Maestro"
78+
- tapOn: "selected"
79+
- tapOn:
80+
text: "^Last name$"
81+
index: -1
82+
- inputText: "Shopify"
83+
- tapOn: "selected"
84+
85+
# Shipping address
86+
- scrollUntilVisible:
87+
element:
88+
text: "Country/Region"
89+
direction: DOWN
90+
timeout: 10000
91+
- tapOn:
92+
text: "Country/Region"
93+
index: 1
94+
- waitForAnimationToEnd:
95+
timeout: 3000
96+
- scrollUntilVisible:
97+
element:
98+
text: "^${COUNTRY}$"
99+
direction: UP
100+
timeout: 10000
101+
visibilityPercentage: 50
102+
centerElement: true
103+
optional: true
104+
- runFlow:
105+
when:
106+
notVisible: "^${COUNTRY}$"
107+
commands:
108+
- scrollUntilVisible:
109+
element:
110+
text: "^${COUNTRY}$"
111+
direction: DOWN
112+
timeout: 30000
113+
visibilityPercentage: 50
114+
centerElement: true
115+
- tapOn:
116+
text: "^${COUNTRY}$"
117+
- waitForAnimationToEnd:
118+
timeout: 3000
119+
120+
- scrollUntilVisible:
121+
element:
122+
text: "Address"
123+
direction: DOWN
124+
timeout: 10000
125+
- tapOn:
126+
text: "Address"
127+
index: -1
128+
- eraseText: 80
129+
- scrollUntilVisible:
130+
element:
131+
text: "^${POSTAL_FIELD}$"
132+
direction: DOWN
133+
timeout: 10000
134+
centerElement: true
135+
- inputText: "${ADDRESS_LINE1} ${CITY} ${POSTAL_CODE}"
136+
- extendedWaitUntil:
137+
visible: ".*${ADDRESS_LINE1}, ${CITY}.*${POSTAL_CODE}.*${COUNTRY}.*"
138+
timeout: 30000
139+
- waitForAnimationToEnd:
140+
timeout: 2000
141+
- tapOn:
142+
text: ".*${ADDRESS_LINE1}, ${CITY}.*${POSTAL_CODE}.*${COUNTRY}.*"
143+
index: 0
144+
retryTapIfNoChange: true
145+
- waitForAnimationToEnd:
146+
timeout: 5000
147+
- extendedWaitUntil:
148+
visible: "^${POSTAL_CODE}$"
149+
timeout: 15000
150+
- tapOn: "selected"
151+
- waitForAnimationToEnd:
152+
timeout: 5000
153+
154+
# Payment
155+
- scrollUntilVisible:
156+
element:
157+
text: "^Field container for: Card number$"
158+
direction: DOWN
159+
timeout: 30000
160+
centerElement: true
161+
- tapOn:
162+
text: "^Field container for: Card number$"
163+
- inputText: "4242424242424242"
164+
- tapOn: "selected"
165+
- tapOn: "Expiration date (MM / YY)"
166+
- inputText: "1230"
167+
- tapOn: "selected"
168+
- tapOn: "Field container for: Security code"
169+
- inputText: "123"
170+
- tapOn: "selected"
171+
- scrollUntilVisible:
172+
element:
173+
text: "^Field container for: Name on card$"
174+
direction: DOWN
175+
timeout: 30000
176+
centerElement: true
177+
- scrollUntilVisible:
178+
element:
179+
text: "^Pay now$"
180+
direction: DOWN
181+
timeout: 30000
182+
- extendedWaitUntil:
183+
visible: "^Pay now$"
184+
timeout: 30000
185+
- tapOn:
186+
text: "^Pay now$"
187+
enabled: true
188+
- extendedWaitUntil:
189+
visible: ".*(Thank you|[Oo]rder (is )?confirmed).*"
190+
timeout: 60000

platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
mainGroup = 4EBBA75E2A5F0CE200193E19;
160160
packageReferences = (
161161
CB00000012345678 /* XCRemoteSwiftPackageReference "apollo-ios" */,
162-
CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../../../checkout-kit" */,
162+
CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../.." */,
163163
);
164164
productRefGroup = 4EBBA7682A5F0CE200193E19 /* Products */;
165165
projectDirPath = "";
@@ -443,9 +443,9 @@
443443
/* End XCConfigurationList section */
444444

445445
/* Begin XCLocalSwiftPackageReference section */
446-
CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../../../checkout-kit" */ = {
446+
CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../.." */ = {
447447
isa = XCLocalSwiftPackageReference;
448-
relativePath = "../../../../../checkout-kit";
448+
relativePath = "../../../..";
449449
};
450450
/* End XCLocalSwiftPackageReference section */
451451

@@ -473,17 +473,17 @@
473473
};
474474
CB001E302F3CDA0300286F69 /* ShopifyCheckoutProtocol */ = {
475475
isa = XCSwiftPackageProductDependency;
476-
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../../../checkout-kit" */;
476+
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../.." */;
477477
productName = ShopifyCheckoutProtocol;
478478
};
479479
CB1B10B42E4CDDB0001713F8 /* ShopifyCheckoutKit */ = {
480480
isa = XCSwiftPackageProductDependency;
481-
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../../../checkout-kit" */;
481+
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../.." */;
482482
productName = ShopifyCheckoutKit;
483483
};
484484
CBED2D4E2F3F5D1B00EC866A /* ShopifyAcceleratedCheckouts */ = {
485485
isa = XCSwiftPackageProductDependency;
486-
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../../../checkout-kit" */;
486+
package = CB2370002FB21BF100F0D914 /* XCLocalSwiftPackageReference "../../../.." */;
487487
productName = ShopifyAcceleratedCheckouts;
488488
};
489489
/* End XCSwiftPackageProductDependency section */

platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/Sources/App/SceneDelegate.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,30 +108,35 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
108108
// Catalog grid view
109109
productGridController.tabBarItem.image = UIImage(systemName: "square.grid.2x2")
110110
productGridController.tabBarItem.title = "Catalog"
111+
productGridController.tabBarItem.accessibilityIdentifier = "catalog-tab"
111112
productGridController.navigationItem.titleView = logoImageView
112113
catalogCartButton = createCartButtonWithBadge()
113114
productGridController.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: catalogCartButton!)
114115

115116
// Product Gallery
116117
productGalleryController.tabBarItem.image = UIImage(systemName: "appwindow.swipe.rectangle")
117118
productGalleryController.tabBarItem.title = "Products"
119+
productGalleryController.tabBarItem.accessibilityIdentifier = "products-tab"
118120
productGalleryController.navigationItem.titleView = logoImageView
119121
galleryCartButton = createCartButtonWithBadge()
120122
productGalleryController.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: galleryCartButton!)
121123

122124
// Cart (UI Kit)
123125
swiftUICartController.tabBarItem.image = UIImage(systemName: "cart")
124126
swiftUICartController.tabBarItem.title = "Cart"
127+
swiftUICartController.tabBarItem.accessibilityIdentifier = "cart-tab"
125128
swiftUICartController.navigationItem.title = "Cart (SwiftUI)"
126129

127130
// Account
128131
accountController.tabBarItem.image = UIImage(systemName: "person.circle")
129132
accountController.tabBarItem.title = "Log in"
133+
accountController.tabBarItem.accessibilityIdentifier = "account-tab"
130134
subscribeToAuthStateChanges()
131135

132136
// Settings
133137
settingsController.tabBarItem.image = UIImage(systemName: "gearshape.2")
134138
settingsController.tabBarItem.title = "Settings"
139+
settingsController.tabBarItem.accessibilityIdentifier = "settings-tab"
135140
}
136141

137142
private func subscribeToAuthStateChanges() {

platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/Sources/Scenes/Cart/CartView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ struct CartView: View {
103103
)
104104
.disabled(isBusy)
105105
.foregroundColor(.white)
106-
.accessibilityIdentifier("checkoutSheetButton")
106+
.accessibilityIdentifier("checkout-button")
107107
}
108108
.padding(.horizontal, 20)
109109
.padding(.bottom, 20)

platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/Sources/Scenes/ProductGridView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ struct ProductGridView: View {
3838
ScrollView {
3939
LazyVGrid(columns: columns, spacing: 2) {
4040
if let products = productCache.collection, !products.isEmpty {
41-
ForEach(products, id: \.id) { product in
41+
ForEach(Array(products.enumerated()), id: \.element.id) { index, product in
4242
ProductGridItem(product: product)
43+
.accessibilityIdentifier("product-\(index)-grid-item")
4344
.onTapGesture {
4445
selectProductAndShowSheet(for: product)
4546
}
@@ -88,6 +89,7 @@ struct ProductSheetView: View {
8889
.padding()
8990
.foregroundStyle(.white)
9091
}
92+
.accessibilityIdentifier("product-sheet-close-button")
9193
.padding([.top, .trailing], 16)
9294
}
9395
.edgesIgnoringSafeArea(.top)

platforms/swift/Samples/MobileBuyIntegration/MobileBuyIntegration/Sources/Scenes/ProductView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ struct ProductView: View {
134134
.foregroundStyle(.white)
135135
.cornerRadius(DesignSystem.cornerRadius)
136136
.disabled(!variant.availableForSale || loading)
137+
.accessibilityIdentifier("add-to-cart-button")
137138

138139
if variant.availableForSale {
139140
AcceleratedCheckoutButtons(variantID: variant.id, quantity: 1)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
maestro --platform ios test --config ../../e2e/config.yaml ../../e2e/swift

0 commit comments

Comments
 (0)