Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cleanup] feat: Delete user cleanup api #45

Merged
merged 5 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ router.post('/api/users', async (req, res) => {
}
});

router.delete('/api/users/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
router.delete('/api/users/', authenticateToken, async (req, res) => {
// get id from decoded jwt token
const id = req.user.userId;

try {
await prisma.user.delete({ where: { id } });
Expand Down
13 changes: 10 additions & 3 deletions backend/tests/service/users.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const email = faker.internet.email();
const password = faker.internet.password();

let token: string;
let createdID: string;

describe('Unauthenticated Flows', () => {
test('Should not be able to get the list of Users', async () => {
Expand Down Expand Up @@ -66,12 +65,20 @@ describe('Authenticated Flows', () => {
});
expect(response.status).toBe(200);
expect(response.body.id).toBeDefined();
createdID = response.body.id;
});

test('Delete a User', async () => {
const newUserLogin = await request(BASE_URL).post('/api/login').send({
password,
username,
});
expect(newUserLogin.status).toBe(200);
expect(newUserLogin.body.token).toBeDefined();
// Set Token for future requests
token = newUserLogin.body.token;

const response = await request(USERS_URL)
.delete(`/${createdID}`)
.delete('/')
.set('Authorization', `Bearer ${token}`);
expect(response.status).toBe(204);
});
Expand Down
8 changes: 2 additions & 6 deletions backend/tests/unit/users.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,15 @@ describe('Delete User', () => {
createdAt: new Date('2024-02-05T23:33:42.252Z'),
updatedAt: new Date('2024-02-05T23:33:42.252Z'),
});
const response = await request(app).delete(
'/api/users/gcf89a7e-b941-4f17-bbe0-4e0c8b2cd272'
);
const response = await request(app).delete('/api/users/');
expect(response.status).toBe(204);
});

test('DELETE with error', async ({}) => {
prisma.user.delete.mockImplementation(() => {
throw new Error('Test error');
});
const response = await request(app).delete(
'/api/users/gcf89a7e-b941-4f17-bbe0-4e0c8b2cd272'
);
const response = await request(app).delete('/api/users/');
expect(response.status).toBe(500);
expect(response.body).toStrictEqual({
error: 'Oops, something went wrong',
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/api/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ export const createUser = async ( user:
return response;
}

export const deleteUser = async (id: string) => {
const response = await api.delete(`users/${id}`);
export const deleteUser = async () => {
const response = await api.delete('users', {
headers: {
"Content-Type": "application/json",
},
});
return response;
}
31 changes: 31 additions & 0 deletions playwright/helpers/apiLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { request, expect, APIResponse } from '@playwright/test';
import getApiUrl from './getApiUrl';

const apiLogin = async (options: {
username: string;
password: string;
}): Promise<APIResponse> => {
if (!options.username || !options.password) {
throw new Error('Username and Password is required to login');
}

const context = await request.newContext({
baseURL: await getApiUrl(),
});

const loginResponse = await context.post('/api/login', {
headers: {
'Content-Type': 'application/json',
},
data: {
username: options.username,
password: options.password,
},
});

expect(loginResponse.ok()).toBeTruthy();
expect(loginResponse.status()).toBe(200);
return loginResponse;
};

export default apiLogin;
26 changes: 26 additions & 0 deletions playwright/helpers/deleteUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { request, expect, APIResponse } from '@playwright/test';
import getApiUrl from './getApiUrl';

const deleteUser = async (token: string | null): Promise<APIResponse> => {
if (!token) {
throw new Error('Token is required to delete user');
}

const context = await request.newContext({
baseURL: await getApiUrl(),
});

const deleteResponse = await context.delete('/api/users', {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});

expect(deleteResponse.ok()).toBeTruthy();
expect(deleteResponse.status()).toBe(204);

return deleteResponse;
};

export default deleteUser;
17 changes: 17 additions & 0 deletions playwright/helpers/getApiUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import playwrightConfig from '../playwright.config';

const getApiUrl = async () => {
const baseURL = playwrightConfig.use?.baseURL;

const apiURL = baseURL?.includes('localhost')
? 'http://localhost:5000'
: 'https://notes-app-full-stack-be.onrender.com';

if (!baseURL) {
throw new Error('Base URL is required to make API requests');
}

return apiURL;
};

export default getApiUrl;
5 changes: 5 additions & 0 deletions playwright/pages/RegistrationPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class RegistrationPage {
accountHeader = () =>
this.page.getByRole('heading', { name: 'Register new account' });
errorMessage = () => this.page.locator('.registration-form-error');
spinnerContainer = () => this.page.getByTestId('spinner-container');

goto = async () => {
await this.page.goto('/register');
Expand Down Expand Up @@ -56,6 +57,10 @@ class RegistrationPage {
});
}
};

getLocalStorage = async () => {
return await this.page.evaluate(() => JSON.stringify(window.localStorage));
};
}

export default RegistrationPage;
24 changes: 22 additions & 2 deletions playwright/tests/registration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { test, expect, Page } from '@playwright/test';
import { test, expect, Page, APIResponse } from '@playwright/test';
import { faker } from '@faker-js/faker';
import { allure } from 'allure-playwright';
import RegistrationPage from '../pages/RegistrationPage';
import deleteUser from '../helpers/deleteUser';
import apiLogin from '../helpers/apiLogin';

let page: Page;
let registrationPage: RegistrationPage;
const timeout = 60 * 1000; // Render.com free tier may take 60 seconds to startup
let token: string;

const username = faker.internet.userName().toLowerCase();
const email = faker.internet.email();
Expand All @@ -19,7 +22,7 @@ test.beforeAll(async ({ browser }) => {
await registrationPage.goto();
});

test.describe('User Registration', async () => {
test.describe('User Registration', { tag: ['@PRODUCTION'] }, async () => {
test('Should be able to register new user', async () => {
await expect(registrationPage.accountHeader()).toBeVisible();

Expand All @@ -30,7 +33,24 @@ test.describe('User Registration', async () => {
});

await expect(registrationPage.successMessage()).toBeVisible({ timeout });

await test.step('Should be able to login with newly registered user', async () => {
const loginResponse: APIResponse = await apiLogin({ username, password });

expect(loginResponse.ok()).toBeTruthy();

const json = await loginResponse.json();
token = json.token;

expect(json.token).toBeTruthy();
});

await test.step('Cleanup: Delete User', async () => {
const deleteUserResponse = await deleteUser(token);
expect(deleteUserResponse.ok()).toBeTruthy();
});
});

test('Should not be able to register with existing username', async () => {
await registrationPage.goto();

Expand Down
2 changes: 1 addition & 1 deletion playwright/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"strict": true,
"skipLibCheck": true
},
"include": ["src", "tests", "pages", "./.eslintrc.js", "./playwright.config.ts"],
"include": ["src", "tests", "pages", "helpers", "./.eslintrc.js", "./playwright.config.ts", "cleanup/Cleanup.ts"],
"exclude": ["node_modules", "dist"],
"overrides": [
{
Expand Down
Loading