Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

사용자 스토리를 기반으로 E2E 테스트 작성하기 #469

Open
wants to merge 34 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8f4c46a
Merge pull request #449 from woowacourse-teams/refactor/430-n+1
donghae-kim Sep 14, 2023
5016eef
Merge pull request #447 from woowacourse-teams/refactor/442-imporve-i…
green-kong Sep 14, 2023
e4b630f
chore: cypress.config에 viewport 설정
geuntaek1013 Sep 20, 2023
3e5db89
feat: 스크롤 시 다음 카페 이미지가 나오는지에 대한 cypress test 작성
geuntaek1013 Sep 20, 2023
9bc7f6b
feat: 좋아요 버튼에 대한 cypress test 작성
geuntaek1013 Sep 20, 2023
c429a99
feat: 홈 화면 더보기 버튼을 클릭하면 카페 상세정보를 확인하는 cypress test
geuntaek1013 Sep 20, 2023
a595acf
feat: 공유 버튼 cypress test 작성
geuntaek1013 Sep 20, 2023
bb72d0f
refactor: 공유 버튼 cypress에 clipboard 설정 추가
geuntaek1013 Sep 20, 2023
7a78fb2
chore: 빌드 시 cypress test를 위한 data-testid 제거 플러그인
geuntaek1013 Sep 20, 2023
9d88842
feat: 메뉴 버튼 시트 창 닫기 cypress test 작성
geuntaek1013 Sep 20, 2023
3feaaef
feat: 이미지 host url 수정
solo5star Sep 20, 2023
e9a72a2
Merge pull request #463 from woowacourse-teams/feat/462-new-image-hos…
solo5star Sep 20, 2023
e1747bc
refactor: 정적 리소스 assets 폴더로 이동
solo5star Sep 20, 2023
becf8fd
Merge pull request #465 from woowacourse-teams/refactor/464-move-to-a…
solo5star Sep 20, 2023
abe2eed
chore: index.html에 잘못된 경로 수정
solo5star Sep 20, 2023
c324406
build: webpack 설정 최적화
solo5star Sep 20, 2023
97d8667
feat: logo를 svg로 렌더링하여 최적화
solo5star Sep 20, 2023
7024f25
refactor: Page postfix
solo5star Sep 20, 2023
3dfd649
refactor: onClick을 a 태그로 교체
solo5star Sep 20, 2023
f8baee0
fix: 조건부 msw import
solo5star Sep 20, 2023
3057440
refactor: 불필요한 Logo 컴포넌트 삭제
solo5star Sep 20, 2023
3cbb378
feat: 라우트 단위의 lazy loading 적용
solo5star Sep 20, 2023
27ad9ab
build: BundleAnalyzerPlugin 제외
solo5star Sep 20, 2023
1775c47
Merge pull request #466 from woowacourse-teams/feat/439-bundle-size-o…
solo5star Sep 20, 2023
f4fdf90
feat: 카페 이미지 좌우 이미지 스크롤 cypress test 추가
geuntaek1013 Sep 20, 2023
bbe5afe
refactor: webpack에 react-remove-properties 제거
geuntaek1013 Sep 20, 2023
2ed6e2f
Merge branch 'main' into feat/436-cypress-e2e-test
solo5star Sep 21, 2023
6557090
chore: package-lock.json 삭제
solo5star Sep 21, 2023
62708b9
refactor: 웹팩 빌드 시 cypress 테스트를 위한 data id 제거 설정
geuntaek1013 Sep 21, 2023
305271d
feat: cypress test를 위한 각 컴포넌트 data-testid 설정
geuntaek1013 Sep 21, 2023
67705f7
chore: .DS_Store 파일 삭제
geuntaek1013 Sep 21, 2023
bcf99e6
refactor: cypress 불필요한 파일 삭제
geuntaek1013 Sep 21, 2023
c6c4eb1
refactor: cypress 실행에 필요한 support 폴더에 commands 추가
geuntaek1013 Sep 21, 2023
ab060cd
fix: pull main
geuntaek1013 Oct 18, 2023
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ server/.nb-gradle/
server/.vscode/

.env

### Mac OS ###
.DS_Store
9 changes: 9 additions & 0 deletions client/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,14 @@ import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 390,
viewportHeight: 844,
},

component: {
devServer: {
framework: 'react',
bundler: 'webpack',
},
},
});
27 changes: 27 additions & 0 deletions client/cypress/e2e/cafe-detail-info.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference types="cypress" />

describe('CafeDetailBottomSheet test', () => {
beforeEach(() => {
cy.visit('/');
});

it('더보기 버튼을 클릭하면 카페 이름, 주소, 영업시간, 상세설명을 볼 수 있다.', () => {
cy.contains('더보기').click({ force: true });

cy.get('[data-testid=cafe-name]').should('be.visible');
cy.get('[data-testid=cafe-address]').should('be.visible');
cy.get('[data-testid=cafe-openingHours]').should('be.visible');
cy.get('[data-testid=cafe-description]').should('be.visible');
});

it('바텀 시트에서 X 버튼이 보이고, X 버튼을 클릭하면 시트가 닫힌다.', () => {
cy.wait(2000);
cy.contains('더보기').click({ force: true });

cy.get('[data-testid=close-button]').should('be.visible');

cy.get('[data-testid=close-button]').click();

cy.get('Container').should('not.exist');
});
});
20 changes: 20 additions & 0 deletions client/cypress/e2e/like-count-button.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/// <reference types="cypress" />
describe('LikeButton', () => {
beforeEach(() => {
cy.visit('/');
});

it('좋아요 버튼을 누르면 좋아요 버튼이 활성화되고, 좋아요 카운트가 1 증가한다.', () => {
cy.get('[data-testid="likeButton"]').click({ multiple: true, force: true });

cy.get('[data-testid="likeButton"]').should('have.css', 'color', 'rgb(255, 255, 255)');
cy.get('[aria-live="assertive"]').should('contain.text', '좋아요가 추가되었습니다.');
});

it('좋아요한 버튼을 다시 누르면 좋아요 버튼이 비활성화되고, 좋아요 카운트가 1 감소한다', () => {
cy.get('[data-testid="likeButton"]').click({ multiple: true, force: true });

cy.get('[data-testid="likeButton"]').should('have.css', 'color', 'rgb(255, 255, 255)');
cy.get('[aria-live="assertive"]').should('contain.text', '좋아요 취소되었습니다.');
});
});
25 changes: 25 additions & 0 deletions client/cypress/e2e/menu-button.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
describe('CafeMenuBottomSheet', () => {
beforeEach(() => {
cy.visit('/');
});

it('메뉴 버튼을 클릭하면 대표메뉴, 메뉴 아이템이 보인다.', () => {
cy.contains('메뉴').click({ force: true });

cy.get('[data-testid="menuBottomSheet"]').should('be.visible');

cy.contains('대표 메뉴').should('be.visible');
cy.get('[data-testid="menu"]').should('be.visible');
cy.get('[data-testid="menuItem"]').should('have.length.greaterThan', 0);
});
it('바텀 시트에서 X 버튼이 보이고, X 버튼을 클릭하면 시트가 닫힌다.', () => {
cy.wait(2000);
cy.contains('더보기').click({ force: true });

cy.get('[data-testid=close-button]').should('be.visible');

cy.get('[data-testid=close-button]').click();

cy.get('Container').should('not.exist');
});
});
49 changes: 48 additions & 1 deletion client/cypress/e2e/scroll-cafes.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="cypress" />

context('Scroll Cafes', () => {
describe('홈 화면에서 카페 이미지를 위로 스크롤하면 다음 카페 이미지가 나온다.', () => {
beforeEach(() => {
cy.visit('/');
});
Expand All @@ -13,4 +13,51 @@ context('Scroll Cafes', () => {
cy.get('main > ul').scrollTo(0, 400);
cy.contains('성수 카페2 (1호점)').should('be.visible');
});

it('스크롤을 2번 하면 세 번째 카페가 나타나야 한다.', () => {
cy.get('main > ul').scrollTo(0, 400 * 3);
cy.contains('성수 카페3 (1호점)').should('be.visible');
});
});

describe('카페 이미지를 좌우로 스크롤하여 다음, 이전 카페 이미지를 불러온다.', () => {
beforeEach(() => {
cy.visit('/');
});

it('should scroll left and right in the carousel', () => {
cy.get('[data-testid="cafe-card"]').should('be.visible');

let currentImageIndex = 0;

const scrollRight = () => {
currentImageIndex++;

cy.get('[data-testid="cafe-card"]')
.get('[data-testid="carousel-image"]')
.should('have.attr', 'src', `/images/cafe-image-${currentImageIndex}.png`);
cy.get('[data-testid="carousel-dots"]').get('[data-testid="dot"]').eq(currentImageIndex);
};

const scrollLeft = () => {
currentImageIndex--;
const expectedImageSrc = `/images/cafe-image-${currentImageIndex}.png`;

cy.get('[data-testid="cafe-card"]')
.find('[data-testid="carousel-image"]')
.should('have.attr', 'src', expectedImageSrc);

cy.get('[data-testid="carousel-dots"]')
.find('[data-testid="dot"]')
.eq(currentImageIndex)
.should('have.class', 'active');

cy.get('[data-testid="cafe-card"]').scrollTo('left');
};

scrollRight();
scrollRight();
scrollLeft();
scrollLeft();
});
});
19 changes: 19 additions & 0 deletions client/cypress/e2e/share-button.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// <reference types="cypress" />

describe('ShareButton', () => {
beforeEach(() => {
cy.visit('/');
cy.window().then((window) => {
cy.stub(window.navigator.clipboard, 'writeText').resolves('https://yozm.cafe/cafes/1');
});
});

it('공유하기 버튼을 클릭하면 해당 카페 URL이 복사되고, alert창을 띄운다.', () => {
cy.contains('공유').click({ force: true });

cy.window().its('navigator.clipboard').invoke('readText');
cy.on('window:alert', (alertText) => {
expect(alertText).to.equal('URL이 복사되었습니다!');
});
});
});
6 changes: 3 additions & 3 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico" />
<meta name="theme-color" content="#FFD3D8" />

<meta name="apple-mobile-web-app-title" content="요즘카페" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="#FFD3D8" />
<link rel="apple-touch-icon" sizes="128x128" href="ios.png" />
<link rel="apple-touch-icon" sizes="128x128" href="/assets/ios.png" />
<link rel="apple-touch-icon-precomposed" sizes="128x128" href="ios.png" />

<meta property="og:title" content="요즘카페" />
<meta property="og:url" content="https://yozm.cafe/" />
<meta property="og:type" content="website" />
<meta property="og:image" content="thumbnail-image.png" />
<meta property="og:image" content="/assets/thumbnail-image.png" />
<meta property="og:description" content="트렌디한 성수 지역의 카페를 손쉽게 탐색하는 서비스, 요즘카페" />

<title>요즘카페</title>
Expand Down
8 changes: 5 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"solo5star <[email protected]>"
],
"scripts": {
"start": "cross-env NODE_ENV=development webpack serve",
"build": "cross-env NODE_ENV=production webpack",
"start": "cross-env NODE_ENV=development webpack serve --config webpack.development.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.production.js",
"storybook": "cross-env NODE_ENV=development storybook dev -p 6006",
"build:storybook": "cross-env NODE_ENV=production storybook build",
"cypress:open": "cypress open --e2e --browser chrome",
Expand All @@ -23,7 +23,7 @@
"axios": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.10.1",
"react-icons": "^4.11.0",
"react-router-dom": "^6.14.1",
"styled-components": "^6.0.2"
},
Expand All @@ -45,6 +45,7 @@
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"babel-loader": "^9.1.2",
"babel-plugin-react-remove-properties": "^0.3.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 사용하려면 빌드 될 때 없어지게 하려면 따로 웹펙에 환경 설정을 해주어야 하는 거 같아요. 그리고 많이 사용되는 플러그인이 아닌거 같은데... 이게 없으면 테스트 진행이 어렵나용? 그리고 컴포넌트 수정된 것도 PR에 없는 거 같은딩...?!

"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"cypress": "^12.17.4",
Expand Down Expand Up @@ -74,6 +75,7 @@
"stylelint-order": "^6.0.3",
"typescript": "^5.1.6",
"webpack": "^5.88.1",
"webpack-bundle-analyzer": "^4.9.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0"
Expand Down
File renamed without changes.
File renamed without changes
1 change: 1 addition & 0 deletions client/public/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions client/src/components/CafeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,19 @@ const CafeCard = (props: CardProps) => {
{`${currentImageIndex + 1}`}/{cafe.images.length}
</CardQuantityContents>
</CardQuantityContainer>
<CarouselImageList ref={ref}>
<CarouselImageList data-testid="cafe-card" ref={ref}>
{cafe.images.map((image, index) => (
<CarouselImage key={index} src={Image.getUrl({ size: '500', filename: image })} alt={`${cafe}의 이미지`} />
<CarouselImage
data-testid="carousel-image"
key={index}
src={Image.getUrl({ size: '500', filename: image })}
alt={`${cafe}의 이미지`}
/>
))}
</CarouselImageList>
<DotsContainer>
<DotsContainer data-testid="carousel-dots">
{cafe.images.map((_, index) => (
<Dot key={index} $active={index === currentImageIndex} />
<Dot data-testid="dot" key={index} $active={index === currentImageIndex} />
))}
</DotsContainer>
<CafeSummary
Expand Down
10 changes: 5 additions & 5 deletions client/src/components/CafeDetailBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const CafeDetailBottomSheet = (props: CafeDetailBottomSheetProps) => {

return (
<Container onClick={handlePreventClickPropagation} role="dialog" aria-modal="true">
<CloseButton>
<CloseButton data-testid="close-button">
<CloseIcon onClick={onClose} />
</CloseButton>
<Title>{cafe.name}</Title>
<Title data-testid="cafe-name">{cafe.name}</Title>
<Spacer $size={'4'} />
<Suspense>
<CafeMenu cafeId={cafe.id} />
Expand All @@ -39,12 +39,12 @@ const CafeDetailBottomSheet = (props: CafeDetailBottomSheetProps) => {
<LocationDetail>
<BsGeoAlt />
<a href={cafe.detail.mapUrl} target="_blank" rel="noopener noreferrer">
<LocationName>
<LocationName data-testid="cafe-address">
{cafe.address} <BsBoxArrowUpRight />
</LocationName>
</a>
</LocationDetail>
<OpeningHoursDetails>
<OpeningHoursDetails data-testid="cafe-openingHours">
<OpeningHoursDetail openingHours={cafe.detail.openingHours} />
</OpeningHoursDetails>
{/* <Info>
Expand All @@ -53,7 +53,7 @@ const CafeDetailBottomSheet = (props: CafeDetailBottomSheetProps) => {
</Info> */}
</InfoContainer>
<Spacer $size={'6'} />
<Content>
<Content data-testid="cafe-description">
{cafe.detail.description.split('\n').map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/CafeMenuBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const CafeMenuBottomSheet = (props: CafeMenuBottomSheetProps) => {
return (
<>
{createPortal(
<Container onClick={handlePreventClickPropagation}>
<CloseButton>
<Container data-testid="menuBottomSheet" onClick={handlePreventClickPropagation}>
<CloseButton data-testid="close-button">
<CloseIcon onClick={onClose} />
</CloseButton>

Expand All @@ -61,7 +61,7 @@ const CafeMenuBottomSheet = (props: CafeMenuBottomSheetProps) => {

{otherMenus.length > 0 && (
<>
<CafeMenuListTitle>메뉴</CafeMenuListTitle>
<CafeMenuListTitle data-testid="menu">메뉴</CafeMenuListTitle>
<CafeMenuList menus={otherMenus} />
<Spacer $size={'8'} />
</>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/CafeMenuList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const CafeMenuList = (props: CafeMenuListProps) => {
const otherMenus = menus.filter((menuItem) => !menuItem.isRecommended);

return (
<MenuList>
<MenuList data-testid="menuItem">
{recommendedMenus.map((menuItem) => (
<MenuListItem key={menuItem.id} menuItem={menuItem} />
))}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/LikeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const LikeButton = (props: LikeButtonProps) => {
};

return (
<Container aria-label="좋아요 버튼" tabIndex={0} role="button">
<Container data-testid="likeButton" aria-label="좋아요 버튼" tabIndex={0} role="button">
<HeartIcon $active={active} onClick={handleLikeClick} />
<LikeCount>{likeCount}</LikeCount>
<Announcement aria-live="assertive" aria-atomic="true" aria-relevant="text">
Expand Down
7 changes: 5 additions & 2 deletions client/src/components/LoginModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { styled } from 'styled-components';
import useAuthUrls from '../hooks/useAuthUrls';
import type { Theme } from '../styles/theme';
import LoginButton from './LoginButton';
import Logo from './Logo';

const brandColors: Record<string, keyof Theme['color']> = {
kakao: 'yellow',
Expand All @@ -30,7 +29,7 @@ const LoginModal = (props: ModalProps) => {
<CloseButtonContainer aria-label="닫기 버튼" role="dialog" aria-modal="true" aria-hidden="true">
<CloseIcon onClick={onClose} />
</CloseButtonContainer>
<Logo fontSize="5xl" />
<Logo />
<LoginTitle>간편 로그인</LoginTitle>
<ButtonContainer>
{urls.map(({ provider, authorizationUrl }) => (
Expand Down Expand Up @@ -129,3 +128,7 @@ const ButtonContainer = styled.section`
justify-content: space-evenly;
width: 100%;
`;

const Logo = styled.img.attrs({ src: '/assets/logo.svg' })`
height: ${({ theme }) => theme.fontSize['5xl']};
`;
17 changes: 0 additions & 17 deletions client/src/components/Logo.stories.tsx

This file was deleted.

Loading