Skip to content

Feature: E2E 테스트 #154

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Playwright Tests
on:
workflow_run:
workflows:
- CICD Web
types:
- completed


jobs:
test:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
WEB_URL: ${{ github.event.workflow_run.outputs.deployment_url }}
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: pnpm
- name: pnpm install
run: pnpm install --frozen-lockfile --prefer-offline

- name: setting environment variables
run: |
echo "${{ secrets.SHARED_ENV_FILE }}" >> packages/shared/.env
echo WEB_URL=${{ env.WEB_URL }} >> packages/shared/.env
Comment on lines +28 to +30
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

환경 변수 파일 생성 방식 개선 필요

환경 변수를 파일에 직접 추가하는 방식은 보안상 위험할 수 있습니다. GitHub Actions의 secrets 기능을 더 안전하게 활용하는 방법을 고려해보세요.

-      - name: setting environment variables
-        run: |
-          echo "${{ secrets.SHARED_ENV_FILE }}" >> packages/shared/.env
-          echo WEB_URL=${{ env.WEB_URL }} >> packages/shared/.env
+      - name: Create environment file
+        uses: SpicyPizza/[email protected]
+        with:
+          envkey_WEB_URL: ${{ env.WEB_URL }}
+          file_name: packages/shared/.env
+          fail_on_empty: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run: |
echo "${{ secrets.SHARED_ENV_FILE }}" >> packages/shared/.env
echo WEB_URL=${{ env.WEB_URL }} >> packages/shared/.env
- name: Create environment file
uses: SpicyPizza/[email protected]
with:
envkey_WEB_URL: ${{ env.WEB_URL }}
file_name: packages/shared/.env
fail_on_empty: true


echo "${{ secrets.WEB_ENV_FILE }}" > packages/web/.env
echo NEXT_PUBLIC_WEB_URL=${{ env.WEB_URL }} >> packages/web/.env

- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: |
turbo telemetry disable
pnpm run test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
1 change: 0 additions & 1 deletion .github/workflows/cicd-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: CICD Extension

on:
- push
- pull_request

jobs:
build-extension:
Expand Down
26 changes: 19 additions & 7 deletions .github/workflows/cicd-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ name: CICD Web

on:
- push
- pull_request

jobs:
build-web:
runs-on: ubuntu-22.04
outputs:
deployment_url: ${{ steps.deploy.outputs.url || steps.deploy-staging.outputs.url || steps.deploy-prod.outputs.url }}
env:
WEB_URL: "${{ github.ref == 'refs/heads/develop' && secrets.STAGING_WEB_URL || secrets.PROD_WEB_URL }}"
steps:
Expand All @@ -32,14 +33,25 @@ jobs:
- name: Vercel Build
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy staging when push on develop branch
- name: Deploy development
if: ${{ github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/master' }}
id: deploy
run: |
VERCEL_DEPLOYED_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
echo "url=$VERCEL_DEPLOYED_URL" >> "$GITHUB_OUTPUT"

- name: Deploy staging
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop'}}
id: deploy-staging
run: |
VERCEL_DEPOLYED_URL="$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})"
vercel alias set "$VERCEL_DEPOLYED_URL" ${{secrets.STAGING_WEB_URL_WITHOUT_PROTOCOL}} --token=${{ secrets.VERCEL_TOKEN }} --scope=gueit214s-projects
VERCEL_DEPLOYED_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
vercel alias set "$VERCEL_DEPLOYED_URL" ${{secrets.STAGING_WEB_URL_WITHOUT_PROTOCOL}} --token=${{ secrets.VERCEL_TOKEN }} --scope=gueit214s-projects
echo "url=$VERCEL_DEPLOYED_URL" >> "$GITHUB_OUTPUT"

- name: Deploy production when push on master branch
- name: Deploy production
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master'}}
id: deploy-prod
run: |
VERCEL_DEPOLYED_URL="$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})"
vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
VERCEL_DEPLOYED_URL=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
vercel alias set "$VERCEL_DEPLOYED_URL" ${{secrets.PROD_WEB_URL_WITHOUT_PROTOCOL}} --token=${{ secrets.VERCEL_TOKEN }} --scope=gueit214s-projects
echo "url=$VERCEL_DEPLOYED_URL" >> "$GITHUB_OUTPUT"
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use server';

import { SUPABASE } from '@extension/shared/constants';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

테스트 계정 정보의 보안 처리가 필요합니다

SUPABASE 상수에서 테스트 계정 정보를 직접 가져오는 것은 보안상 위험할 수 있습니다. 다음 사항들을 고려해보세요:

  • 환경 변수 사용
  • 테스트 환경에서만 사용 가능하도록 제한
  • 프로덕션 빌드에서 제외

Also applies to: 7-7

import { Button } from '@src/components/ui';
import type { LanguageType } from '@src/modules/i18n';
import useTranslation from '@src/modules/i18n/util.server';
import { signInWithOAuth } from '@src/modules/supabase/util.server';
import { signInWithEmail, signInWithOAuth } from '@src/modules/supabase/util.server';
import Image from 'next/image';

interface LoginSectionProps extends LanguageType {}
Expand All @@ -19,21 +20,26 @@ export default async function LoginSection({ lng }: LoginSectionProps) {
<form className="flex w-full flex-col gap-4 px-4">
{/* 'use server' 함수를 사용하기 위해 bind 사용 */}
<Button
data-testid="kakao-login-button"
formAction={signInWithOAuth.bind(null, 'kakao')}
className="h-12 bg-[rgb(247,228,76)] text-black hover:bg-[rgb(247,228,76)]">
<Image src="/images/svgs/kakao.svg" width={16} height={16} alt="kakao" />
{t('login.kakaoLogin')}
</Button>
{/* 'use server' 함수를 사용하기 위해 bind 사용 */}
<Button formAction={signInWithOAuth.bind(null, 'google')} className="h-12 bg-white text-black hover:bg-white">
<Button
data-testid="google-login-button"
formAction={signInWithOAuth.bind(null, 'google')}
className="h-12 bg-white text-black hover:bg-white">
<Image src="/images/svgs/google.svg" width={16} height={16} alt="google" />
{t('login.googleLogin')}
</Button>
{/* <Button
<Button
data-testid="test-login-button"
formAction={signInWithEmail.bind(null, SUPABASE.testEmail, SUPABASE.testPassword)}
className="h-12 bg-green-300 text-black hover:bg-green-300">
테스트 계정으로 로그인
</Button> */}
{t('login.testLogin')}
</Button>
Comment on lines +37 to +42
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

테스트 계정 로그인 버튼의 환경별 표시 여부 제어가 필요합니다

테스트 계정 로그인 버튼은 개발 환경에서만 표시되어야 합니다. 다음과 같은 조건부 렌더링을 추가하는 것이 좋습니다:

+const isDevelopment = process.env.NODE_ENV === 'development';

// ...

+{isDevelopment && (
  <Button
    data-testid="test-login-button"
    formAction={signInWithEmail.bind(null, SUPABASE.testEmail, SUPABASE.testPassword)}
    className="h-12 bg-green-300 text-black hover:bg-green-300">
    {t('login.testLogin')}
  </Button>
+)}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
data-testid="test-login-button"
formAction={signInWithEmail.bind(null, SUPABASE.testEmail, SUPABASE.testPassword)}
className="h-12 bg-green-300 text-black hover:bg-green-300">
테스트 계정으로 로그인
</Button> */}
{t('login.testLogin')}
</Button>
const isDevelopment = process.env.NODE_ENV === 'development';
{isDevelopment && (
<Button
data-testid="test-login-button"
formAction={signInWithEmail.bind(null, SUPABASE.testEmail, SUPABASE.testPassword)}
className="h-12 bg-green-300 text-black hover:bg-green-300">
{t('login.testLogin')}
</Button>
)}

</form>
</section>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/web/src/modules/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"welcomeDescription": "Let's make memo-taking easier and more convenient together!",
"kakaoLogin": "Continue with Kakao",
"googleLogin": "Continue with Google",
"testLogin": "Continue with Test Account",
"askGoInstallExtension": "Looks like you haven't installed our extension yet. You'll need it to use all the great memo features. Would you like to install it now?",
"personalInformationInfo": "Don't worry! Your personal information is strictly protected and will never be used for marketing purposes."
},
Expand Down Expand Up @@ -180,8 +181,8 @@
"description": "Ready to start? Press '{{key}} + S' to open the side panel"
},
"save": {
"title": "Save memos",
"description": "Great! Now you can write memos. Don't worry, they save automatically"
"title": "Save Your Memos",
"description": "Great job!\nNow you can write memos in the side panel.\nDon't worry, memos are saved automatically"
},
"check": {
"title": "Check memos",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"welcomeDescription": "웹 메모와 함께 더 쉽고 편리하게 메모를 관리해보세요.",
"kakaoLogin": "카카오 계정으로 시작하기",
"googleLogin": "구글 계정으로 시작하기",
"testLogin": "테스트 계정으로 로그인하기",
"askGoInstallExtension": "메모 기능을 사용하시려면 확장 프로그램이 필요해요. 지금 설치하러 가시겠어요?",
"personalInformationInfo": "고객님의 소중한 개인정보는 안전하게 보호되며, 마케팅 목적으로는 절대 사용되지 않아요."
},
Expand Down
1 change: 1 addition & 0 deletions pages/content-ui/src/components/OpenSidePanelButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ExtensionBridge } from '@extension/shared/modules/extension-bridge';
export default function OpenSidePanelButton() {
return (
<button
id="open-side-panel"
className="fixed bottom-4 right-4 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-white shadow-lg hover:bg-gray-50"
type="button"
onClick={ExtensionBridge.requestOpenSidePanel}>
Expand Down
2 changes: 0 additions & 2 deletions pages/content-ui/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { ExtensionBridge } from '@extension/shared/modules/extension-bridge';
import { isProduction } from '@extension/shared/utils';
import { createRoot } from 'react-dom/client';

import { OpenSidePanelButton } from './components';

import { attachShadowTree } from './utils';

const renderOpenSidePanelButton = async () => {
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/LoginPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ test.describe('Login Page', () => {
expect(page).toHaveURL(REGEXR.page.login);
});
test('카카오 로그인 버튼을 클릭하면 카카오 로그인 페이지로 이동한다.', async ({ page }) => {
await page.getByRole('button', { name: 'kakao 카카오 로그인' }).click();
await page.getByTestId('kakao-login-button').click();

await page.waitForURL(REGEXR.page.kakaoLogin);

expect(page).toHaveURL(REGEXR.page.kakaoLogin);
});
test('구글 로그인 버튼을 클릭하면 구글 로그인 페이지로 이동한다.', async ({ page }) => {
await page.getByRole('button', { name: 'google 구글 로그인' }).click();
await page.getByTestId('google-login-button').click();

await page.waitForURL(REGEXR.page.googleLogin);

expect(page).toHaveURL(REGEXR.page.googleLogin);
});
test('테스트 계정으로 로그인 시, memos페이지로 이동한다.', async ({ page }) => {
await page.getByRole('button', { name: '테스트 계정으로 로그인' }).click();
await page.getByTestId('test-login-button').click();

await page.waitForURL(REGEXR.page.memos);

Expand Down
12 changes: 7 additions & 5 deletions tests/e2e/MemosPage.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { expect, test } from './fixtures';

test.describe('Login Page', () => {
test.describe('Memo Page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000');
await page.getByRole('button', { name: '테스트 계정으로 로그인' }).click();
await page.getByTestId('test-login-button').click();
await page.waitForURL(/.*memos/);
});

test.describe('가이드 기능', () => {
test('메모 페이지 최초 접속시, 가이드를 볼 수 있다.', async ({ page }) => {
expect(page.locator('#driver-popover-description')).toHaveText(
'메모를 한 번 해볼까요?\nOption + S를 눌러 사이드 패널을 열어보세요 !',
"Ready to start? Press 'Option + S' to open the side panel",
);
Comment on lines +13 to 14
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

가이드 텍스트의 국제화 처리가 필요합니다

하드코딩된 영어 텍스트를 i18n 시스템을 통해 관리하는 것이 좋습니다. 이는 다국어 지원을 용이하게 만들 것입니다.

});

test('사이드 패널을 열 시, 다음 가이드 페이지로 이동한다.', async ({ page, baseURL }) => {
await page.goto('https://blog.toss.im/article/toss-team-culture');
await page.locator('#open-side-panel').click();
Expand All @@ -21,9 +22,10 @@ test.describe('Login Page', () => {
await page.waitForTimeout(1000);

expect(page.locator('#driver-popover-description')).toContainText(
'이제 이 사이드 패널에서 메모를 기록하실 수 있답니다.',
"Great job!\nNow you can write memos in the side panel.\nDon't worry, memos are saved automatically",
);
});

test('새로고침 버튼을 누르면, 가이드가 종료된다.', async ({ page }) => {
await page.locator('.driver-popover-next-btn').click();
await page.locator('.driver-popover-next-btn').click();
Expand All @@ -43,7 +45,7 @@ test.describe('Login Page', () => {
test('사이드 패널에서 메모를 저장하면 메모를 확인할 수 있다.', async ({ page, sidePanelPage }) => {
const text = String(new Date());
await sidePanelPage.locator('#memo-textarea').fill(text);
await sidePanelPage.locator('#memo-textarea').press('ControlOrMeta+s');
await sidePanelPage.waitForTimeout(500);

await page.reload();
expect(page.getByText(text)).toBeVisible();
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/SidePanel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect, test } from './fixtures';
test.describe('SidePanel', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000');
await page.getByRole('button', { name: '테스트 계정으로 로그인' }).click();
await page.getByTestId('test-login-button').click();
await page.waitForURL(/.*memos/);

await page.goto('https://blog.toss.im/article/toss-team-culture');
Expand Down