Full example: Account → Proof of Address#
This complete spec brings the earlier helper explanations together. Its test bodies express actions through reusable functions, while the imports and lifecycle show how those actions share a browser session. The helper-file tour, screenshot walkthrough, and diagnostic walkthrough provide the implementation details used below.
The following complete Proof of Address spec shows the imports, shared lifecycle, test steps, and screenshot calls together.
The complete file#
/**
* Account -> Proof of Address
*
* Tests the Proof of Address List page: status dropdown, columns, detail view.
*/
import { test, BrowserContext, Page } from '@playwright/test';
import { columnNames, statusOptions } from '../../HelperFiles/cit-app-config.js';
import { takeNamedScreenshot, takeDocScreenshot } from '../../HelperFiles/components/helperFunctions/utilities.js';
import { setupTestPage } from '../../HelperFiles/components/settings-helpers.js';
import { verifyHeadingPresence, checkColumnHeaders, verifyFieldsPresent } from '../../HelperFiles/components/verification_checks.js';
import { testDropdownOptions } from '../../HelperFiles/components/dropdowns.js';
import { clickViewActionInTable } from '../../HelperFiles/components/common.js';
import { safeClick } from '../../HelperFiles/components/buttons.js';
test.describe.serial('Account -> Proof of Address', () => {
let context: BrowserContext;
let page: Page;
test.beforeAll(async ({ browser }) => {
({ context, page } = await setupTestPage(browser, {
menuItem: 'Account',
subMenuItem: 'Proof of Address'
}));
});
test.afterAll(async () => {
await page?.close();
await context?.close();
});
test('Page loads with correct heading', async () => {
await verifyHeadingPresence(page, 'Proof of Address', false);
await checkColumnHeaders(page, columnNames.ProofOfAddress.HomePage);
await takeNamedScreenshot(page, 'proof_of_address', 'list_page');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-homepage');
});
test('Test Status dropdown options', async () => {
await testDropdownOptions(page, 'Status', statusOptions.ProofOfAddress, 'All');
await takeNamedScreenshot(page, 'proof_of_address', 'status_dropdown');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-status-filter');
});
test('View proof of address details', async () => {
await clickViewActionInTable(page);
await takeNamedScreenshot(page, 'proof_of_address', 'detail_view');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-view-details');
});
test('Verify detail fields', async () => {
await verifyFieldsPresent(page, columnNames.ProofOfAddress.DetailFields);
await takeNamedScreenshot(page, 'proof_of_address', 'detail_fields');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-detail-fields');
});
test('Verify history table headers', async () => {
await checkColumnHeaders(page, columnNames.ProofOfAddress.HistoryTable);
await takeNamedScreenshot(page, 'proof_of_address', 'history_table');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-table-columns');
});
test('Navigate back to list', async () => {
await safeClick(page, 'Back');
await verifyHeadingPresence(page, 'Proof of Address', false);
await takeNamedScreenshot(page, 'proof_of_address', 'back_to_list');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-back-to-list');
});
});
What the imports contribute#
Import |
Responsibility in this file |
|---|---|
|
Declares the suite, its lifecycle hooks, and the six test steps. |
|
Types for the browser session and tab shared by those steps. |
|
Supply expected application labels and dropdown values from |
Captures an image with a stable feature-and-step name for test artifacts and documentation tooling. |
|
Captures an image using a documentation section and filename that map to manual assets. |
|
|
Creates the context and page, applies settings, logs in, and attempts navigation to the requested submenu. |
|
Centralize the page checks; their exact failure behavior depends on the helper. |
|
Exercises configured dropdown options and restores the requested default. |
|
Encapsulates the table’s View interaction. |
|
Centralizes the Back button interaction and its locator/recovery strategies. |
This is the visible benefit of the module layout: a reader can follow the workflow before learning how every widget is located. The helper-file tour explains where those implementation details live.
The lifecycle, step by step#
test.describe.serial('Account -> Proof of Address', () => {
let context: BrowserContext;
let page: Page;
test.beforeAll(async ({ browser }) => {
({ context, page } = await setupTestPage(browser, {
menuItem: 'Account',
subMenuItem: 'Proof of Address'
}));
});
test.afterAll(async () => {
await page?.close();
await context?.close();
});
test.describe.serial groups an ordered workflow. The shared context and page variables allow the detail-field and history checks to continue on the detail screen opened by the preceding test. The callback uses the runner’s browser fixture to create its own shared session.
test.beforeAll calls the harness before the group’s test steps. The destructuring assignment stores the two returned resources in the outer variables. The navigation object supplies the destination: Account → Proof of Address. It lets the test state where it needs to begin while the harness owns session creation and login.
setupTestPage creates a context and page, sets a 60-second default page timeout and a 1280 × 720 viewport, calls loginAsSuperAdmin, and attempts sidebar navigation. It has a Settings-specific fallback and a fixed wait internally. It can swallow a non-Settings navigation error, so the setup call does not guarantee successful navigation.
test.afterAll closes the shared page and context. Optional chaining avoids calling close on an undefined resource. It does not catch a rejected close operation.
The shared session reduces repeated setup and preserves the recorded journey. It also introduces state dependencies: the detail checks require the earlier View step to succeed. That dependency is a tradeoff of the shared serial workflow.
What each step does#
Test |
What the reader should understand |
Documentation material |
|---|---|---|
Page loads with correct heading |
Checks the starting screen and configured list columns. |
The list-page screenshot and an overview of the screen. |
Test Status dropdown options |
Uses the central Proof of Address statuses instead of embedding a list of values in the test. |
The status-filter image and available filter labels. |
View proof of address details |
Uses a reusable table action to open a record. |
A screenshot for the “View details” procedure. |
Verify detail fields |
Asserts visibility of configured detail field labels. |
A draft list of fields to explain. |
Verify history table headers |
Invokes the shared table-header helper for the history section. |
An image and draft structure for a history-table explanation. |
Navigate back to list |
Calls the shared Back action and heading check. |
The final step in the navigation procedure. |
The false argument in verifyHeadingPresence(page, 'Proof of Address', false) means non-exact text matching. It is not a flag for ignoring errors.
How the screenshots become useful outside testing#
await takeNamedScreenshot(page, 'proof_of_address', 'list_page');
await takeDocScreenshot(page, 'Investors', 'proof-of-address-homepage');
The named capture produces proof_of_address_list_page.png under the configured screenshot root. The documentation capture uses the Investors section mapping, so its default destination is docs/Account/Investors/proof-of-address-homepage.png under that root. With DOC_SCREENSHOT_DIR set, it writes a .jpg under that override’s Account/Investors directory.
This mapping is used even though the tested screen is Proof of Address. The screenshot section is an asset destination, not the login role.
The named-screenshot chapter follows both implementations through output paths and an actual Markdown figure example. These stable filenames give the manual-draft workflow something concrete to connect to steps and pages. Screenshots are captured evidence of a browser state; their existence alone does not validate the surrounding prose or the business meaning of every field.
Readability and assertion behavior#
I kept the test bodies readable by moving repeated locators, conditional clicks, and waits into reusable helpers.
One implementation limitation remains: verifyHeadingPresence returns a boolean and logs a warning when it finds no matching visible heading, but this spec does not assert the returned boolean. Likewise, checkColumnHeaders collects missing-header failures and logs them. By comparison, verifyFieldsPresent uses visibility assertions.
These helpers could be improved by making required checks fail the test consistently. The agent repair loop also needed to preserve that behavior when changing a helper.