epam-ai-conductor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/bin/workshop.js +6 -0
- package/dist/auth-check.d.ts +1 -0
- package/dist/auth-check.js +45 -0
- package/dist/auth-check.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +156 -0
- package/dist/cli.js.map +1 -0
- package/dist/content/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content-loader.d.ts +7 -0
- package/dist/content-loader.js +26 -0
- package/dist/content-loader.js.map +1 -0
- package/dist/context-builder.d.ts +2 -0
- package/dist/context-builder.js +116 -0
- package/dist/context-builder.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 1
|
|
3
|
+
sidebar_label: 'TASK'
|
|
4
|
+
title: 'Module 1. React Components'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Hello friend!
|
|
8
|
+
Let me say a few words what we are going to do with you.
|
|
9
|
+
We will write a small React application that allows users to manage a list of courses
|
|
10
|
+
with features like creating, deleting, and viewing course information.
|
|
11
|
+
Additionally, the application will have authorization functionality to ensure
|
|
12
|
+
secure access to the application's features.
|
|
13
|
+
|
|
14
|
+
The main goal of this task is to create app skeleton - set of
|
|
15
|
+
some simple components for the app.
|
|
16
|
+
|
|
17
|
+
Please, follow next steps:
|
|
18
|
+
|
|
19
|
+
## Steps:
|
|
20
|
+
|
|
21
|
+
1. Generate a new project:
|
|
22
|
+
|
|
23
|
+
```http request
|
|
24
|
+
npx create-react-app courses-app
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
:::note
|
|
28
|
+
You can use `git bash` to run the command.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
`courses-app` is a name of the project. You can give any name you want.
|
|
32
|
+
:::
|
|
33
|
+
|
|
34
|
+
2. Open you new project via code editor.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
3. Configure `eslint` and `prettier`.
|
|
38
|
+
See instruction in the [ESLINT instruction](eslint).
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
4. Configure `pre-commit hook`.
|
|
42
|
+
See instruction in the [PRE COMMIT instruction](pre_commit).
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
5. Run your project.
|
|
46
|
+
```http request
|
|
47
|
+
npm run start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Result in the browser:
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
:::tip
|
|
54
|
+
Now it's good time to push your INITIAL COMMIT to the repo and create a new branch for the first module.
|
|
55
|
+
:::
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Project structure requirements
|
|
59
|
+
|
|
60
|
+
1. You should use **function** components for your app.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
2. Rename `App.js` to `App.jsx` / `App.tsx`. `App` - main app file.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
3. Delete unnecessary files:
|
|
67
|
+
- `src/App.test.js`
|
|
68
|
+
- `src/logo.svg`
|
|
69
|
+
- `src/reportWebVitals.js`
|
|
70
|
+
- all files in the `public` folder EXCEPT `public/index.html` and `manifest.json`
|
|
71
|
+
|
|
72
|
+
4. Delete default code in files:
|
|
73
|
+
|
|
74
|
+
- `scr/App`
|
|
75
|
+
- `src/App.css`
|
|
76
|
+
|
|
77
|
+
You can do like this:
|
|
78
|
+
```js
|
|
79
|
+
function App() {
|
|
80
|
+
return <div>React</div>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default App;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Delete unnecessary import and function call in the `index.js` file
|
|
87
|
+
```js
|
|
88
|
+
import reportWebVitals from './reportWebVitals';
|
|
89
|
+
...
|
|
90
|
+
reportWebVitals();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Result in the browser:
|
|
94
|
+

|
|
95
|
+
|
|
96
|
+
:::tip
|
|
97
|
+
If you use `App.tsx` you need to fix its import in the `index.js` file
|
|
98
|
+
```js
|
|
99
|
+
import App from './App.tsx';
|
|
100
|
+
```
|
|
101
|
+
:::
|
|
102
|
+
|
|
103
|
+
5. Create folders and files for each component. Follow the architecture below:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
src
|
|
107
|
+
|-- common
|
|
108
|
+
| |--Button
|
|
109
|
+
| | |__ Button.jsx/.tsx
|
|
110
|
+
| |
|
|
111
|
+
| |__Input
|
|
112
|
+
| | |__ Input.jsx/.tsx
|
|
113
|
+
| | |
|
|
114
|
+
| |__ //any common components you want to add
|
|
115
|
+
|
|
|
116
|
+
|-- components
|
|
117
|
+
| |-- CourseInfo
|
|
118
|
+
| | |__ CourseInfo.jsx/.tsx
|
|
119
|
+
| |
|
|
120
|
+
| |-- Courses
|
|
121
|
+
| | |__ components
|
|
122
|
+
| | |__ CourseCard
|
|
123
|
+
| | |__ CourseCard.jsx/.tsx
|
|
124
|
+
| | |__ SearchBar (extra task)
|
|
125
|
+
| | | |__ SearchBar.jsx/.tsx
|
|
126
|
+
| | |__ Courses.jsx/.tsx
|
|
127
|
+
| |
|
|
128
|
+
| |__ // any components you want to add
|
|
129
|
+
| |
|
|
130
|
+
| |-- EmptyCourseList
|
|
131
|
+
| | |__ EmptyCourseList.jsx/.tsx
|
|
132
|
+
| |
|
|
133
|
+
| |-- Header
|
|
134
|
+
| | |__ Header.jsx/.tsx
|
|
135
|
+
| | |__ components
|
|
136
|
+
| | |__ Logo
|
|
137
|
+
| | |__ Logo.jsx/.tsx
|
|
138
|
+
|
|
|
139
|
+
|-- helpers
|
|
140
|
+
| |
|
|
141
|
+
| |--getCourseDuration.js/.ts // a helper to format course duration
|
|
142
|
+
| |
|
|
143
|
+
| |--formatCreationDate.js/.ts // to format date that we will receive from server
|
|
144
|
+
| |
|
|
145
|
+
| |__ // any helpers you want to add
|
|
146
|
+
|
|
|
147
|
+
|-- constants.js/.ts // file with mocked data
|
|
148
|
+
|
|
|
149
|
+
|-- App.jsx/.tsx
|
|
150
|
+
|-- App.css
|
|
151
|
+
|-- index.js
|
|
152
|
+
|_ ...
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
:::caution
|
|
157
|
+
The presented architecture is required for use, BUT you can add files and folders at your discretion.
|
|
158
|
+
:::
|
|
159
|
+
|
|
160
|
+
7. You can use any way to add styles to components (styled components, Bootstrap, CSS modules etc). Also, you can use preprocessors like SASS [instruction.](https://create-react-app.dev/docs/adding-a-sass-stylesheet/)
|
|
161
|
+
|
|
162
|
+
:::tip
|
|
163
|
+
It's recommended to keep each component's style file in their appropriate folders.
|
|
164
|
+
:::
|
|
165
|
+
|
|
166
|
+
8. You can choose the design at your discretion.
|
|
167
|
+
**It is important to keep the layout** (the arrangement of elements on the page).
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+

|
|
171
|
+
**App initial view**
|
|
172
|
+
|
|
173
|
+
## Final preparations:
|
|
174
|
+
1. Support browsers: Google Chrome.
|
|
175
|
+
2. Type checking: you can use TypeScript (it's not required but recommended).
|
|
176
|
+
|
|
177
|
+
Please, read requirements for the first task. To find the full description of each criteria please go to
|
|
178
|
+
the [COMPONENTS page.](components)
|
|
179
|
+
|
|
180
|
+
## Criteria (30 points max)
|
|
181
|
+
|
|
182
|
+
### Common
|
|
183
|
+
|
|
184
|
+
* [1 point] - The architecture of the application **should be** the same as presented above.
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
* [1 point] - Components are presented as [**function components**](https://react.dev/reference/react/Component) (not class components).
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
* [1 point] - Use `.jsx` extensions for files with [`jsx` syntax](https://react.dev/learn/writing-markup-with-jsx).
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
* [1 point] - All inputs and buttons should be reusable components.
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
* [2 points] - `eslint`, `prettier` and `pre-commit hook` work correctly.
|
|
197
|
+
|
|
198
|
+
### [Header component](/docs/module-1/home-task/components#header)
|
|
199
|
+
* [3 points] - Should display `Header` component with:
|
|
200
|
+
* `Logo` (any appropriate image)
|
|
201
|
+
* `Logout` button (without functionality)
|
|
202
|
+
|
|
203
|
+
### [CourseCard component](/docs/module-1/home-task/components#create-coursecard-component)
|
|
204
|
+
* [5 points] - Should display `CourseCard` component with:
|
|
205
|
+
* course title;
|
|
206
|
+
* course description;
|
|
207
|
+
* authors list;
|
|
208
|
+
* course duration;
|
|
209
|
+
* creation date;
|
|
210
|
+
* `Show course` button.
|
|
211
|
+
|
|
212
|
+
* [2 point] Show `CourseInfo` component with current course information by clicking `Show course` button.
|
|
213
|
+
|
|
214
|
+
### [EmptyCourseList component](/docs/module-1/home-task/components#emptycourselist-component)
|
|
215
|
+
|
|
216
|
+
* [2 points] - Show `EmptyCourseList` component when no courses.
|
|
217
|
+
|
|
218
|
+
* [2 point] - Component Should contain:
|
|
219
|
+
* title 'Your List Is Empty';
|
|
220
|
+
* subtitle 'Please use "Add New Course" button to add your first course'.
|
|
221
|
+
* `Add New Course` button (without functionality for current task).
|
|
222
|
+
|
|
223
|
+
### [Courses component](/docs/module-1/home-task/components#courses)
|
|
224
|
+
* [2 points] - Show list of courses (use [mocked course list](/docs/module-1/home-task/components#mocked-courses-and-authors-data) for this task).
|
|
225
|
+
|
|
226
|
+
* [1 point] - Show `Add new course` button (without functionality for current task).
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
### [CourseInfo Component](/docs/module-1/home-task/components#course-info)
|
|
230
|
+
|
|
231
|
+
* [5 points] - Create component that contains:
|
|
232
|
+
* course title
|
|
233
|
+
* course description
|
|
234
|
+
* course ID
|
|
235
|
+
* course duration
|
|
236
|
+
* creation date
|
|
237
|
+
* course authors
|
|
238
|
+
* `Back` button
|
|
239
|
+
|
|
240
|
+
* [2 point] - By clicking `Back` button `CourseInfo` component should be replaced by `Courses` component.
|
|
241
|
+
|
|
242
|
+
## Extra Task
|
|
243
|
+
### [Searching](/docs/module-1/home-task/components#search-extra-task)
|
|
244
|
+
* Implement searching functionality by title. Reset search result when searchbar is empty.
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
* Implement searching functionality by id. Reset search result when searchbar is empty.
|
|
248
|
+
|
|
249
|
+
[Full description](/docs/module-1/home-task/components#search-extra-task)
|
|
250
|
+
|
|
251
|
+
:::info
|
|
252
|
+
Please find a detailed description of components and functionality in the [COMPONENTS](components) section.
|
|
253
|
+
:::
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, fireEvent, screen } from "@testing-library/react";
|
|
3
|
+
import { Button } from "../common/Button/Button";
|
|
4
|
+
|
|
5
|
+
describe('Button component', () => {
|
|
6
|
+
test("Should render button with provided label", () => {
|
|
7
|
+
const handleClickMock = jest.fn();
|
|
8
|
+
render(<Button buttonText='Hello' handleClick={handleClickMock}/>);
|
|
9
|
+
const buttonElement = screen.getByText("Hello");
|
|
10
|
+
|
|
11
|
+
expect(buttonElement).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("Should call provided callback", () => {
|
|
15
|
+
const handleClickMock = jest.fn();
|
|
16
|
+
render(<Button handleClick={handleClickMock} buttonText='Click me'/>);
|
|
17
|
+
const buttonElement = screen.getByText("Click me");
|
|
18
|
+
|
|
19
|
+
fireEvent.click(buttonElement);
|
|
20
|
+
expect(handleClickMock).toHaveBeenCalledTimes(1);
|
|
21
|
+
});
|
|
22
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { CourseCard } from "../components/Courses/components/CourseCard/CourseCard";
|
|
4
|
+
|
|
5
|
+
describe('CourseCard component', () => {
|
|
6
|
+
const course = {
|
|
7
|
+
id: "1",
|
|
8
|
+
title: "Course Title",
|
|
9
|
+
description: "Course Description",
|
|
10
|
+
creationDate: "08/03/2021",
|
|
11
|
+
duration: 60,
|
|
12
|
+
authors: [
|
|
13
|
+
"27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
14
|
+
"f762978b-61eb-4096-812b-ebde22838167",
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
test("Should contain title", () => {
|
|
19
|
+
render(<CourseCard course={course} />);
|
|
20
|
+
|
|
21
|
+
const titleElement = screen.getByText("Course Title");
|
|
22
|
+
|
|
23
|
+
expect(titleElement).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("Should contain description", () => {
|
|
27
|
+
render(<CourseCard course={course} />);
|
|
28
|
+
const descriptionElement = screen.getByText("Course Description");
|
|
29
|
+
expect(descriptionElement).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("Should contain duration", () => {
|
|
33
|
+
render(<CourseCard course={course} />);
|
|
34
|
+
|
|
35
|
+
const durationElement = screen.getByText(/01:00 hour/);
|
|
36
|
+
|
|
37
|
+
expect(durationElement).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("Should contain date", () => {
|
|
41
|
+
render(<CourseCard course={course} />);
|
|
42
|
+
|
|
43
|
+
const creationDateElement = screen.getByText(/08.03.2021/);
|
|
44
|
+
|
|
45
|
+
expect(creationDateElement).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("Should contain authors list", () => {
|
|
49
|
+
render(<CourseCard course={course} />);
|
|
50
|
+
|
|
51
|
+
const creationDateElement = screen.getByText(/Vasiliy Dobkin, Nicolas Kim/);
|
|
52
|
+
|
|
53
|
+
expect(creationDateElement).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("Should contain authors list", () => {
|
|
57
|
+
render(<CourseCard course={course} />);
|
|
58
|
+
|
|
59
|
+
const creationDateElement = screen.getByText(/Show course/);
|
|
60
|
+
|
|
61
|
+
expect(creationDateElement).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
})
|
|
64
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { CourseInfo } from "../components/CourseInfo/CourseInfo";
|
|
4
|
+
import { formatCreationDate, getCourseDuration } from "../helpers";
|
|
5
|
+
|
|
6
|
+
const authorsList = [
|
|
7
|
+
{ id: "1", name: "Author 1" },
|
|
8
|
+
{ id: "2", name: "Author 2" },
|
|
9
|
+
{ id: "3", name: "Author 3" },
|
|
10
|
+
{ id: "4", name: "Author 4" },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const course = {
|
|
14
|
+
id: "1",
|
|
15
|
+
title: "Course Title",
|
|
16
|
+
description: "Course Description",
|
|
17
|
+
authors: ["1", "3"],
|
|
18
|
+
creationDate: "08/03/2021",
|
|
19
|
+
duration: 120,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("CourseInfo", () => {
|
|
23
|
+
test("renders title correctly", () => {
|
|
24
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
25
|
+
|
|
26
|
+
const courseTitle = screen.getByRole("heading", { level: 1 });
|
|
27
|
+
|
|
28
|
+
expect(courseTitle.textContent).toBe("Course Title");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("renders description correctly", () => {
|
|
32
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
33
|
+
|
|
34
|
+
const courseDescription = screen.getByText("Course Description");
|
|
35
|
+
|
|
36
|
+
expect(courseDescription).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("renders course duration correctly", () => {
|
|
40
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
41
|
+
|
|
42
|
+
const courseDuration = screen.getByText(getCourseDuration(120));
|
|
43
|
+
|
|
44
|
+
expect(courseDuration).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("renders course date correctly", () => {
|
|
48
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
49
|
+
|
|
50
|
+
const courseCreationDate = screen.getByText(
|
|
51
|
+
formatCreationDate(course.creationDate)
|
|
52
|
+
);
|
|
53
|
+
expect(courseCreationDate).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("renders course authors' names correctly", () => {
|
|
57
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
58
|
+
|
|
59
|
+
const authorNames = screen
|
|
60
|
+
.getAllByRole("listitem")
|
|
61
|
+
.map((li) => li.textContent);
|
|
62
|
+
|
|
63
|
+
expect(authorNames).toEqual(["Author 1", "Author 3"]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("renders Back button correctly", () => {
|
|
67
|
+
render(<CourseInfo course={course} authorsList={authorsList} />);
|
|
68
|
+
|
|
69
|
+
const backButton = screen.getByRole("button", { name: "Back" });
|
|
70
|
+
|
|
71
|
+
expect(backButton).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Courses } from "../components/Courses/Courses";
|
|
4
|
+
|
|
5
|
+
describe('Courses component', () => {
|
|
6
|
+
const mockedCoursesList = [
|
|
7
|
+
{
|
|
8
|
+
id: "1",
|
|
9
|
+
title: "Course 1",
|
|
10
|
+
description: "Course 1 description",
|
|
11
|
+
creationDate: "2022-01-01",
|
|
12
|
+
duration: 60,
|
|
13
|
+
authors: [
|
|
14
|
+
"27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
15
|
+
"f762978b-61eb-4096-812b-ebde22838167",
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "2",
|
|
20
|
+
title: "Course 2",
|
|
21
|
+
description: "Course 2 description",
|
|
22
|
+
creationDate: "2022-02-01",
|
|
23
|
+
duration: 90,
|
|
24
|
+
authors: ["df32994e-b23d-497c-9e4d-84e4dc02882f"],
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
const onAddClick = jest.fn();
|
|
28
|
+
|
|
29
|
+
test("Should render list of courses", () => {
|
|
30
|
+
render(<Courses courseList={mockedCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
31
|
+
|
|
32
|
+
const courseElements = screen.getAllByTestId("courseCard");
|
|
33
|
+
|
|
34
|
+
expect(courseElements[0]).toBeInTheDocument();
|
|
35
|
+
expect(courseElements).toHaveLength(2);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("Should render 'Add new course' button", () => {
|
|
39
|
+
render(<Courses courseList={mockedCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
40
|
+
|
|
41
|
+
const button = screen.getByText(/Add new course/);
|
|
42
|
+
|
|
43
|
+
expect(button).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("Should render EmptyCourseList component if no courses", () => {
|
|
47
|
+
const mockedEmptyCoursesList: [] = [];
|
|
48
|
+
render(<Courses courseList={mockedEmptyCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
49
|
+
|
|
50
|
+
const emptyContainerElement = screen.getByTestId("emptyContainer");
|
|
51
|
+
const addButtonElement = screen.getByTestId("addCourse");
|
|
52
|
+
|
|
53
|
+
expect(emptyContainerElement).toBeInTheDocument();
|
|
54
|
+
expect(addButtonElement).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('EmptyCourseList', () => {
|
|
59
|
+
const mockedEmptyCoursesList: [] = [];
|
|
60
|
+
const onAddClick = jest.fn();
|
|
61
|
+
|
|
62
|
+
test("Should render title", () => {
|
|
63
|
+
render(<Courses courseList={mockedEmptyCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
64
|
+
|
|
65
|
+
const title = screen.getByText(/Your List Is Empty/);
|
|
66
|
+
|
|
67
|
+
expect(title).toBeInTheDocument();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("Should render subtitle", () => {
|
|
71
|
+
render(<Courses courseList={mockedEmptyCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
72
|
+
|
|
73
|
+
const subTitle = screen.getByText(/Please use/);
|
|
74
|
+
|
|
75
|
+
expect(subTitle).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("Should render button", () => {
|
|
79
|
+
render(<Courses courseList={mockedEmptyCoursesList} onAddClick={onAddClick} setSelectedCourseId={(id) => {}}/>);
|
|
80
|
+
|
|
81
|
+
const button = screen.getByText('ADD NEW COURSE');
|
|
82
|
+
|
|
83
|
+
expect(button).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Header } from "../components/Header/Header";
|
|
4
|
+
|
|
5
|
+
describe('Header component', () => {
|
|
6
|
+
test("Should render logo", () => {
|
|
7
|
+
render(<Header />);
|
|
8
|
+
|
|
9
|
+
const imageElement = screen.getByRole("img");
|
|
10
|
+
|
|
11
|
+
expect(imageElement).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("Should render logout button", () => {
|
|
15
|
+
render(<Header />);
|
|
16
|
+
|
|
17
|
+
const logoutButtonElement = screen.getByRole("button");
|
|
18
|
+
|
|
19
|
+
expect(logoutButtonElement).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, fireEvent, screen } from "@testing-library/react";
|
|
3
|
+
import { Input } from "../common/Input/Input";
|
|
4
|
+
|
|
5
|
+
describe('Input component', () => {
|
|
6
|
+
const placeholderTextMock = 'Enter a value';
|
|
7
|
+
|
|
8
|
+
test("should render input with provided label text", () => {
|
|
9
|
+
render(<Input labelText="Test Input" placeholderText={placeholderTextMock} />);
|
|
10
|
+
|
|
11
|
+
const inputElement = screen.getByLabelText("Test Input");
|
|
12
|
+
expect(inputElement).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("should render input with provided placeholder text", () => {
|
|
16
|
+
const { getByPlaceholderText } = render(
|
|
17
|
+
<Input labelText='Test Input' placeholderText={placeholderTextMock} />
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(getByPlaceholderText(placeholderTextMock)).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("Should call provided callback for onChange event", () => {
|
|
24
|
+
const handleChange = jest.fn();
|
|
25
|
+
|
|
26
|
+
render(<Input labelText="Test Input" onChange={handleChange} placeholderText={placeholderTextMock}/>);
|
|
27
|
+
|
|
28
|
+
const inputElement = screen.getByLabelText("Test Input") as HTMLInputElement;
|
|
29
|
+
fireEvent.change(inputElement, { target: { value: "Hello" } });
|
|
30
|
+
|
|
31
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(inputElement.value).toBe("Hello");
|
|
33
|
+
});
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { formatCreationDate, getCourseDuration } from '../helpers';
|
|
2
|
+
|
|
3
|
+
test('formatCreationDate function returns formatted date', () => {
|
|
4
|
+
const date = '2022/01/01';
|
|
5
|
+
const formattedDate = formatCreationDate(date);
|
|
6
|
+
expect(formattedDate).toBe('2022.01.01');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('getCourseDuration function returns formatted duration', () => {
|
|
10
|
+
expect(getCourseDuration(60)).toBe('01:00 hour');
|
|
11
|
+
expect(getCourseDuration(90)).toBe('01:30 hour');
|
|
12
|
+
expect(getCourseDuration(120)).toBe('02:00 hours');
|
|
13
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
sidebar_label: 'Element Render'
|
|
4
|
+
title: 'Element Render'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What is Element?
|
|
8
|
+
|
|
9
|
+
**Elements are the smallest building blocks of React apps.**
|
|
10
|
+
|
|
11
|
+
An element describes what you want to see on the screen:
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
const element = <h1>Hello, world</h1>;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Unlike browser DOM elements, React elements are plain objects, and are cheap to create.
|
|
18
|
+
React DOM takes care of updating the DOM to match the React elements.
|
|
19
|
+
:::note
|
|
20
|
+
One might confuse elements with a more widely known concept of "components".
|
|
21
|
+
We will introduce components in the [next topic](components-and-props.mdx).
|
|
22
|
+
Elements are what components are "made of".
|
|
23
|
+
:::
|
|
24
|
+
|
|
25
|
+
## Rendering an Element into the DOM
|
|
26
|
+
Let's say there is a `<div>` somewhere in your HTML file:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
<div id="root"></div>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
It's called a **"root" DOM node** because everything inside it will be managed by React DOM.
|
|
33
|
+
|
|
34
|
+
Applications built with just React usually have a single root DOM node.
|
|
35
|
+
If you are integrating React into an existing app, you may have as many isolated root DOM nodes as you like.
|
|
36
|
+
|
|
37
|
+
To render a React element into a root DOM node, you need to create a React root and then pass the element to `root.render()`:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const element = <h1>Hello, world</h1>;
|
|
41
|
+
|
|
42
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
43
|
+
|
|
44
|
+
root.render(element);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
It displays "Hello, world" on the page.
|
|
48
|
+
|
|
49
|
+
## React Only Updates What's Necessary
|
|
50
|
+
React DOM compares the element and its children to the previous one,
|
|
51
|
+
and only applies the DOM updates necessary to bring the DOM to the desired state.
|
|
52
|
+
|
|
53
|
+
It is possible because React uses [Virtual Dom](https://legacy.reactjs.org/docs/faq-internals.html).
|
|
54
|
+
|
|
55
|
+
## Virtual DOM
|
|
56
|
+
|
|
57
|
+
**The virtual DOM (VDOM)** is a way of doing things in programming. It means having an ideal or "virtual" version of a user interface (UI) stored in the computer's memory. This virtual version is then matched up with the "real" DOM using a library like ReactDOM, and this matching process is called reconciliation.
|
|
58
|
+
|
|
59
|
+
This method makes it possible for React to work in a declarative way: *you tell React how you want the UI to look, and it makes sure the actual DOM matches that look*. This saves you from having to mess with attributes, handle events, or manually update the DOM, which you would have to do if you were building your app differently.
|
|
60
|
+
|
|
61
|
+
Because "virtual DOM" is more like a way of doing things than a specific technology, people might have different ideas about what it means. In the world of React, we often link the term "virtual DOM" with React elements because they are the objects that show the UI. However, React also uses internal objects called ["fibers"](https://github.com/acdlite/react-fiber-architecture) to keep extra information about the component tree. So, you could think of them as part of how React does its "virtual DOM" work.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Materials
|
|
66
|
+
1. [Rendering Elements. Documentation](https://react.dev/learn/writing-markup-with-jsx)
|