# PRD — playwright_tests

Evidence date: 2026-10-06. Inspected revision: `main` at `41f992c`.

This document describes the test repository. Requirements of SAWIS, ShopFlow, and IPW as products are recorded only where a spec, helper, or in-repo document states an expected behaviour. Those statements are expectations encoded by tests, not a signed product specification. Secret values are omitted.

Provenance labels: **Documented** (text in this repo), **Implemented** (code or config), **Inferred**, **Planned** (changelog or comment only), **Unknown**.

## Product purpose

**Implemented.** Give testers three separate Playwright projects that open real websites and assert UI behaviour.

**Documented** purpose statements:

- SAWIS BI: "ensures the platform's reliability, performance, and user experience" (`bi.sawisonline.co.za/README.md`).
- ShopFlow: "comprehensive Playwright tests for the ShopFlow application" (`qa.shopflow.io.dedi1534.jnb2.host-h.net/README.md`).
- IPW: automated testing of `staging.ipw.co.za` covering authentication, security, accessibility, performance, main functionality, and post-login behaviour (`staging.ipw.co.za/README.md`).

There is no single product owner, user role model, or backlog in this repository. **Unknown / Requires Confirmation:** who owns each suite and which environment is allowed to be automated.

## Users and interactions

| Actor | Evidence | What they do |
|---|---|---|
| Test author / local tester | `package.json` scripts, READMEs, `run-tests.js` | Installs dependencies and browsers, copies env examples, runs `npm test` or a narrower script from a suite directory |
| ShopFlow tester with mailbox access | `utils/email-2fa-fetcher.ts`, README 2FA section | Optionally sets `GMAIL_APP_PASSWORD` so login can read a 2FA email |
| Report recipient | `scripts/send-email-report.js` | Receives an email at `jaco@overdrive.co.za` when `npm run test:email` is run in the IPW suite |
| Person named in a template prompt | `TEST_SPECIFICATION_TEMPLATE.md` under two hostname folders | The prompt asks an agent to generate tests. No specs were generated for those folders |

**Inferred.** There is no end-user of a hosted `playwright_tests` application. The people who run Node on a workstation are the users of this repository.

CI is described in changelogs ("CI/CD ready", retries when `CI` is set). **Implemented** config does set `forbidOnly`, `retries: 2`, and `workers: 1` when `process.env.CI` is truthy. **No pipeline file is in git.** A CI actor is **Planned / not established**.

## Systems under test

### FR-001 — SAWIS BI dashboard is the BI suite target

Requirement: BI tests open `https://bi.sawisonline.co.za`.

Who: tester running `bi.sawisonline.co.za`.

Trigger: `npm test` or `node run-tests.js <suite>`.

Inputs: network access to that origin. No `.env` is required by `playwright.config.ts` (base URL is hardcoded). `dotenv` is a dependency but the BI config does not call it.

Expected behaviour: Playwright uses `testDir: ./tests'`, HTML + JSON + JUnit reporters, screenshots and video retained on failure, trace on first retry, action timeout 15s, navigation timeout 60s. Projects: `chromium`, `firefox`, `webkit`, `Mobile Chrome` (Pixel 5), `Mobile Safari` (iPhone 12).

Outputs: `playwright-report/`, `test-results/results.json`, `test-results/results.xml`.

Validation: `forbidOnly` when `CI` is set.

Error conditions: navigation beyond 60s fails the action. README text says navigation timeout is 30s and action timeout is 10s. **Discrepancy:** README vs `playwright.config.ts`.

Evidence: `bi.sawisonline.co.za/playwright.config.ts`, `package.json`, `run-tests.js`.

Implementation: Implemented.

Confidence: High.

### FR-002 — BI coverage areas

Requirement: the BI suite contains specs for homepage, login modal, filters, tabs, charts, and performance.

Who: tester.

Trigger: the matching npm script or a spec path.

Inputs: the live dashboard. Login specs do not submit a real account. `login.spec.ts` includes a case "should handle form submission with valid data"; whether that case uses a real account was not fully traced beyond the file's described modal checks. **Unknown** whether any BI test completes a real login. The README's critical journey "User attempts login → handles validation → accesses secure features" is broader than a modal-only reading of the file list.

Expected behaviour (from spec titles and the README, which match the file names):

- Homepage loads, shows navigation, key metrics, chart sections, filter sidebar, footer, a responsive layout, accessibility elements, and no critical console errors. `run-tests.js` quick mode greps `should load homepage successfully`.
- Login control opens a modal. Tests look for form validation, reCAPTCHA, forgot-password link, create-account link, cancel, outside click, keyboard, Escape, focus, labels, and accessibility attributes.
- Filters cover year, ending month, reporting-period slider, product type, country, package type, and cultivar/type, plus apply/clear, select-all, combinations, and state across navigation.
- Tabs: Overview (default), Volume, Value, Price Range.
- Charts: sections, reporting period, Tables and Export buttons, interactions, loading, legends, tooltips, responsiveness, accessibility, error states.
- Performance specs assert load, filter, navigation, large data, memory, latency, concurrent interaction, back/forward, refresh, resources, chart rendering, and mobile.

Documented thresholds in the BI README, not in config: page load under 10s, data loading under 15s, filter application under 8s, navigation under 5s, large dataset under 20s. **Inferred** that the performance spec encodes some of these. Each numeric assert was not re-read line by line. **Requires confirmation** if those numbers are pass/fail thresholds or documentation only.

Outputs: Playwright report.

Evidence: `bi.sawisonline.co.za/tests/*.spec.ts`, `README.md`, `CHANGELOG.md` 1.0.0.

Implementation: Implemented as specs. Pass/fail against the live site was not re-run.

Confidence: High for presence of specs. Medium for numeric thresholds.

### FR-003 — ShopFlow target URL

Requirement: ShopFlow tests use `process.env.SHOPFLOW_URL`, otherwise `https://shopflow.php8`.

Who: tester, after copying `config.env.example` to `.env` (README and `playwright.config.ts` both load `.env` via dotenv).

Trigger: `npm test` (`playwright test --project=chromium`).

Inputs: `SHOPFLOW_URL`, `TEST_EMAIL`, `TEST_PASSWORD`. Optional `GMAIL_APP_PASSWORD`, `GMAIL_IMAP_HOST`, `GMAIL_IMAP_PORT`, `GMAIL_IMAP_USER`, `GMAIL_IMAP_PASSWORD`, `TWO_FA_MAX_WAIT_TIME` (default 30000), `TWO_FA_RETRY_INTERVAL` (default 5000).

Expected behaviour: `ignoreHTTPSErrors: true`. Action timeout 10s, navigation timeout 30s, test timeout 60s. One worker. Not fully parallel. HTML reporter and `test-results.json`.

Error conditions: `validateTestConfig` warns if URL, email, or password are missing. Fallbacks in `loadTestConfig()` supply a URL, email, and password even when `.env` is absent, so the warning path is not the normal one. **Discrepancy:** `config.env.example` uses placeholder email `sdg` and placeholder password text, while `test-config.ts` fallbacks are a real mailbox and a real password. The example file is not what the code uses when variables are unset.

The suite directory is named `qa.shopflow.io.dedi1534.jnb2.host-h.net`. That hostname does not appear in `playwright.config.ts`. **Unknown / Requires Confirmation:** whether the QA hostname and `https://shopflow.php8` are the same system.

Evidence: `playwright.config.ts`, `utils/test-config.ts`, `config.env.example`, `README.md`.

Implementation: Implemented.

Confidence: High.

### FR-004 — ShopFlow runs in six ordered phases

Requirement: a full `npm test` runs phase projects in order 01 → 02 → 03 → 04 → 05 → 06 because each project depends on the previous one, and the `chromium` project depends on `06-seller-portal` and has no `testMatch` of its own.

Who: tester.

Trigger: `npm test`, `npm run test:all`, or `npm run test:phases` (all three scripts are `playwright test --project=chromium`). Phase scripts `test:phase1` … `test:phase6` select one project.

Inputs: for phases 2–6, a file `auth.json` in the suite directory. It is gitignored and not committed.

Expected behaviour:

- Phase 1 (`tests/01-authentication/`) does not set `storageState`. It can start on a clean tree.
- Phases 2–6 call `test.use({ storageState: 'auth.json' })`. Playwright requires that file before those projects start. `helpers/test-helpers.ts` and `fixtures/authenticated.fixture.ts` can write it after a login. `jab-orders.spec.ts` can also save head-office store selection back into `auth.json`.
- Cart tests are intentionally serial. Config comments say parallel runs conflict on cart state.

Error conditions: running phase 2 or later on a clean checkout fails until `auth.json` exists. Blocked IP or "Access denied" / "You are not allowed to use this service" text in `.alert-danger` makes `ShopFlowAuthenticator.authenticate` return failure. Invalid or wrong password text also returns failure. Missing Gmail app password skips automated 2FA and logs that manual input would be required. A 2FA error containing "Incorrect validation code" or "Please try again" fails the helper. The helper waits up to about 10 seconds to leave `/2fa-validation`.

Outputs: session file, HTML report, `test-results.json`.

Evidence: `playwright.config.ts`, `utils/authentication.ts`, `helpers/test-helpers.ts`, spec `test.use` lines.

Implementation: Implemented.

Confidence: High.

### FR-005 — ShopFlow authentication behaviour the tests encode

Requirement: login is an email field, a password field, and a button labelled LOGIN or Login, on the base URL (not a separate `/login` path in the helper). Successful login may navigate to a URL containing `/2fa-validation`. The 2FA email subject searched over IMAP is `2FA Validation`. Code entry uses a VERIFY button. A comment in `login.spec.ts` says remember-me is on the 2FA screen, not the login screen.

Who: the authenticator and phase 1 specs.

Trigger: login specs or `ensureAuthenticated`.

Inputs: test mailbox and password. Optional IMAP password. IMAP TLS is configured with `rejectUnauthorized: false` in `email-2fa-fetcher.ts`.

Expected behaviour of the specs (phase 1, 25 `test()` calls):

| File | `test()` count | Describe title |
|---|---|---|
| `login.spec.ts` | 5 | `01_Authentication - Login @auth @p1 @smoke` |
| `logout.spec.ts` | 6 | `01_Authentication - Logout @auth @p2` |
| `invalid-login.spec.ts` | 8 | `01_Authentication - Invalid Login @auth @p2 @security` |
| `2fa-validation.spec.ts` | 6 | `01_Authentication - 2FA Validation @auth @p1 @2fa` |

`PROJECT_PATTERNS_AND_RULES.md` says describe titles should carry tags and cases should be named `Test Case N:`. Phase 1 follows that. Later phases use the numbered case names and mostly omit the tags on the describe title. **Discrepancy** between the patterns doc and most spec files.

The README still documents suites that are not in the tree: `shopflow-comprehensive.spec.ts`, `shopflow-login-flow.spec.ts`, `shopflow-ecommerce.spec.ts`, `shopflow-admin.spec.ts`, `shopflow-security.spec.ts`, `shopflow-performance.spec.ts`, `shopflow-2fa-automated.spec.ts`, and npm scripts `test:shopflow`, `test:login`, `test:security`, `test:performance`, `test:comprehensive`, `test:2fa`, `test:app`, `test:ecommerce`, `test:admin`. **Discrepancy:** README vs `package.json` and the `tests/` tree. The README's "100+ test cases" is true as a lower bound. The tree has 278 `test()` calls.

Implementation: Implemented for the phase files. The legacy file list is documentation only.

Confidence: High.

### FR-006 — ShopFlow functional areas and routes the tests open

These paths are what the specs navigate to. They are **Implemented** test behaviour, not a confirmed route map of ShopFlow. A missing route in this list does not mean the application lacks it.

| Phase | Directory | `test()` calls | Routes or areas the specs open |
|---|---|---|---|
| 2 Core commerce | `tests/02-core-commerce/` | 73 | Product catalog, add to cart, cart, checkout (README: immediate and scheduled), order management, order history, saved carts, JAB orders. README documents JAB as "Just-A-Box". |
| 3 User management | `tests/03-user-management/` | 40 | User list, profile view, profile edit. Roles at `/roles-permissions`, table `#rolesTable`, links labelled "Access Menu" whose `href` contains `role=<id>`. If no row is found, the spec falls back to role id `2`. |
| 4 Partner management | `tests/04-partner-management/` | 49 | Buyer groups, sellers, seller groups, stores at `/store-management/stores`, industries. `DATATABLE_NAVIGATION_FIX.md` mentions seller detail URLs matching `seller-management/sellers/details/<id>` and table `#sellersTable`. |
| 5 Price management | `tests/05-price-management/` | 53 | Specs start at `/productmanagement`: price files, upload, applications, pricing rules, product catalog management. |
| 6 Seller portal | `tests/06-seller-portal/` | 38 | `/seller/dashboard`, `/seller/orders` (unprocessed and processed tabs in test titles), `/seller/products`, `/seller/analytics` and `/seller/analytics/products`, `/seller/settings` and `/seller/settings/profile`. |

`test-data/products.json` defines two fixture products at 100.00 and 250.00 with VAT 15.00 and 37.50 and inclusive prices 115.00 and 287.50. **Inferred** from the fixture: those examples assume 15% VAT. Whether checkout specs assert that rate on the live cart was not confirmed line by line. **Requires confirmation** before treating 15% VAT as a ShopFlow business rule.

`helpers/assertions.ts` checks price text, date text, price equality within 0.01, and authenticated UI (avatar, profile link, logout link). Those are test helpers, not application code.

Implementation: Implemented as tests and helpers.

Confidence: High for routes that appear in `page.goto`. Medium for VAT.

### FR-007 — IPW staging target

Requirement: IPW tests open `http://staging.ipw.co.za` (HTTP, not HTTPS).

Who: tester in `staging.ipw.co.za/`.

Trigger: `npm test` (`playwright test`), which loads `playwright.config.ts` because that file exists and Playwright checks `.ts` before `.js`. Scripts do not pass `--config`.

Expected behaviour of the TypeScript config: `fullyParallel: true`, HTML + `test-results/results.json` + `test-results/results.xml`, trace on first retry, screenshot and video on failure, projects Chromium, Firefox, WebKit, Pixel 5, iPhone 12. No `globalSetup`. No `ignoreHTTPSErrors`. No action timeout override. No env block.

**Discrepancy:** `playwright.config.js` sets `ignoreHTTPSErrors: true`, action and navigation timeouts of 30s, test timeout 60s, expect timeout 10s, a fixed Chrome user agent, `globalSetup` / `globalTeardown`, and default env for DB and test credentials. README section "Configuration" points at `playwright.config.js`. A default `npm test` does not load that file.

Documented performance thresholds in the README: page load under 5s, DOM content loaded under 2s, first contentful paint under 2s, touch response under 100ms. **Unknown** whether `performance.spec.js` asserts those exact numbers.

Evidence: both config files, `package.json`, `README.md`.

Implementation: Two configs implemented. Only the TypeScript file is on the default npm path.

Confidence: High.

### FR-008 — IPW spec areas

Requirement: the staging suite contains the spec files below (68 `test()` calls).

| File | `test()` count | Describe title |
|---|---|---|
| `tests/login.spec.ts` | 9 | `IPW Staging Login Tests` |
| `tests/navigation.spec.ts` | 9 | `IPW Staging Navigation Tests` |
| `tests/authentication.spec.js` | 7 | `Authentication Tests` |
| `tests/post-login-functionality.spec.js` | 8 | `Post-Login Functionality Tests` |
| `tests/main-functionality.spec.js` | 7 | `Main Functionality Tests` |
| `tests/site-accessibility.spec.js` | 4 | `Site Accessibility Tests` |
| `tests/accessibility.spec.js` | 8 | `Accessibility Tests` |
| `tests/security.spec.js` | 9 | `Security and Vulnerability Tests` |
| `tests/performance.spec.js` | 7 | `Performance Tests` |

Navigation titles cover Home, About Us, Contact Us, an Afrikaans language switch, logo, breadcrumbs, and browser back/forward.

Security titles cover XSS in inputs, security headers, SQL injection in forms, CSRF, directory traversal, rate limiting, input length, clickjacking, and authentication bypass. These are browser probes against the staging site. They are not a penetration-test procedure and they are not evidence that the site implements those controls. `test-summary.json` marks the security suite `NOT_EXECUTED` because authentication failed.

`post-login-functionality.spec.js` contains a `test.skip()`.

**Documented result, stale relative to the tree:** `test-summary.json` (2024-12-04) and the README say site accessibility passed (the JSON says 20/20) and authentication failed because the login form did not match expected selectors. The JSON total is 93 tests. The current files sum to 68 `test()` calls. **Requires confirmation** before anyone treats that JSON as the current result.

**Discrepancy:** `CHANGELOG.md` `[Unreleased]` lists performance, accessibility, and database integration as planned. Those files already exist. `[1.0.0]` documents login credentials that match `login.spec.ts` and do not match the README / `playwright.config.js` / `global-setup.js` / `test-summary.json`.

Implementation: Specs implemented. Last recorded run in-repo does not match the current count.

Confidence: High for the file list. Low for current pass/fail.

### FR-009 — IPW login identities

Requirement that the code actually implements: two different identities, selected by which file runs.

- `tests/login.spec.ts` and `CHANGELOG.md` `[1.0.0]` hardcode username `jason@overdrive.co.za` and password `test`.
- `README.md`, `playwright.config.js` env defaults, `scripts/global-setup.js`, and `test-summary.json` use username `daniel`. The password in those files is omitted from this PRD.

Who: the spec or the setup script.

Trigger: the login spec versus any code path that reads `TEST_USERNAME` from the JavaScript config or global setup. Global setup is not attached to `playwright.config.ts`, so `npm test` does not run it.

**Requires confirmation:** which account is approved for `http://staging.ipw.co.za`.

Implementation: Both are implemented in different files.

Confidence: High that they differ.

### FR-010 — IPW reports, database, and email

Requirement: optional steps, each behind its own npm script, not behind `npm test`.

| Script | Behaviour |
|---|---|
| `test:run-and-save` / `test:save-results` | Run tests (first script only) then `scripts/save-test-results.js` |
| `test:full` | Playwright HTML reporter, then save results |
| `test:comprehensive` | `scripts/run-comprehensive-tests.js` |
| `test:report` | `scripts/generate-html-report.js` |
| `test:pdf` | `scripts/generate-pdf-report.js` (Puppeteer) |
| `test:email` | `scripts/send-email-report.js` |
| `check-db` / `check-schema` | MySQL connection and table setup |

**Documented** tables, if the database scripts are used: `playwright_test_results`, `playwright_execution_summaries`, `playwright_performance_trends`.

**Implemented** email: SMTP `mail.appnotify.co.za:587`, user `noreply@appnotify.co.za`, to `jaco@overdrive.co.za`, subject about `staging.ipw.co.za`, attaching HTML and a generated PDF. The mailbox password is in the script and is omitted here. The script's HTML body also hardcodes an old summary (site accessibility 100%, authentication 0%). That body is not computed from the latest run.

`playwright.config.js` default database name is `playwright_tests`, host `localhost`, user `root`, empty password. **Unknown** whether any developer database was actually created.

Implementation: Scripts implemented. Not on the default `npm test` path. Email password in source is a defect relative to the suite's own `.gitignore`, which ignores `.env`.

Confidence: High.

### FR-011 — Hostname folders without specs

Requirement: none implemented.

| Folder | What exists | What the text asks for |
|---|---|---|
| `label.sawis.co.za/` | Template only | The prompt at the top asks for Playwright tests of `http://staging.ipw.co.za/`, plus an emailed report. It does not ask for tests of `label.sawis.co.za`. Login and SMTP secrets are in the prompt. Omitted here. |
| `qa.d3t8evj08x1w1g.amplifyapp.com/` | Template only | The prompt asks for UI, post-login, and security tests of that Amplify URL, plus the same email report. Login and SMTP secrets are in the prompt. Omitted here. The product behind the URL is **Unknown**. |
| `sawisonline.co.za/` | Template (no prompt header) and a tracked `.env` | `.env` sets `BASE_URL=https://bi.sawisonline.co.za`, `TEST_ENV=production`, `DB_NAME=timsheets2`, `DB_HOST=localhost`, `DB_USER=root`. `DB_PASSWORD` is present and omitted. No `package.json` reads this file. |

**Inferred.** These folders are leftovers from a prompt used to generate the IPW suite, copied per hostname. That is an inference from the duplicated template and the IPW prompt text inside the label folder.

Implementation: Not implemented as test suites.

Confidence: High that no specs exist. Medium on why the folders exist.

## Major workflows

### W1 — Run SAWIS BI locally

1. `cd bi.sawisonline.co.za`
2. `npm install` and `npm run install:browsers` (documented Node 16+).
3. `npm run test:quick` runs one homepage test on Chromium, or `npm test` runs every spec on every configured project (five browser projects).
4. `npm run test:report` opens the HTML report.

No credentials. The live site must be reachable. **Unknown** whether the dashboard requires a session for charts. Specs attempt to read the public page.

### W2 — Run ShopFlow

1. `cd` into the ShopFlow directory.
2. `npm install`, `npx playwright install`.
3. Copy `config.env.example` to `.env` and set URL, email, password, and optionally the Gmail app password.
4. `npm test` runs the chromium dependency chain.
5. Phase 1 can create session state. Phases 2–6 read `auth.json`.
6. If 2FA automation is unset, the authenticator does not fetch a code. The README says tests then wait for manual input. The helper logs that it is skipping automated 2FA. Whether the spec actually pauses for a human was not confirmed beyond that log line. **Requires confirmation** if unattended runs are expected to pass without IMAP.

### W3 — Run IPW and send a report

1. `cd staging.ipw.co.za`
2. `npm install`, `npx playwright install`.
3. `npm test` uses `playwright.config.ts` and the credentials inside `login.spec.ts` for that file. Other spec files that read `TEST_USERNAME` only see global-setup defaults when global setup runs, which the TypeScript config does not register.
4. Optional: `npm run check-db`, `npm run test:email`.

The README says authentication tests are currently failing and other suites are blocked. That statement matches `test-summary.json` and may be stale. **Requires confirmation.**

## Integrations

| System | Direction | Trigger | Data | Auth | Failure behaviour |
|---|---|---|---|---|---|
| `https://bi.sawisonline.co.za` | Suite → site | Any BI test | Browser navigation only | None in config | Playwright timeout / assertion failure |
| `https://shopflow.php8` or `SHOPFLOW_URL` | Suite → site | ShopFlow tests | Browser session, form posts performed by the UI | Email, password, optional 2FA code | Alert text returned by `ShopFlowAuthenticator`; blocked IP stops login |
| Gmail IMAP | Suite → Gmail | 2FA step when a password is configured | Search subject `2FA Validation` since the login time; extract a code | IMAP user defaults to the test mailbox; password from env | Skip automation or return an error result. TLS certificate checks are disabled in the IMAP client |
| `http://staging.ipw.co.za` | Suite → site | IPW tests | Browser navigation and form fills | One of the two hardcoded identities, depending on file | README: login form did not match selectors |
| MySQL | Suite → DB | `check-db`, `check-schema`, save-results scripts | Test result rows | Defaults in `playwright.config.js` or env | Script failure. Not on the default test path |
| `mail.appnotify.co.za` | Suite → SMTP | `npm run test:email` | HTML report and PDF | Username in `send-email-report.js`; password omitted | Nodemailer error |

No webhooks, queues, or cloud deploy config were found.

## Constraints

- Do not commit `auth.json` (root `.gitignore`). ShopFlow phases 2–6 still require it locally.
- Root `.gitignore` lists `.env`, but `sawisonline.co.za/.env` is tracked. Gitignore does not untrack a file that was committed.
- Root ignore for `/playwright-report/` and `/test-results/` applies only at the repository root. The BI copies under `bi.sawisonline.co.za/` are tracked (501 + 1060 files).
- ShopFlow tests are serial because of shared cart state (**Documented** in config comments).
- This PRD does not repeat passwords, SMTP secrets, or Gmail app passwords. They are present in the source files named in `technical-documentation.md`.

## Non-goals

- Hosting SAWIS, ShopFlow, or IPW.
- Replacing Katalon. ShopFlow comments say the login spec mirrors a Katalon case. The Katalon project is not in this repo.
- A SonarQube scan. Not in git.

## Open conflicts

1. Code and README disagree on ShopFlow legacy specs, npm scripts, example credentials versus code fallbacks, and the "100+" count versus 278 `test()` calls.
2. IPW has two Playwright configs and two login identities.
3. IPW changelog `[Unreleased]` describes as future work files that are already in the tree.
4. `test-summary.json` counts do not match current specs.
5. BI README timeouts do not match `playwright.config.ts`.
6. Onboarding metadata listed Sonar files that git does not contain.
7. Template prompts and several scripts contain live credentials. That conflicts with `.gitignore` intent. Rotation is a human decision. This PRD does not change those files.
