Files
goodgo-platform/e2e/web/smoke.spec.ts
Ho Ngoc Hai 26b6b37cee
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 12s
Deploy / Build AI Services Image (push) Failing after 10s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m25s
Security Scanning / Trivy Scan — Web Image (push) Failing after 46s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 43s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Staging (push) Has been skipped
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 32s
Deploy / Build API Image (push) Failing after 26s
Deploy / Build Web Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 21s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 5s
Security Scanning / Trivy Filesystem Scan (push) Failing after 42s
Deploy / Rollback Production (push) Has been skipped
feat(qa): add smoke test suite + post-deploy workflow
- e2e/api/smoke.spec.ts — 9 @smoke API tests covering health, auth roundtrip,
  token refresh, listings, search, payments, subscriptions, and inquiries
- e2e/web/smoke.spec.ts — 7 @smoke Web tests covering homepage, login/register
  pages, listings, search, listing detail 404 handling, and console-error check
- playwright.config.ts — smoke-api and smoke-web projects (grep: /@smoke/)
  allowing targeted post-deploy execution without the full suite
- .github/workflows/smoke.yml — workflow_dispatch + workflow_call trigger for
  running only the @smoke subset against staging or production URLs

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 00:47:40 +07:00

112 lines
5.0 KiB
TypeScript

/**
* Smoke suite — Web
*
* Runs after every deploy to verify critical frontend pages load and
* render key UI elements. Tagged with @smoke for targeted execution:
*
* npx playwright test --project=web --grep @smoke
*
* Tests avoid user interactions that require a live API/auth session so
* they remain fast and resilient in staging environments.
*/
import { test, expect } from '@playwright/test';
// ── Homepage ──────────────────────────────────────────────────────────────────
test('@smoke homepage loads', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/.+/);
// Search bar or hero section must be visible
const searchInput = page.getByRole('searchbox').or(page.getByPlaceholder(/tìm kiếm|search/i));
await expect(searchInput.first()).toBeVisible({ timeout: 10_000 });
});
// ── Auth pages ────────────────────────────────────────────────────────────────
test('@smoke login page renders form', async ({ page }) => {
await page.goto('/login');
await expect(page.getByRole('heading', { name: /đăng nhập/i })).toBeVisible();
await expect(page.getByLabel(/số điện thoại/i)).toBeVisible();
await expect(page.getByLabel(/mật khẩu/i)).toBeVisible();
await expect(page.getByRole('button', { name: /đăng nhập/i })).toBeVisible();
});
test('@smoke register page renders form', async ({ page }) => {
await page.goto('/register');
await expect(page.getByRole('heading', { name: /đăng ký/i })).toBeVisible();
await expect(page.getByLabel(/số điện thoại/i)).toBeVisible();
});
// ── Listings ──────────────────────────────────────────────────────────────────
test('@smoke listings page loads without JS errors', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', (err) => errors.push(err.message));
await page.goto('/listings');
// Allow time for async data loading
await page.waitForLoadState('networkidle', { timeout: 15_000 }).catch(() => {
// networkidle may not be reached if polling; continue
});
// Page must not show an unhandled error boundary
const errorHeading = page.getByRole('heading', { name: /500|something went wrong|lỗi/i });
await expect(errorHeading).not.toBeVisible();
// Filter JS errors that are not related to missing env vars in test
const fatalErrors = errors.filter(
(e) => !e.includes('NEXT_PUBLIC') && !e.includes('mapbox'),
);
expect(fatalErrors).toHaveLength(0);
});
// ── Search ────────────────────────────────────────────────────────────────────
test('@smoke search page is accessible', async ({ page }) => {
await page.goto('/search?q=apartment');
await expect(page).not.toHaveTitle(/404|not found/i);
// Some results container or empty-state must be present
const container = page
.locator('[data-testid="search-results"]')
.or(page.locator('[data-testid="empty-state"]'))
.or(page.getByText(/kết quả|results|không tìm thấy/i));
// Be lenient — search service may be unavailable in staging
await page.waitForTimeout(3000);
// Just confirm no crash
const errorBoundary = page.getByRole('heading', { name: /500|server error/i });
await expect(errorBoundary).not.toBeVisible();
});
// ── Listing Detail ────────────────────────────────────────────────────────────
test('@smoke listing detail page handles missing id gracefully', async ({ page }) => {
const res = await page.goto('/listings/nonexistent-smoke-test-id');
// Should render 404 page, not crash with 500
const status = res?.status();
if (status && status >= 500) {
throw new Error(`Listing detail returned ${status} for unknown ID (expected 404)`);
}
});
// ── Static / infra ────────────────────────────────────────────────────────────
test('@smoke no console errors on login page', async ({ page }) => {
const consoleErrors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') consoleErrors.push(msg.text());
});
await page.goto('/login');
await page.waitForLoadState('domcontentloaded');
// Filter noise from third-party scripts / mapbox in test env
const meaningful = consoleErrors.filter(
(e) =>
!e.includes('mapbox') &&
!e.includes('NEXT_PUBLIC') &&
!e.includes('Failed to load resource') &&
!e.includes('net::ERR'),
);
expect(meaningful).toHaveLength(0);
});