Skip to content

Commit

Permalink
test: cypress 적용 및 행사 생성 flow e2e 테스트 작성 (#331)
Browse files Browse the repository at this point in the history
* chore: cypress 설정

Co-authored-by: JinHo Kim <[email protected]>

* test: 이벤트 생성 cypress 테스트 코드 작성

Co-authored-by: JinHo Kim <[email protected]>

* chore: cypress workflow 추가

* chore: dev 환경에서 eval source map 사용하도록 변경

* chore: cypress 명령어 세분화

* chore: cypress workflow 추가

* chore: 사용하지 않는 코드 제거

* chore: 사용하지 않는 dotenv 제거

* test: interceptAPI command 작성

* test: 반복되는 테스트 코드 매직넘버 상수화

* chore: postEvent fixture 생성

* test: API 요청이 필요한 부분 intercept

* style: 불필요한 주석 제거

---------

Co-authored-by: JinHo Kim <[email protected]>
  • Loading branch information
Todari and jinhokim98 authored Aug 14, 2024
1 parent eb5d9ce commit f3f05f6
Show file tree
Hide file tree
Showing 11 changed files with 1,824 additions and 70 deletions.
15 changes: 13 additions & 2 deletions .github/workflows/frontend-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: '20.15.1'

Expand All @@ -32,3 +32,14 @@ jobs:
- name: Run lint
working-directory: ./client
run: npm run lint

- name: Cypress test
run: npm run dev &
env:
CI: true

- name: Wait for the server to start
run: sleep 3

- name: Run Cypress tests
run: npm run cypress-run
12 changes: 12 additions & 0 deletions client/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {defineConfig} from 'cypress';

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 430,
viewportHeight: 930,
// setupNodeEvents(on, config) {
// // implement node event listeners here
// },
},
});
6 changes: 6 additions & 0 deletions client/cypress/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const CONSTANTS = {
eventName: '테스트 이벤트',
eventPassword: '1234',
};

export default CONSTANTS;
79 changes: 79 additions & 0 deletions client/cypress/e2e/createEvent.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import CONSTANTS from '../constants/constants';
beforeEach(() => {
cy.blockSentry();
});

describe('Flow: 랜딩 페이지에서부터 이벤트를 생성 완료하는 flow', () => {
it('랜딩페이지에서 "행사 생성하기" 버튼을 눌러 행사 이름 입력 페이지로 이동해야 한다.', () => {
cy.visit('/');
cy.get('button').should('have.text', '행사 생성하기').click();
cy.url().should('include', '/event/create/name');
});

context('행사 이름 입력 페이지', () => {
beforeEach(() => {
cy.visit('/event/create/name');
});

it('행사 이름 입력 페이지에서 input이 포커싱 되어 있고, "다음" 버튼이 비활성화 되어 있어야 한다.', () => {
cy.get('input').focused();
cy.get('button').contains('다음').should('have.attr', 'disabled');
cy.url().should('include', '/event/create/name');
});

it('행사 이름이 1자 이상 입력된 경우 "다음" 버튼이 활성화 되고, 값이 없는 경우 "다음" 버튼이 비활성화 되어야 한다.', () => {
cy.get('input').type(CONSTANTS.eventName);
cy.get('button').contains('다음').should('not.have.attr', 'disabled');
cy.get('input').clear();
cy.get('input').should('have.value', '');
cy.get('button').contains('다음').should('have.attr', 'disabled');
cy.url().should('include', '/event/create/name');
});

it('행사 이름을 입력한 후 "다음" 버튼을 누르면 행사 비밀번호 설정 화면으로 이동해야 한다.', () => {
cy.get('input').type(CONSTANTS.eventName);
cy.get('button').contains('다음').click();
cy.url().should('include', '/event/create/password');
});
});

context('행사 비밀번호 입력 페이지', () => {
beforeEach(() => {
cy.createEventName(CONSTANTS.eventName);
});

it('행사 비밀번호 입력 페이지에서 input이 포커싱 되어 있고, "행동 개시!" 버튼이 비활성화 되어 있어야 한다.', () => {
cy.get('input').focused();
cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
cy.url().should('include', '/event/create/password');
});

it('행사 비밀번호에 숫자가 아닌 입력을 할 경우 값이 입력되지 않아야 한다.', () => {
cy.get('input').type('테스트');
cy.get('input').should('have.value', '');
});

it('행사 비밀번호에 4자리 이상 입력을 할 경우 처음 네 자리만 입력되어야 한다.', () => {
cy.get('input').type('12345');
cy.get('input').should('have.value', CONSTANTS.eventPassword);
});

it('행사 비밀번호이 1자 이상 입력된 경우 "행동 개시!" 버튼이 활성화 되고, 값이 없는 경우 "행동 개시!" 버튼이 비활성화 되어야 한다.', () => {
cy.get('input').type(CONSTANTS.eventPassword);
cy.get('button').contains('행동 개시!').should('not.have.attr', 'disabled');
cy.get('input').clear();
cy.get('input').should('have.value', '');
cy.get('button').contains('행동 개시!').should('have.attr', 'disabled');
cy.url().should('include', '/event/create/password');
});

it('행사 비밀번호을 입력한 후 "행동 개시!" 버튼을 누르면 행사 생성 완료 화면으로 이동해야 한다.', () => {
cy.interceptAPI({type: 'postEvent', statusCode: 200});
cy.interceptAPI({type: 'getEventName', statusCode: 200});
cy.get('input').type(CONSTANTS.eventPassword);
cy.get('button').contains('행동 개시!').click();

cy.url().should('include', '/event/create/complete');
});
});
});
3 changes: 3 additions & 0 deletions client/cypress/fixtures/postEvent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"eventId": "550e8400-e29b-41d4-a716-446655440000"
}
58 changes: 58 additions & 0 deletions client/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import CONSTANTS from '../constants/constants';

type APIType = 'sentry' | 'postEvent' | 'getEventName';

interface InterceptAPIProps {
type: APIType;
delay?: number;
statusCode?: number;
}
const POST_EVENT = {
method: 'POST',
url: /.*api\/events.*/,
};

const GET_EVENT_NAME = {
method: 'GET',
url: /.*api\/events\.*/,
};

Cypress.Commands.add('blockSentry', () => {
cy.intercept('POST', /.*sentry.io\/api.*/, {statusCode: 200}).as('sentry');
});

Cypress.Commands.add('interceptAPI', ({type, delay = 0, statusCode = 200}: InterceptAPIProps) => {
if (type === 'postEvent')
cy.intercept(POST_EVENT, {
delay,
statusCode,
fixture: 'postEvent.json',
}).as('postEvent');
if (type === 'getEventName')
cy.intercept(GET_EVENT_NAME, {
delay,
statusCode,
body: {
eventName: CONSTANTS.eventName,
},
}).as('getEventName');
});

Cypress.Commands.add('createEventName', (eventName: string) => {
cy.visit('/event/create/name');
cy.get('input').type(eventName);
cy.get('button').contains('다음').click();
cy.url().should('include', '/event/create/password');
});

declare global {
namespace Cypress {
interface Chainable {
blockSentry(): Chainable<void>;
interceptAPI(props: InterceptAPIProps): Chainable<void>;
createEventName(eventName: string): Chainable<void>;
}
}
}

export {};
1 change: 1 addition & 0 deletions client/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './commands';
Loading

0 comments on commit f3f05f6

Please sign in to comment.