Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
[FE] test: 테스트 코드 뼈대 구현 (#472)
Browse files Browse the repository at this point in the history
* feat: MSW에서 auth 정보가 맞지 않을 때 처리 구현

* feat: MSW에서 로그인 성공시 가짜 accessToken을 넣어주도록 구현

* test: 로그인 테스트 작성

* test: space 페이지 테스트 분리

---------

Co-authored-by: Youngjin Park <youngjin988@gmail.com>
  • Loading branch information
nangkyeonglim and yogjin authored Sep 28, 2023
1 parent 87d72b1 commit 6ab7842
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 66 deletions.
54 changes: 15 additions & 39 deletions frontend/cypress/e2e/main-page.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,27 @@ describe('동글 메인 페이지', () => {
cy.visit(`/`);
});

describe('글 업로드 테스트', () => {
it('Add Post 버튼을 누르면 글 가져오기 모달 창이 열린다.', () => {
cy.findByText('Add Post').click();
describe('로그인 성공 테스트', () => {
it('로그인 하기 버튼을 누르면 로그인 모달 창이 열린다.', () => {
cy.findByText('로그인하기').click();

cy.findByText('글 가져오기').should('exist');
cy.findByText('간편 로그인').should('exist');
});

it('드래그 앤 드롭으로 마크다운 파일을 업로드할 수 있다.', () => {
cy.findByText('Add Post').click();
cy.findByLabelText('파일 업로드').attachFile('markdown-test.md', {
subjectType: 'drag-n-drop',
});

cy.findByText('markdown-test').should('exist');
cy.findByText('e2e 테스트를 위한 마크다운 파일입니다.').should('exist');
cy.findByText('글 정보').should('exist');
cy.findByLabelText('오른쪽 사이드바 토글').should('exist');
});

it('기본 카테고리에서 업로드한 글을 확인할 수 있다.', () => {
cy.findByText('기본').click();

cy.findAllByText('markdown-test').should('exist');
it('카카오 로그인 버튼을 누르면 카카오로 로그인 할 수 있는 화면이 나타난다.', () => {
cy.findByText('로그인하기').click();
cy.findByLabelText('카카오 로그인 화면으로 이동').click();
cy.visit(`/oauth/login/kakao?code=mock`);
cy.findByText('로그아웃').should('exist');
});
});

describe('카테고리 테스트', () => {
it('카테고리 추가 버튼을 클릭하여 입력 창에 이름을 입력하고 엔터를 쳐서 카테고리를 추가할 수 있다.', () => {
cy.findByLabelText('카테고리 추가 입력 창 열기').click();
cy.findByLabelText('카테고리 추가 입력 창').focus().type('동글이{enter}');
cy.findByText('동글이').should('exist');
});

it('카테고리 이름 수정 버튼을 클릭하여 입력 창에 이름을 입력하고 엔터를 쳐서 카테고리 이름을 수정할 수 있다.', () => {
cy.findByText('동글이').realHover();
cy.findByLabelText('동글이 카테고리 이름 수정').click();
cy.findByLabelText('동글이 카테고리 이름 수정 입력 창').focus().type('동글동글이{enter}');
cy.findByText('동글이').should('not.exist');
cy.findByText('동글동글이').should('exist');
});

it('카테고리 삭제 버튼을 클릭하여 카테고리를 삭제할 수 있다.', () => {
cy.findByText('동글동글이').realHover();
cy.findByLabelText('동글동글이 카테고리 삭제').click();
cy.findByText('동글동글이').should('not.exist');
describe('로그인 실패 테스트', () => {
it('카카오 로그인 버튼을 누르면 카카오로 로그인 할 수 있는 화면이 나타난다.', () => {
cy.findByText('로그인하기').click();
cy.findByLabelText('카카오 로그인 화면으로 이동').click();
cy.visit(`/oauth/login/kakao`);
cy.findByText('로그인을 실패했습니다').should('exist');
});
});
});
54 changes: 54 additions & 0 deletions frontend/cypress/e2e/space-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
describe('동글 스페이스 페이지', () => {
beforeEach(() => {
cy.viewport(1440, 810);
cy.visit(`/`);
});

describe('글 업로드 테스트', () => {
it('Add Post 버튼을 누르면 글 가져오기 모달 창이 열린다.', () => {
cy.findByText('Add Post').click();

cy.findByText('글 가져오기').should('exist');
});

it('드래그 앤 드롭으로 마크다운 파일을 업로드할 수 있다.', () => {
cy.findByText('Add Post').click();
cy.findByLabelText('파일 업로드').attachFile('markdown-test.md', {
subjectType: 'drag-n-drop',
});

cy.findByText('markdown-test').should('exist');
cy.findByText('e2e 테스트를 위한 마크다운 파일입니다.').should('exist');
cy.findByText('글 정보').should('exist');
cy.findByLabelText('오른쪽 사이드바 토글').should('exist');
});

it('기본 카테고리에서 업로드한 글을 확인할 수 있다.', () => {
cy.findByText('기본').click();

cy.findAllByText('markdown-test').should('exist');
});
});

describe('카테고리 테스트', () => {
it('카테고리 추가 버튼을 클릭하여 입력 창에 이름을 입력하고 엔터를 쳐서 카테고리를 추가할 수 있다.', () => {
cy.findByLabelText('카테고리 추가 입력 창 열기').click();
cy.findByLabelText('카테고리 추가 입력 창').focus().type('동글이{enter}');
cy.findByText('동글이').should('exist');
});

it('카테고리 이름 수정 버튼을 클릭하여 입력 창에 이름을 입력하고 엔터를 쳐서 카테고리 이름을 수정할 수 있다.', () => {
cy.findByText('동글이').realHover();
cy.findByLabelText('동글이 카테고리 이름 수정').click();
cy.findByLabelText('동글이 카테고리 이름 수정 입력 창').focus().type('동글동글이{enter}');
cy.findByText('동글이').should('not.exist');
cy.findByText('동글동글이').should('exist');
});

it('카테고리 삭제 버튼을 클릭하여 카테고리를 삭제할 수 있다.', () => {
cy.findByText('동글동글이').realHover();
cy.findByLabelText('동글동글이 카테고리 삭제').click();
cy.findByText('동글동글이').should('not.exist');
});
});
});
18 changes: 18 additions & 0 deletions frontend/src/mocks/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DefaultBodyType, PathParams, RestRequest } from 'msw';

export const MOCK_ACCESS_TOKEN = 'mockAccessToken';

export const ERROR_RESPONSE = {
error: {
message: '유효하지 않은 토큰입니다.',
hint: 'AccessToken이 만료되었습니다. RefreshToken값을 요청하세요.',
code: 4011,
},
};

export const isValidAccessToken = (req: RestRequest<DefaultBodyType, PathParams<string>>) => {
const authorizationHeader = req.headers.get('Authorization');

if (authorizationHeader === `Bearer ${MOCK_ACCESS_TOKEN}`) return true;
return false;
};
17 changes: 15 additions & 2 deletions frontend/src/mocks/handlers/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ import {
UpdateCategoryTitleArgs,
} from 'types/apis/category';
import { hasDefinedField } from 'utils/typeGuard';
import { ERROR_RESPONSE, isValidAccessToken } from 'mocks/auth';

export const categoryHandlers = [
// 카테고리 목록 조회
rest.get(categoryURL, (_, res, ctx) => {
rest.get(categoryURL, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.json(categories), ctx.delay(300), ctx.status(200));
}),

// 카테고리 추가
rest.post(categoryURL, (req, res, ctx) => {
const body = req.body as AddCategoriesRequest;

if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

if (!body || !body.categoryName)
return res(
ctx.delay(300),
Expand All @@ -30,6 +35,8 @@ export const categoryHandlers = [

// 카테고리 글 목록 조회
rest.get(`${categoryURL}/:categoryId`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

const categoryId = Number(req.params.categoryId);

if (categoryId !== 1 && categoryId !== 3)
Expand All @@ -51,6 +58,8 @@ export const categoryHandlers = [
const categoryId = Number(req.params.categoryId);
const body = await req.json();

if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

// 카테고리 순서 변경
if (hasDefinedField<UpdateCategoryOrderArgs['body']>(body, 'nextCategoryId')) {
if (!body.nextCategoryId) {
Expand Down Expand Up @@ -83,6 +92,8 @@ export const categoryHandlers = [

// 카테고리 이름 수정
if (hasDefinedField<UpdateCategoryTitleArgs['body']>(body, 'categoryName')) {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

if (!body.categoryName)
return res(
ctx.delay(300),
Expand All @@ -96,7 +107,9 @@ export const categoryHandlers = [
}),

// 카테고리 삭제
rest.delete(`${categoryURL}/:categoryId`, (_, res, ctx) => {
rest.delete(`${categoryURL}/:categoryId`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.delay(300), ctx.status(204));
}),
];
25 changes: 19 additions & 6 deletions frontend/src/mocks/handlers/connections.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
import { connectionsURL } from 'constants/apis/url';
import { ERROR_RESPONSE, isValidAccessToken } from 'mocks/auth';
import { rest } from 'msw';

export const connectionsHandlers = [
// 티스토리 정보 저장
rest.post(`${connectionsURL}/tistory`, (_, res, ctx) => {
rest.post(`${connectionsURL}/tistory`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 미디움 정보 저장
rest.post(`${connectionsURL}/medium`, (_, res, ctx) => {
rest.post(`${connectionsURL}/medium`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 노션 정보 저장
rest.post(`${connectionsURL}/notion`, (_, res, ctx) => {
rest.post(`${connectionsURL}/notion`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 티스토리 연결 해제
rest.post(`${connectionsURL}/tistory/disconnect`, (_, res, ctx) => {
rest.post(`${connectionsURL}/tistory/disconnect`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 미디움 연결 해제
rest.post(`${connectionsURL}/medium/disconnect`, (_, res, ctx) => {
rest.post(`${connectionsURL}/medium/disconnect`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 노션 연결 해제
rest.post(`${connectionsURL}/notion/disconnect`, (_, res, ctx) => {
rest.post(`${connectionsURL}/notion/disconnect`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),
];
3 changes: 2 additions & 1 deletion frontend/src/mocks/handlers/login.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { authURL, loginURL } from 'constants/apis/url';
import { MOCK_ACCESS_TOKEN } from 'mocks/auth';
import { rest } from 'msw';

export const loginHandlers = [
// 카카오 로그인/회원가입: POST
rest.post(`${loginURL}/kakao`, (_, res, ctx) => {
return res(ctx.delay(300), ctx.status(200));
return res(ctx.delay(300), ctx.status(200), ctx.json({ accessToken: MOCK_ACCESS_TOKEN }));
}),

rest.post(`${authURL}/logout`, (_, res, ctx) => {
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/mocks/handlers/member.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { memberURL } from 'constants/apis/url';
import { ERROR_RESPONSE, isValidAccessToken } from 'mocks/auth';
import { member } from 'mocks/memberContentsMock';
import { rest } from 'msw';

export const memberHandlers = [
rest.get(memberURL, (_, res, ctx) => {
// 멤버 정보 가져오기: GET
rest.get(memberURL, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.json(member), ctx.status(200));
}),

// 회원 탈퇴: POST
rest.post(`${memberURL}/delete`, (_, res, ctx) => {
rest.post(`${memberURL}/delete`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),
];
13 changes: 10 additions & 3 deletions frontend/src/mocks/handlers/trash.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { trashURL } from 'constants/apis/url';
import { ERROR_RESPONSE, isValidAccessToken } from 'mocks/auth';
import { deletedWritings } from 'mocks/trashCanContentsMock';
import { rest } from 'msw';

export const trashHandlers = [
// 글 휴지통으로 이동 / 글 영구 삭제
rest.post(trashURL, (_, res, ctx) => {
rest.post(trashURL, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),

// 휴지통에서 글 목록 조회
rest.get(trashURL, (_, res, ctx) => {
rest.get(trashURL, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.json(deletedWritings), ctx.status(200));
}),

// 휴지통에서 글 복구
rest.post(`${trashURL}/restore`, (_, res, ctx) => {
rest.post(`${trashURL}/restore`, (req, res, ctx) => {
if (!isValidAccessToken(req)) return res(ctx.status(401), ctx.json(ERROR_RESPONSE));

return res(ctx.status(200));
}),
];
Loading

0 comments on commit 6ab7842

Please sign in to comment.