Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0e5a22c
feat: 상품 구매 페이지 라우팅 연결, 금액 투입 기능 구현
usageness Mar 31, 2022
fca128f
refactor: valueAsNumber 적용
usageness Mar 31, 2022
0fce0d5
feat: PurchasableProductItemList 컴포넌트 추가
usageness Mar 31, 2022
52fd6f9
feat: 잔돈 반환 기능 구현
usageness Mar 31, 2022
4b7e16f
docs: step2 요구 사항 정리
usageness Apr 1, 2022
a9abc17
test: 자판기에 투입된 총 금액이 10,00원을 넘으면 오류를 발생시킨다
usageness Apr 1, 2022
1a1fff1
feat: 자판기에 투입된 총 금액이 10,000원을 넘으면, 오류를 발생시킨다.
usageness Apr 1, 2022
0d6f10d
test: 자판기 투입 금액 유효성 검사 추가
usageness Apr 1, 2022
8cad147
fix: 사용자가 금액을 투입할때 기존 자판기 금액을 참조하던 오류 수정
usageness Apr 1, 2022
634e67b
test: 잔돈을 반환할때 자판기에 충전된 잔돈이 유저가 투입한 금액에 비해 부족하면, 오류를 발생시킨다.
usageness Apr 1, 2022
2149520
feat: 잔돈을 반환할때 자판기에 충전된 잔돈이 유저가 투입한 금액에 비해 부족하면, 오류를 발생시킨다.
usageness Apr 1, 2022
a5a7563
test: 상품을 구매할때 가격보다 투입한 금액이 적으면, 오류를 발생시킨다.
usageness Apr 1, 2022
b326e6f
feat: 상품을 구매할때 가격보다 투입한 금액이 적으면, 오류를 발생시키는 기능 구현
usageness Apr 1, 2022
6d5f676
feat: 상품 구매 기능 ui와 연결
usageness Apr 1, 2022
14e3def
feat: 스낵바 구현 및 기존 alert 스낵바로 대체
usageness Apr 4, 2022
2908f8a
refactor: throwableFunctionHandler 추가
usageness Apr 4, 2022
eb890c7
fix: 상품 정보 수정시 사용자 권한의 아이템으로 보이던 오류 수정
usageness Apr 4, 2022
3d53c77
refactor: snackbar css 수정
usageness Apr 4, 2022
75159ad
chore: json server auth 설치 및 세팅
usageness Apr 4, 2022
e39d1db
chore: 로그인 버튼 마크업 작성
usageness Apr 4, 2022
f043681
feat: 로그인 페이지 마크업 및 연결
usageness Apr 4, 2022
82a8eb0
fix: 로그인 페이지 중복 로드 오류 수정에 따른 라우터 구조 변경
usageness Apr 5, 2022
90945af
feat: 회원가입 페이지 마크업 작성 및 라우터 연결
usageness Apr 5, 2022
b67bdfd
feat: 회원가입 api 연결 및 기본 에러 처리
usageness Apr 5, 2022
d9fa193
refactor: 회원가입 오류 메시지 및 기존 에러메시지 상수화
usageness Apr 5, 2022
9a83778
feat: 회원가입 유효성 확인 로직 구현
usageness Apr 5, 2022
6c792a4
feat: 로그인 api 연결
usageness Apr 5, 2022
ca21c06
chore: jwt-decode 설치
usageness Apr 5, 2022
6a229e5
feat: 로그인 확인 기능 구현
usageness Apr 5, 2022
afd29f6
feat: 유효하지 않은 토큰에 대한 에러 핸들링 추가
usageness Apr 6, 2022
6a3db7f
feat: 유저 메뉴 마크업 작성
usageness Apr 6, 2022
6a52c4c
feat: 회원 정보 수정 기능/로그아웃 기능 구현
usageness Apr 6, 2022
1e670fc
fix: 회원정보 수정 시 스토리지에 잘못된 값이 저장되던 오류 수정
usageness Apr 6, 2022
f12a9b1
feat: 관리자 권환 확인 기능 구현
usageness Apr 6, 2022
32b3991
chore: 에러 핸들링 객체 스펙 변경에 따른 호출부 변경
usageness Apr 6, 2022
dcd83c3
feat: 이름 길이 제한 추가
usageness Apr 6, 2022
40d76b7
docs: 요구 사항 체크
usageness Apr 6, 2022
21aae94
fix: 잘못된 이름 유효성 조건 수정
usageness Apr 6, 2022
bc2ed5f
test: 잘못된 이메일 주소로 계정 생성을 시도하면, 오류를 발생시킨다.
usageness Apr 6, 2022
df3720b
test: 이름의 길이가 2~6자 사이가 아니면, 오류를 발생시킨다.
usageness Apr 6, 2022
a667a90
chore: 비밀번호 구성 조건 변경 (특수문자 필수)
usageness Apr 6, 2022
a7a2b4e
test: 비밀번호가 6~20자 사이의 영문과 숫자, 특수문자 조합이 아니면 오류를 발생시킨다.
usageness Apr 6, 2022
bd98f37
test: 비밀번호와 비밀번호 확인이 같지 않으면 오류를 발생시킨다.
usageness Apr 6, 2022
3f2b41b
chore: 라우터 기본 연결값 추가
usageness Apr 6, 2022
5714262
chore: cypress 설치 및 기본 세팅
usageness Apr 6, 2022
15f8fa7
test: 회원가입/로그인 e2e 테스트 추가
usageness Apr 6, 2022
8a8163f
test: 관리자 기능 e2e 테스트 작성
usageness Apr 6, 2022
f1239ab
chore: 개발자 모드 상수 DEV_MODE 추가 및 적용
usageness Apr 6, 2022
e54f6e6
test: 사용자 기능 테스트 추가
usageness Apr 6, 2022
51941d1
fix: 잔액과 상관없이 상품이 결제되던 오류 수정
usageness Apr 6, 2022
27d64d6
chore: 요구 사항 문서 체크
usageness Apr 6, 2022
aae4175
chore: db file 정리
usageness Apr 6, 2022
43ef0fa
refactor: server 주소 상수화
usageness Apr 6, 2022
d0d5d52
chore: heroku 배포 주소 적용
usageness Apr 6, 2022
5418214
chore: npm start 명령어 수정
usageness Apr 6, 2022
d7c4eea
chore: 배포 서버 환경에 맞게 연결
usageness Apr 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Dependency directories
node_modules/

dist
# build folder
dist

# database file
db.json

# test file
cypress/fixtures/
cypress/videos/
4 changes: 4 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"integrationFolder": "cypress/integration",
"testFiles": "*.spec.js"
}
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
139 changes: 139 additions & 0 deletions cypress/integration/app.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { ALERT_MESSAGE } from '../../src/js/constants';
const userEmail = `${Date.now()}@test.com`;
const userName = 'test';
const userPassword = '1q2w3e!!';
const productName = '사이다';
const productPrice = '1000';
const productAmount = '10';
const productModifyAmount = '20';
const addChangeMoney = 10000;

describe('기본 시나리오 동작 확인', () => {
before(() => {
cy.visit('http://localhost:9000/');
});

describe('로그인 / 회원가입 테스트', () => {
it('로그인 버튼을 누르면 로그인 화면으로 이동해야한다.', () => {
cy.get('#login-page-button')
.click()
.then(() => {
cy.url().should('contain', '#!/login');
});
});

it('회원가입 버튼을 누르면 회원가입 화면으로 이동해야한다.', () => {
cy.get('#register-link')
.click()
.then(() => {
cy.url().should('contain', '#!/register');
});
});

it('입력창에 유효한 값을 입력하여 회원가입을 할 수 있어야한다.', () => {
cy.get('#email-input').type(userEmail);
cy.get('#name-input').type(userName);
cy.get('#password-input').type(userPassword);
cy.get('#password-check-input').type(userPassword);

cy.get('#register-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.REGISTER_SUCCESS);
});
});

it('회원가입한 계정을 입력하여 로그인을 할 수 있어야한다.', () => {
cy.get('#email-input').type(userEmail);
cy.get('#password-input').type(userPassword);

cy.get('#login-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.LOGIN_SUCCESS(userName));
});
});
});

describe('관리자 기능 테스트', () => {
it('관리자는 자판기에 상품을 추가할 수 있어야 한다.', () => {
cy.addProduct(productName, productPrice, productAmount).then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.ADD_PRODUCT_SUCCESS(productName));
});
});

it('관리자는 자판기의 상품을 수정할 수 있어야 한다.', () => {
cy.get('.product-modify-button').click();
cy.get('.product-amount-modify-input').clear().type(productModifyAmount);
cy.get('.product-modify-submit-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.MODIFY_PRODUCT_SUCCESS(productName));
});
});

it('관리자는 자판기의 상품을 삭제할 수 있어야 한다.', () => {
cy.get('.product-remove-button')
.click()
.then(() => {
cy.on('window:confirm', text => {
expect(text).to.contains(REMOVE_CONFIRM_MESSAGE);
return true;
});
cy.get('.snackbar').should('contain', ALERT_MESSAGE.DELETE_PRODUCT_SUCCESS(productName));
cy.addProduct(productName, productPrice, productAmount);
});
});

it('관리자는 자판기의 잔돈을 채울 수 있어야 한다.', () => {
cy.get('nav > #change-add-button').click();
cy.get('#change-add-input').type(addChangeMoney);
cy.get('#change-add-form > #change-add-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.ADD_CHARGE_SUCCESS(addChangeMoney));
});
});

it('관리자는 로그아웃 버튼을 눌러 로그아웃 할 수 있어야 한다.', () => {
cy.login(userEmail, userPassword);

cy.get('.user-menu-symbol').click();
cy.get('#logout')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.LOGOUT_SUCCESS);
});
});
});

describe('사용자 기능 테스트', () => {
const inputMoney = 5000;

it('사용자는 금액을 입력하여 돈을 투입할 수 있어야 한다.', () => {
cy.get('#input-money').type(inputMoney);
cy.get('#input-money-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.INPUT_MONEY_SUCCESS(inputMoney));
});
});

it('사용자는 구매 버튼을 눌러 상품을 구입할 수 있어야 한다.', () => {
cy.get('.product-purchase-button')
.click()
.then(() => {
cy.get('.snackbar').should('contain', ALERT_MESSAGE.PURCHASE_PRODUCT_SUCCESS(productName));
});
});

it('사용자는 잔돈 반환 버튼을 눌러 잔돈을 반환 받을 수 있어야 한다.', () => {
cy.get('#return-change-button')
.click()
.then(() => {
cy.get('#total-money').should('have.text', '0');
cy.get('.snackbar').should('contain', ALERT_MESSAGE.RETURN_CHARGE_SUCCESS);
});
});
});
});
22 changes: 22 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
40 changes: 40 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

Cypress.Commands.add('login', (userEmail, userPassword) => {
cy.get('#login-page-button').click();
cy.get('#email-input').type(userEmail);
cy.get('#password-input').type(userPassword);
cy.get('#login-button').click();
});

Cypress.Commands.add('addProduct', (productName, productPrice, productAmount) => {
cy.get('#product-name-input').clear().type(productName);
cy.get('#product-price-input').clear().type(productPrice);
cy.get('#product-amount-input').clear().type(productAmount);

cy.get('#product-add-button').click();
});
20 changes: 20 additions & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
3 changes: 3 additions & 0 deletions db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"users": []
}
16 changes: 5 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,11 @@

<body>
<div id="app">
<header>
<h1>🍿 자판기 🍿</h1>
<nav>
<button type="button" id="product-manage-button">상품 관리</button>
<button type="button" id="change-add-button">잔돈 충전</button>
<button type="button" id="product-purchase-button">상품 구매</button>
</nav>
</header>
<section class="input-section"></section>

<section class="contents-container"></section>
<div id="login-input-container" class="input-page"></div>
<div class="main-contents"></div>
</div>
<div class="snackbar-wrapper">
<div class="snackbar"></div>
</div>
</body>
</html>
Loading