feat: Establish Playwright testing infrastructure with initial tests for authentication, admin, and instructor modules, and fix instructor video and quiz lesson management pages.
All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 1m17s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 8s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 2s

This commit is contained in:
Missez 2026-03-02 15:48:47 +07:00
parent 734d922393
commit 9bc24fbe8a
18 changed files with 1344 additions and 7 deletions

View file

@ -0,0 +1,96 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
/**
* Admin Audit Log Page Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Audit Log', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminAuditLog);
await page.waitForLoadState('networkidle');
});
test('check display page', async ({ page }) => {
// Header
await expect(page.getByText('Audit Logs', { exact: true })).toBeVisible();
await expect(page.getByText('ประวัติการใช้งานระบบและกิจกรรมต่างๆ')).toBeVisible();
// Stats cards
await expect(page.getByText('Logs ทั้งหมด')).toBeVisible();
await expect(page.getByText('Logs วันนี้')).toBeVisible();
await expect(page.getByRole('button', { name: 'รีเฟรช' })).toBeVisible();
await expect(page.getByRole('button', { name: 'ล้างประวัติเก่า' })).toBeVisible();
});
test('should refresh data when clicking refresh button', async ({ page }) => {
const refreshBtn = page.getByRole('button', { name: 'รีเฟรช' });
await refreshBtn.click();
await page.waitForLoadState('networkidle');
// Table should still be visible after refresh
await expect(page.locator('.q-table')).toBeVisible();
});
test('should filter by Action', async ({ page }) => {
// Click the Action select
const actionSelect = page.locator('label').filter({ hasText: 'Action' });
await actionSelect.click();
await page.waitForTimeout(300);
// Select an option from dropdown
const option = page.locator('.q-item__label').filter({ hasText: 'LOGIN' }).first();
await option.click();
// Click search
await page.getByRole('button', { name: 'ค้นหา' }).click();
await page.waitForLoadState('networkidle');
// Table should be visible
await expect(page.locator('.q-table')).toBeVisible();
await page.getByRole('button', { name: 'ล้างตัวกรอง' }).click();
await page.waitForLoadState('networkidle');
// Entity type input should be cleared
await expect(option).not.toBeVisible();
});
test('check open details dialog', async ({ page }) => {
// Wait for table data to load
const viewBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await viewBtn.isVisible().catch(() => false);
if (hasData) {
await viewBtn.click();
await page.waitForTimeout(300);
// Details dialog should be visible
await expect(page.getByText('รายละเอียด Audit Log')).toBeVisible();
// Close button
await page.getByRole('button', { name: 'ปิด' }).click();
}
});
test('check close cleanup dialog on cancel', async ({ page }) => {
await page.getByRole('button', { name: 'ล้างประวัติเก่า' }).click();
await page.waitForTimeout(300);
await expect(page.locator('.q-dialog')).toBeVisible();
// Click cancel
await page.getByRole('button', { name: 'ยกเลิก' }).click();
await expect(page.locator('.q-dialog')).not.toBeVisible();
});
test('check close cleanup dialog on confirm', async ({ page }) => {
await page.getByRole('button', { name: 'ล้างประวัติเก่า' }).click();
await page.waitForTimeout(300);
await expect(page.locator('.q-dialog')).toBeVisible();
// Click confirm
await page.getByRole('button', { name: 'ลบข้อมูล', exact: true }).click();
await expect(page.locator('.q-dialog')).not.toBeVisible();
});
});

View file

@ -0,0 +1,158 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
import { faker } from '@faker-js/faker';
/**
* Admin Categories Page Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Categories', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminCategories);
await page.waitForLoadState('networkidle');
});
test('check display page title and table', async ({ page }) => {
await expect(page.getByText('รายการหมวดหมู่')).toBeVisible();
await expect(page.locator('.q-table')).toBeVisible();
await expect(page.getByText('ชื่อหมวดหมู่')).toBeVisible();
await expect(page.getByText('คำอธิบาย')).toBeVisible();
await expect(page.getByText('วันที่สร้าง')).toBeVisible();
await expect(page.getByText('การจัดการ')).toBeVisible();
});
test('check search input', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="ค้นหาหมวดหมู่"]');
await expect(searchInput).toBeVisible();
await searchInput.fill('ธุรกิจ');
await page.waitForTimeout(500);
await expect(page.getByText('ธุรกิจ', { exact: true })).toBeVisible();
});
test('check filter categories by search', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="ค้นหาหมวดหมู่"]');
await searchInput.fill('ไม่มีหมวดหมู่นี้แน่นอน');
await page.waitForTimeout(500);
// ตารางควรไม่มีข้อมูล
await expect(page.getByText('No data available')).toBeVisible();
});
test('should add new category successfully', async ({ page }) => {
// สร้างข้อมูลสุ่มด้วย faker
const nameTh = faker.word.noun();
const nameEn = faker.commerce.department();
const slug = faker.helpers.slugify(nameEn.toLowerCase()) + '-' + faker.string.alpha({ length: 6, casing: 'lower' });
const descTh = faker.lorem.sentence();
const descEn = faker.lorem.sentence();
// 1. กดปุ่มเพิ่มหมวดหมู่
await page.getByRole('button', { name: /เพิ่มหมวดหมู่ใหม่/ }).click();
await expect(page.getByText('เพิ่ม', { exact: true })).toBeVisible();
// 2. กรอกข้อมูลในฟอร์ม
const dialog = page.locator('.q-dialog');
const inputs = dialog.locator('input');
await inputs.nth(0).fill(nameTh);
await inputs.nth(1).fill(nameEn);
await inputs.nth(2).fill(slug);
const textareas = dialog.locator('textarea');
await textareas.nth(0).fill(descTh);
await textareas.nth(1).fill(descEn);
// 3. กดปุ่ม "เพิ่ม"
await page.getByRole('button', { name: 'เพิ่ม', exact: true }).click();
// 4. ตรวจสอบว่ามี notification สำเร็จ
await expect(page.locator('.q-notification')).toBeVisible({ timeout: 10_000 });
// 5. modal ต้องปิด
await expect(dialog).not.toBeVisible();
// 6. ตรวจสอบว่าหมวดหมู่ใหม่แสดงในตาราง
const searchInput = page.locator('input[placeholder*="ค้นหาหมวดหมู่"]');
await searchInput.fill(nameTh);
await page.waitForTimeout(500);
await expect(page.getByText(nameTh, { exact: true })).toBeVisible();
const editBtn = page.locator('.q-table button .q-icon').filter({ hasText: 'edit' }).first();
const deleteBtn = page.locator('.q-table button .q-icon').filter({ hasText: 'delete' }).first();
await expect(editBtn).toBeVisible();
await expect(deleteBtn).toBeVisible();
await editBtn.click();
await page.waitForTimeout(500);
await expect(page.getByText('แก้ไขหมวดหมู่', { exact: true })).toBeVisible();
await inputs.nth(0).fill(nameTh + ' edit');
await inputs.nth(1).fill(nameEn + ' edit');
await inputs.nth(2).fill(slug + 'edit');
await textareas.nth(0).fill(descTh + ' edit');
await textareas.nth(1).fill(descEn + ' edit');
await page.getByRole('button', { name: 'บันทึก', exact: true }).click();
await page.waitForTimeout(500);
await searchInput.fill(nameTh + ' edit');
await page.waitForTimeout(500);
await expect(page.getByText(nameTh + ' edit', { exact: true })).toBeVisible();
await deleteBtn.click();
await page.waitForTimeout(500);
await page.getByRole('button', { name: 'OK' }).click();
await page.waitForTimeout(500);
await expect(page.locator('.q-notification').filter({ hasText: 'deleted' })).toBeVisible({ timeout: 10_000 });
});
test('should show validation error when submitting empty form', async ({ page }) => {
// 1. เปิด modal เพิ่มหมวดหมู่
await page.getByRole('button', { name: /เพิ่มหมวดหมู่ใหม่/ }).click();
await expect(page.getByText('เพิ่ม', { exact: true })).toBeVisible();
// 2. กดปุ่ม "เพิ่ม" โดยไม่กรอกข้อมูล
await page.getByRole('button', { name: 'เพิ่ม', exact: true }).click();
// 3. ตรวจสอบ validation error
await expect(page.getByText('กรุณากรอกชื่อ')).toBeVisible();
});
test('should close add modal on cancel', async ({ page }) => {
// 1. เปิด modal
await page.getByRole('button', { name: /เพิ่มหมวดหมู่ใหม่/ }).click();
await expect(page.getByText('เพิ่ม', { exact: true })).toBeVisible();
// 2. กดยกเลิก
await page.getByRole('button', { name: 'ยกเลิก' }).click();
// 3. modal ต้องหายไป
await expect(page.locator('.q-dialog')).not.toBeVisible();
});
test('should open edit modal when clicking edit button', async ({ page }) => {
// 1. กดปุ่ม edit ของแถวแรก
const editBtn = page.locator('.q-table button[class*="q-btn"]').first();
await expect(editBtn).toBeVisible();
await editBtn.click();
// 2. ตรวจสอบว่า modal แก้ไขเปิดขึ้น
await expect(page.getByText('แก้ไขหมวดหมู่')).toBeVisible();
// 3. ฟอร์มต้องมีข้อมูลอยู่แล้ว (ไม่ว่าง)
const nameInput = page.locator('.q-dialog input').first();
await expect(nameInput).not.toHaveValue('');
// 4. ปุ่มต้องเป็น "บันทึก" ไม่ใช่ "เพิ่ม"
await expect(page.getByRole('button', { name: 'บันทึก' })).toBeVisible();
});
test('should show delete confirmation dialog', async ({ page }) => {
// 1. กดปุ่ม delete ของแถวแรก (ปุ่มที่สองใน actions)
const deleteBtn = page.locator('.q-table .flex.gap-2 button').nth(1);
await expect(deleteBtn).toBeVisible();
await deleteBtn.click();
// 2. ตรวจสอบว่า dialog ยืนยันการลบแสดง
await expect(page.getByText('ยืนยันการลบ')).toBeVisible();
});
});

View file

@ -0,0 +1,117 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
/**
* Admin Pending Courses Page Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Pending Courses', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminPendingCourses);
await page.waitForLoadState('networkidle');
});
test('should display stats cards', async ({ page }) => {
await expect(page.getByText('รอตรวจสอบ')).toBeVisible();
await expect(page.getByText('บททั้งหมด')).toBeVisible();
await expect(page.getByText('บทเรียนทั้งหมด')).toBeVisible();
});
test('should search courses by keyword "พื้นฐาน"', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="ค้นหา"]');
await expect(searchInput).toBeVisible();
// ค้นหาคำว่า "พื้นฐาน"
await searchInput.fill('พื้นฐาน');
await page.waitForTimeout(500);
// ตรวจสอบว่าแสดงผลลัพธ์ที่ตรงกัน หรือแสดง empty state
const emptyState = page.getByText('ไม่มีคอร์สที่รอการอนุมัติ');
const isEmpty = await emptyState.isVisible().catch(() => false);
if (isEmpty) {
await expect(emptyState).toBeVisible();
} else {
// ถ้ามีผลลัพธ์ ชื่อคอร์สต้องมีคำว่า "พื้นฐาน"
const courseCards = page.locator('.bg-white.rounded-xl.shadow-sm.overflow-hidden');
const count = await courseCards.count();
expect(count).toBeGreaterThan(0);
for (let i = 0; i < count; i++) {
await expect(courseCards.nth(i)).toContainText('พื้นฐาน');
}
}
});
test('should filter courses by search text', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="ค้นหา"]');
await searchInput.fill('nonexistent-course-xyz');
// Should show empty state or filtered results
// Wait briefly for filter to apply
await page.waitForTimeout(500);
// Either shows "ไม่มีคอร์สที่รอการอนุมัติ" or filtered results
const emptyState = page.getByText('ไม่มีคอร์สที่รอการอนุมัติ');
const courseCards = page.locator('.bg-white.rounded-xl.shadow-sm.overflow-hidden');
// One of these should be visible
const isEmpty = await emptyState.isVisible().catch(() => false);
if (isEmpty) {
await expect(emptyState).toBeVisible();
}
});
test('should toggle between card and table view', async ({ page }) => {
// Find the view toggle buttons
const toggleGroup = page.locator('.q-btn-toggle');
await expect(toggleGroup).toBeVisible();
// Click table view
await page.locator('.q-btn-toggle button').last().click();
await page.waitForTimeout(300);
// Table should be visible
await expect(page.locator('.q-table')).toBeVisible();
// Switch back to card view
await page.locator('.q-btn-toggle button').first().click();
await page.waitForTimeout(300);
await page.locator('.q-btn-toggle button').last().click();
await page.waitForTimeout(300);
// Table should have expected columns
await expect(page.getByText('ชื่อคอร์ส')).toBeVisible();
await expect(page.getByText('ผู้สอน')).toBeVisible();
await expect(page.getByText('วันที่ส่ง')).toBeVisible();
});
test('should have refresh button', async ({ page }) => {
const refreshBtn = page.getByRole('button', { name: /รีเฟรช/ });
await expect(refreshBtn).toBeVisible();
// Click refresh
await refreshBtn.click();
// Should trigger loading (spinner appears briefly)
await page.waitForLoadState('networkidle');
});
test('should navigate to course detail on click if ', async ({ page }) => {
// Check if there are any courses
const courseExists = await page.locator('.bg-white.rounded-xl').first().isVisible().catch(() => false);
if (courseExists) {
// Click "ดูรายละเอียด" button on first course
const viewBtn = page.locator('.q-table button[class*="q-btn"]').first();
if (await viewBtn.isVisible()) {
await viewBtn.click();
await page.waitForURL('**/admin/courses/**');
await expect(page).toHaveURL(/\/admin\/courses\/\d+/);
await page.waitForTimeout(1000);
}
}
});
});

View file

@ -0,0 +1,44 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
/**
* Admin Dashboard Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Dashboard', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminDashboard);
await page.waitForLoadState('networkidle');
});
test('show display dashboard', async ({ page }) => {
await expect(page.locator('h1')).toContainText('สวัสดี');
await expect(page.locator("//p[normalize-space(text())='คอร์สรออนุมัติ']")).toBeVisible();
await expect(page.getByText('กิจกรรมวันนี้')).toBeVisible();
await expect(page.getByText('ผู้ใช้งานทั้งหมด')).toBeVisible();
});
test('navigate to pending courses and audit-log page', async ({ page }) => {
await page.locator('a[href*="pending"]', { hasText: 'ดูทั้งหมด' }).click();
await page.waitForURL('**/admin/courses/pending**');
await expect(page).toHaveURL(/\/admin\/courses\/pending/);
await page.waitForTimeout(500);
await page.goto(TEST_URLS.adminDashboard);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.locator('a[href*="audit-log"]', { hasText: 'ดูทั้งหมด' }).click();
await page.waitForURL('**/admin/audit-log**');
await expect(page).toHaveURL(/\/admin\/audit-log/);
await page.waitForTimeout(500);
});
test('show user menu on avatar click', async ({ page }) => {
// Click the avatar / user icon area
await page.locator('.w-12.h-12.rounded-full').click();
await page.waitForTimeout(500);
// Menu should appear with profile and logout options
await expect(page.getByText('โปรไฟล์')).toBeVisible();
});
});

View file

@ -0,0 +1,67 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
/**
* Admin Recommended Courses Page Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Recommended Courses', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminRecommendedCourses);
await page.waitForLoadState('networkidle');
});
test('check display page', async ({ page }) => {
// Header
await expect(page.getByText('จัดการคอร์สแนะนำ')).toBeVisible();
await expect(page.getByText('Recommended Courses Management')).toBeVisible();
const toggles = page.locator('.q-table .q-toggle');
const hasData = await toggles.first().isVisible().catch(() => false);
if (hasData) {
await expect(toggles.first()).toBeVisible();
}
});
test('should toggle recommendation status', async ({ page }) => {
const toggle = page.locator('.q-table .q-toggle').first();
const hasData = await toggle.isVisible().catch(() => false);
if (hasData) {
await toggle.click();
await page.waitForTimeout(500);
// Should show notification (success or error)
await expect(page.locator('.q-notification')).toBeVisible({ timeout: 10_000 });
await toggle.click();
await page.waitForTimeout(500);
// Should show notification (success or error)
await expect(page.locator('.q-notification').last()).toBeVisible({ timeout: 10_000 });
}
});
test('open course details dialog', async ({ page }) => {
const viewBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await viewBtn.isVisible().catch(() => false);
if (hasData) {
await viewBtn.click();
await page.waitForTimeout(500);
// Dialog should be visible with course details
await expect(page.getByText('รายละเอียดคอร์ส (Course Details)')).toBeVisible();
// Detail sections
await expect(page.getByText('รายละเอียด (Description)')).toBeVisible();
await expect(page.getByText('หมวดหมู่ (Category):')).toBeVisible();
await expect(page.getByText('ผู้สอน (Instructors)')).toBeVisible();
// Close dialog via close button in q-bar
await page.locator('.q-bar .q-btn').filter({ has: page.locator('[class*="q-icon"]') }).click();
await page.waitForTimeout(300);
await expect(page.getByText('รายละเอียดคอร์ส (Course Details)')).not.toBeVisible();
}
});
});

View file

@ -0,0 +1,213 @@
import { test, expect } from '@playwright/test';
import { TEST_URLS } from '../fixtures/test-data';
/**
* Admin Users Page Tests
* cookies admin-setup project ( login )
*/
test.describe('Admin Users', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_URLS.adminUsers);
await page.waitForLoadState('networkidle');
});
test('check display page', async ({ page }) => {
// Header
await expect(page.getByText('จัดการผู้ใช้งาน')).toBeVisible();
// Search input
await expect(page.locator('input[placeholder*="ค้นหาชื่อ"]')).toBeVisible();
// Stats cards
await expect(page.getByText('ผู้ใช้ทั้งหมด')).toBeVisible();
await expect(page.getByText('Admin', { exact: true })).toBeVisible();
await expect(page.getByText('Instructor', { exact: true })).toBeVisible();
await expect(page.getByText('Student', { exact: true })).toBeVisible();
// Table
await expect(page.locator('.q-table')).toBeVisible();
});
test('should filter users by search', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="ค้นหาชื่อ"]');
await searchInput.fill('admin');
await page.waitForTimeout(500);
// Table should still be visible with filtered results
await expect(page.locator('.q-table')).toBeVisible();
});
test('should filter users by role', async ({ page }) => {
// ใช้ outlined q-select (ตัว filter role ไม่ใช่ pagination)
const roleSelect = page.locator('.q-select.q-field--outlined');
await roleSelect.click();
await page.waitForTimeout(300);
// Select Instructor
await page.locator('.q-item__label').filter({ hasText: 'Instructor' }).click();
await page.waitForTimeout(500);
// Table should show filtered results
await expect(page.locator('.q-table')).toBeVisible();
await roleSelect.click();
await page.waitForTimeout(300);
// Select Student
await page.locator('.q-item__label').filter({ hasText: 'Student' }).click();
await page.waitForTimeout(500);
// Table should show filtered results
await expect(page.locator('.q-table')).toBeVisible();
await roleSelect.click();
await page.waitForTimeout(300);
// Select Admin
await page.locator('.q-item__label').filter({ hasText: 'Admin' }).click();
await page.waitForTimeout(500);
// Table should show filtered results
await expect(page.locator('.q-table')).toBeVisible();
// กลับไปเลือก "บทบาททั้งหมด"
await roleSelect.click();
await page.waitForTimeout(300);
await page.locator('.q-item__label').filter({ hasText: 'บทบาททั้งหมด' }).click();
await page.waitForTimeout(500);
});
test('should open action menu', async ({ page }) => {
// Click more_vert button on first row
const actionBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await actionBtn.isVisible().catch(() => false);
if (hasData) {
await actionBtn.click();
await page.waitForTimeout(300);
// Menu should show options
await expect(page.getByText('ดู', { exact: true })).toBeVisible();
await expect(page.getByText('แก้ Role', { exact: true })).toBeVisible();
await expect(page.getByText('ลบ', { exact: true })).toBeVisible();
}
});
test('should open view user dialog', async ({ page }) => {
const actionBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await actionBtn.isVisible().catch(() => false);
if (hasData) {
await actionBtn.click();
await page.waitForTimeout(300);
// Click "ดู" option
await page.getByText('ดู', { exact: true }).click();
await page.waitForTimeout(300);
// Dialog should show user details
await expect(page.getByText('รายละเอียดผู้ใช้')).toBeVisible();
// Close button
await page.getByRole('button', { name: 'ปิด' }).click();
await expect(page.locator('.q-dialog')).not.toBeVisible();
}
});
test('open change role dialog', async ({ page }) => {
const actionBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await actionBtn.isVisible().catch(() => false);
// Table should still be visible with filtered results
await expect(page.locator('.q-table')).toBeVisible();
if (hasData) {
await actionBtn.click();
await page.waitForTimeout(300);
// Click "แก้ Role"
await page.getByText('แก้ Role').click();
await page.waitForTimeout(300);
// Change role dialog should appear
await expect(page.getByText('เปลี่ยน Role')).toBeVisible();
// Cancel the dialog
await page.getByRole('button', { name: 'Cancel' }).click();
await page.waitForTimeout(300);
}
});
test('change role ', async ({ page }) => {
const actionBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
const hasData = await actionBtn.isVisible().catch(() => false);
const searchInput = page.locator('input[placeholder*="ค้นหาชื่อ"]');
await searchInput.fill('lertp');
await page.waitForTimeout(500);
// Table should still be visible with filtered results
await expect(page.locator('.q-table')).toBeVisible();
if (hasData) {
// Click "แก้ Role Student"
await actionBtn.click();
await page.waitForTimeout(300);
await page.getByText('แก้ Role').click();
await page.waitForTimeout(300);
// Change role dialog should appear
await expect(page.getByText('เปลี่ยน Role')).toBeVisible();
// select role Student
await page.waitForTimeout(300);
await page.locator('.q-dialog .q-radio__label').filter({ hasText: 'Student' }).click();
await page.waitForTimeout(500);
// save
await page.getByRole('button', { name: 'OK' }).click();
await page.waitForTimeout(300);
// Click "แก้ Role Admin"
await actionBtn.click();
await page.waitForTimeout(300);
await page.getByText('แก้ Role').click();
await page.waitForTimeout(300);
// Change role dialog should appear
await expect(page.getByText('เปลี่ยน Role')).toBeVisible();
// select role Admin
await page.waitForTimeout(300);
await page.locator('.q-dialog .q-radio__label').filter({ hasText: 'Admin' }).click();
await page.waitForTimeout(500);
// save
await page.getByRole('button', { name: 'OK' }).click();
await page.waitForTimeout(300);
// Click "แก้ Role Instructor"
await actionBtn.click();
await page.waitForTimeout(300);
await page.getByText('แก้ Role').click();
await page.waitForTimeout(300);
// Change role dialog should appear
await expect(page.getByText('เปลี่ยน Role')).toBeVisible();
// select role Instructor
await page.waitForTimeout(300);
await page.locator('.q-dialog .q-radio__label').filter({ hasText: 'Instructor' }).click();
await page.waitForTimeout(500);
// save
await page.getByRole('button', { name: 'OK' }).click();
await page.waitForTimeout(300);
}
});
// test('should open delete confirmation dialog', async ({ page }) => {
// const actionBtn = page.locator('.q-table .q-btn').filter({ has: page.locator('.q-icon') }).first();
// const hasData = await actionBtn.isVisible().catch(() => false);
// if (hasData) {
// await actionBtn.click();
// await page.waitForTimeout(300);
// // Click "ลบ"
// await page.getByText('ลบ').click();
// await page.waitForTimeout(300);
// // Delete confirmation dialog
// await expect(page.getByText('ยืนยันการลบ')).toBeVisible();
// // Cancel the dialog
// await page.getByRole('button', { name: 'Cancel' }).click();
// await page.waitForTimeout(300);
// }
// });
});