How to Test Contact List on Web (Complete Guide)
Contact lists are a core feature in many web applications—CRMs, messaging platforms, e‑commerce sites, and internal tools. When a user adds, edits, searches, or deletes a contact, they expect the oper
Why Testing a Contact List Matters
Contact lists are a core feature in many web applications—CRMs, messaging platforms, e‑commerce sites, and internal tools. When a user adds, edits, searches, or deletes a contact, they expect the operation to be instantaneous, accurate, and safe. Failures in this area manifest as:
- Data loss – a newly entered contact disappears after a page reload.
- Incorrect display – phone numbers appear scrambled, or email addresses are truncated.
- Security leaks – contact details are exposed in network responses or accessible to unauthorized users.
- Accessibility barriers – screen‑reader users cannot navigate the list or activate actions.
- Performance regressions – infinite scrolling stalls, causing the UI to freeze.
Because the contact list touches data persistence, UI rendering, state management, and often third‑party integrations (e.g., address‑book APIs), a defect here can cascade into downstream workflows such as messaging, billing, or reporting. A systematic test strategy catches these issues before they reach production, reduces support overhead, and preserves user trust.
Comprehensive Test Matrix
Below is a matrix that groups test ideas by category, sub‑category, and typical verification points. Use it as a checklist when designing manual or automated suites. Each row represents a distinct test scenario; the “Expected Result” column describes the observable outcome that indicates a pass.
| Category | Sub‑category | Test ID | Description | Expected Result |
|---|---|---|---|---|
| Happy Path | Add contact | HP‑01 | Fill all required fields (first name, last name, phone, email) and submit. | New contact appears at the top/bottom of the list with correct values. |
| Edit contact | HP‑02 | Open an existing contact, change the phone number, save. | Updated phone number reflects in the list; other fields unchanged. | |
| Delete contact | HP‑03 | Select a contact and confirm deletion. | Contact removed from list; no trace in UI or storage. | |
| Search | HP‑04 | Type a substring that matches one contact’s name. | Only matching contacts displayed; others hidden. | |
| Filter by group | HP‑05 | Apply a filter for a custom group (e.g., “Work”). | List shows only contacts belonging to that group. | |
| Bulk select | HP‑06 | Use checkbox‑select‑all, then delete selected contacts. | All selected contacts removed; unselected remain. | |
| Error Paths | Required field missing | EP‑01 | Submit add form with first name left blank. | Inline validation error highlights the empty field; form does not submit. |
| Invalid email format | EP‑02 | Enter “user@” in email field. | Error message indicates invalid email; submission blocked. | |
| Phone number too short | EP‑03 | Enter “123” in phone field. | Validation prevents save; tooltip shows required length. | |
| Duplicate detection | EP‑04 | Attempt to add a contact with same email as existing. | System warns of duplicate and blocks creation (or offers merge). | |
| Network failure on save | EP‑05 | Simulate offline state while submitting. | UI shows offline banner; data queued and syncs when connection restored. | |
| Server error (500) | EP‑06 | Mock API to return 500 on add request. | Error toast displayed; no partial contact appears in list. | |
| Edge Cases | Very long strings | EC‑01 | Input 200‑character name, 300‑character phone. | UI truncates or wraps gracefully; no overflow or layout break. |
| Special characters | EC‑02 | Name contains emojis, Unicode accents, or HTML tags (). | Characters rendered correctly; no XSS execution. | |
| Leading/trailing spaces | EC‑03 | Enter “ John Doe ” in name fields. | System trims spaces on save; displayed name has no extra spaces. | |
| Zero contacts state | EC‑04 | Fresh user with no contacts saved. | Empty state illustration or message shown; no JavaScript errors. | |
| Pagination / infinite scroll | EC‑05 | Load more than 200 contacts; scroll to bottom repeatedly. | New batches load without duplication; scroll position stable. | |
| Keyboard navigation | EC‑06 | Tab through add form, use Enter to submit, Escape to cancel. | Focus moves logically; form submits on Enter, cancels on Escape. | |
| Touch gestures (mobile viewport) | EC‑07 | Swipe left on a contact to reveal delete button (if implemented). | Delete action triggered; swipe right restores original view. | |
| Accessibility | Screen reader labels | A‑01 | Navigate list with NVDA or VoiceOver. | Each list item announces name, phone, email, and available actions. |
| Color contrast | A‑02 | Verify contrast ratio of text vs. background meets WCAG AA (4.5:1). | All text passes contrast test. | |
| Focus order | A‑03 | Tab through controls; ensure logical order. | Focus never jumps unexpectedly; modal traps focus when open. | |
| ARIA live regions | A‑04 | After adding a contact, listen for announcement. | Live region announces “Contact added”. | |
| Touch target size | A‑05 | Measure tap areas on buttons (minimum 44×44 dp). | All actionable elements meet size guideline. | |
| Security / Privacy | Data exposure in network | SP‑01 | Inspect XHR/fetch payloads when listing contacts. | No unnecessary fields (e.g., internal IDs, passwords) sent. |
| Authorization bypass | SP‑02 | Attempt to access /api/contacts/42 without valid token. | Server returns 401/403; UI shows error or redirects. | |
| XSS via contact fields | SP‑03 | Insert into name field. | Script does not execute; content escaped or sanitized. | |
| CSRF on delete | SP‑04 | Perform a cross‑site delete request lacking CSRF token. | Request rejected; contact remains. | |
| GDPR right to be forgotten | SP‑05 | Delete a contact; verify removal from backups/logs (if applicable). | Contact not retrievable via any API after deletion. | |
| Performance | Initial load time | PF‑01 | Measure time to render list with 50 contacts on 3G throttled. | First meaningful paint < 2 seconds. |
| Scroll jank | PF‑02 | Record FPS while scrolling rapidly through 500 contacts. | Average FPS ≥ 55; no dropped frames causing visible stutter. | |
| Memory leak | PF‑03 | Repeatedly add/delete 100 contacts over 5 minutes. | Memory growth stays within acceptable bounds (< 10 MB increase). | |
| Cache utilization | PF‑04 | Reload page after adding a contact; check if request to server avoided. | Subsequent load uses cached data or optimized delta sync. |
How to Use the Matrix
- Manual testing – pick a subset of IDs that match your sprint goals, execute steps, and record pass/fail.
- Automated testing – map each ID to a test case in your test framework; use data‑driven techniques for variations (e.g., different invalid email formats).
- Regression – after each release, run the full matrix (or a risk‑based selection) to ensure no new defects slipped in.
Manual Testing Approach – Step‑by‑Step
Even when automation covers the bulk of regression, a disciplined manual session uncovers usability quirks, visual regressions, and context‑specific issues that scripts may miss. Follow this procedure for a thorough exploratory pass.
- Environment preparation
- Use a clean browser profile (no extensions, cache cleared).
- Set device emulation to both desktop (1920×1080) and mobile (360×640) to catch responsive bugs.
- Enable developer tools → network throttling (Slow 3G) and CPU slowdown (4×).
- Login / state setup
- Log in with a test account that has zero pre‑existing contacts.
- Verify you land on the contacts landing page (URL often
/contactsor/addressbook).
- Happy‑path walkthrough
- Click “New Contact”.
- Fill each field with realistic data (e.g., “Ada Lovelace”, “ada@example.com”, “+1‑555‑0123”).
- Submit; observe UI feedback (toast, inline success).
- Locate the new entry in the list; validate each column.
- Open the contact for edit; change one field; save; confirm update.
- Select the contact; press delete; confirm in dialog; ensure removal.
- Error‑path injection
- Repeat the add flow but deliberately leave required fields blank.
- Note where validation messages appear; ensure they are associated via
aria-describedby. - Try invalid email patterns, phone letters, oversized strings.
- For each, confirm the form blocks submission and the UI guides correction.
- Edge‑case probing
- Paste a 250‑character name generated via
python -c "print('x'*250)". - Check for horizontal scroll, clipped text, or broken layout.
- Insert emojis (
😀) and HTML (bold) to see if they render as text or cause markup injection. - Test leading/trailing spaces; verify they are trimmed after save.
- With an empty list, confirm the empty‑state illustration appears and is accessible (has appropriate
alttext).
- Search & filter validation
- Type a query that matches zero results, many‑ Verify the query term and filter respects case‑clear after removing the query field and group filter. filter list for group “Friends”. Verify that only contacts with that group tag appear. Remove filter; all contacts return.
- Bulk actions
- Use the checkbox column header to select all visible contacts.
- Choose “Delete selected”. Confirm in modal.
- Verify that only the selected contacts disappear; any off‑screen contacts (if pagination) remain untouched unless explicitly selected.
- Accessibility audit (manual)
- Turn on screen reader (NVDA on Windows, VoiceOver on macOS).
- Navigate to the list; listen for announcement of each item’s name, phone, and actions.
- Tab through the add form; ensure focus order follows visual order.
- Open a modal (e.g., delete confirmation); verify focus traps inside and returns to trigger element on close.
- Use a color‑contrast analyzer extension to spot any low‑contrast text.
- Security sniffing
- Open DevTools → Network; preserve log.
- Perform add/edit/delete; inspect request payloads and responses.
- Look for accidental leakage of internal IDs, tokens, or other users’ data.
- Attempt to directly call an endpoint (e.g.,
GET /api/contacts) without auth token; expect 401/403.
- Performance check
- With the Network tab throttled to Slow 3G, reload the contacts page.
- Record time to first paint (via Performance tab) and time to list population.
- Scroll quickly; watch the FPS counter in the Performance panel.
- If using virtual scrolling, ensure that off‑screen rows are detached from DOM.
- Cleanup
- Delete any test contacts created.
- Log out and log back in to confirm persistence works across sessions.
Document each step’s outcome in a test‑run spreadsheet, attaching screenshots or video clips for failures. This manual baseline becomes the oracle for your automated tests.
Automated Testing Strategies for Web Contact Lists
Automation excels at repeatable validation of functional correctness, regression detection, and CI gating. Below are the layers you should implement, with concrete examples using popular frameworks.
Unit / Service Layer
If your front‑end consumes a contacts API via a service module (e.g., contactsService.js), unit test that module in isolation.
// contactsService.test.js
import { addContact, updateContact, deleteContact } from './contactsService';
import { mockApi } from './testUtils'; // a library like msw or jest‑mock‑axios
describe('contactsService', () => {
beforeEach(() => mockApi.reset());
test('addContact sends correct payload', async () => {
mockApi.onPost('/api/contacts').reply(201, { id: 42, ...payload });
const result = await addContact({ firstName: 'Ada', lastName: 'Lovelace', email: 'ada@example.com' });
expect(result.id).toBe(42);
const [request] = mockApi.history.post;
expect(request.data).toMatchObject({
firstName: 'Ada',
lastName: 'Lovelace',
email: 'ada@example.com',
});
});
test('updateContact handles 400 validation error', async () => {
mockApi.onPatch('/api/contacts/42').reply(400, { error: 'Invalid email' });
await expect(updateContact(42, { email: 'bad' })).rejects.toMatchObject({ error: 'Invalid email' });
});
});
*Use Jest or Vitest for the test runner; MSW (Mock Service Worker) to intercept network calls.*
Component / UI Tests
Render the contact‑list component in isolation with React Testing Library, Vue Test Utils, or Svelte Testing Library. Focus on interactions and DOM assertions.
// ContactList.test.jsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import ContactList from './ContactList';
import { mockApi } from './testUtils';
beforeEach(() => mockApi.reset());
test('displays added contact after submit', async () => {
render(<ContactList />);
// open add form
await fireEvent.click(screen.getByRole('button', { name: /add contact/i }));
// fill form
await fireEvent.change(screen.getByLabelText(/first name/i), { target: { value: 'Ada' } });
await fireEvent.change(screen.getByLabelText(/last name/i), { target: { value: 'Lovelace' } });
await fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'ada@example.com' } });
await fireEvent.change(screen.getByLabelText(/phone/i), { target: { value: '+1-555-0123' } });
// submit
await fireEvent.click(screen.getByRole('button', { name: /save/i }));
// wait for list update
await waitFor(() => {
expect(screen.getByText('Ada Lovelace')).toBeInTheDocument();
expect(screen.getByText('ada@example.com')).toBeInTheDocument();
expect(screen.getByText('+1-555-0123')).toBeInTheDocument();
});
});
*Key points*: use role‑based queries (getByRole, getByLabelText) to mirror how assistive tech perceives the UI; avoid brittle selectors like CSS classes unless they are part of a design system contract.
End‑to‑End (E2E) Tests
E2E validates the full stack—routing, state management, API contracts, and third‑party integrations. Playwright and Cypress are the most common choices for modern web apps.
#### Playwright Example (TypeScript)
// tests/contact-list.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Contact List', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/contacts');
// ensure clean state via API call (if your app supports it)
await page.route('**/api/contacts**', route => {
route.fulfill({ status: 200, json: { contacts: [] } });
});
await page.reload();
});
test('adds a new contact successfully', async ({ page }) => {
await page.click('button:has-text("Add Contact")');
await page.fill('input[label="First Name"]', 'Ada');
await page.fill('input[label="Last Name"]', 'Lovelace');
await page.fill('input[label="Email"]', 'ada@example.com');
await page.fill('input[label="Phone"]', '+1-555-0123');
await page.click('button:has-text("Save")');
// verify toast
await expect(page.locator('text=Contact added')).toBeVisible({ timeout: 5000 });
// verify list entry
const row = page.locator('tr:has-text("Ada Lovelace")');
await expect(row).toBeVisible();
await expect(row.locator('td:nth-child(2)')).toHaveText('ada@example.com');
await expect(row.locator('td:nth-child(3)')).toHaveText('+1-555-0123');
});
test('blocks submission with invalid email', async ({ page }) => {
await page.click('button:has-text("Add Contact")');
await page.fill('input[label="Email"]', 'notanemail');
await page.click('button:has-text("Save")');
await expect(page.locator('text=Please enter a valid email')).toBeVisible();
await expect(page.getByRole('button', { name: /save/i })).toBeDisabled();
});
test('deletes a contact and confirms removal', async ({ page }) => {
// seed a contact via API
await page.route('**/api/contacts**', route => {
route.fulfill({
status: 200,
json: { contacts: [{ id: 1, firstName: 'Ada', lastName: 'Lovelace', email: 'ada@example.com', phone: '+1-555-0123' }] },
});
});
await page.reload();
await page.click('tr:has-text("Ada Lovelace") button[aria-label="Delete"]');
await page.click('button:has-text("Confirm")');
await expect(page.locator('tr:has-text("Ada Lovelace")')).not.toBeInViewport();
await expect(page.locator('text=No contacts found')).toBeVisible();
});
});
#### Cypress Equivalent (JavaScript)
// cypress/e2e/contact_list.cy.js
describe('Contact List', () => {
beforeEach(() => {
cy.visit('/contacts');
cy.intercept('GET', '**/api/contacts', { contacts: [] }).as('getContacts');
cy.wait('@getContacts');
});
it('adds a contact', () => {
cy.contains('button', 'Add Contact').click();
cy.get('input[placeholder="First Name"]').type('Ada');
cy.get('input[placeholder="Last Name"]').type('Lovelace');
cy.get('input[placeholder="Email"]').type('ada@example.com');
cy.get('input[placeholder="Phone"]').type('+1-555-0123');
cy.contains('button', 'Save').click();
cy.contains('Contact added').should('be.visible');
cy.contains('tr', 'Ada Lovelace')
.should('exist')
.find('td')
.eq(1).should('have.text', 'ada@example.com')
.end()
.eq(2).should('have.text', '+1-555-0123');
});
it('shows error for invalid email', () => {
cy.contains('button', 'Add Contact').click();
cy.get('input[placeholder="Email"]').type('bad@@');
cy.contains('button', 'Save').click();
cy.contains('Please enter a valid email').should('be.visible');
cy.contains('button', 'Save').should('be.disabled');
});
it('deletes a contact', () => {
cy.intercept('GET', '**/api/contacts', {
contacts: [{ id: 9, firstName: 'Ada', lastName: 'Lovelace', email: 'ada@example.com', phone: '+1-555-0123' }],
}).as('seed');
cy.visit('/contacts');
cy.wait('@seed');
cy.contains('tr', 'Ada Lovelace').find('button[aria-label="Delete"]').click();
cy.contains('button', 'Confirm').click();
cy.contains('tr', 'Ada Lovelace').should('not.exist');
cy.contains('No contacts found').should('be.visible');
});
});
Tips for stable E2E
- Use
data-testidorroleselectors rather than fragile CSS classes. - Mock external services (payment gateways, third‑party address‑book APIs) to eliminate flakiness.
- Run tests in parallel on CI shards to keep feedback loops short.
- Capture screenshots/video on failure for rapid triage.
Tooling Landscape for Web Contact‑List Testing
Choosing the right stack influences maintenance effort, test fidelity, and developer experience. Below is a comparison of widely‑adopted tools across the three testing layers.
| Layer | Tool | Language / Framework | Strengths | Weaknesses / Gotchas |
|---|---|---|---|---|
| Unit / Service | Jest | JavaScript/TypeScript | Fast, built‑in mocking, snapshot support | Requires additional setup for ES modules; mocking fetch can be verbose |
| Unit / Service | Vitest | JavaScript/TypeScript | Native ESM support, Jest‑compatible API, lightning fast | Smaller ecosystem, fewer plugins |
| Component | React Testing Library (RTL) | React | Encourages accessible queries, minimal DOM reliance | Not suited for testing lifecycle hooks that depend on external stores |
| Component | Vue Test Utils | Vue 2/3 | Full Vue instance access, easy mounting | API differs between Vue 2 and 3; need version‑specific docs |
| Component | Svelte Testing Library | Svelte | Simple rendering, integrates with RTL queries | Limited to Svelte; community smaller |
| E2E | Playwright | TypeScript/JavaScript/Python/.NET/Java | Auto‑wait, built‑in tracing, multi‑browser, API mocking, no flaky waits | Heavier binary download (~100 MB) |
| E2E | Cypress | JavaScript/TypeScript | Time‑travel debugging, rich UI, extensive plugins | Runs only in Chromium/Firefox/WebKit via bundled browsers; limited cross‑origin navigation |
| E2E | Selenium WebDriver | Java/JavaScript/Python/C#/Ruby | Industry standard, works with any browser via drivers | Verbose API, requires explicit waits, slower execution |
| Accessibility | axe-core | JS/TS (integrates with Jest, Playwright, Cypress) | Comprehensive WCAG checks, easy CI integration | Only static analysis; does not replace manual screen‑reader testing |
| Performance | Lighthouse CI | Node | Audits performance, accessibility, SEO, best practices | Lab data only; not a substitute for real‑user monitoring |
| Visual Regression | Percy / Chromatic | SDKs for Storybook integration | Detects pixel‑level changes, handles dynamic content | Requires baseline management; false positives on anti‑aliasing differences |
| API Mocking | MSW (Mock Service Worker) | Node / Browser | Intercepts requests at network level, works both in tests and dev | Needs careful cleanup to avoid leaking mocks across test files |
When to pick what
- If your team already uses Jest for unit tests, stay there for service mocks; add Vitest only if you are on a pure‑ESM codebase and want faster startup.
- For component tests, React Testing Library is the de‑facto standard; pair it with
@testing-library/jest-domfor expressive matchers. - Choose Playwright over Cypress when you need true multi‑browser support (including mobile emulation) or want to test scenarios that involve multiple tabs, origins, or file downloads.
- Use Selenium only if you must integrate with an existing enterprise grid that already runs Selenium nodes.
- Run axe‑core in every CI build as a gate; treat any violation as a blocker until resolved.
Edge Cases That Surface Only in Production
Even the most exhaustive test matrix can miss issues that arise under real‑world traffic, data variance, or infrastructure quirks. Below are categories of production‑only bugs and concrete ways to surface them during testing.
1. Data‑Driven UI Glitches
*Problem*: A contact’s name contains a rare Unicode character (e.g., 𝔘) that causes a font‑fallback bug, leading to missing glyphs or layout shift.
*Detection*:
- Use a data‑generation library like
fakerwith a locale that includes exotic scripts. - In Playwright, after rendering the list, evaluate
window.getComputedStyle(element).fontFamilyand compare to expected fallback chain. - Add a visual‑regression check for the specific character.
2. Race Conditions in Optimistic UI
*Problem*: The UI optimistically shows a newly added contact, but the backend rejects it due to a server‑side validation (e.g., duplicate email). The UI then needs to roll back, but the rollback fails leaving a phantom entry.
*Detection*:
- Mock the API to delay the response (e.g., 2 seconds) and intermittently return 409 Conflict.
- In Cypress, use
cy.interceptwithdelayand a conditional reply. - After submit, assert that the optimistic row is removed when the error response arrives.
3. Network Partition & Offline Sync
*Problem*: The app queues edits while offline, but upon reconnection it sends stale payloads, overwriting newer changes made on another device.
*Detection*:
- Use Playwright’s
routeto simulatenet::ERR_INTERNET_DISCONNECTED. - Perform an edit offline, then edit the same contact via a second browser tab (representing another device) while online.
- Restore network and verify that the final state reflects the later edit, not the queued stale one.
4. Third‑Party Address‑Book API Rate Limiting
*Problem*: When importing contacts from Google or Outlook, the app exceeds the provider’s rate limit, resulting in HTTP 429 responses that are not handled, causing the import to stall silently.
*Detection*:
- Mock the external endpoint to return 429 after N successful calls.
- Observe whether the UI shows a retry mechanism, exponential backoff, or a user‑visible error message.
5. Memory Leak in Virtual Scrolling
*Problem*: A virtual‑scroll list fails to unmount detached rows, causing DOM growth and eventual browser slowdown after hundreds of add/delete cycles.
*Detection*:
- In a test loop, add 100 contacts, then delete 100, repeat 20 times.
- After each cycle, take a heap snapshot (via Chrome DevTools Protocol in Playwright) and assert that the number of DOM nodes returns to baseline.
6. CSP Violations from User‑Generated Content
*Problem*: A contact’s note field accepts raw HTML; a malicious user injects a script that violates the Content Security Policy, which is blocked but not reported to the user, leaving them confused why the note disappears.
*Detection*:
- Submit a note containing
. - Check the browser console for CSP violation messages.
- Ensure the UI either sanitizes the input (shows escaped text) or presents a clear error that the content was rejected.
7. Localization Layout Breakage
*Problem*: When the UI language switches to Arabic (right‑to‑left), the contact‑list columns misalign, causing action buttons to overlap with text.
*Detection*:
- Set
document.documentElement.lang = 'ar'anddir = 'rtl'before mounting the component. - Run the same interaction tests and assert that padding/margin values are mirrored (use
getComputedStyleto checkmargin-leftvsmargin-right).
8. Session Expiration Mid‑Flow
*Problem*: A user begins editing a contact, the auth token expires silently, and the subsequent save request returns 401; the app redirects to login losing the unsaved edits.
*Detection*:
- Shorten the token’s TTL in test environment (e.g., 10 seconds).
- Start an edit, wait for token expiry, then attempt to save.
- Verify that the app either refreshes the token silently or prompts to re‑login *after* saving a draft to localStorage.
Mitigation Strategies
- Integrate property‑based testing (e.g.,
fast-check) to generate random strings, numbers, and objects for fields. - Use chaos‑testing tools like
toxiproxyto inject latency, packet loss, or bandwidth limits during E2E runs. - Keep a production‑like dataset (scrubbed PII) in a staging environment and run a nightly exploratory suite with SUSA (see next section) to catch regressions that unit tests miss.
Autonomous, Persona‑Driven Exploration with SUSA
Traditional scripted tests verify known paths; they rarely stumble upon the surprising interactions that real users exhibit. SUSA (Susatest) introduces autonomous exploration driven by configurable user personas, each with distinct behavior patterns, goals, and tolerances for friction.
How It Works
- Model Building – SUSA crawls the application, constructing a state graph of screens, UI elements, and possible actions (taps, keystrokes, scrolls).
- Persona Profiling – You select or define personas (e.g., “Impatient Power User”, “Elderly Novice”, “Adversarial Tester”). Each persona has a probability distribution over actions: speed of interaction, likelihood to use keyboard shortcuts, propensity to ignore validation messages, etc.
- Guided Exploration – The engine walks the state graph, making decisions according to the chosen persona’s policy. It automatically handles dialogs, fills forms with generated data (respecting constraints), and can inject network faults or accessibility‑mode toggles on the fly.
- Issue Detection – While exploring, SUSA monitors for: JavaScript errors, uncaught promises, ANR‑equivalent long tasks, accessibility violations (via integrated axe checks), security red flags (e.g., reflected input in responses), and UX friction signals (rage clicks, repeated back‑button presses).
- Regression Script Generation – After a run, SUSA exports the traversed flows as executable test scripts: Appium for Android WebView equivalents, Playwright for pure web, or Cypress for those who prefer its syntax. These scripts capture the exact sequences the persona exercised, providing a reproducible baseline for CI.
Practical Example: Testing the Contact‑List with an “Adversarial” Persona
Suppose you want to see how the app behaves when a user deliberately tries to break it (e.g., pasting huge strings, rapid double‑clicks, using keyboard shortcuts in unexpected orders).
# Install the SUSA agent (Node‑based CLI)
npm i -g susatest-agent
# Run an exploratory session targeting the contact list page
susatest run \
--url https://app.example.com/contacts \
--persona adversarial \
--max-depth 8 \
--output ./susareport \
--export-playwright ./generated-tests/contactListAdv.spec.ts
*What happens under the hood*
- The agent loads the page, identifies the “Add Contact” button, the input fields, the list rows, and the delete icons.
- Guided by the adversarial policy, it:
- Enters a 5000‑character string into the first name field (testing overflow).
- Rapidly double‑clicks the save button 10 times (testing debounce).
- Pastes a string containing
into the notes field (testing XSS). - Toggles the browser’s high‑contrast mode via keyboard shortcut (testing accessibility‑mode interaction).
- Throughout, SUSA logs any console errors, network 500s, or axe violations.
When the run finishes, you receive:
- A HTML report with screenshots at each failure point.
- A Playwright spec file that you can drop into your repo and run on every commit.
Why This Finds Bugs Scripts Miss
- Combinatorial explosion – A manual tester might try a few long strings; an adversarial persona can generate thousands of variations automatically.
- Real‑world timing – Personas emulate human hesitation, rapid
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free