Skip to content

Commit 403ff32

Browse files
authored
Merge pull request #18 from jetbrains-academy/frontend
Add the Frontend section
2 parents b8ac74c + 8b6e5eb commit 403ff32

File tree

430 files changed

+18856
-8082
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

430 files changed

+18856
-8082
lines changed

.courseignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
syntax: regexp
2+
^\.git/

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ web_modules/
7373
.yarn-integrity
7474

7575
# dotenv environment variable files
76-
.env
76+
#.env
7777
.env.development.local
7878
.env.test.local
7979
.env.production.local
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type: framework
2+
custom_name: Backend Connection
3+
content:
4+
- lesson_overview
5+
- register_form
6+
- login_form
7+
- routing_update
8+
- retrieving_messages
9+
- sending_messages
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type: theory
2+
custom_name: Lesson overview
3+
files:
4+
- name: task.js
5+
visible: true
6+
propagatable: false

Frontend/BackendConnection/lesson_overview/task.js

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
We've already had some practice with React, so now it's time to get started on our project:
2+
specifically, setting up interaction with the backend.
3+
4+
In this lesson, you will learn how to:
5+
- Communicate with the backend using a REST API.
6+
- Handle authentication tokens on the frontend.
7+
8+
### Project structure
9+
Next in the course, you will encounter the familiar project structure from the backend part of the course:
10+
```text
11+
.
12+
├── backend/
13+
├── frontend/
14+
└── package.json // project configuration file
15+
```
16+
17+
### Backend API
18+
To remind yourself about the backend API, take a look at the [Backend connection](course://Frontend/Introduction/backend_connection) task described in the Introduction lesson.
19+
Feel free to look into the backend source code if you need it.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
JWT_SECRET=testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestte==
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import request from 'supertest';
2+
import { httpServer } from '../src/index.js';
3+
import {dbReset} from "../src/data/dataServices.js";
4+
5+
describe('Authentication API', () => {
6+
7+
afterAll((done) => {
8+
httpServer.close(done);
9+
});
10+
11+
describe('POST /api/auth/register', () => {
12+
beforeEach(async() => {
13+
await dbReset();
14+
15+
});
16+
17+
it('should register a new user successfully', async () => {
18+
const response = await request(httpServer)
19+
.post('/api/auth/register')
20+
.send({
21+
username: 'testuser',
22+
password: 'password123'
23+
})
24+
.timeout(4000);
25+
26+
expect(response.status).toBe(201);
27+
expect(response.body).toHaveProperty('username', 'testuser');
28+
});
29+
30+
it('should not allow duplicate usernames', async () => {
31+
// Register first user
32+
await request(httpServer)
33+
.post('/api/auth/register')
34+
.send({
35+
username: 'testuser',
36+
password: 'password123'
37+
})
38+
.timeout(4000);
39+
40+
// Try to register the same username
41+
const response = await request(httpServer)
42+
.post('/api/auth/register')
43+
.send({
44+
username: 'testuser',
45+
password: 'differentpassword'
46+
})
47+
.timeout(4000);
48+
49+
expect(response.status).toBe(409);
50+
expect(response.body).toHaveProperty('message', 'Username already exists');
51+
});
52+
});
53+
54+
describe('POST /api/auth/login', () => {
55+
beforeAll(async () => {
56+
await dbReset();
57+
// Create a test user before login test
58+
await request(httpServer)
59+
.post('/api/auth/register')
60+
.send({
61+
username: 'testuser',
62+
password: 'password123'
63+
})
64+
.timeout(4000);
65+
});
66+
67+
it('should log in successfully with correct credentials', async () => {
68+
const response = await request(httpServer)
69+
.post('/api/auth/login')
70+
.send({
71+
username: 'testuser',
72+
password: 'password123'
73+
})
74+
.timeout(4000);
75+
76+
expect(response.status).toBe(200);
77+
expect(response.body).toHaveProperty('username', 'testuser');
78+
expect(response.body).toHaveProperty('token');
79+
});
80+
81+
it('should fail with incorrect password', async () => {
82+
const response = await request(httpServer)
83+
.post('/api/auth/login')
84+
.send({
85+
username: 'testuser',
86+
password: 'wrongpassword'
87+
})
88+
.timeout(4000);
89+
90+
expect(response.status).toBe(401);
91+
expect(response.body).toHaveProperty('message', 'Invalid username or password');
92+
});
93+
94+
it('should fail with non-existent username', async () => {
95+
const response = await request(httpServer)
96+
.post('/api/auth/login')
97+
.send({
98+
username: 'nonexistent',
99+
password: 'password123'
100+
})
101+
.timeout(4000);
102+
103+
expect(response.status).toBe(401);
104+
expect(response.body).toHaveProperty('message', 'Invalid username or password');
105+
});
106+
});
107+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {dbReset, userService, messageService} from '../src/data/dataServices.js';
2+
3+
describe('Test database-backed data layer', () => {
4+
beforeEach(async () => {
5+
await dbReset();
6+
});
7+
8+
describe('Test userService', () => {
9+
it('Test new user creation', async () => {
10+
const newUser = await userService.createUser('Tom', 'password123');
11+
expect(newUser.username).toBe('Tom');
12+
});
13+
14+
it('Test existing user creation', async () => {
15+
await userService.createUser('John', '1234567890');
16+
17+
await expect(userService.createUser('John', 'newPassword'))
18+
.rejects
19+
.toThrow('Username already exists');
20+
});
21+
22+
it('Test get existing user', async () => {
23+
const existingUser = await userService.createUser('Alice', 'mypassword');
24+
const fetchedUser = await userService.getUser('Alice');
25+
expect(fetchedUser.username).toEqual(existingUser.username);
26+
});
27+
28+
it('Test get non-existing user', async () => {
29+
const nonExistentUser = await userService.getUser('NonExistentUser');
30+
expect(nonExistentUser).toBeUndefined();
31+
});
32+
});
33+
34+
describe('Test messageService', () => {
35+
it('Test add new message', async () => {
36+
const message = await messageService.addMessage('Alice', 'Test message');
37+
expect(message.username).toBe('Alice');
38+
expect(message.content).toBe('Test message');
39+
expect(message.id).toBeDefined();
40+
});
41+
42+
it('Test get messages', async () => {
43+
await messageService.addMessage('Tom', 'Message 1');
44+
await messageService.addMessage('John', 'Message 2');
45+
46+
const messages = await messageService.getMessages();
47+
expect(messages.length).toBe(2);
48+
expect(messages[0].content).toBe('Message 1');
49+
expect(messages[1].content).toBe('Message 2');
50+
});
51+
52+
xit('Test delete the only message', async () => {
53+
const newMessage = await messageService.addMessage('Alice', 'Single message');
54+
const result = await messageService.deleteMessage(newMessage.id);
55+
expect(result).toBe(true);
56+
57+
const messages = await messageService.getMessages();
58+
expect(messages.length).toBe(0);
59+
});
60+
61+
xit('Test delete message with others', async () => {
62+
await messageService.addMessage('Alice', 'Message A');
63+
const messageToDelete = await messageService.addMessage('Alice', 'Message B');
64+
await messageService.addMessage('Alice', 'Message C');
65+
66+
const result = await messageService.deleteMessage(messageToDelete.id);
67+
expect(result).toBe(true);
68+
69+
const messages = await messageService.getMessages();
70+
expect(messages.length).toBe(2);
71+
expect(messages[0].content).toBe('Message A');
72+
expect(messages[1].content).toBe('Message C');
73+
});
74+
75+
xit('Test delete non-existing message', async () => {
76+
const result = await messageService.deleteMessage(999999);
77+
expect(result).toBe(false);
78+
});
79+
});
80+
});

0 commit comments

Comments
 (0)