epam-ai-conductor 0.3.0 → 0.3.2
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/dist/content/{content/module-1/tests/Button.test.tsx → module-1/tests/Button.test.js} +8 -8
- package/dist/content/module-1/tests/CourseCard.test.js +68 -0
- package/dist/content/module-1/tests/CourseInfo.test.js +136 -0
- package/dist/content/module-1/tests/Courses.test.js +81 -0
- package/dist/content/{content/module-1/tests/Header.test.tsx → module-1/tests/Header.test.js} +5 -7
- package/dist/content/module-1/tests/Input.test.js +42 -0
- package/dist/content/module-1/tests/helpers.test.js +13 -0
- package/dist/content/module-2/tests/App.test.js +130 -0
- package/dist/content/module-2/tests/Button.test.js +22 -0
- package/dist/content/module-2/tests/CourseCard.test.js +93 -0
- package/dist/content/module-2/tests/CourseInfo.test.js +125 -0
- package/dist/content/module-2/tests/Courses.test.js +106 -0
- package/dist/content/module-2/tests/CreateCourse.test.js +117 -0
- package/dist/content/module-2/tests/Header.test.js +45 -0
- package/dist/content/module-2/tests/Input.test.js +42 -0
- package/dist/content/module-2/tests/Login.test.js +128 -0
- package/dist/content/module-2/tests/Registration.test.js +119 -0
- package/dist/content/module-2/tests/helpers.test.js +13 -0
- package/dist/content/module-2/tests/services.test.js +79 -0
- package/dist/content/module-3/tests/App.test.js +192 -0
- package/dist/content/{module-1/tests/Button.test.tsx → module-3/tests/Button.test.js} +8 -8
- package/dist/content/module-3/tests/CourseCard.test.js +50 -0
- package/dist/content/module-3/tests/CourseInfo.test.js +148 -0
- package/dist/content/module-3/tests/Courses.test.js +107 -0
- package/dist/content/module-3/tests/CreateAuthor.test.js +36 -0
- package/dist/content/module-3/tests/CreateCourse.test.js +66 -0
- package/dist/content/module-3/tests/Header.test.js +76 -0
- package/dist/content/module-3/tests/Input.test.js +42 -0
- package/dist/content/module-3/tests/Login.test.js +62 -0
- package/dist/content/{module-2/tests/Registration.test.tsx → module-3/tests/Registration.test.js} +13 -13
- package/dist/content/module-3/tests/helpers.test.js +13 -0
- package/dist/content/module-3/tests/services.test.js +83 -0
- package/dist/content/module-3/tests/slices.test.js +146 -0
- package/dist/content/module-4/tests/App.test.js +309 -0
- package/dist/content/module-4/tests/Button.test.js +22 -0
- package/dist/content/module-4/tests/CourseCard.test.js +111 -0
- package/dist/content/module-4/tests/CourseForm.test.js +108 -0
- package/dist/content/module-4/tests/CourseInfo.test.js +146 -0
- package/dist/content/module-4/tests/Courses.test.js +129 -0
- package/dist/content/module-4/tests/CreateAuthor.test.js +37 -0
- package/dist/content/module-4/tests/Header.test.js +79 -0
- package/dist/content/{content/module-1/tests/Input.test.tsx → module-4/tests/Input.test.js} +17 -11
- package/dist/content/module-4/tests/Login.test.js +61 -0
- package/dist/content/module-4/tests/PrivateRoute.test.js +45 -0
- package/dist/content/{content/module-2/tests/Registration.test.tsx → module-4/tests/Registration.test.js} +14 -13
- package/dist/content/module-4/tests/helpers.test.js +13 -0
- package/dist/content/module-4/tests/services.test.js +267 -0
- package/dist/content/module-4/tests/slices.test.js +146 -0
- package/dist/content/module-4/tests/thunk.test.js +238 -0
- package/package.json +4 -3
- package/scripts/postinstall.cjs +24 -0
- package/dist/content/content/module-1/task/module-1_home-task_components.md +0 -257
- package/dist/content/content/module-1/task/module-1_home-task_good_practices.md +0 -69
- package/dist/content/content/module-1/task/module-1_home-task_task.md +0 -253
- package/dist/content/content/module-1/tests/CourseCard.test.tsx +0 -64
- package/dist/content/content/module-1/tests/CourseInfo.test.tsx +0 -73
- package/dist/content/content/module-1/tests/Courses.test.tsx +0 -87
- package/dist/content/content/module-1/tests/helpers.test.ts +0 -13
- package/dist/content/content/module-1/theory/module-1_elements-render.md +0 -66
- package/dist/content/content/module-1/theory/module-1_hooks_useEffect.md +0 -178
- package/dist/content/content/module-1/theory/module-1_hooks_useState.md +0 -131
- package/dist/content/content/module-1/theory/module-1_react.md +0 -64
- package/dist/content/content/module-1/theory/module-1_spa.md +0 -74
- package/dist/content/content/module-2/task/module-2_home-task_components.md +0 -313
- package/dist/content/content/module-2/task/module-2_home-task_good_practices.md +0 -35
- package/dist/content/content/module-2/task/module-2_home-task_task.md +0 -210
- package/dist/content/content/module-2/tests/App.test.tsx +0 -54
- package/dist/content/content/module-2/tests/Login.test.tsx +0 -73
- package/dist/content/content/module-2/tests/SearchBar.test.tsx +0 -39
- package/dist/content/content/module-2/theory/module-2_custom-hooks.md +0 -201
- package/dist/content/content/module-2/theory/module-2_hooks.md +0 -117
- package/dist/content/content/module-2/theory/module-2_react-router.md +0 -328
- package/dist/content/content/module-3/task/module-3_home-task_components.md +0 -94
- package/dist/content/content/module-3/task/module-3_home-task_good_practices.md +0 -26
- package/dist/content/content/module-3/task/module-3_home-task_task.md +0 -170
- package/dist/content/content/module-3/tests/App.test.tsx +0 -54
- package/dist/content/content/module-3/tests/Courses.test.tsx +0 -87
- package/dist/content/content/module-3/tests/CreateAuthor.test.tsx +0 -44
- package/dist/content/content/module-3/tests/CreateCourse.test.tsx +0 -122
- package/dist/content/content/module-3/theory/module-3_redux-hooks.md +0 -194
- package/dist/content/content/module-3/theory/module-3_state-actions-reducers.md +0 -445
- package/dist/content/content/module-4/task/module-4_home-task_components.md +0 -187
- package/dist/content/content/module-4/task/module-4_home-task_task.md +0 -139
- package/dist/content/content/module-4/tests/App.test.tsx +0 -54
- package/dist/content/content/module-4/tests/Courses.test.tsx +0 -87
- package/dist/content/content/module-4/tests/CreateCourse.test.tsx +0 -122
- package/dist/content/content/module-4/tests/Login.test.tsx +0 -73
- package/dist/content/content/module-4/theory/module-4_async-redux.md +0 -99
- package/dist/content/content/module-4/theory/module-4_private-routes.md +0 -55
- package/dist/content/content/module-5/task/module-5_home-task_instruction.md +0 -68
- package/dist/content/content/module-5/task/module-5_home-task_task.md +0 -154
- package/dist/content/content/module-5/tests/App.test.tsx +0 -54
- package/dist/content/content/module-5/tests/CourseCard.test.tsx +0 -64
- package/dist/content/content/module-5/tests/Courses.test.tsx +0 -87
- package/dist/content/content/module-5/tests/Header.test.tsx +0 -23
- package/dist/content/content/module-5/theory/module-5_react-testing-library_example.md +0 -379
- package/dist/content/content/module-5/theory/module-5_redux-writing-tests.md +0 -246
- package/dist/content/module-1/tests/CourseCard.test.tsx +0 -64
- package/dist/content/module-1/tests/CourseInfo.test.tsx +0 -73
- package/dist/content/module-1/tests/Courses.test.tsx +0 -87
- package/dist/content/module-1/tests/Header.test.tsx +0 -23
- package/dist/content/module-1/tests/Input.test.tsx +0 -36
- package/dist/content/module-1/tests/helpers.test.ts +0 -13
- package/dist/content/module-2/tests/App.test.tsx +0 -54
- package/dist/content/module-2/tests/Login.test.tsx +0 -73
- package/dist/content/module-2/tests/SearchBar.test.tsx +0 -39
- package/dist/content/module-3/tests/App.test.tsx +0 -54
- package/dist/content/module-3/tests/Courses.test.tsx +0 -87
- package/dist/content/module-3/tests/CreateAuthor.test.tsx +0 -44
- package/dist/content/module-3/tests/CreateCourse.test.tsx +0 -122
- package/dist/content/module-4/tests/App.test.tsx +0 -54
- package/dist/content/module-4/tests/Courses.test.tsx +0 -87
- package/dist/content/module-4/tests/CreateCourse.test.tsx +0 -122
- package/dist/content/module-4/tests/Login.test.tsx +0 -73
- package/dist/content/module-5/tests/App.test.tsx +0 -54
- package/dist/content/module-5/tests/CourseCard.test.tsx +0 -64
- package/dist/content/module-5/tests/Courses.test.tsx +0 -87
- package/dist/content/module-5/tests/Header.test.tsx +0 -23
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { CourseInfo } from "../../components/CourseInfo";
|
|
4
|
+
import { formatCreationDate, getCourseDuration } from "../../helpers";
|
|
5
|
+
import { MemoryRouter } from "react-router";
|
|
6
|
+
|
|
7
|
+
const mockedCoursesList = [
|
|
8
|
+
{
|
|
9
|
+
id: "1",
|
|
10
|
+
title: "Course 1",
|
|
11
|
+
description: "Course 1 description",
|
|
12
|
+
creationDate: "2022-01-01",
|
|
13
|
+
duration: 60,
|
|
14
|
+
authors: [
|
|
15
|
+
"27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
16
|
+
"f762978b-61eb-4096-812b-ebde22838167",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "2",
|
|
21
|
+
title: "Course 2",
|
|
22
|
+
description: "Course 2 description",
|
|
23
|
+
creationDate: "2022-02-01",
|
|
24
|
+
duration: 90,
|
|
25
|
+
authors: ["df32994e-b23d-497c-9e4d-84e4dc02882f"],
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const authorsList = [
|
|
29
|
+
{ id: "df32994e-b23d-497c-9e4d-84e4dc02882f", name: "name1" },
|
|
30
|
+
{ id: "27cc3006-e93a-4748-8ca8-73d06aa93b6d", name: "name2" },
|
|
31
|
+
{ id: "f762978b-61eb-4096-812b-ebde22838167", name: "name3" },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
jest.mock("react-router-dom", () => ({
|
|
35
|
+
...jest.requireActual("react-router-dom"),
|
|
36
|
+
useParams: () => ({ courseId: "1" }),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
describe("CourseInfo", () => {
|
|
40
|
+
test("should render correct title (find correct course from coursesList based on courseId param use useParams hook)", () => {
|
|
41
|
+
render(
|
|
42
|
+
<MemoryRouter>
|
|
43
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
44
|
+
</MemoryRouter>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const courseTitle = screen.getByRole("heading", { level: 1 });
|
|
48
|
+
|
|
49
|
+
expect(courseTitle.textContent).toBe("Course 1");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("should render correct description", () => {
|
|
53
|
+
render(
|
|
54
|
+
<MemoryRouter>
|
|
55
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
56
|
+
</MemoryRouter>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const courseDescription = screen.getByText("Course 1 description");
|
|
60
|
+
|
|
61
|
+
expect(courseDescription).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("should render correct course duration (use getCourseDuration)", () => {
|
|
65
|
+
render(
|
|
66
|
+
<MemoryRouter>
|
|
67
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
68
|
+
</MemoryRouter>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const courseDuration = screen.queryByText(getCourseDuration(60));
|
|
72
|
+
|
|
73
|
+
expect(courseDuration).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("should render correct course creation date (use formatCreationDate)", () => {
|
|
77
|
+
render(
|
|
78
|
+
<MemoryRouter>
|
|
79
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
80
|
+
</MemoryRouter>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const courseCreationDate = screen.getByText(
|
|
84
|
+
formatCreationDate(mockedCoursesList[0].creationDate)
|
|
85
|
+
);
|
|
86
|
+
expect(courseCreationDate).toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("should render correct course authors names (find a course in the coursesList and match data from course authors and authorsList prop)", () => {
|
|
90
|
+
render(
|
|
91
|
+
<MemoryRouter>
|
|
92
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
93
|
+
</MemoryRouter>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const authorNames = screen
|
|
97
|
+
.getAllByRole("listitem")
|
|
98
|
+
.map((li) => li.textContent);
|
|
99
|
+
|
|
100
|
+
expect(authorNames).toEqual(["name2", "name3"]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should render "BACK" button as a Link provided by react-router-dom', () => {
|
|
104
|
+
render(
|
|
105
|
+
<MemoryRouter>
|
|
106
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
107
|
+
</MemoryRouter>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const backButton = screen.queryByText(/back/i);
|
|
111
|
+
|
|
112
|
+
expect(backButton.closest("a")).toBeInTheDocument();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should render "BACK" link to="/courses"', () => {
|
|
116
|
+
render(
|
|
117
|
+
<MemoryRouter>
|
|
118
|
+
<CourseInfo coursesList={mockedCoursesList} authorsList={authorsList} />
|
|
119
|
+
</MemoryRouter>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const backButton = screen.queryByText(/back/i);
|
|
123
|
+
expect(backButton.closest("a")).toHaveAttribute("href", "/courses");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Courses } from "../../components/Courses/Courses";
|
|
4
|
+
import { MemoryRouter } from "react-router";
|
|
5
|
+
|
|
6
|
+
describe("Courses component", () => {
|
|
7
|
+
const mockedCoursesList = [
|
|
8
|
+
{
|
|
9
|
+
id: "1",
|
|
10
|
+
title: "Course 1",
|
|
11
|
+
description: "Course 1 description",
|
|
12
|
+
creationDate: "2022-01-01",
|
|
13
|
+
duration: 60,
|
|
14
|
+
authors: [
|
|
15
|
+
"27cc3006-e93a-4748-8ca8-73d06aa93b6d",
|
|
16
|
+
"f762978b-61eb-4096-812b-ebde22838167",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "2",
|
|
21
|
+
title: "Course 2",
|
|
22
|
+
description: "Course 2 description",
|
|
23
|
+
creationDate: "2022-02-01",
|
|
24
|
+
duration: 90,
|
|
25
|
+
authors: ["df32994e-b23d-497c-9e4d-84e4dc02882f"],
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const authorsList = [
|
|
29
|
+
{ id: "df32994e-b23d-497c-9e4d-84e4dc02882f", name: "name" },
|
|
30
|
+
{ id: "27cc3006-e93a-4748-8ca8-73d06aa93b6d", name: "name" },
|
|
31
|
+
{ id: "f762978b-61eb-4096-812b-ebde22838167", name: "name" },
|
|
32
|
+
];
|
|
33
|
+
const onAddClick = jest.fn();
|
|
34
|
+
|
|
35
|
+
test('should render list of courses from coursesList prop with data-testid="courseCard"', () => {
|
|
36
|
+
render(
|
|
37
|
+
<MemoryRouter>
|
|
38
|
+
<Courses
|
|
39
|
+
coursesList={mockedCoursesList}
|
|
40
|
+
authorsList={authorsList}
|
|
41
|
+
onAddClick={onAddClick}
|
|
42
|
+
handleShowCourse={(id) => {}}
|
|
43
|
+
/>
|
|
44
|
+
</MemoryRouter>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const courseElements = screen.getAllByTestId("courseCard");
|
|
48
|
+
|
|
49
|
+
expect(courseElements[0]).toBeInTheDocument();
|
|
50
|
+
expect(courseElements).toHaveLength(2);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should render 'ADD NEW COURSE' button", () => {
|
|
54
|
+
render(
|
|
55
|
+
<MemoryRouter>
|
|
56
|
+
<Courses
|
|
57
|
+
coursesList={mockedCoursesList}
|
|
58
|
+
authorsList={authorsList}
|
|
59
|
+
onAddClick={onAddClick}
|
|
60
|
+
handleShowCourse={(id) => {}}
|
|
61
|
+
/>
|
|
62
|
+
</MemoryRouter>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const button = screen.getByText(/add new/i);
|
|
66
|
+
|
|
67
|
+
expect(button).toBeInTheDocument();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("should render 'ADD NEW COURSE' button as a Link with provided by react-router-dom to='/courses/add'", () => {
|
|
71
|
+
render(
|
|
72
|
+
<MemoryRouter>
|
|
73
|
+
<Courses
|
|
74
|
+
coursesList={mockedCoursesList}
|
|
75
|
+
authorsList={authorsList}
|
|
76
|
+
onAddClick={onAddClick}
|
|
77
|
+
handleShowCourse={(id) => {}}
|
|
78
|
+
/>
|
|
79
|
+
</MemoryRouter>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const link = screen.getByText(/add new/i).closest("a");
|
|
83
|
+
|
|
84
|
+
expect(link).toBeInTheDocument();
|
|
85
|
+
expect(link).toHaveAttribute("href", "/courses/add");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("should render EmptyCourseList component if no courses (with 'Your List Is Empty' text and button with data-testid='addCourse')", () => {
|
|
89
|
+
render(
|
|
90
|
+
<MemoryRouter>
|
|
91
|
+
<Courses
|
|
92
|
+
coursesList={[]}
|
|
93
|
+
authorsList={authorsList}
|
|
94
|
+
onAddClick={onAddClick}
|
|
95
|
+
handleShowCourse={(id) => {}}
|
|
96
|
+
/>
|
|
97
|
+
</MemoryRouter>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const emptyText = screen.queryByText(/your list is empty/i);
|
|
101
|
+
const addButtonElement = screen.getByTestId("addCourse");
|
|
102
|
+
|
|
103
|
+
expect(emptyText).toBeInTheDocument();
|
|
104
|
+
expect(addButtonElement).toBeInTheDocument();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, fireEvent, screen } from "@testing-library/react";
|
|
3
|
+
import { CourseForm } from "../../components/CourseForm/CourseForm";
|
|
4
|
+
import { MemoryRouter } from "react-router";
|
|
5
|
+
|
|
6
|
+
describe("CourseForm", () => {
|
|
7
|
+
const mockAuthors = [
|
|
8
|
+
{ id: "1", name: "Author 1" },
|
|
9
|
+
{ id: "2", name: "Author 2" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const mockCourseForm = jest.fn();
|
|
13
|
+
const mockCreateAuthor = jest.fn();
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockCourseForm.mockClear();
|
|
17
|
+
mockCreateAuthor.mockClear();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should create a new course with the entered data in all field`s (createCourse prop should be called on createCourseButton click)", () => {
|
|
21
|
+
render(
|
|
22
|
+
<MemoryRouter>
|
|
23
|
+
<CourseForm
|
|
24
|
+
authorsList={mockAuthors}
|
|
25
|
+
createCourse={mockCourseForm}
|
|
26
|
+
createAuthor={mockCreateAuthor}
|
|
27
|
+
/>
|
|
28
|
+
</MemoryRouter>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const titleInput = screen.getByTestId("titleInput");
|
|
32
|
+
const durationInput = screen.getByTestId("durationInput");
|
|
33
|
+
const descriptionTextArea = screen.getByTestId("descriptionTextArea");
|
|
34
|
+
const addAuthorButton = screen.getAllByTestId("addAuthor")[0];
|
|
35
|
+
const createCourseButton = screen.getByTestId("createCourseButton");
|
|
36
|
+
|
|
37
|
+
fireEvent.change(titleInput, { target: { value: "Course Title" } });
|
|
38
|
+
fireEvent.change(durationInput, { target: { value: "20" } });
|
|
39
|
+
fireEvent.click(addAuthorButton);
|
|
40
|
+
fireEvent.change(descriptionTextArea, {
|
|
41
|
+
target: { value: "Course Description" },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
fireEvent.click(createCourseButton);
|
|
45
|
+
|
|
46
|
+
expect(mockCourseForm).toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should not create a new course with the empty data (createCourse prop should NOT be called on createCourseButton click)", () => {
|
|
50
|
+
render(
|
|
51
|
+
<MemoryRouter>
|
|
52
|
+
<CourseForm
|
|
53
|
+
authorsList={mockAuthors}
|
|
54
|
+
createCourse={mockCourseForm}
|
|
55
|
+
createAuthor={mockCreateAuthor}
|
|
56
|
+
/>
|
|
57
|
+
</MemoryRouter>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const createCourseButton = screen.getByTestId("createCourseButton");
|
|
61
|
+
|
|
62
|
+
fireEvent.click(createCourseButton);
|
|
63
|
+
|
|
64
|
+
expect(mockCourseForm).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should add an author to the course when CreateAuthor component creates a new author (createAuthor prop should be called on createAuthorButton click)", () => {
|
|
68
|
+
render(
|
|
69
|
+
<MemoryRouter>
|
|
70
|
+
<CourseForm
|
|
71
|
+
authorsList={mockAuthors}
|
|
72
|
+
createCourse={mockCourseForm}
|
|
73
|
+
createAuthor={mockCreateAuthor}
|
|
74
|
+
/>
|
|
75
|
+
</MemoryRouter>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const createAuthorButton = screen.getByTestId("createAuthorButton");
|
|
79
|
+
const nameInput = screen.getByTestId("createAuthorInput");
|
|
80
|
+
|
|
81
|
+
fireEvent.change(nameInput, { target: { value: "John" } });
|
|
82
|
+
|
|
83
|
+
fireEvent.click(createAuthorButton);
|
|
84
|
+
|
|
85
|
+
expect(mockCreateAuthor).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should render the course authors with data-testid="authorItem" in the course authors list', () => {
|
|
89
|
+
const mockCourseAuthors = ["1", "2"];
|
|
90
|
+
render(
|
|
91
|
+
<MemoryRouter>
|
|
92
|
+
<CourseForm
|
|
93
|
+
authorsList={mockAuthors}
|
|
94
|
+
createCourse={mockCourseForm}
|
|
95
|
+
createAuthor={mockCreateAuthor}
|
|
96
|
+
/>
|
|
97
|
+
</MemoryRouter>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const courseAuthorItems = screen.getAllByTestId("authorItem");
|
|
101
|
+
expect(courseAuthorItems.length).toBe(mockCourseAuthors.length);
|
|
102
|
+
});
|
|
103
|
+
it('should display the "Authors list is empty" message when there are no authors is selected for the course', () => {
|
|
104
|
+
render(
|
|
105
|
+
<MemoryRouter>
|
|
106
|
+
<CourseForm
|
|
107
|
+
authorsList={[]}
|
|
108
|
+
createCourse={mockCourseForm}
|
|
109
|
+
createAuthor={mockCreateAuthor}
|
|
110
|
+
/>
|
|
111
|
+
</MemoryRouter>
|
|
112
|
+
);
|
|
113
|
+
const emptyText = screen.queryByText(/list is empty/i);
|
|
114
|
+
|
|
115
|
+
expect(emptyText).toBeInTheDocument();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { Header } from "../../components/Header/Header";
|
|
4
|
+
import { MemoryRouter } from "react-router";
|
|
5
|
+
|
|
6
|
+
describe("Header component", () => {
|
|
7
|
+
test("should render logo (<img /> tag)", () => {
|
|
8
|
+
render(
|
|
9
|
+
<MemoryRouter>
|
|
10
|
+
<Header />
|
|
11
|
+
</MemoryRouter>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const imageElement = screen.getByRole("img");
|
|
15
|
+
|
|
16
|
+
expect(imageElement).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should not render logout button without "token" in localStorage', () => {
|
|
20
|
+
render(
|
|
21
|
+
<MemoryRouter>
|
|
22
|
+
<Header />
|
|
23
|
+
</MemoryRouter>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const logoutButtonElement = screen.queryByText(/logout/i);
|
|
27
|
+
|
|
28
|
+
expect(logoutButtonElement).not.toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should immediately remove "token" from localStorage on "LOGOUT" button click', () => {
|
|
32
|
+
localStorage.setItem("token", "token");
|
|
33
|
+
render(
|
|
34
|
+
<MemoryRouter>
|
|
35
|
+
<Header />
|
|
36
|
+
</MemoryRouter>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const logoutButtonElement = screen.queryByText(/logout/i);
|
|
40
|
+
|
|
41
|
+
fireEvent.click(logoutButtonElement);
|
|
42
|
+
|
|
43
|
+
expect(localStorage.getItem("token")).toBeFalsy();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, fireEvent, screen } from "@testing-library/react";
|
|
3
|
+
import { Input } from "../../common/Input";
|
|
4
|
+
|
|
5
|
+
describe("Input component", () => {
|
|
6
|
+
const placeholderTextMock = "Enter a value";
|
|
7
|
+
|
|
8
|
+
test("should render input with provided labelText prop value", () => {
|
|
9
|
+
render(
|
|
10
|
+
<Input labelText="Test Input" placeholderText={placeholderTextMock} />
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const inputElement = screen.getByLabelText("Test Input");
|
|
14
|
+
expect(inputElement).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should render input with provided placeholderText prop value", () => {
|
|
18
|
+
const { getByPlaceholderText } = render(
|
|
19
|
+
<Input labelText="Test Input" placeholderText={placeholderTextMock} />
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(getByPlaceholderText(placeholderTextMock)).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("should call provided callback for onChange event", () => {
|
|
26
|
+
const handleChange = jest.fn();
|
|
27
|
+
|
|
28
|
+
render(
|
|
29
|
+
<Input
|
|
30
|
+
labelText="Test Input"
|
|
31
|
+
onChange={handleChange}
|
|
32
|
+
placeholderText={placeholderTextMock}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const inputElement = screen.getByLabelText("Test Input");
|
|
37
|
+
fireEvent.change(inputElement, { target: { value: "Hello" } });
|
|
38
|
+
|
|
39
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(inputElement.value).toBe("Hello");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, fireEvent, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router-dom";
|
|
4
|
+
import { Login } from "../../components/Login/Login";
|
|
5
|
+
import * as services from "../../services";
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.spyOn(services, "login").mockImplementation(
|
|
9
|
+
jest.fn(() =>
|
|
10
|
+
Promise.resolve({
|
|
11
|
+
successful: true,
|
|
12
|
+
result: "token123",
|
|
13
|
+
user: {
|
|
14
|
+
name: "John Doe",
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
jest.spyOn(global, "fetch").mockImplementation(
|
|
21
|
+
jest.fn(() =>
|
|
22
|
+
Promise.resolve({
|
|
23
|
+
ok: true,
|
|
24
|
+
json: () =>
|
|
25
|
+
Promise.resolve({
|
|
26
|
+
result: "token",
|
|
27
|
+
user: {
|
|
28
|
+
name: "John Doe",
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("Login", () => {
|
|
37
|
+
test("should render login form with 2 inputs, registration info text and link", () => {
|
|
38
|
+
render(
|
|
39
|
+
<MemoryRouter>
|
|
40
|
+
<Login />
|
|
41
|
+
</MemoryRouter>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(screen.queryByText(/email/i)).toBeInTheDocument();
|
|
45
|
+
expect(screen.queryByText(/password/i)).toBeInTheDocument();
|
|
46
|
+
expect(screen.queryByText(/an account you/i)).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByRole("link")).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("should submits login form calls login service with entered email and password", async () => {
|
|
51
|
+
render(
|
|
52
|
+
<MemoryRouter>
|
|
53
|
+
<Login setName={jest.fn()} />
|
|
54
|
+
</MemoryRouter>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
fireEvent.change(screen.queryByText(/email/i).querySelector("input"), {
|
|
58
|
+
target: { value: "test@example.com" },
|
|
59
|
+
});
|
|
60
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
61
|
+
target: { value: "password123" },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
fireEvent.click(screen.getByRole("button"));
|
|
65
|
+
|
|
66
|
+
expect(services.login).toHaveBeenCalledWith({
|
|
67
|
+
email: "test@example.com",
|
|
68
|
+
password: "password123",
|
|
69
|
+
});
|
|
70
|
+
await waitFor(() => expect(localStorage.getItem("token")).toBe("token123"));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should set "token" to localstorage after login submit', async () => {
|
|
74
|
+
render(
|
|
75
|
+
<MemoryRouter>
|
|
76
|
+
<Login setName={jest.fn()} />
|
|
77
|
+
</MemoryRouter>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
fireEvent.change(screen.queryByText(/email/i).querySelector("input"), {
|
|
81
|
+
target: { value: "test@example.com" },
|
|
82
|
+
});
|
|
83
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
84
|
+
target: { value: "password123" },
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
fireEvent.click(screen.getByRole("button"));
|
|
88
|
+
|
|
89
|
+
expect(services.login).toHaveBeenCalledWith({
|
|
90
|
+
email: "test@example.com",
|
|
91
|
+
password: "password123",
|
|
92
|
+
});
|
|
93
|
+
await waitFor(() => expect(localStorage.getItem("token")).toBe("token123"));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should render the "Password is required" validation message and NOT call login service', async () => {
|
|
97
|
+
render(
|
|
98
|
+
<MemoryRouter>
|
|
99
|
+
<Login setName={jest.fn()} />
|
|
100
|
+
</MemoryRouter>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
fireEvent.change(screen.queryByText(/email/i).querySelector("input"), {
|
|
104
|
+
target: { value: "test@example.com" },
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
fireEvent.click(screen.getByRole("button"));
|
|
108
|
+
|
|
109
|
+
expect(screen.queryByText(/password is required/i)).toBeInTheDocument();
|
|
110
|
+
expect(services.login).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
test('should render the "Email is required" validation message and NOT call login service', async () => {
|
|
113
|
+
render(
|
|
114
|
+
<MemoryRouter>
|
|
115
|
+
<Login setName={jest.fn()} />
|
|
116
|
+
</MemoryRouter>
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
120
|
+
target: { value: "password123" },
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
fireEvent.click(screen.getByRole("button"));
|
|
124
|
+
|
|
125
|
+
expect(screen.queryByText(/email is required/i)).toBeInTheDocument();
|
|
126
|
+
expect(services.login).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router-dom";
|
|
4
|
+
import { Registration } from "../../components/Registration/Registration";
|
|
5
|
+
import * as services from "../../services";
|
|
6
|
+
|
|
7
|
+
const mockedUsedNavigate = jest.fn();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest
|
|
11
|
+
.spyOn(services, "createUser")
|
|
12
|
+
.mockImplementation(jest.fn(() => Promise.resolve()));
|
|
13
|
+
|
|
14
|
+
jest.spyOn(global, "fetch").mockImplementation(
|
|
15
|
+
jest.fn(() =>
|
|
16
|
+
Promise.resolve({
|
|
17
|
+
ok: true,
|
|
18
|
+
json: () => Promise.resolve(),
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
jest.mock("react-router-dom", () => ({
|
|
24
|
+
...jest.requireActual("react-router-dom"),
|
|
25
|
+
useNavigate: () => mockedUsedNavigate,
|
|
26
|
+
}));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("Registration", () => {
|
|
30
|
+
test("should render the registration form with 3 inputs, login info text and link", () => {
|
|
31
|
+
render(
|
|
32
|
+
<MemoryRouter>
|
|
33
|
+
<Registration />
|
|
34
|
+
</MemoryRouter>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(screen.queryByText(/name/i)).toBeInTheDocument();
|
|
38
|
+
expect(screen.queryByText(/email/i)).toBeInTheDocument();
|
|
39
|
+
expect(screen.queryByText(/password/i)).toBeInTheDocument();
|
|
40
|
+
expect(screen.getByRole("button")).toBeInTheDocument();
|
|
41
|
+
expect(screen.getByRole("link")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should submit the registration form, call the "createUser" service with the entered name, email and password', async () => {
|
|
45
|
+
render(
|
|
46
|
+
<MemoryRouter>
|
|
47
|
+
<Registration />
|
|
48
|
+
</MemoryRouter>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
fireEvent.change(screen.queryByText(/name/i).querySelector("input"), {
|
|
52
|
+
target: { value: "John Doe" },
|
|
53
|
+
});
|
|
54
|
+
fireEvent.change(screen.queryByText(/email/i).querySelector("input"), {
|
|
55
|
+
target: { value: "john.doe@example.com" },
|
|
56
|
+
});
|
|
57
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
58
|
+
target: { value: "password123" },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
fireEvent.click(screen.getByRole("button"));
|
|
62
|
+
|
|
63
|
+
expect(services.createUser).toHaveBeenCalledTimes(1);
|
|
64
|
+
await expect(services.createUser).toHaveBeenCalledWith({
|
|
65
|
+
name: "John Doe",
|
|
66
|
+
email: "john.doe@example.com",
|
|
67
|
+
password: "password123",
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should render the "Password is required" validation message and NOT call registration service', async () => {
|
|
72
|
+
render(
|
|
73
|
+
<MemoryRouter>
|
|
74
|
+
<Registration setName={jest.fn()} />
|
|
75
|
+
</MemoryRouter>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
fireEvent.change(screen.queryByText(/email/i).querySelector("input"), {
|
|
79
|
+
target: { value: "test@example.com" },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
fireEvent.click(screen.getByRole("button"));
|
|
83
|
+
|
|
84
|
+
expect(screen.queryByText(/password is required/i)).toBeInTheDocument();
|
|
85
|
+
expect(services.createUser).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
test('should render the "Email is required" validation message and NOT call registration service', async () => {
|
|
88
|
+
render(
|
|
89
|
+
<MemoryRouter>
|
|
90
|
+
<Registration setName={jest.fn()} />
|
|
91
|
+
</MemoryRouter>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
95
|
+
target: { value: "password123" },
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
fireEvent.click(screen.getByRole("button"));
|
|
99
|
+
|
|
100
|
+
expect(screen.queryByText(/email is required/i)).toBeInTheDocument();
|
|
101
|
+
expect(services.createUser).not.toHaveBeenCalled();
|
|
102
|
+
});
|
|
103
|
+
test('should render the "Name is required" validation message and NOT call registration service', async () => {
|
|
104
|
+
render(
|
|
105
|
+
<MemoryRouter>
|
|
106
|
+
<Registration setName={jest.fn()} />
|
|
107
|
+
</MemoryRouter>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
fireEvent.change(screen.queryByText(/password/i).querySelector("input"), {
|
|
111
|
+
target: { value: "password123" },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
fireEvent.click(screen.getByRole("button"));
|
|
115
|
+
|
|
116
|
+
expect(screen.queryByText(/name is required/i)).toBeInTheDocument();
|
|
117
|
+
expect(services.createUser).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -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
|
+
});
|