Skip to content

Commit 88c0b25

Browse files
committed
Add Swift iOS checkout e2e flow
1 parent ec98086 commit 88c0b25

8 files changed

Lines changed: 280 additions & 9 deletions

File tree

e2e/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ terminal.
4646
| Platform | From | Command |
4747
| ------------------ | ------------------------------- | ------------------ |
4848
| React Native, iOS | `platforms/react-native/` | `pnpm e2e:ios` |
49-
| Swift, iOS | TBD | TBD |
49+
| Swift, iOS | `platforms/swift/` | `./Scripts/e2e_maestro_ios` |
5050
| Android (native) | TBD | TBD |
5151
| RN, Android | `platforms/react-native/` | `pnpm e2e:android` |
5252

e2e/swift/checkout-completion.yaml

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

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
@@ -85,30 +85,35 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
8585
// Catalog grid view
8686
productGridController.tabBarItem.image = UIImage(systemName: "square.grid.2x2")
8787
productGridController.tabBarItem.title = "Catalog"
88+
productGridController.tabBarItem.accessibilityIdentifier = "catalog-tab"
8889
productGridController.navigationItem.titleView = logoImageView
8990
catalogCartButton = createCartButtonWithBadge()
9091
productGridController.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: catalogCartButton!)
9192

9293
// Product Gallery
9394
productGalleryController.tabBarItem.image = UIImage(systemName: "appwindow.swipe.rectangle")
9495
productGalleryController.tabBarItem.title = "Products"
96+
productGalleryController.tabBarItem.accessibilityIdentifier = "products-tab"
9597
productGalleryController.navigationItem.titleView = logoImageView
9698
galleryCartButton = createCartButtonWithBadge()
9799
productGalleryController.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: galleryCartButton!)
98100

99101
// Cart (UI Kit)
100102
swiftUICartController.tabBarItem.image = UIImage(systemName: "cart")
101103
swiftUICartController.tabBarItem.title = "Cart"
104+
swiftUICartController.tabBarItem.accessibilityIdentifier = "cart-tab"
102105
swiftUICartController.navigationItem.title = "Cart (SwiftUI)"
103106

104107
// Account
105108
accountController.tabBarItem.image = UIImage(systemName: "person.circle")
106109
accountController.tabBarItem.title = "Log in"
110+
accountController.tabBarItem.accessibilityIdentifier = "account-tab"
107111
subscribeToAuthStateChanges()
108112

109113
// Settings
110114
settingsController.tabBarItem.image = UIImage(systemName: "gearshape.2")
111115
settingsController.tabBarItem.title = "Settings"
116+
settingsController.tabBarItem.accessibilityIdentifier = "settings-tab"
112117
}
113118

114119
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
@@ -80,7 +80,7 @@ struct CartView: View {
8080
)
8181
.disabled(isBusy)
8282
.foregroundColor(.white)
83-
.accessibilityIdentifier("checkoutSheetButton")
83+
.accessibilityIdentifier("checkout-button")
8484
}
8585
.padding(.horizontal, 20)
8686
.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
@@ -15,8 +15,9 @@ struct ProductGridView: View {
1515
ScrollView {
1616
LazyVGrid(columns: columns, spacing: 2) {
1717
if let products = productCache.collection, !products.isEmpty {
18-
ForEach(products, id: \.id) { product in
18+
ForEach(Array(products.enumerated()), id: \.element.id) { index, product in
1919
ProductGridItem(product: product)
20+
.accessibilityIdentifier("product-\(index)-grid-item")
2021
.onTapGesture {
2122
selectProductAndShowSheet(for: product)
2223
}
@@ -65,6 +66,7 @@ struct ProductSheetView: View {
6566
.padding()
6667
.foregroundStyle(.white)
6768
}
69+
.accessibilityIdentifier("product-sheet-close-button")
6870
.padding([.top, .trailing], 16)
6971
}
7072
.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
@@ -111,6 +111,7 @@ struct ProductView: View {
111111
.foregroundStyle(.white)
112112
.cornerRadius(DesignSystem.cornerRadius)
113113
.disabled(!variant.availableForSale || loading)
114+
.accessibilityIdentifier("add-to-cart-button")
114115

115116
if variant.availableForSale {
116117
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)