Files
imphnen-frontend-service/apps/dimentorin/e2e/screenshots.test.ts
T
Asep Haryana SaputraandGitHub 48411d3a08 Dashboard users part 2 (#79)
* fix: add FigmaImage directory to .gitignore

* feat: enhance dashboard tests and add new components for rendering

* feat: add navigation functionality and UI enhancements to dashboard components

* feat: add article preview and mentor contact modals, implement article builder and FAQ page

* refactor: remove FAQ route and update settings layout for improved responsiveness

* feat: enhance two-step authentication UI and functionality with improved styles and user feedback

* feat: add mentoring detail and feedback modals, enhance mentoring page functionality and UI

* fix: handle JSON parse failure in edit profile modal error handling
2026-04-15 12:20:05 +07:00

216 lines
8.8 KiB
TypeScript

import { test, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';
/**
* Dynamically extract all routes from the TanStack Router generated file.
*/
function getDynamicRoutes(): string[] {
const routeTreePath = path.resolve(__dirname, '../src/routeTree.gen.ts');
if (!fs.existsSync(routeTreePath)) {
console.warn('Route tree file not found, falling back to basic routes');
return ['/'];
}
const content = fs.readFileSync(routeTreePath, 'utf-8');
// Extract the FileRoutesByFullPath interface block
const interfaceMatch = content.match(/export interface FileRoutesByFullPath \{([\s\S]*?)\}/);
if (!interfaceMatch) return ['/'];
const block = interfaceMatch[1];
// Extract all strings in single quotes
const routeMatches = block.match(/'([^']+)'/g);
if (!routeMatches) return ['/'];
return routeMatches.map(m => {
let route = m.replace(/'/g, '');
// Normalize trailing slashes (Optional cleanup)
if (route !== '/' && route.endsWith('/')) {
route = route.slice(0, -1);
}
// Replace dynamic parameters with sample values
return route
.replace(/\$slug/g, 'sample-article')
.replace(/\$id/g, 'sample-id')
.replace(/\$taskId/g, 'sample-task');
});
}
test.describe('Automated Route Discovery & Screenshots', () => {
const baseURL = process.env.BASE_URL || 'http://localhost:3000';
const screenshotDir = path.resolve(__dirname, '../screenshots');
const routes = [...new Set(getDynamicRoutes())]; // Unique routes
test.beforeAll(() => {
if (!fs.existsSync(screenshotDir)) {
fs.mkdirSync(screenshotDir, { recursive: true });
}
});
console.log(`Discovered ${routes.length} routes for processing.`);
for (const route of routes) {
test(`capture screenshot: ${route}`, async ({ page }) => {
console.log(`Processing: ${baseURL}${route}`);
try {
// Mock auth session if accessing protected routes
if (route.startsWith('/dashboard') || route.startsWith('/profile')) {
await page.addInitScript(() => {
window.localStorage.setItem('auth-storage', JSON.stringify({
state: {
session: {
user: {
id: 'sample-user-id',
fullname: 'John Doe',
email: 'john@example.com',
avatar: '/image/mascot-character.webp'
},
token: 'mock-token'
}
},
version: 0
}));
});
}
await page.goto(`${baseURL}${route}`, {
waitUntil: 'networkidle',
timeout: 60000
});
// Wait for rendering
await page.waitForTimeout(1500);
const fileName = route.replace(/\//g, '_').replace(/^_/, '').replace(/[:$]/g, '') || 'home';
const screenshotPath = path.join(screenshotDir, `${fileName}.png`);
await page.screenshot({
path: screenshotPath,
fullPage: true
});
console.log(`Success: ${fileName}.png`);
// --- Custom logic for Modals and Tabs ---
// 1. Mentoring Modals
if (route === '/dashboard/mentoring') {
// Detail modal
const detailButton = page.getByRole('button', { name: 'Cek Detail' }).first();
if (await detailButton.isVisible()) {
await detailButton.click({ timeout: 5000 });
const detailModal = page.locator('div.fixed.inset-0').filter({ hasText: 'Detail Sesi Mentoring' }).first();
await detailModal.waitFor({ state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_mentoring_modal_detail.png') });
await detailModal.locator('button').first().click({ timeout: 5000 });
await detailModal.waitFor({ state: 'hidden', timeout: 5000 });
}
// Contact modal
const cancelButton = page.getByRole('button', { name: 'Cancel' }).first();
if (await cancelButton.isVisible()) {
await cancelButton.click({ timeout: 5000 });
const contactModal = page.locator('div.fixed.inset-0').filter({ hasText: 'Hubungi mentor melalui platform berikut' }).first();
await contactModal.waitFor({ state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_mentoring_modal_contact.png') });
await contactModal.getByRole('button', { name: 'Tutup' }).click({ timeout: 5000 });
await contactModal.waitFor({ state: 'hidden', timeout: 5000 });
}
// Feedback modal
const feedbackBtn = page.getByRole('button', { name: 'Kirim Feedback' }).first();
if (await feedbackBtn.isVisible()) {
await feedbackBtn.click({ timeout: 5000 });
const feedbackModal = page
.locator('div.fixed.inset-0')
.filter({ hasText: /Beri Feedback|Feedback Mentor/ })
.first();
await feedbackModal.waitFor({ state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_mentoring_modal_feedback.png') });
const closeFeedbackBtn = feedbackModal.getByRole('button', { name: 'Batal' });
if (await closeFeedbackBtn.isVisible()) {
await closeFeedbackBtn.click({ timeout: 5000 });
await feedbackModal.waitFor({ state: 'hidden', timeout: 5000 });
}
}
}
// 2. Learning Path Tabs
if (route === '/dashboard/learning-path') {
await page.click('button:has-text("Article")');
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_learning-path_tab_article.png') });
}
// 3. Settings Sections & 2FA Modals
if (route === '/dashboard/settings') {
const sections = ['Privasi & Keamanan', 'FAQ', 'Laporkan Kendala', 'Umpan Balik'];
for (const section of sections) {
const sectionButton = page.getByRole('button', { name: section }).first();
if (!(await sectionButton.isVisible())) {
console.warn(`Section button not found: ${section}`);
continue;
}
await sectionButton.click({ timeout: 5000 });
await page.waitForTimeout(300);
const slug = section
.toLowerCase()
.replace(/&/g, 'and')
.replace(/\s+/g, '_')
.replace(/[^a-z0-9_]/g, '');
await page.screenshot({ path: path.join(screenshotDir, `dashboard_settings_section_${slug}.png`) });
// If in Privacy, test 2FA modal
if (section === 'Privasi & Keamanan') {
const enable2FaButton = page.getByRole('button', { name: 'Aktifkan 2FA' }).first();
if (!(await enable2FaButton.isVisible())) {
console.warn('2FA trigger button not found');
continue;
}
await enable2FaButton.click({ timeout: 5000 });
const emailModal = page.locator('div.fixed.inset-0').filter({ hasText: 'Verifikasi Email' }).first();
await emailModal.waitFor({ state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_settings_modal_2fa_step1.png') });
await emailModal.getByRole('button', { name: 'Kirim Kode' }).click({ timeout: 5000 });
const otpModal = page.locator('div.fixed.inset-0').filter({ hasText: 'Masukkan Kode OTP' }).first();
await otpModal.waitFor({ state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_settings_modal_2fa_step2.png') });
await otpModal.locator('button').first().click({ timeout: 5000 });
await otpModal.waitFor({ state: 'hidden', timeout: 5000 });
}
}
}
// 4. Header Notifications
if (route === '/dashboard') {
const notificationButton = page.locator('header button').first();
if (await notificationButton.isVisible()) {
await notificationButton.click({ timeout: 5000 });
await page.waitForTimeout(500);
await page.screenshot({ path: path.join(screenshotDir, 'dashboard_header_notifications.png') });
} else {
console.warn('Notification button not found on dashboard header');
}
}
} catch (error) {
console.error(`Failed to capture ${route}:`, error.message);
}
});
}
});