Skip to content

Commit

Permalink
Merge pull request #420 from woowacourse-teams/develop-frontend
Browse files Browse the repository at this point in the history
프론트엔드 브랜치 병합
  • Loading branch information
jaeml06 authored Aug 21, 2024
2 parents 5ee80e6 + b44ca11 commit 006f8b7
Show file tree
Hide file tree
Showing 472 changed files with 26,566 additions and 5,927 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/cd-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: frontend-deploy

on:
push:
branches:
- develop-frontend

jobs:
deploy:
runs-on: self-hosted

steps:
- name: deploy
run: |
cd ~/deploy && ./deploy-fe.sh
46 changes: 46 additions & 0 deletions .github/workflows/ci-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: frontend-integration

on:
pull_request:
branches:
- develop-frontend

jobs:
test:
runs-on: ubuntu-latest

defaults:
run:
shell: bash
working-directory: ./frontend

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

- name: Check Caching
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '20.15.0'

- name: Install Dependencies
run: npm install --frozen-lockfile

- name: Create .env file
run: |
echo "BASE_URL=${{ secrets.BASE_URL }}" > .env
echo "REACT_APP_GOOGLE_ANALYTICS=${{ secrets.REACT_APP_GOOGLE_ANALYTICS }}" >> .env
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
- name: Run tests
run: npm run test

- name: Run linter
run: npm run lint
5 changes: 3 additions & 2 deletions frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
'plugin:react-hooks/recommended',
'plugin:react/recommended',
'plugin:storybook/recommended',
'plugin:cypress/recommended',
'prettier',
],
ignorePatterns: [
Expand All @@ -18,14 +19,14 @@ module.exports = {
'webpack.prod.js',
],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh', '@emotion', "compat"],
plugins: ['react-refresh', '@emotion', 'compat'],
settings: {
react: {
version: 'detect',
},
},
rules: {
"compat/compat": "warn",
'compat/compat': 'warn',
'react/react-in-jsx-scope': 'off',
'react-refresh/only-export-components': [
'warn',
Expand Down
7 changes: 6 additions & 1 deletion frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
node_modules
dist
*storybook.log
*storybook.log
.env
.env*
coverage

public/firebaseConfig.js
59 changes: 59 additions & 0 deletions frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Configuration } from 'webpack';
import type { StorybookConfig } from '@storybook/react-webpack5';
import path from 'path';

const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
Expand Down Expand Up @@ -27,5 +29,62 @@ const config: StorybookConfig = {
},
},
}),
webpackFinal: async (config: Configuration) => {
const { resolve } = config;

if (resolve) {
resolve.alias = {
...resolve.alias,
'@_apis': path.resolve(__dirname, '../src/apis'),
'@_constants': path.resolve(__dirname, '../src/constants'),
'@_common': path.resolve(__dirname, '../src/common'),
'@_components': path.resolve(__dirname, '../src/components'),
'@_hooks': path.resolve(__dirname, '../src/hooks'),
'@_layouts': path.resolve(__dirname, '../src/layouts'),
'@_pages': path.resolve(__dirname, '../src/pages'),
'@_types': path.resolve(__dirname, '../src/types'),
'@_utils': path.resolve(__dirname, '../src/utils'),
'@_routes': path.resolve(__dirname, '../src/routes'),
'@_mocks': path.resolve(__dirname, '../src/mocks'),
'@_service': path.resolve(__dirname, '../src/service'),
};
}

config?.module?.rules?.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve('@emotion/babel-preset-css-prop')],
},
});
if (config.module?.rules) {
config.module = config.module || {};
config.module.rules = config.module.rules || [];

const imageRule = config.module.rules.find((rule) =>
rule?.['test']?.test('.svg'),
);
if (imageRule) {
imageRule['exclude'] = /\.svg$/;
}

config.module.rules.push({
test: /\.svg$/i,
oneOf: [
{
use: ['@svgr/webpack'],
issuer: /\.[jt]sx?$/,
resourceQuery: { not: [/url/] },
},
{
type: 'asset/resource',
resourceQuery: /url/,
},
],
});
}
return config;
},
};

export default config;
23 changes: 20 additions & 3 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import type { Preview } from '@storybook/react';
import React from 'react';
import reset from '../src/common/reset.style';
import { Global, ThemeProvider } from '@emotion/react';
import { theme } from '../src/common/theme/theme.style';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';

initialize();

const queryClient = new QueryClient();
const preview: Preview = {
parameters: {
controls: {
Expand All @@ -11,10 +20,18 @@ const preview: Preview = {
},
},
decorators: [
mswDecorator,
(Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<Global styles={reset} />
<BrowserRouter>
<div style={{ margin: '3em' }}>
<Story />
</div>
</BrowserRouter>
</ThemeProvider>
</QueryClientProvider>
),
],
};
Expand Down
7 changes: 7 additions & 0 deletions frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"stylelint.configFile": ".stylelintrc.json",
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "always"
},
"stylelint.validate": ["typescript", "typescriptreact"]
}
9 changes: 9 additions & 0 deletions frontend/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
37 changes: 37 additions & 0 deletions frontend/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
20 changes: 20 additions & 0 deletions frontend/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
25 changes: 25 additions & 0 deletions frontend/jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"preset": "ts-jest",
"testEnvironment": "jest-environment-jsdom",



"moduleNameMapper": {
"^@_apis/(.*)$": "<rootDir>/src/apis/$1",
"^@_constants/(.*)$": "<rootDir>/src/constants/$1",
"^@_common/(.*)$": "<rootDir>/src/common/$1",
"^@_components/(.*)$": "<rootDir>/src/components/$1",
"^@_hooks/(.*)$": "<rootDir>/src/hooks/$1",
"^@_layouts/(.*)$": "<rootDir>/src/layouts/$1",
"^@_pages/(.*)$": "<rootDir>/src/pages/$1",
"^@_types/(.*)$": "<rootDir>/src/types/$1",
"^@_utils/(.*)$": "<rootDir>/src/utils/$1",
"^@_routes/(.*)$": "<rootDir>/src/routes/$1",
"^@_mocks/(.*)$": "<rootDir>/src/mocks/$1"
},
"testEnvironmentOptions": {
"customExportConditions": [""]
},
"setupFiles": ["dotenv/config", "./jest.polyfills.js"],
"setupFilesAfterEnv": ["<rootDir>/jest.setup.ts"]
}
32 changes: 32 additions & 0 deletions frontend/jest.polyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @note The block below contains polyfills for Node.js globals
* required for Jest to function when running JSDOM tests.
* These HAVE to be require's and HAVE to be in this exact
* order, since "undici" depends on the "TextEncoder" global API.
*
* Consider migrating to a more modern test runner if
* you don't want to deal with this.
*/

const { TextDecoder, TextEncoder } = require('node:util');
const { ReadableStream, TransformStream } = require('node:stream/web');

Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
ReadableStream: { value: ReadableStream },
TransformStream: { value: TransformStream },
});

const { Blob, File } = require('node:buffer');
const { fetch, Headers, FormData, Request, Response } = require('undici');

Object.defineProperties(globalThis, {
fetch: { value: fetch, writable: true },
Blob: { value: Blob },
File: { value: File },
Headers: { value: Headers },
FormData: { value: FormData },
Request: { value: Request },
Response: { value: Response },
});
20 changes: 20 additions & 0 deletions frontend/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import '@testing-library/jest-dom';

import dotenv from 'dotenv';
import { server } from './src/mocks/server';

dotenv.config({ path: './.env' });

beforeAll(() => {
server.listen({
onUnhandledRequest: 'error', // 이 옵션을 통해 핸들되지 않은 요청을 경고로 표시
});
});

afterEach(() => {
server.resetHandlers();
});
// Clean up after the tests are finished.
afterAll(() => {
server.close();
});
Loading

0 comments on commit 006f8b7

Please sign in to comment.