Skip to content

Commit 01b3db5

Browse files
committed
Add Swift iOS checkout e2e flow
1 parent 814e529 commit 01b3db5

8 files changed

Lines changed: 293 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: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
appId: com.shopify.example.MobileBuyIntegration
2+
name: Checkout submits and shows result
3+
tags:
4+
- swift
5+
- ios
6+
- checkout
7+
8+
env:
9+
PRODUCT_INDEX: "1"
10+
11+
# CI shipping fixture
12+
COUNTRY_LABEL: "United States"
13+
ADDRESS_LINE1: "700 S Flower St"
14+
CITY: "Los Angeles"
15+
STATE_FIELD_LABEL: "State"
16+
STATE_LABEL: "California"
17+
POSTAL_CODE: "90017"
18+
POSTAL_FIELD_LABEL: "ZIP code"
19+
20+
# CI payment fixture
21+
CARD_NUMBER: "4242424242424242"
22+
CARD_EXPIRY: "1230"
23+
CARD_SECURITY_CODE: "123"
24+
25+
# Accepted terminal checkout states for this smoke test.
26+
POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation|Shipping not available|There was a problem|declined|couldn.t process).*"
27+
---
28+
# Timeout tiers:
29+
# 10000 - in-app interactions (taps, animations)
30+
# 30000 - checkout step transitions (network)
31+
# 60000 - cold starts, first checkout paint, final submit
32+
33+
# Product and cart
34+
- launchApp:
35+
clearState: true
36+
arguments:
37+
AppleLocale: en_US
38+
AppleLanguages: "(en)"
39+
- runFlow:
40+
when:
41+
visible:
42+
id: catalog-tab
43+
commands:
44+
- tapOn:
45+
id: catalog-tab
46+
- extendedWaitUntil:
47+
visible:
48+
id: product-0-grid-item
49+
timeout: 60000
50+
- scrollUntilVisible:
51+
element:
52+
id: product-${PRODUCT_INDEX}-grid-item
53+
direction: DOWN
54+
timeout: 10000
55+
centerElement: true
56+
- tapOn:
57+
id: product-${PRODUCT_INDEX}-grid-item
58+
- extendedWaitUntil:
59+
visible:
60+
id: add-to-cart-button
61+
timeout: 10000
62+
- tapOn:
63+
id: add-to-cart-button
64+
enabled: true
65+
- extendedWaitUntil:
66+
visible: "^Added$"
67+
timeout: 30000
68+
- tapOn:
69+
id: product-sheet-close-button
70+
- waitForAnimationToEnd:
71+
timeout: 10000
72+
- tapOn:
73+
id: cart-tab
74+
- extendedWaitUntil:
75+
visible:
76+
id: checkout-button
77+
timeout: 30000
78+
- tapOn:
79+
id: checkout-button
80+
enabled: true
81+
82+
# Contact
83+
- extendedWaitUntil:
84+
visible:
85+
text: "^Email( or mobile phone number)?$"
86+
timeout: 60000
87+
- tapOn:
88+
text: "^Email( or mobile phone number)?$"
89+
index: -1
90+
- inputText: "maestro.e2e@shopify.com"
91+
- tapOn: "selected"
92+
- tapOn:
93+
text: "^First name( \\(optional\\))?$"
94+
index: -1
95+
- inputText: "Maestro"
96+
- tapOn: "selected"
97+
- tapOn:
98+
text: "^Last name$"
99+
index: -1
100+
- inputText: "Shopify"
101+
- tapOn: "selected"
102+
103+
# Shipping address
104+
- scrollUntilVisible:
105+
element:
106+
text: "Country/Region"
107+
direction: DOWN
108+
timeout: 10000
109+
- tapOn:
110+
text: "Country/Region"
111+
index: 1
112+
- waitForAnimationToEnd:
113+
timeout: 10000
114+
- scrollUntilVisible:
115+
element:
116+
text: "^${COUNTRY_LABEL}$"
117+
direction: UP
118+
timeout: 10000
119+
visibilityPercentage: 10
120+
optional: true
121+
- scrollUntilVisible:
122+
element:
123+
text: "^${COUNTRY_LABEL}$"
124+
direction: DOWN
125+
timeout: 10000
126+
visibilityPercentage: 10
127+
optional: true
128+
- tapOn:
129+
text: "^${COUNTRY_LABEL}$"
130+
- waitForAnimationToEnd:
131+
timeout: 10000
132+
133+
- scrollUntilVisible:
134+
element:
135+
text: "Address"
136+
direction: DOWN
137+
timeout: 10000
138+
- tapOn:
139+
text: "Address"
140+
index: -1
141+
- eraseText: 80
142+
- inputText: "${ADDRESS_LINE1}"
143+
- tapOn: "selected"
144+
- scrollUntilVisible:
145+
element:
146+
text: "^City$"
147+
direction: DOWN
148+
timeout: 10000
149+
centerElement: true
150+
- tapOn:
151+
text: "^City$"
152+
index: -1
153+
- eraseText: 80
154+
- inputText: "${CITY}"
155+
- tapOn: "selected"
156+
- scrollUntilVisible:
157+
element:
158+
text: "^${STATE_FIELD_LABEL}$"
159+
direction: DOWN
160+
timeout: 10000
161+
centerElement: true
162+
- tapOn:
163+
text: "^${STATE_FIELD_LABEL}$"
164+
index: -1
165+
- waitForAnimationToEnd:
166+
timeout: 10000
167+
- scrollUntilVisible:
168+
element:
169+
text: "^${STATE_LABEL}$"
170+
direction: UP
171+
timeout: 10000
172+
visibilityPercentage: 100
173+
optional: true
174+
- scrollUntilVisible:
175+
element:
176+
text: "^${STATE_LABEL}$"
177+
direction: DOWN
178+
timeout: 10000
179+
visibilityPercentage: 100
180+
optional: true
181+
- tapOn:
182+
text: "^${STATE_LABEL}$"
183+
- waitForAnimationToEnd:
184+
timeout: 10000
185+
- extendedWaitUntil:
186+
notVisible: "Select a state"
187+
timeout: 10000
188+
- extendedWaitUntil:
189+
visible: "^${STATE_LABEL}$"
190+
timeout: 10000
191+
- scrollUntilVisible:
192+
element:
193+
text: "^${POSTAL_FIELD_LABEL}$"
194+
direction: DOWN
195+
timeout: 10000
196+
centerElement: true
197+
- tapOn:
198+
text: "^${POSTAL_FIELD_LABEL}$"
199+
index: -1
200+
- eraseText: 80
201+
- inputText: "${POSTAL_CODE}"
202+
- tapOn: "selected"
203+
- extendedWaitUntil:
204+
visible: "^${POSTAL_CODE}$"
205+
timeout: 10000
206+
- waitForAnimationToEnd:
207+
timeout: 10000
208+
209+
# Payment
210+
- scrollUntilVisible:
211+
element:
212+
text: "^Complete order$"
213+
direction: DOWN
214+
timeout: 10000
215+
centerElement: true
216+
optional: true
217+
- runFlow:
218+
when:
219+
notVisible: "^Complete order$"
220+
commands:
221+
- scrollUntilVisible:
222+
element:
223+
text: "^Field container for: Card number$"
224+
direction: DOWN
225+
timeout: 10000
226+
centerElement: true
227+
optional: true
228+
- runFlow:
229+
when:
230+
visible: "^Field container for: Card number$"
231+
commands:
232+
- tapOn:
233+
text: "^Field container for: Card number$"
234+
- inputText: "${CARD_NUMBER}"
235+
- tapOn: "selected"
236+
- tapOn: "Expiration date (MM / YY)"
237+
- inputText: "${CARD_EXPIRY}"
238+
- tapOn: "selected"
239+
- tapOn: "Field container for: Security code"
240+
- inputText: "${CARD_SECURITY_CODE}"
241+
- tapOn: "selected"
242+
- scrollUntilVisible:
243+
element:
244+
text: "^Field container for: Name on card$"
245+
direction: DOWN
246+
timeout: 10000
247+
centerElement: true
248+
- scrollUntilVisible:
249+
element:
250+
text: "^Pay now$"
251+
direction: DOWN
252+
timeout: 10000
253+
centerElement: true
254+
optional: true
255+
- runFlow:
256+
when:
257+
visible: "^Pay now$"
258+
commands:
259+
- tapOn:
260+
text: "^Pay now$"
261+
enabled: true
262+
- runFlow:
263+
when:
264+
visible: "^Complete order$"
265+
commands:
266+
- tapOn:
267+
text: "^Complete order$"
268+
enabled: true
269+
- extendedWaitUntil:
270+
visible: "${POST_SUBMIT_RESULT_PATTERN}"
271+
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)

0 commit comments

Comments
 (0)