Add Playwright configuration and update codebase
The commit introduces Playwright configuration for End-to-End testing and modifies several files to optimize the project's structure. It also modifies the middleware to interact with Next.js and fix URL creation. Changes in database types were made to refine their structure.
This commit is contained in:
54
apps/e2e/tests/authentication/auth.po.ts
Normal file
54
apps/e2e/tests/authentication/auth.po.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Page } from '@playwright/test';
|
||||
import { Mailbox } from '../utils/mailbox';
|
||||
|
||||
export class AuthPageObject {
|
||||
private readonly page: Page;
|
||||
private readonly mailbox: Mailbox;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.mailbox = new Mailbox(page);
|
||||
}
|
||||
|
||||
goToSignIn() {
|
||||
return this.page.goto('/auth/sign-in');
|
||||
}
|
||||
|
||||
goToSignUp() {
|
||||
return this.page.goto('/auth/sign-up');
|
||||
}
|
||||
|
||||
async signIn(params: {
|
||||
email: string,
|
||||
password: string
|
||||
}) {
|
||||
await this.page.locator('input[name="email"]').clear();
|
||||
|
||||
await this.page.fill('input[name="email"]', params.email);
|
||||
await this.page.fill('input[name="password"]', params.password);
|
||||
await this.page.click('button[type="submit"]');
|
||||
}
|
||||
|
||||
async signUp(params: {
|
||||
email: string,
|
||||
password: string,
|
||||
repeatPassword: string
|
||||
}) {
|
||||
await this.page.fill('input[name="email"]', params.email);
|
||||
await this.page.fill('input[name="password"]', params.password);
|
||||
await this.page.fill('input[name="repeatPassword"]', params.repeatPassword);
|
||||
await this.page.click('button[type="submit"]');
|
||||
}
|
||||
|
||||
async visitConfirmEmailLink(email: string) {
|
||||
await this.page.waitForTimeout(300);
|
||||
|
||||
return this.mailbox.visitMailbox(email);
|
||||
}
|
||||
|
||||
createRandomEmail() {
|
||||
const value = Math.random() * 1000;
|
||||
|
||||
return `${value.toFixed(0)}@makerkit.dev`;
|
||||
}
|
||||
}
|
||||
57
apps/e2e/tests/authentication/auth.spec.ts
Normal file
57
apps/e2e/tests/authentication/auth.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { AuthPageObject } from './auth.po';
|
||||
|
||||
test.describe('Auth flow', () => {
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
let email: string;
|
||||
|
||||
test('will sign-up and redirect to the home page', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
await auth.goToSignUp();
|
||||
|
||||
email = auth.createRandomEmail();
|
||||
|
||||
console.log(`Signing up with email ${email} ...`);
|
||||
|
||||
await auth.signUp({
|
||||
email,
|
||||
password: 'password',
|
||||
repeatPassword: 'password',
|
||||
});
|
||||
|
||||
await auth.visitConfirmEmailLink(email);
|
||||
|
||||
expect(page.url()).toContain('http://localhost:3000/home');
|
||||
});
|
||||
|
||||
test('will sign-in with the correct credentials', async ({ page }) => {
|
||||
const auth = new AuthPageObject(page);
|
||||
await auth.goToSignIn();
|
||||
|
||||
console.log(`Signing in with email ${email} ...`);
|
||||
|
||||
await auth.signIn({
|
||||
email,
|
||||
password: 'password',
|
||||
});
|
||||
|
||||
await page.waitForURL('http://localhost:3000/home');
|
||||
|
||||
expect(page.url()).toContain('http://localhost:3000/home');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Protected routes', () => {
|
||||
test('will redirect to the sign-in page if not authenticated', async ({ page }) => {
|
||||
await page.goto('/home/settings');
|
||||
|
||||
expect(page.url()).toContain('/auth/sign-in?next=/home/settings');
|
||||
});
|
||||
|
||||
test('will return a 404 for the admin page', async ({ page }) => {
|
||||
await page.goto('/admin')
|
||||
|
||||
expect(page.url()).toContain('/auth/sign-in');
|
||||
});
|
||||
})
|
||||
68
apps/e2e/tests/utils/mailbox.ts
Normal file
68
apps/e2e/tests/utils/mailbox.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Page } from '@playwright/test';
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
export class Mailbox {
|
||||
constructor(
|
||||
private readonly page: Page
|
||||
) {
|
||||
}
|
||||
|
||||
async visitMailbox(email: string) {
|
||||
const mailbox = email.split('@')[0];
|
||||
|
||||
console.log(`Visiting mailbox ${mailbox} ...`)
|
||||
|
||||
if (!mailbox) {
|
||||
throw new Error('Invalid email');
|
||||
}
|
||||
|
||||
const json = await this.getInviteEmail(mailbox);
|
||||
|
||||
const html = (json.body as { html: string }).html;
|
||||
const el = parse(html);
|
||||
|
||||
const linkHref = el.querySelector('a')?.getAttribute('href');
|
||||
|
||||
if (!linkHref) {
|
||||
throw new Error('No link found in email');
|
||||
}
|
||||
|
||||
console.log(`Visiting ${linkHref} ...`);
|
||||
|
||||
return this.page.goto(linkHref);
|
||||
}
|
||||
|
||||
async getInviteEmail(
|
||||
mailbox: string,
|
||||
params = {
|
||||
deleteAfter: true
|
||||
}
|
||||
) {
|
||||
const url = `http://localhost:54324/api/v1/mailbox/${mailbox}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
const json = (await response.json()) as Array<{ id: string }>;
|
||||
|
||||
if (!json || !json.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messageId = json[0]?.id;
|
||||
const messageUrl = `${url}/${messageId}`;
|
||||
|
||||
const messageResponse = await fetch(messageUrl);
|
||||
|
||||
if (!messageResponse.ok) {
|
||||
throw new Error(`Failed to fetch email: ${messageResponse.statusText}`);
|
||||
}
|
||||
|
||||
// delete message
|
||||
if (params.deleteAfter) {
|
||||
await fetch(messageUrl, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
return await messageResponse.json();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user