├── .eslintrc.js
├── .gitignore
├── README.md
├── __mocks__
└── react-redux.js
├── babel.config.js
├── fixtures
└── tasks.js
├── index.html
├── jest.config.js
├── jest.setup.js
├── package-lock.json
├── package.json
├── src
├── App.jsx
├── App.test.jsx
├── actions.js
├── actions.test.js
├── index.jsx
├── reducer.js
├── reducer.test.js
├── services
│ ├── __mocks__
│ │ └── api.js
│ └── api.js
└── store.js
└── webpack.config.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | jest: true,
6 | },
7 | extends: [
8 | 'plugin:react/recommended',
9 | 'airbnb',
10 | ],
11 | globals: {
12 | Atomics: 'readonly',
13 | SharedArrayBuffer: 'readonly',
14 | Feature: 'readonly',
15 | Scenario: 'readonly',
16 | context: 'readonly',
17 | },
18 | parserOptions: {
19 | ecmaFeatures: {
20 | jsx: true,
21 | },
22 | ecmaVersion: 12,
23 | sourceType: 'module',
24 | },
25 | plugins: [
26 | 'react',
27 | ],
28 | rules: {
29 | indent: ['error', 2],
30 | 'no-trailing-spaces': 'error',
31 | curly: 'error',
32 | 'brace-style': 'error',
33 | 'no-multi-spaces': 'error',
34 | 'space-infix-ops': 'error',
35 | 'space-unary-ops': 'error',
36 | 'no-whitespace-before-property': 'error',
37 | 'func-call-spacing': 'error',
38 | 'space-before-blocks': 'error',
39 | 'keyword-spacing': ['error', { before: true, after: true }],
40 | 'comma-spacing': ['error', { before: false, after: true }],
41 | 'comma-style': ['error', 'last'],
42 | 'comma-dangle': ['error', 'always-multiline'],
43 | 'space-in-parens': ['error', 'never'],
44 | 'block-spacing': 'error',
45 | 'array-bracket-spacing': ['error', 'never'],
46 | 'object-curly-spacing': ['error', 'always'],
47 | 'key-spacing': ['error', { mode: 'strict' }],
48 | 'arrow-spacing': ['error', { before: true, after: true }],
49 | 'jsx-a11y/label-has-associated-control': ['error', { assert: 'either' }],
50 | 'react/prop-types': 'off',
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # frontend-tdd-feconf2020
2 |
3 | FEConf2020 Frontend TDD로 개발하기 예제.
4 |
5 | - 라이브 코딩 시작할 때 코드: https://github.com/1mptera/frontend-tdd-feconf2020
6 | - 완성된 코드: https://github.com/1mptera/frontend-tdd-feconf2020/tree/todo-app
7 |
--------------------------------------------------------------------------------
/__mocks__/react-redux.js:
--------------------------------------------------------------------------------
1 | export const useDispatch = jest.fn();
2 |
3 | export const useSelector = jest.fn();
4 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | '@babel/preset-react',
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/fixtures/tasks.js:
--------------------------------------------------------------------------------
1 | const tasks = [
2 | { id: 1, title: '아무 일도 하기 싫다' },
3 | { id: 2, title: '건물 매입' },
4 | ];
5 |
6 | export default tasks;
7 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | setupFilesAfterEnv: [
3 | 'jest-plugin-context/setup',
4 | './jest.setup',
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend-tdd-feconf2020",
3 | "version": "1.0.0",
4 | "description": "FEConf2020 Frontend TDD로 개발하기 예제.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack serve",
8 | "test": "jest",
9 | "watch": "jest --watchAll",
10 | "lint": "eslint --fix --ext js,jsx ."
11 | },
12 | "author": "wholemann",
13 | "dependencies": {
14 | "axios": "^0.20.0",
15 | "react": "^16.13.1",
16 | "react-dom": "^16.13.1",
17 | "react-redux": "^7.2.1",
18 | "redux": "^4.0.5",
19 | "redux-thunk": "^2.3.0"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.11.6",
23 | "@babel/preset-env": "^7.11.5",
24 | "@babel/preset-react": "^7.10.4",
25 | "@testing-library/jest-dom": "^5.11.4",
26 | "@testing-library/react": "^11.1.0",
27 | "@types/jest": "^26.0.14",
28 | "babel-jest": "^26.5.2",
29 | "babel-loader": "^8.1.0",
30 | "eslint": "^7.11.0",
31 | "jest": "^26.5.3",
32 | "jest-plugin-context": "^2.9.0",
33 | "redux-mock-store": "^1.5.4",
34 | "webpack": "^5.1.0",
35 | "webpack-cli": "^4.0.0",
36 | "webpack-dev-server": "^3.11.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function App() {
4 | return (
5 |
6 |
To-do
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/App.test.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from '@testing-library/react';
4 |
5 | import App from './App';
6 |
--------------------------------------------------------------------------------
/src/actions.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/megaptera-kr/frontend-tdd-feconf2020/7809107e39e2445a12ad3ecc1e3447299b0623a0/src/actions.js
--------------------------------------------------------------------------------
/src/actions.test.js:
--------------------------------------------------------------------------------
1 | import configureStore from 'redux-mock-store';
2 |
3 | import thunk from 'redux-thunk';
4 |
5 | import { fetchTasks } from './services/api';
6 |
7 | import {
8 | loadTasks,
9 | } from './actions';
10 |
11 | jest.mock('./services/api');
12 |
13 | const middlewares = [thunk];
14 | const mockStore = configureStore(middlewares);
15 |
16 | describe('loadTasks', () => {
17 | it('', () => {
18 |
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import { Provider } from 'react-redux';
5 |
6 | import App from './App';
7 |
8 | import store from './store';
9 |
10 | ReactDOM.render(
11 | (
12 |
13 |
14 |
15 | ),
16 | document.getElementById('app'),
17 | );
18 |
--------------------------------------------------------------------------------
/src/reducer.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | };
3 |
4 | export default function reducer(state = initialState, action) {
5 | }
6 |
--------------------------------------------------------------------------------
/src/reducer.test.js:
--------------------------------------------------------------------------------
1 | import reducer from './reducer';
2 |
3 | describe('reducer', () => {
4 | });
5 |
--------------------------------------------------------------------------------
/src/services/__mocks__/api.js:
--------------------------------------------------------------------------------
1 | export const fetchTasks = jest.fn(async () => []);
2 |
3 | export default {};
4 |
--------------------------------------------------------------------------------
/src/services/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const TASKS_URL = 'https://jsonplaceholder.typicode.com/todos';
4 |
5 | export async function fetchTasks() {
6 | const { data } = await axios.get(TASKS_URL);
7 | return data;
8 | }
9 |
10 | export default {};
11 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 |
3 | import thunk from 'redux-thunk';
4 |
5 | import reducer from './reducer';
6 |
7 | const store = createStore(reducer, applyMiddleware(thunk));
8 |
9 | export default store;
10 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: path.resolve(__dirname, 'src/index.jsx'),
5 | module: {
6 | rules: [
7 | {
8 | test: /\.jsx?$/,
9 | exclude: /node_modules/,
10 | use: 'babel-loader',
11 | },
12 | ],
13 | },
14 | resolve: {
15 | extensions: ['.js', '.jsx'],
16 | },
17 | };
18 |
--------------------------------------------------------------------------------