Skip to content

Commit 0e23cee

Browse files
authored
Merge pull request #1 from go-lani/step1
feat: step1 초안 완료
2 parents 740713a + a63fc10 commit 0e23cee

23 files changed

+976
-137
lines changed

cypress/e2e/change-charger.cy.js

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import {
2+
CHANGE_CHARGE_MENU_SELECTOR,
3+
CHARGER_INPUT_SELECTOR,
4+
COIN_AMOUNT_SELECTOR,
5+
COIN_CHARGE_BUTTON_SELECTOR,
6+
COIN_CHARGING_FORM_SELECTOR,
7+
COIN_INVENTORY_SELECTOR,
8+
COIN_UNIT_SELECTOR,
9+
HOLDING_AMOUNT_SELECTOR,
10+
PRODUCT_MANAGE_MENU_SELECTOR,
11+
} from "../support/selectors.js";
12+
import { ERROR_MESSAGE, MINIMUM_CHARGE_PRICE } from "../support/constants";
13+
import { calculateCoinCount } from "../../src/js/utils/utils.js";
14+
15+
describe("잔돈 충전 테스트", () => {
16+
beforeEach(() => {
17+
cy.visit("/");
18+
cy.get(CHANGE_CHARGE_MENU_SELECTOR).click();
19+
});
20+
21+
context("잔돈 충전을 할 수 있다.", () => {
22+
it("잔돈 충전 입력 폼이 보인다", () => {
23+
cy.get(COIN_CHARGING_FORM_SELECTOR).should("exist");
24+
});
25+
26+
it("잔돈을 입력할 수 있는 Input이 존재한다.", () => {
27+
cy.get(CHARGER_INPUT_SELECTOR).should("exist");
28+
});
29+
30+
it("최초 보유한 금액은 0원이다", () => {
31+
cy.get(HOLDING_AMOUNT_SELECTOR).should("have.text", "0");
32+
});
33+
34+
it("100원부터 충전이 가능하며 잘못 입력시 alert가 뜬다", () => {
35+
cy.alert({
36+
action: () => {
37+
cy.get(CHARGER_INPUT_SELECTOR).type("50");
38+
return cy.get(COIN_CHARGE_BUTTON_SELECTOR).click();
39+
},
40+
message: ERROR_MESSAGE.INVALID_AMOUNT,
41+
});
42+
});
43+
44+
it("잔돈은 10원 단위로 충전이 가능하다", () => {
45+
cy.alert({
46+
action: () => {
47+
cy.get(CHARGER_INPUT_SELECTOR).type("101");
48+
return cy.get(COIN_CHARGE_BUTTON_SELECTOR).click();
49+
},
50+
message: ERROR_MESSAGE.INVALID_UNIT,
51+
});
52+
});
53+
54+
it("최초 보유 금액은 0원이다.", () => {
55+
cy.get(HOLDING_AMOUNT_SELECTOR).should("have.text", "0");
56+
});
57+
58+
it("잔돈 입력 후 Enter키를 눌러서 충전할 수 있다", () => {
59+
cy.get(CHARGER_INPUT_SELECTOR).type(`${MINIMUM_CHARGE_PRICE}{enter}`);
60+
cy.get(HOLDING_AMOUNT_SELECTOR).should("have.text", MINIMUM_CHARGE_PRICE);
61+
});
62+
63+
it("잔돈 입력후 충전하기 버튼을 눌러서 충전할 수 있다", () => {
64+
cy.get(CHARGER_INPUT_SELECTOR).type(String(MINIMUM_CHARGE_PRICE));
65+
66+
cy.get(COIN_CHARGE_BUTTON_SELECTOR)
67+
.click()
68+
.then(() => {
69+
cy.get(HOLDING_AMOUNT_SELECTOR).should(
70+
"have.text",
71+
MINIMUM_CHARGE_PRICE
72+
);
73+
});
74+
});
75+
76+
it("잔돈은 누적하여 충전할 수 있다", () => {
77+
const FIRST_CHARGE = MINIMUM_CHARGE_PRICE;
78+
const SECOND_CHARGE = MINIMUM_CHARGE_PRICE * 2;
79+
80+
cy.get(CHARGER_INPUT_SELECTOR).type(String(FIRST_CHARGE));
81+
cy.get(COIN_CHARGE_BUTTON_SELECTOR).click();
82+
83+
cy.get(CHARGER_INPUT_SELECTOR).type(String(SECOND_CHARGE));
84+
cy.get(COIN_CHARGE_BUTTON_SELECTOR).click();
85+
86+
cy.get(HOLDING_AMOUNT_SELECTOR).should(
87+
"have.text",
88+
String(FIRST_CHARGE + SECOND_CHARGE)
89+
);
90+
});
91+
});
92+
93+
context("보유한 동전을 갯수를 확인할 수 있다.", () => {
94+
it("잔돈 현황을 확인할 수 있는 테이블이 보인다", () => {
95+
cy.get(COIN_INVENTORY_SELECTOR).should("exist");
96+
});
97+
98+
it("최초 보유한 동전의 갯수는 각각 0개이다", () => {
99+
cy.get(COIN_AMOUNT_SELECTOR).each((amount) => {
100+
expect(amount).text("0개");
101+
});
102+
});
103+
104+
it("500원, 100원, 50원, 10원 단위에 따른 동전의 갯수로 표시된다", () => {
105+
const coinUnit = ["500", "100", "50", "10"];
106+
cy.get(COIN_UNIT_SELECTOR).each((unit, index) => {
107+
expect(unit).text(coinUnit[index]);
108+
});
109+
});
110+
111+
it("보유한 동전은 X개 형식으로 확인할 수 있다", () => {
112+
const charge = 1000;
113+
cy.get(CHARGER_INPUT_SELECTOR).type(`${charge}{enter}`);
114+
const result = calculateCoinCount(charge);
115+
116+
cy.get(COIN_AMOUNT_SELECTOR).each((amount) => {
117+
const id = amount.closest("tr").attr("id");
118+
const unit = id.replace("coin-", "");
119+
120+
expect(amount).text(`${result[unit]}개`);
121+
});
122+
});
123+
});
124+
125+
it("다른 메뉴로 이동 후 다시 돌아왔을 경우 값은 유지된다.", () => {
126+
const charge = 1000;
127+
cy.get(CHARGER_INPUT_SELECTOR).type(`${charge}{enter}`);
128+
const result = calculateCoinCount(charge);
129+
130+
cy.get(COIN_AMOUNT_SELECTOR).each((amount) => {
131+
const id = amount.closest("tr").attr("id");
132+
const unit = id.replace("coin-", "");
133+
134+
expect(amount).text(`${result[unit]}개`);
135+
});
136+
137+
cy.get(PRODUCT_MANAGE_MENU_SELECTOR).click();
138+
139+
cy.get(CHANGE_CHARGE_MENU_SELECTOR).click();
140+
141+
cy.get(COIN_AMOUNT_SELECTOR).each((amount) => {
142+
const id = amount.closest("tr").attr("id");
143+
const unit = id.replace("coin-", "");
144+
145+
expect(amount).text(`${result[unit]}개`);
146+
});
147+
});
148+
});

cypress/e2e/menu.cy.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
CHANGE_CHARGE_MENU_SELECTOR,
3+
COIN_CHARGING_CONTAINER_SELECTOR,
4+
MENU_SELECTOR,
5+
PRODUCT_MANAGE_MENU_SELECTOR,
6+
PRODUCT_MANAGER_CONTAINER_SELECTOR,
7+
PRODUCT_PURCHASE_CONTAINER_SELECTOR,
8+
PRODUCT_PURCHASE_MENU_SELECTOR,
9+
} from "../support/selectors.js";
10+
11+
describe("메뉴 탭 UI 테스트", () => {
12+
beforeEach(() => {
13+
cy.visit("/");
14+
});
15+
it("메뉴 탭 리스트 UI가 화면에 존재한다.", () => {
16+
cy.get(MENU_SELECTOR).should("exist");
17+
});
18+
19+
it("메뉴는 상품 관리, 잔돈 충전, 상품 구매가 있다.", () => {
20+
cy.get(`${MENU_SELECTOR} button`).should(($menuButton) => {
21+
expect($menuButton).to.have.length(3);
22+
23+
const menuTexts = $menuButton.map((_, el) => el.innerText);
24+
25+
expect(menuTexts.get()).to.deep.equal([
26+
"상품 관리",
27+
"잔돈 충전",
28+
"상품 구매",
29+
]);
30+
});
31+
});
32+
33+
context("각 메뉴 클릭시 해당 메뉴화면이 노출된다.", () => {
34+
it("잔돈 충전 메뉴 클릭시 해당 화면이 노출된다.", () => {
35+
cy.get(CHANGE_CHARGE_MENU_SELECTOR)
36+
.click()
37+
.then(() => {
38+
cy.get(COIN_CHARGING_CONTAINER_SELECTOR).should("exist");
39+
});
40+
});
41+
it("상품 구매 메뉴 클릭시 해당 화면이 노출된다.", () => {
42+
cy.get(PRODUCT_PURCHASE_MENU_SELECTOR)
43+
.click()
44+
.then(() => {
45+
cy.get(PRODUCT_PURCHASE_CONTAINER_SELECTOR).should("exist");
46+
});
47+
});
48+
it("상품 관리 메뉴 클릭시 해당 화면이 노출된다.", () => {
49+
cy.get(PRODUCT_MANAGE_MENU_SELECTOR)
50+
.click()
51+
.then(() => {
52+
cy.get(PRODUCT_MANAGER_CONTAINER_SELECTOR).should("exist");
53+
});
54+
});
55+
});
56+
});

cypress/e2e/product-manager.cy.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import {
2+
CHANGE_CHARGE_MENU_SELECTOR,
3+
PRODUCT_ADD_BUTTON_SELECTOR,
4+
PRODUCT_INVENTORY_CONTAINER_SELECTOR,
5+
PRODUCT_INVENTORY_SELECTOR,
6+
PRODUCT_MANAGE_MENU_SELECTOR,
7+
PRODUCT_MANAGER_FORM_SELECTOR,
8+
PRODUCT_NAME_INPUT_SELECTOR,
9+
PRODUCT_PRICE_INPUT_SELECTOR,
10+
PRODUCT_QUANTITY_INPUT_SELECTOR,
11+
} from "../support/selectors.js";
12+
import { ERROR_MESSAGE } from "../support/constants.js";
13+
14+
describe("상품관리 테스트", () => {
15+
beforeEach(() => {
16+
cy.visit("/");
17+
});
18+
19+
const addProduct = ({ name, price, quantity }) => {
20+
cy.get(PRODUCT_NAME_INPUT_SELECTOR).type(name);
21+
cy.get(PRODUCT_PRICE_INPUT_SELECTOR).type(price);
22+
cy.get(PRODUCT_QUANTITY_INPUT_SELECTOR).type(quantity);
23+
return cy.get(PRODUCT_ADD_BUTTON_SELECTOR).click();
24+
};
25+
26+
context("상품을 관리할 수 있는 화면이 존재한다.", () => {
27+
it("상품 추가할 수 있는 영역이 존재한다", () => {
28+
cy.get(PRODUCT_MANAGER_FORM_SELECTOR).should("exist");
29+
});
30+
it("상품 현황을 파악할 수 있는 영역이 존재한다", () => {
31+
cy.get(PRODUCT_INVENTORY_SELECTOR).should("exist");
32+
});
33+
it("최초 상품 현황은 비워져있다", () => {
34+
cy.get(PRODUCT_INVENTORY_CONTAINER_SELECTOR).should("be.empty");
35+
});
36+
});
37+
38+
context("상품을 추가할 수 있다.", () => {
39+
it("빈 값은 입력할 수 없다.", () => {
40+
cy.alert({
41+
action: () => {
42+
return addProduct({ name: " ", price: "100", quantity: "1" });
43+
},
44+
message: ERROR_MESSAGE.EMPTY_VALUE,
45+
});
46+
});
47+
48+
it("가격은 100원 이상 입력할 수 있다.", () => {
49+
cy.alert({
50+
action: () => {
51+
return addProduct({ name: "코카콜라", price: "80", quantity: "1" });
52+
},
53+
message: ERROR_MESSAGE.INVALID_AMOUNT,
54+
});
55+
});
56+
57+
it("가격은 10원 단위로 입력할 수 있다", () => {
58+
cy.alert({
59+
action: () => {
60+
return addProduct({ name: "코카콜라", price: "101", quantity: "1" });
61+
},
62+
message: ERROR_MESSAGE.INVALID_UNIT,
63+
});
64+
});
65+
66+
it("추가된 상품은 상품 현향 목룍에 추가된다", () => {
67+
addProduct({ name: "코카콜라", price: "100", quantity: "1" });
68+
69+
cy.contains("코카콜라").should("exist");
70+
cy.contains("100").should("exist");
71+
cy.contains("1").should("exist");
72+
cy.get(PRODUCT_INVENTORY_CONTAINER_SELECTOR)
73+
.children()
74+
.should("have.length", 1);
75+
});
76+
77+
it("상품이 추가되면 추가하기 입력 폼에 입력된 값은 초기화된다.", () => {
78+
addProduct({ name: "코카콜라", price: "100", quantity: "1" });
79+
80+
cy.get(PRODUCT_NAME_INPUT_SELECTOR).should("have.value", "");
81+
cy.get(PRODUCT_PRICE_INPUT_SELECTOR).should("have.value", "");
82+
cy.get(PRODUCT_QUANTITY_INPUT_SELECTOR).should("have.value", "");
83+
});
84+
85+
it("이미 등록된 상품을 재등록시 해당 상품은 입력된 값으로 갱신된다", () => {
86+
addProduct({ name: "코카콜라", price: "100", quantity: "1" });
87+
addProduct({ name: "코카콜라", price: "1000", quantity: "10" });
88+
89+
cy.get(PRODUCT_INVENTORY_CONTAINER_SELECTOR)
90+
.children()
91+
.should("have.length", 1);
92+
93+
cy.contains("코카콜라").next().should("have.text", "1000");
94+
cy.contains("코카콜라").next().next().should("have.text", "10");
95+
});
96+
});
97+
98+
it("상품 목록은 탬을 이동하여도 기존 상품 목록이 유지되어야 한다.", () => {
99+
addProduct({ name: "코카콜라", price: "100", quantity: "1" });
100+
101+
cy.contains("코카콜라").next().should("have.text", "100");
102+
cy.contains("코카콜라").next().next().should("have.text", "1");
103+
104+
cy.get(CHANGE_CHARGE_MENU_SELECTOR).click();
105+
cy.get(PRODUCT_MANAGE_MENU_SELECTOR).click();
106+
107+
cy.contains("코카콜라").next().should("have.text", "100");
108+
cy.contains("코카콜라").next().next().should("have.text", "1");
109+
});
110+
});

cypress/e2e/ui.cy.js

-5
This file was deleted.

cypress/support/commands.js

+9-25
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
1-
// ***********************************************
2-
// This example commands.js shows you how to
3-
// create various custom commands and overwrite
4-
// existing commands.
5-
//
6-
// For more comprehensive examples of custom
7-
// commands please read more here:
8-
// https://on.cypress.io/custom-commands
9-
// ***********************************************
10-
//
11-
//
12-
// -- This is a parent command --
13-
// Cypress.Commands.add('login', (email, password) => { ... })
14-
//
15-
//
16-
// -- This is a child command --
17-
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18-
//
19-
//
20-
// -- This is a dual command --
21-
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22-
//
23-
//
24-
// -- This will overwrite an existing command --
25-
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
1+
Cypress.Commands.add("alert", ({ action, message }) => {
2+
const alertStub = cy.stub();
3+
4+
cy.on("window:alert", alertStub);
5+
6+
action().then(() => {
7+
expect(alertStub.getCall(0)).to.be.calledWith(message);
8+
});
9+
});

cypress/support/constants.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const MINIMUM_PRODUCT_PRICE = 100;
2+
3+
export const MINIMUM_CHARGE_PRICE = 100;
4+
5+
export const PRODUCT_PRICE_UNIT = 10;
6+
7+
export const ERROR_MESSAGE = {
8+
INVALID_MENU: "잘못된 메뉴를 클릭했습니다.",
9+
EMPTY_VALUE: "빈 값이 입력되었습니다.",
10+
INVALID_AMOUNT: `${MINIMUM_PRODUCT_PRICE}원 이상 입력 가능합니다.`,
11+
INVALID_UNIT: `${PRODUCT_PRICE_UNIT}원 단위로 입력 가능합니다.`,
12+
};

cypress/support/selectors.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export const MENU_SELECTOR = "#menu";
2+
3+
export const PRODUCT_MANAGE_MENU_SELECTOR = "#product-manage-menu";
4+
export const PRODUCT_MANAGER_CONTAINER_SELECTOR = "#product-manager-container";
5+
export const PRODUCT_MANAGER_FORM_SELECTOR = "#product-manager-form";
6+
export const PRODUCT_INVENTORY_SELECTOR = "#product-inventory";
7+
export const PRODUCT_INVENTORY_CONTAINER_SELECTOR =
8+
"#product-inventory-container";
9+
10+
export const PRODUCT_NAME_INPUT_SELECTOR = "input.product-input[name=name]";
11+
export const PRODUCT_PRICE_INPUT_SELECTOR = "input.product-input[name=price]";
12+
export const PRODUCT_QUANTITY_INPUT_SELECTOR =
13+
"input.product-input[name=quantity]";
14+
export const PRODUCT_ADD_BUTTON_SELECTOR = "#product-add-button";
15+
16+
export const CHANGE_CHARGE_MENU_SELECTOR = "#change-charge-menu";
17+
export const COIN_CHARGING_CONTAINER_SELECTOR = "#coin-charging-container";
18+
export const COIN_CHARGING_FORM_SELECTOR = "#coin-charging-form";
19+
export const CHARGER_INPUT_SELECTOR = ".charger-input";
20+
export const COIN_CHARGE_BUTTON_SELECTOR = "#coin-charge-button";
21+
export const HOLDING_AMOUNT_SELECTOR = "#holding-amount";
22+
export const COIN_INVENTORY_SELECTOR = "#coin-inventory";
23+
export const COIN_UNIT_SELECTOR = ".coin-unit";
24+
export const COIN_AMOUNT_SELECTOR = ".coin-amount";
25+
26+
export const PRODUCT_PURCHASE_MENU_SELECTOR = "#product-purchase-menu";
27+
export const PRODUCT_PURCHASE_CONTAINER_SELECTOR =
28+
"#product-purchase-container";

0 commit comments

Comments
 (0)