Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f231a18
Add Playwright E2E test suite with global auth and split serving tests
dnplkndll Mar 6, 2026
0d37230
fix(test): resolve flaky settings tests on clean DB
dnplkndll Mar 6, 2026
7a20cc5
fix(tests): update nav selector from #primaryNavButton to #site-header
dnplkndll Mar 7, 2026
7168922
fix(tests): remove nav element dependency in auth helper
dnplkndll Mar 7, 2026
fcdf1a8
fix(test): wait for church selection dialog instead of email input hide
dnplkndll Mar 7, 2026
11564c6
fix(test): detect auth state by racing navButton vs emailInput
dnplkndll Mar 7, 2026
d3526ce
fix(test): use 20s timeouts and confirm navButton ready before returning
dnplkndll Mar 7, 2026
78fea7b
fix(test): increase CI test timeout to 60s (login takes 20s+navButton…
dnplkndll Mar 7, 2026
59d1fce
fix(test): reduce emailInput wait to 5s, navButton to 30s — cuts per-…
dnplkndll Mar 7, 2026
0b5bdf9
debug: add instrumentation to failing settings tests
dnplkndll Mar 8, 2026
1201f79
fix: Mobile Settings tests navigate via primary nav, remove debug
dnplkndll Mar 8, 2026
572450c
fix: use data-testid selector for Mobile nav item
dnplkndll Mar 9, 2026
0770f37
chore: add CI diagnostics for Mobile Settings debugging
dnplkndll Mar 9, 2026
126e9f4
chore: remove CI diagnostic instrumentation from settings tests
dnplkndll Mar 9, 2026
1b1eda8
fix: increase actionTimeout and fix dashboard test navigation
dnplkndll Mar 9, 2026
f31011e
fix: target correct form in edit/cancel tests to avoid seed data coll…
dnplkndll Mar 9, 2026
c55ccb6
fix: target test form in delete test to avoid deleting seed data form
dnplkndll Mar 9, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,4 @@ dist

# TernJS port file
.tern-port
.auth-state.json
43 changes: 27 additions & 16 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,52 @@
import { defineConfig, devices } from '@playwright/test';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const STORAGE_STATE_PATH = path.join(__dirname, 'tests', '.auth-state.json');

export default defineConfig({
testDir: './tests',
testMatch: /.*\.spec\.ts/,
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 1,
workers: 4,
reporter: 'html',
timeout: 30 * 1000,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: process.env.CI ? 'list' : 'html',
timeout: process.env.CI ? 60 * 1000 : 30 * 1000,
expect: { timeout: 5 * 1000 },

globalSetup: './tests/global-setup.ts',

use: {
baseURL: 'https://demo.b1.church',
baseURL: process.env.BASE_URL || 'https://demo.b1.church',
storageState: STORAGE_STATE_PATH,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
actionTimeout: 5 * 1000,
actionTimeout: 15 * 1000,
navigationTimeout: 10 * 1000,
},

projects: [
// Settings must run first — it renames the church, which website tests depend on
{
name: 'settings',
use: {
...devices['Desktop Chrome'],
headless: true,
},
testMatch: /settings\.spec\.ts/,
},
// All other tests run in parallel after settings completes
{
name: 'chromium',
use: {
dependencies: ['settings'],
use: {
...devices['Desktop Chrome'],
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-extensions',
'--disable-gpu',
'--disable-web-security',
'--disable-features=VizDisplayCompositor'
]
},
testIgnore: /settings\.spec\.ts/,
},
],
});
26 changes: 12 additions & 14 deletions tests/attendance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ test.describe('Attendance Management', () => {
await menuBtn.click();
const peopleHomeBtn = page.locator('[data-testid="nav-item-people"]');
await peopleHomeBtn.click();
await page.waitForTimeout(5000);
await expect(page).toHaveURL(/\/people/);
const attHomeBtn = page.locator('[id="secondaryMenu"]').getByText('Attendance');
await attHomeBtn.click();
await page.waitForTimeout(2000);
await expect(page).toHaveURL(/\/attendance/);
});

Expand All @@ -31,7 +29,7 @@ test.describe('Attendance Management', () => {
await campusName.fill('Octavian Test Campus');
const saveBtn = page.locator('button').getByText('Save');
await saveBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const verifiedName = page.locator('button').getByText('Octavian Test Campus');
await expect(verifiedName).toHaveCount(1);
});
Expand All @@ -53,7 +51,7 @@ test.describe('Attendance Management', () => {
await campusName.fill('Octavius Test Campus');
const saveBtn = page.locator('button').getByText('Save');
await saveBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const verifiedName = page.locator('button').getByText('Octavius Test Campus');
await expect(verifiedName).toHaveCount(1);
});
Expand Down Expand Up @@ -94,7 +92,7 @@ test.describe('Attendance Management', () => {
await servName.fill('Octavius Test Service');
const saveBtn = page.locator('button').getByText('Save');
await saveBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const verifiedServ = page.locator('button').getByText('Octavius Test Service');
await expect(verifiedServ).toHaveCount(1);
});
Expand All @@ -106,7 +104,7 @@ test.describe('Attendance Management', () => {
await expect(campusSelect).toHaveCount(1);
const cancelBtn = page.locator('button').getByText('Cancel');
await cancelBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(campusSelect).toHaveCount(0);
});

Expand Down Expand Up @@ -136,7 +134,7 @@ test.describe('Attendance Management', () => {
await timeName.fill('Octavius Test Time');
const saveBtn = page.locator('button').getByText('Save');
await saveBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const verifiedTime = page.locator('button').getByText('Octavius Test Time');
await expect(verifiedTime).toHaveCount(1);
});
Expand All @@ -148,7 +146,7 @@ test.describe('Attendance Management', () => {
await expect(servSelect).toHaveCount(1);
const cancelBtn = page.locator('button').getByText('Cancel');
await cancelBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(servSelect).toHaveCount(0);
});

Expand All @@ -163,7 +161,7 @@ test.describe('Attendance Management', () => {
await time.click();
const deleteBtn = page.locator('button').getByText('Delete');
await deleteBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(time).toHaveCount(0);
});

Expand All @@ -178,7 +176,7 @@ test.describe('Attendance Management', () => {
await serv.click();
const deleteBtn = page.locator('button').getByText('Delete');
await deleteBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(serv).toHaveCount(0);
});

Expand Down Expand Up @@ -212,7 +210,7 @@ test.describe('Attendance Management', () => {
test('should filter attendance trends', async ({ page }) => {
const trendTab = page.locator('button[role="tab"]').getByText('Attendance Trend');
await trendTab.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);

const campusName = page.locator('[id="mui-component-select-campusId"]');
await campusName.click();
Expand All @@ -232,7 +230,7 @@ test.describe('Attendance Management', () => {
await groupSel.click();
const runBtn = page.locator('button').getByText('Run Report');
await runBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);

const resultsTableRows = page.locator('[id="reportsBox"] table tr');
expect(resultsTableRows).toHaveCount(36);
Expand All @@ -242,7 +240,7 @@ test.describe('Attendance Management', () => {
// completed as I can, correcting reports display info is up to father. Data does not load in.
const trendTab = page.locator('button[role="tab"]').getByText('Group Attendance');
await trendTab.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);

const campusName = page.locator('[id="mui-component-select-campusId"]');
await campusName.click();
Expand All @@ -256,7 +254,7 @@ test.describe('Attendance Management', () => {
await weekBox.fill('2024-03-03');
const runBtn = page.locator('button').getByText('Run Report');
await runBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const report = page.locator('td').getByText('10:30 AM Service');
await report.click();
});
Expand Down
21 changes: 9 additions & 12 deletions tests/dashboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { login } from './helpers/auth';
test.describe('Dashboard Management', () => {
test.beforeEach(async ({ page }) => {
await login(page);
const menuBtn = page.locator('[id="primaryNavButton"]').getByText('expand_more');
await menuBtn.click();
const dashboardHomeBtn = page.locator('[data-testid="nav-item-dashboard"]');
await dashboardHomeBtn.click();
await page.waitForTimeout(5000);
await page.goto('/dashboard');
await expect(page.locator('h6').first()).toBeVisible({ timeout: 15000 });
});

/* test('should load dashboard', async ({ page }) => {
Expand All @@ -18,10 +15,10 @@ test.describe('Dashboard Management', () => {
}); */

test('should load group from dashboard', async ({ page }) => {
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const firstGroup = page.locator('h6').first();
await firstGroup.click();
await page.waitForTimeout(2000);
await page.waitForTimeout(200);
await expect(page).toHaveURL(/\/groups\/GRP\d+/);
});

Expand All @@ -30,10 +27,10 @@ test.describe('Dashboard Management', () => {
await searchBox.fill('Dorothy Jackson');
const searchBtn = page.locator('[data-testid="dashboard-search-button"]');
await searchBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const results = page.locator('h6').getByText('Dorothy Jackson');
await results.click();
await page.waitForTimeout(2000);
await page.waitForTimeout(200);
await expect(page).toHaveURL(/\/people\/PER\d+/);
const validatedName = page.locator('p').getByText('Dorothy Jackson');
await expect(validatedName).toHaveCount(1);
Expand All @@ -56,15 +53,15 @@ test.describe('Dashboard Management', () => {
await taskNotes.fill('Octavian Testing (Playwright)');
const saveBtn = page.locator('button').getByText('Save');
await saveBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
const validatedTask = page.locator('a').getByText('Test Task');
await expect(validatedTask).toHaveCount(2);
});

test('should load task from dashboard', async ({ page }) => {
const task = page.locator('a').getByText('Test Task').first();
await task.click();
await page.waitForTimeout(2000);
await page.waitForTimeout(200);
await expect(page).toHaveURL(/\/tasks\/[^/]+/);
});

Expand All @@ -75,7 +72,7 @@ test.describe('Dashboard Management', () => {
await expect(assignInput).toHaveCount(1);
const cancelBtn = page.locator('button').getByText('Cancel');
await cancelBtn.click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(assignInput).toHaveCount(0);
});

Expand Down
Loading