test(ui): add e2e and integration tests
This commit is contained in:
38
tests/e2e/admin.spec.js
Normal file
38
tests/e2e/admin.spec.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Admin Panel', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/admin');
|
||||
});
|
||||
|
||||
test('displays admin navigation', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'CV Admin' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('navigation links work', async ({ page }) => {
|
||||
await page.click('text=Erfahrung');
|
||||
await expect(page).toHaveURL(/.*experience/);
|
||||
|
||||
await page.click('text=Skills');
|
||||
await expect(page).toHaveURL(/.*skills/);
|
||||
|
||||
await page.click('text=Persönlich');
|
||||
await expect(page).toHaveURL(/.*admin$/);
|
||||
});
|
||||
|
||||
test('personal form displays fields', async ({ page }) => {
|
||||
const form = page.locator('form');
|
||||
await expect(form.locator('input[name="name"]')).toBeVisible();
|
||||
await expect(form.locator('input[name="title"]')).toBeVisible();
|
||||
await expect(form.locator('input[name="email"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('can navigate to experience section', async ({ page }) => {
|
||||
await page.click('text=Erfahrung');
|
||||
await expect(page.getByRole('heading', { name: 'Berufserfahrung' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('export button is present', async ({ page }) => {
|
||||
await expect(page.getByRole('button', { name: /json export/i })).toBeVisible();
|
||||
});
|
||||
});
|
||||
39
tests/e2e/cv.spec.js
Normal file
39
tests/e2e/cv.spec.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('CV Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('displays hero section with name', async ({ page }) => {
|
||||
await expect(page.locator('h1')).toContainText('Tuan-Dat Tran');
|
||||
});
|
||||
|
||||
test('displays job title', async ({ page }) => {
|
||||
const heroTitle = page.locator('section').first().locator('h2');
|
||||
await expect(heroTitle).toContainText('DevOps Engineer');
|
||||
});
|
||||
|
||||
test('displays experience section', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Berufserfahrung' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays skills section', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Skills' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays education section', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Ausbildung' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('displays projects section', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Projekte' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('contact links are present', async ({ page }) => {
|
||||
const heroSection = page.locator('section').first();
|
||||
await expect(heroSection.getByRole('link', { name: 'Email' })).toBeVisible();
|
||||
await expect(heroSection.getByRole('link', { name: 'GitHub' })).toBeVisible();
|
||||
await expect(heroSection.getByRole('link', { name: 'LinkedIn' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
17
tests/integration/docker-compose.test.yml
Normal file
17
tests/integration/docker-compose.test.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend-test:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "3002:3001"
|
||||
environment:
|
||||
- PORT=3001
|
||||
- DB_PATH=/app/data/test.db
|
||||
volumes:
|
||||
- test-data:/app/data
|
||||
|
||||
volumes:
|
||||
test-data:
|
||||
205
tests/integration/fullstack.test.js
Normal file
205
tests/integration/fullstack.test.js
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import { mkdirSync, existsSync, rmSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
const testDbPath = join(process.cwd(), 'tests', 'integration', 'test.db');
|
||||
let app;
|
||||
let db;
|
||||
|
||||
function setupTestDB() {
|
||||
const dbDir = join(process.cwd(), 'tests', 'integration');
|
||||
if (!existsSync(dbDir)) {
|
||||
mkdirSync(dbDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (existsSync(testDbPath)) {
|
||||
rmSync(testDbPath);
|
||||
}
|
||||
|
||||
db = new Database(testDbPath);
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS cv_data (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
data TEXT NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
const defaultData = {
|
||||
personal: { name: 'Test User', title: 'Developer', email: 'test@test.com' },
|
||||
experience: [],
|
||||
skills: {},
|
||||
education: [],
|
||||
projects: []
|
||||
};
|
||||
|
||||
db.prepare('INSERT INTO cv_data (id, data) VALUES (1, ?)').run(JSON.stringify(defaultData));
|
||||
}
|
||||
|
||||
function createTestApp() {
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/api/cv', (req, res) => {
|
||||
const row = db.prepare('SELECT data FROM cv_data WHERE id = 1').get();
|
||||
if (!row) return res.status(404).json({ error: 'CV data not found' });
|
||||
res.json(JSON.parse(row.data));
|
||||
});
|
||||
|
||||
app.put('/api/cv', (req, res) => {
|
||||
const data = req.body;
|
||||
if (!data?.personal?.name) {
|
||||
return res.status(400).json({ error: 'personal.name is required' });
|
||||
}
|
||||
db.prepare('UPDATE cv_data SET data = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1')
|
||||
.run(JSON.stringify(data));
|
||||
res.json({ success: true, message: 'CV data updated' });
|
||||
});
|
||||
|
||||
app.get('/api/cv/export', (req, res) => {
|
||||
const row = db.prepare('SELECT data FROM cv_data WHERE id = 1').get();
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="cv.json"');
|
||||
res.send(row.data);
|
||||
});
|
||||
|
||||
app.post('/api/cv/import', (req, res) => {
|
||||
const data = req.body;
|
||||
if (!data?.personal?.name) {
|
||||
return res.status(400).json({ error: 'personal.name is required' });
|
||||
}
|
||||
db.prepare('UPDATE cv_data SET data = ?, updated_at = CURRENT_TIMESTAMP WHERE id = 1')
|
||||
.run(JSON.stringify(data));
|
||||
res.json({ success: true, message: 'CV data imported' });
|
||||
});
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('Integration: Full Stack CV API', () => {
|
||||
beforeAll(() => {
|
||||
setupTestDB();
|
||||
app = createTestApp();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
if (existsSync(testDbPath)) {
|
||||
rmSync(testDbPath);
|
||||
}
|
||||
});
|
||||
|
||||
describe('GET /api/cv', () => {
|
||||
it('returns CV data from database', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('personal');
|
||||
expect(response.body.personal.name).toBe('Test User');
|
||||
});
|
||||
|
||||
it('returns all required CV sections', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body).toHaveProperty('personal');
|
||||
expect(response.body).toHaveProperty('experience');
|
||||
expect(response.body).toHaveProperty('skills');
|
||||
expect(response.body).toHaveProperty('education');
|
||||
expect(response.body).toHaveProperty('projects');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /api/cv', () => {
|
||||
it('updates CV data in database', async () => {
|
||||
const newData = {
|
||||
personal: { name: 'Updated Name', title: 'Senior Dev', email: 'updated@test.com' },
|
||||
experience: [{ id: 1, role: 'New Role' }],
|
||||
skills: { 'Backend': ['Node.js'] },
|
||||
education: [],
|
||||
projects: []
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.put('/api/cv')
|
||||
.send(newData);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
|
||||
const verifyResponse = await request(app).get('/api/cv');
|
||||
expect(verifyResponse.body.personal.name).toBe('Updated Name');
|
||||
expect(verifyResponse.body.experience).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('validates required fields', async () => {
|
||||
const response = await request(app)
|
||||
.put('/api/cv')
|
||||
.send({ personal: {} });
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.error).toContain('name is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/cv/export', () => {
|
||||
it('exports CV as JSON file', async () => {
|
||||
const response = await request(app).get('/api/cv/export');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers['content-type']).toContain('application/json');
|
||||
expect(response.headers['content-disposition']).toContain('cv.json');
|
||||
|
||||
const data = JSON.parse(response.text);
|
||||
expect(data).toHaveProperty('personal');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/cv/import', () => {
|
||||
it('imports valid CV data', async () => {
|
||||
const importData = {
|
||||
personal: { name: 'Imported User', title: 'Dev', email: 'import@test.com' },
|
||||
experience: [],
|
||||
skills: {},
|
||||
education: [],
|
||||
projects: []
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/cv/import')
|
||||
.send(importData);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
|
||||
const verifyResponse = await request(app).get('/api/cv');
|
||||
expect(verifyResponse.body.personal.name).toBe('Imported User');
|
||||
});
|
||||
|
||||
it('rejects invalid CV data', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/cv/import')
|
||||
.send({ personal: { title: 'No Name' } });
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /health', () => {
|
||||
it('returns health status', async () => {
|
||||
const response = await request(app).get('/health');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.status).toBe('ok');
|
||||
expect(response.body).toHaveProperty('timestamp');
|
||||
});
|
||||
});
|
||||
});
|
||||
55
tests/performance/k6/load.js
Normal file
55
tests/performance/k6/load.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import http from 'k6/http';
|
||||
import { check, sleep } from 'k6';
|
||||
import { Rate, Trend } from 'k6/metrics';
|
||||
|
||||
const errorRate = new Rate('errors');
|
||||
const responseTime = new Trend('response_time');
|
||||
|
||||
export const options = {
|
||||
stages: [
|
||||
{ duration: '30s', target: 10 },
|
||||
{ duration: '1m', target: 50 },
|
||||
{ duration: '30s', target: 100 },
|
||||
{ duration: '30s', target: 0 },
|
||||
],
|
||||
thresholds: {
|
||||
errors: ['rate<0.1'],
|
||||
http_req_duration: ['p(95)<500', 'p(99)<1000'],
|
||||
response_time: ['p(95)<500'],
|
||||
},
|
||||
};
|
||||
|
||||
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001';
|
||||
|
||||
export default function () {
|
||||
const response = http.get(`${BASE_URL}/api/cv`);
|
||||
|
||||
check(response, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
'has personal data': (r) => {
|
||||
const body = r.json();
|
||||
return body.personal && body.personal.name;
|
||||
},
|
||||
'has experience array': (r) => {
|
||||
const body = r.json();
|
||||
return Array.isArray(body.experience);
|
||||
},
|
||||
'has skills object': (r) => {
|
||||
const body = r.json();
|
||||
return body.skills && typeof body.skills === 'object';
|
||||
},
|
||||
'response time < 200ms': (r) => r.timings.duration < 200,
|
||||
});
|
||||
|
||||
errorRate.add(response.status !== 200);
|
||||
responseTime.add(response.timings.duration);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
export function handleSummary(data) {
|
||||
return {
|
||||
'stdout': JSON.stringify(data, null, 2),
|
||||
'tests/performance/results.json': JSON.stringify(data, null, 2),
|
||||
};
|
||||
}
|
||||
21
tests/performance/lighthouserc.js
Normal file
21
tests/performance/lighthouserc.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
ci: {
|
||||
collect: {
|
||||
numberOfRuns: 3,
|
||||
settings: {
|
||||
preset: 'desktop',
|
||||
},
|
||||
},
|
||||
assert: {
|
||||
assertions: {
|
||||
'categories:performance': ['warn', { minScore: 0.8 }],
|
||||
'categories:accessibility': ['error', { minScore: 0.9 }],
|
||||
'categories:best-practices': ['warn', { minScore: 0.8 }],
|
||||
'categories:seo': ['warn', { minScore: 0.8 }],
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
target: 'temporary-public-storage',
|
||||
},
|
||||
},
|
||||
};
|
||||
109
tests/regression/__snapshots__/api-snapshot.test.js.snap
Normal file
109
tests/regression/__snapshots__/api-snapshot.test.js.snap
Normal file
@@ -0,0 +1,109 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Regression: API Snapshots > GET /api/cv response matches snapshot > cv-data-response 1`] = `
|
||||
{
|
||||
"education": [
|
||||
{
|
||||
"degree": "CS Degree",
|
||||
"id": 1,
|
||||
"institution": "Test University",
|
||||
"period": "2016-2020",
|
||||
"status": "Completed",
|
||||
},
|
||||
],
|
||||
"experience": [
|
||||
{
|
||||
"company": "Test Corp",
|
||||
"description": "Testing",
|
||||
"highlights": [
|
||||
"Automated tests",
|
||||
],
|
||||
"id": 1,
|
||||
"period": "2020-2024",
|
||||
"role": "QA Engineer",
|
||||
"type": "Vollzeit",
|
||||
},
|
||||
],
|
||||
"personal": {
|
||||
"email": "regression@test.com",
|
||||
"name": "Regression Test User",
|
||||
"title": "Test Engineer",
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"description": "Test framework",
|
||||
"id": 1,
|
||||
"name": "Test Project",
|
||||
"tech": [
|
||||
"Vitest",
|
||||
],
|
||||
"url": "https://test.com",
|
||||
},
|
||||
],
|
||||
"skills": {
|
||||
"Testing": [
|
||||
"Vitest",
|
||||
"Playwright",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Regression: API Snapshots > education section matches snapshot > education-section 1`] = `
|
||||
[
|
||||
{
|
||||
"degree": "CS Degree",
|
||||
"id": 1,
|
||||
"institution": "Test University",
|
||||
"period": "2016-2020",
|
||||
"status": "Completed",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Regression: API Snapshots > experience section matches snapshot > experience-section 1`] = `
|
||||
[
|
||||
{
|
||||
"company": "Test Corp",
|
||||
"description": "Testing",
|
||||
"highlights": [
|
||||
"Automated tests",
|
||||
],
|
||||
"id": 1,
|
||||
"period": "2020-2024",
|
||||
"role": "QA Engineer",
|
||||
"type": "Vollzeit",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Regression: API Snapshots > personal section matches snapshot > personal-section 1`] = `
|
||||
{
|
||||
"email": "regression@test.com",
|
||||
"name": "Regression Test User",
|
||||
"title": "Test Engineer",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Regression: API Snapshots > projects section matches snapshot > projects-section 1`] = `
|
||||
[
|
||||
{
|
||||
"description": "Test framework",
|
||||
"id": 1,
|
||||
"name": "Test Project",
|
||||
"tech": [
|
||||
"Vitest",
|
||||
],
|
||||
"url": "https://test.com",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Regression: API Snapshots > skills section matches snapshot > skills-section 1`] = `
|
||||
{
|
||||
"Testing": [
|
||||
"Vitest",
|
||||
"Playwright",
|
||||
],
|
||||
}
|
||||
`;
|
||||
104
tests/regression/api-snapshot.test.js
Normal file
104
tests/regression/api-snapshot.test.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import { mkdirSync, existsSync, rmSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
const testDbPath = join(process.cwd(), 'tests', 'regression', 'api-snapshots', 'test.db');
|
||||
const snapshotDir = join(process.cwd(), 'tests', 'regression', 'api-snapshots');
|
||||
let app;
|
||||
let db;
|
||||
|
||||
function setupTestDB() {
|
||||
if (!existsSync(snapshotDir)) {
|
||||
mkdirSync(snapshotDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (existsSync(testDbPath)) {
|
||||
rmSync(testDbPath);
|
||||
}
|
||||
|
||||
db = new Database(testDbPath);
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS cv_data (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
data TEXT NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
const defaultData = {
|
||||
personal: { name: 'Regression Test User', title: 'Test Engineer', email: 'regression@test.com' },
|
||||
experience: [{ id: 1, role: 'QA Engineer', company: 'Test Corp', period: '2020-2024', type: 'Vollzeit', description: 'Testing', highlights: ['Automated tests'] }],
|
||||
skills: { 'Testing': ['Vitest', 'Playwright'] },
|
||||
education: [{ id: 1, degree: 'CS Degree', institution: 'Test University', period: '2016-2020', status: 'Completed' }],
|
||||
projects: [{ id: 1, name: 'Test Project', description: 'Test framework', url: 'https://test.com', tech: ['Vitest'] }]
|
||||
};
|
||||
|
||||
db.prepare('INSERT INTO cv_data (id, data) VALUES (1, ?)').run(JSON.stringify(defaultData));
|
||||
}
|
||||
|
||||
function createTestApp() {
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/api/cv', (req, res) => {
|
||||
const row = db.prepare('SELECT data FROM cv_data WHERE id = 1').get();
|
||||
if (!row) return res.status(404).json({ error: 'CV data not found' });
|
||||
res.json(JSON.parse(row.data));
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('Regression: API Snapshots', () => {
|
||||
beforeAll(() => {
|
||||
setupTestDB();
|
||||
app = createTestApp();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
if (existsSync(testDbPath)) {
|
||||
rmSync(testDbPath);
|
||||
}
|
||||
});
|
||||
|
||||
it('GET /api/cv response matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
expect(response.body).toMatchSnapshot('cv-data-response');
|
||||
});
|
||||
|
||||
it('personal section matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body.personal).toMatchSnapshot('personal-section');
|
||||
});
|
||||
|
||||
it('experience section matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body.experience).toMatchSnapshot('experience-section');
|
||||
});
|
||||
|
||||
it('skills section matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body.skills).toMatchSnapshot('skills-section');
|
||||
});
|
||||
|
||||
it('education section matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body.education).toMatchSnapshot('education-section');
|
||||
});
|
||||
|
||||
it('projects section matches snapshot', async () => {
|
||||
const response = await request(app).get('/api/cv');
|
||||
expect(response.body.projects).toMatchSnapshot('projects-section');
|
||||
});
|
||||
});
|
||||
44
tests/regression/visual.test.js
Normal file
44
tests/regression/visual.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Visual Regression: CV Page', () => {
|
||||
test('hero section visual snapshot', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const hero = page.locator('section').first();
|
||||
await expect(hero).toHaveScreenshot('hero-section.png', {
|
||||
maxDiffPixels: 100,
|
||||
});
|
||||
});
|
||||
|
||||
test('full page visual snapshot', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveScreenshot('full-page.png', {
|
||||
fullPage: true,
|
||||
maxDiffPixels: 500,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Visual Regression: Admin Page', () => {
|
||||
test('admin panel visual snapshot', async ({ page }) => {
|
||||
await page.goto('/admin');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveScreenshot('admin-panel.png', {
|
||||
maxDiffPixels: 200,
|
||||
});
|
||||
});
|
||||
|
||||
test('personal form visual snapshot', async ({ page }) => {
|
||||
await page.goto('/admin');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const form = page.locator('form').first();
|
||||
await expect(form).toHaveScreenshot('personal-form.png', {
|
||||
maxDiffPixels: 100,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user