Skip to content

Commit e14d43c

Browse files
Merge pull request #4 from hungdao-testing/feature/summary-tab-spec
Feature/summary tab spec
2 parents f825174 + 1d29388 commit e14d43c

File tree

7 files changed

+302
-9
lines changed

7 files changed

+302
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
},
88
"scripts": {
99
"test": "echo \"Error: no test specified\" && exit 1",
10-
"wdio": "wdio run ./wdio.conf.ts"
10+
"wdio": "wdio run ./wdio.conf.ts --spec=test/specs/checkout-summary.spec.ts"
1111
},
1212
"keywords": [],
1313
"author": "",

test/flows/checkout-flow.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import HomePage from "../pageobjects/home.page";
2+
import PaymentTab from "../pageobjects/payment.tab";
3+
import PersonalTab from "../pageobjects/personal.tab";
4+
import { PaymentInfo, PersonalInfo } from "../type";
5+
6+
export async function checkoutFlow(personalInfo: PersonalInfo, paymentInfo: PaymentInfo){
7+
const home = new HomePage();
8+
const personalTab = new PersonalTab();
9+
const paymentTab = new PaymentTab();
10+
await home.openCheckOut();
11+
12+
await personalTab.setFullName(personalInfo.fullName);
13+
await personalTab.setAddress(personalInfo.address);
14+
await personalTab.setCity(personalInfo.city);
15+
await personalTab.setPostCode(personalInfo.postCode);
16+
await personalTab.setPhone(personalInfo.phoneNumber);
17+
await personalTab.setCountry(personalInfo.country.name);
18+
await personalTab.setDob(
19+
personalInfo.dateOfBirth.day,
20+
personalInfo.dateOfBirth.month,
21+
personalInfo.dateOfBirth.year
22+
);
23+
await personalTab.submit()
24+
25+
await paymentTab.setCardNumber(paymentInfo.cardNumber);
26+
await paymentTab.setExpiredDate(paymentInfo.expireDate);
27+
await paymentTab.setCvv(paymentInfo.cvv)
28+
await paymentTab.setCreditCardBox(paymentInfo.saveCard ?? false);
29+
await paymentTab.submit()
30+
}

test/pageobjects/page.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@ export default class Page {
1010
}
1111

1212
protected async getContentOfInputField(selector: string) {
13-
if(this.platform === 'android'){
13+
if (this.platform === "android") {
1414
return $(selector).getAttribute("text");
15-
}else{
15+
} else {
1616
const value = await $(selector).getAttribute("value");
1717
const label = await $(selector).getAttribute("label");
1818
return value || label;
1919
}
20-
20+
}
21+
22+
protected expectedForDob(date: { day: number; month: number; year: number }) {
23+
if (this.platform === "ios") {
24+
return `${date.day}/${date.month}/${date.year}`;
25+
} else {
26+
return `${date.month}/${date.day}/${date.year}`;
27+
}
2128
}
2229
}

test/pageobjects/summary.tab.ts

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,173 @@
1-
export default class SummaryTab{
2-
3-
}
1+
import { locatorHelper } from "../helpers/locator";
2+
import { PaymentInfo, PersonalInfo } from "../type";
3+
import Page from "./page";
4+
import { $, driver, expect } from "@wdio/globals";
5+
6+
const SCREEN_SELECTOR = {
7+
ios: {
8+
personalCard: {
9+
main: "personalInfo",
10+
editBtn: '**/XCUIElementTypeStaticText[`name == "Edit"`][1]',
11+
},
12+
paymentCard: {
13+
main: "paymentInfo",
14+
editBtn: '**/XCUIElementTypeStaticText[`name == "Edit"`][2]',
15+
},
16+
submitBtn: "Submit-button",
17+
},
18+
android: {
19+
personalCard: {
20+
main: 'new UiSelector().resourceId("personalInfo")',
21+
editBtn: 'new UiSelector().resourceId("editPersonal")',
22+
},
23+
paymentCard: {
24+
main: 'new UiSelector().resourceId("paymentInfo")',
25+
editBtn: 'new UiSelector().resourceId("editPayment")',
26+
},
27+
submitBtn: "Submit",
28+
},
29+
};
30+
31+
export default class SummaryTab extends Page {
32+
private personalCard: string;
33+
private paymentCard: string;
34+
private editBtnInPersonalCard: string;
35+
private editBtnInPaymentCard: string;
36+
private submitBtn: string;
37+
38+
constructor(
39+
private personalInfo: PersonalInfo,
40+
private paymentInfo: PaymentInfo
41+
) {
42+
super();
43+
this.personalCard = locatorHelper.generateSelector(
44+
SCREEN_SELECTOR[this.platform].personalCard.main
45+
);
46+
this.paymentCard = locatorHelper.generateSelector(
47+
SCREEN_SELECTOR[this.platform].paymentCard.main,
48+
"accessibility_id"
49+
);
50+
this.editBtnInPersonalCard = locatorHelper.generateSelector(
51+
SCREEN_SELECTOR[this.platform].personalCard.editBtn, 'class_chain'
52+
);
53+
this.editBtnInPaymentCard = locatorHelper.generateSelector(
54+
SCREEN_SELECTOR[this.platform].paymentCard.editBtn, 'class_chain'
55+
);
56+
this.submitBtn = locatorHelper.generateSelector(
57+
SCREEN_SELECTOR[this.platform].submitBtn,
58+
"accessibility_id"
59+
);
60+
}
61+
62+
public async getInfoInPersonalCard(infoType: keyof PersonalInfo) {
63+
if (this.platform === "ios") {
64+
const label = await $(
65+
locatorHelper.generateSelector(
66+
`**/XCUIElementTypeStaticText[\`name CONTAINS "${infoType}"\`][1]`,
67+
"class_chain"
68+
)
69+
).getAttribute("label");
70+
return label.split(":")[1].trim();
71+
} else {
72+
const txt = await $(
73+
locatorHelper.generateSelector(
74+
`new UiSelector().textContains("${infoType}")`
75+
)
76+
).getAttribute("text");
77+
return txt.split(":")[1].trim();
78+
}
79+
}
80+
81+
public async getInfoInPaymentCard(infoType: keyof PaymentInfo) {
82+
if (this.platform === "ios") {
83+
const label = await $(
84+
locatorHelper.generateSelector(
85+
`**/XCUIElementTypeStaticText[\`name CONTAINS "${infoType}"\`][1]`,
86+
"class_chain"
87+
)
88+
).getAttribute("label");
89+
return label.split(":")[1].trim();
90+
} else {
91+
const txt = await $(
92+
locatorHelper.generateSelector(
93+
`new UiSelector().textContains("${infoType}")`
94+
)
95+
).getAttribute("text");
96+
return txt.split(":")[1].trim();
97+
}
98+
}
99+
100+
private async assertPersonalInfoLoaded() {
101+
expect(await this.getInfoInPersonalCard("fullName")).toBe(
102+
this.personalInfo.fullName
103+
);
104+
expect(await this.getInfoInPersonalCard("address")).toBe(
105+
this.personalInfo.address
106+
);
107+
expect(await this.getInfoInPersonalCard("city")).toBe(
108+
this.personalInfo.city
109+
);
110+
expect([
111+
this.personalInfo.country.code,
112+
this.personalInfo.country.name,
113+
]).toContain(await this.getInfoInPersonalCard("country"));
114+
115+
expect(await this.getInfoInPersonalCard("phoneNumber")).toBe(
116+
this.personalInfo.phoneNumber
117+
);
118+
expect(await this.getInfoInPersonalCard("phoneNumber")).toBe(
119+
this.personalInfo.phoneNumber
120+
);
121+
expect(await this.getInfoInPersonalCard("dateOfBirth")).toBe(
122+
this.expectedForDob({
123+
day: this.personalInfo.dateOfBirth.day,
124+
month: this.personalInfo.dateOfBirth.month,
125+
year: this.personalInfo.dateOfBirth.year,
126+
})
127+
);
128+
}
129+
130+
private async assertPaymentInfoLoaded() {
131+
expect(await this.getInfoInPaymentCard("cardNumber")).toBe(
132+
this.paymentInfo.cardNumber
133+
);
134+
expect(await this.getInfoInPaymentCard("cvv")).toBe(this.paymentInfo.cvv);
135+
expect(await this.getInfoInPaymentCard("expireDate")).toBe(
136+
this.paymentInfo.expireDate
137+
);
138+
expect(await this.getInfoInPaymentCard("saveCard")).toBe(
139+
`${this.paymentInfo.saveCard}`
140+
);
141+
}
142+
143+
public async isAt() {
144+
await Promise.all([
145+
$(this.personalCard).waitForDisplayed(),
146+
$(this.paymentCard).waitForDisplayed(),
147+
this.assertPaymentInfoLoaded(),
148+
this.assertPersonalInfoLoaded(),
149+
]);
150+
return true;
151+
}
152+
153+
private async editPersonal() {
154+
$(this.editBtnInPersonalCard).click();
155+
}
156+
157+
private async editPayment() {
158+
$(this.editBtnInPaymentCard).click();
159+
}
160+
public async editOnCard(cardName: "payment" | "personal") {
161+
if (cardName === "personal") {
162+
await this.editPersonal();
163+
} else {
164+
await this.editPayment();
165+
}
166+
}
167+
168+
public async submit() {
169+
await $(this.submitBtn).click();
170+
}
171+
172+
173+
}

test/specs/checkout-summary.spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { expect } from "@wdio/globals";
2+
import * as COUNTRIES from "../data/countries.json";
3+
import { checkoutFlow } from "../flows/checkout-flow";
4+
import { PaymentInfo, PersonalInfo } from "../type";
5+
import SummaryTab from "../pageobjects/summary.tab";
6+
import PersonalTab from "../pageobjects/personal.tab";
7+
import PaymentTab from "../pageobjects/payment.tab";
8+
9+
describe("Checkout - Summary @personal-summary", () => {
10+
const now = new Date(Date.now());
11+
const personalInfo: PersonalInfo = {
12+
fullName: "David James",
13+
address: "2 Ap Bac, TB district",
14+
city: "Saigon",
15+
postCode: "70001",
16+
phoneNumber: "0792715261",
17+
dateOfBirth: {
18+
day: now.getDate(),
19+
month: now.getMonth() + 1,
20+
year: now.getFullYear(),
21+
},
22+
country: { name: COUNTRIES[0].name, code: COUNTRIES[0].code },
23+
};
24+
const paymentInfo: PaymentInfo = {
25+
cardNumber: "1890987223243",
26+
expireDate: "12/35",
27+
cvv: "345",
28+
saveCard: true,
29+
};
30+
31+
let summaryTab = new SummaryTab(personalInfo, paymentInfo);
32+
33+
it("User could edit personal and payment info", async () => {
34+
//arrange
35+
const personalTab = new PersonalTab();
36+
const paymentTab = new PaymentTab();
37+
const updatedFullName = "Josh Will";
38+
const updatedCardNumber = "9990987223000";
39+
40+
await checkoutFlow(personalInfo, paymentInfo);
41+
expect(await summaryTab.isAt()).toBeTruthy();
42+
43+
//act
44+
45+
await summaryTab.editOnCard("personal");
46+
await personalTab.setFullName(updatedFullName);
47+
await personalTab.submit();
48+
await paymentTab.submit();
49+
50+
await summaryTab.editOnCard("payment");
51+
await paymentTab.setCardNumber(updatedCardNumber);
52+
await personalTab.submit();
53+
54+
55+
56+
//assert
57+
summaryTab = new SummaryTab(
58+
{ ...personalInfo, fullName: updatedFullName },
59+
{ ...paymentInfo, cardNumber: updatedCardNumber }
60+
);
61+
expect(await summaryTab.isAt()).toBeTruthy();
62+
});
63+
});

test/type.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface PersonalInfo {
2+
fullName: string;
3+
address: string;
4+
city: string;
5+
postCode: string;
6+
country: {
7+
name: string;
8+
code: string;
9+
};
10+
phoneNumber: string;
11+
dateOfBirth: {
12+
day: number;
13+
month: number;
14+
year: number;
15+
};
16+
}
17+
18+
export interface PaymentInfo {
19+
cardNumber: string;
20+
expireDate: string;
21+
cvv: string;
22+
saveCard?: boolean;
23+
}

wdio.conf.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const ANDROID_CAP = {
88
"appium:noReset": false,
99
"appium:app": "./resources/formscli_android.apk",
1010
"appium:maxTypingFrequency": 30, // delay sending key stroke
11-
"appium:isHeadless": true,
11+
// "appium:isHeadless": true,
1212
};
1313

1414
const IOS_CAP = {
@@ -21,7 +21,7 @@ const IOS_CAP = {
2121
"appium:noReset": false,
2222
"appium:app": "./resources/formscli_ios.zip",
2323
"appium:maxTypingFrequency": 30, // delay sending key stroke
24-
"appium:isHeadless": true,
24+
// "appium:isHeadless": true,
2525

2626
};
2727

0 commit comments

Comments
 (0)