Skip to content

Commit

Permalink
Merge pull request #318 from SCBJ-7/feature/#317
Browse files Browse the repository at this point in the history
[#317] 로그인/회원가입 e2e 테스트
  • Loading branch information
im-na0 authored Jan 29, 2024
2 parents 041055a + 1ddb9a0 commit 773b5b0
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ dist-ssr
/playwright/.cache/

.eslintcache
.env
.env
auth.json
40 changes: 30 additions & 10 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,50 @@ export default defineConfig({
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
globalSetup: "./tests/e2eTests/global.setup.ts",
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',

baseURL: "http://localhost:5173/",
storageState: "./tests/e2eTests/auth.json",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},

/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
name: "setup",
testMatch: /global\.setup\.ts/,
},

{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
name: "logged in chromium",
dependencies: ["setup"],
use: {
...devices["Desktop Chrome"],
storageState: "./tests/e2eTests/auth.json",
},
testIgnore: ["**/authentication/**/*.ts"],
},

{
name: "webkit",
use: { ...devices["Desktop Safari"] },
name: "authentication",
use: { ...devices["Desktop Chrome"] },
testMatch: "**/authentication/**/*.ts",
testIgnore: ["**/global.setup.ts"],
},
// {
// name: "chromium",
// use: { ...devices["Desktop Chrome"] },
// },
//
// {
// name: "firefox",
// use: { ...devices["Desktop Firefox"] },
// },
//
// {
// name: "webkit",
// use: { ...devices["Desktop Safari"] },
// },

/* Test against mobile viewports. */
// {
Expand Down
8 changes: 0 additions & 8 deletions tests/e2eTests/example.spec.ts

This file was deleted.

23 changes: 23 additions & 0 deletions tests/e2eTests/global.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { chromium, FullConfig } from "@playwright/test";
import { LoginPage } from "@tests/e2eTests/models/LoginPage.ts";

async function globalSetup(config: FullConfig) {
const { storageState } = config.projects[1].use;
const browser = await chromium.launch();
const page = await browser.newPage();

const loginPage = new LoginPage(page);
await loginPage.navigate();
console.log("로그인 시도");
await loginPage.login("[email protected]", "asdf1234!");
const accessToken = await loginPage.getAccessToken();
const refreshToken = await loginPage.getRefreshToken();
// const fcmToken = await loginPage.getFcmToken();
console.log("accessToken", accessToken);
console.log("refreshToken", refreshToken);
// console.log("fcmToken", fcmToken);
await page.context().storageState({ path: storageState as string });
await browser.close();
}

export default globalSetup;
41 changes: 41 additions & 0 deletions tests/e2eTests/models/LoginPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Page } from "@playwright/test";

export class LoginPage {
page: Page;

constructor(page: Page) {
this.page = page;
}

async navigate(): Promise<void> {
await this.page.goto("http://localhost:5173/signin");
}

async login(email: string, password: string): Promise<void> {
const navigationPromise = this.page.waitForNavigation();
await this.page.fill('input[name="email"]', email);
await this.page.fill('input[name="password"]', password);
await this.page.click('button:has-text("로그인")');

await navigationPromise;
}

async getAccessToken(): Promise<string | null> {
await this.page.waitForLoadState("networkidle");
return this.page.evaluate(() => {
return localStorage.getItem("accessToken");
});
}
async getRefreshToken(): Promise<string | null> {
await this.page.waitForLoadState("networkidle");
return this.page.evaluate(() => {
return localStorage.getItem("refreshToken");
});
}
// async getFcmToken(): Promise<string | null> {
// await this.page.waitForLoadState("networkidle");
// return this.page.evaluate(() => {
// return localStorage.getItem("fcmToken");
// });
// }
}
73 changes: 73 additions & 0 deletions tests/e2eTests/models/SignupPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Page } from "@playwright/test";

export class Signup {
page: Page;

constructor(page: Page) {
this.page = page;
}

async navigate(): Promise<void> {
await this.page.goto("http://localhost:5173/signup");
}

async fillSignupForm(
email: string,
password: string,
checkPassword: string,
name: string,
phone: string,
): Promise<void> {
await this.page.fill('input[name="email"]', email);
await this.page.fill('input[name="password"]', password);
await this.page.fill('input[name="checkPassword"]', checkPassword);
await this.page.fill('input[name="name"]', name);
await this.page.fill('input[name="phone"]', phone);
}

async fillVerificationCode(code: string): Promise<void> {
await this.page.fill('input[name="code"]', code);
}

async clickVerificationCode(): Promise<void> {
await this.page.click('button:has-text("인증 확인")');
}

async submitSignup(): Promise<void> {
const navigationPromise = this.page.waitForNavigation();
await this.page.click('button:has-text("가입하기")');
await navigationPromise;
}

async clickAllAgreeCheckbox(): Promise<void> {
const checkboxLocator = this.page
.locator("label")
.filter({ hasText: "전체동의" })
.locator("span")
.first();

await checkboxLocator.click();
}

async getVerifyEmail(): Promise<string | null> {
await this.page.click('button:has-text("인증 요청")');

// await this.page.waitForLoadState("networkidle");

const response = await this.page.waitForResponse(
(response) =>
response.url() === "https://3.34.147.187.nip.io/v1/members/email" &&
response.status() === 200,
);

const responseData = await response.json();
console.log(responseData);
return responseData.data;
}
// async getFcmToken(): Promise<string | null> {
// await this.page.waitForLoadState("networkidle");
// return this.page.evaluate(() => {
// return localStorage.getItem("fcmToken");
// });
// }
}
22 changes: 22 additions & 0 deletions tests/e2eTests/specs/authentication/login.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { test, expect } from "@playwright/test";

test.use({ storageState: { cookies: [], origins: [] } });

test("로그인 성공", async ({ page }) => {
await page.goto("http://localhost:5173/signin");

await page.fill('input[name="email"]', "[email protected]");
await page.fill('input[name="password"]', "asdf1234!");

await page.click('button:has-text("로그인")');

await page.waitForNavigation();

const localStorageValue = await page.evaluate(() => {
return localStorage.getItem("accessToken");
});

expect(typeof localStorageValue).toBe("string");

expect(page.url()).toBe("http://localhost:5173/");
});
35 changes: 35 additions & 0 deletions tests/e2eTests/specs/authentication/register.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { test } from "@playwright/test";
import { Signup } from "@tests/e2eTests/models/SignupPage";

test.use({ storageState: { cookies: [], origins: [] } });

test("회원가입 성공", async ({ page }) => {
const signup = new Signup(page);
const userEmail = `test${Math.floor(Math.random() * 10000)}@example.com`;
const userPassword = "asdf1234!";
const userName = "집주인";
const userPhone = "010-1234-5678";

await signup.navigate();

await signup.fillSignupForm(
userEmail,
userPassword,
userPassword,
userName,
userPhone,
);

const verificationCode = await signup.getVerifyEmail();
if (!verificationCode) {
throw new Error("인증 이메일을 받지 못했습니다.");
}

await signup.fillVerificationCode(verificationCode);

await signup.clickVerificationCode();

await signup.clickAllAgreeCheckbox();

await signup.submitSignup();
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getEmailVerification } from "@mocks/handlers/email";
import { server } from "@mocks/server";
import VerificationPage from "@pages/connectYanoljaPage/verificationPage/VerificationPage";
import { theme } from "@styles/theme";
import { getEmailVerification } from "@mocks/handlers/email.ts";
import { server } from "@mocks/server.ts";
import VerificationPage from "@pages/connectYanoljaPage/verificationPage/VerificationPage.tsx";
import { theme } from "@styles/theme.ts";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { getEmailVerification } from "@mocks/handlers/email";
import { server } from "@mocks/server";
import { useValidateEmailMutation } from "@hooks/api/useValidateEmailMutation.ts";
import { getEmailVerification } from "@mocks/handlers/email.ts";
import { server } from "@mocks/server.ts";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { act, renderHook, waitFor } from "@testing-library/react";

import { useValidateEmailMutation } from "@/hooks/api/useValidateEmailMutation";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"@routes/*": ["./src/routes/*"],
"@utils/*": ["./src/utils/*"],
"@type/*": ["./src/types/*"],
"@store/*": ["./src/store/*"]
"@store/*": ["./src/store/*"],
"@tests/*": ["./tests/*"],
}
},
"include": ["src", "tests"],
Expand Down
10 changes: 6 additions & 4 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/// <reference types="vitest" />

import react from '@vitejs/plugin-react'
import path from 'path'
import {defineConfig} from 'vite'
import svgr from 'vite-plugin-svgr'
import path from "path";

import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import svgr from "vite-plugin-svgr";

// https://vitejs.dev/config/
export default defineConfig({
Expand All @@ -29,6 +30,7 @@ export default defineConfig({
"@utils": path.resolve(__dirname, "./src/utils"),
"@type": path.resolve(__dirname, "./src/types"),
"@store": path.resolve(__dirname, "./src/store"),
"@tests": path.resolve(__dirname, "./tests"),
},
},
});

0 comments on commit 773b5b0

Please sign in to comment.