---
name: "ui_stress_testing"
description: "Stress-test an interface with hostile data before users do: very long and unbroken strings, missing, null, and absent fields, huge, zero, and negative numbers, non-Latin, CJK, and right-to-left text, 1000 row lists, and empty, loading, and overflow states, with each break scored by severity and recorded with a screenshot and a fix. Use when auditing a form, table, dashboard, or any UI before release, when test data is all short and tidy, or when asked to harden a UI against real world data."
---

# Purpose

Normal test data hides real breaks. Names fit, counts stay small, fields are always present, and text is always short Latin script. Real data is not like that. This skill loads hostile values on purpose, one field at a time, records exactly what breaks with evidence, scores each break, and holds release until blocker breaks are fixed. Fixtures ship in `references/fixtures.json`, scoring and fix patterns in `references/scoring-and-fixes.md`, and three worked audits in `references/worked-audits.md`.

# Workflow

## 1. Pick surfaces and map fields

- List the surfaces in scope: form, table, dashboard, list, detail view, card, navigation, or search results. One audit covers one surface well rather than five surfaces thinly.
- Map each visible field, cell, title, badge, and count to the fixture categories it can receive: long, missing, numbers, intl, dates, volume.
- Record the viewports and zoom levels for the audit before loading data: a desktop width, 320 CSS pixels wide, and 200 percent text resize. WCAG 2.2 criterion 1.4.10 Reflow requires content with no loss at 320 CSS pixels for vertical scrolling content, with exceptions for content that needs two dimensional layout such as data tables. Criterion 1.4.4 requires text resize to 200 percent without loss of content or function.

## 2. Load the hostile fixtures

- Load fixtures from `references/fixtures.json` with the bundled tool: `python3 bin/stress_tool.py list` prints all of them, and `python3 bin/stress_tool.py list --category intl` prints one category.
- Put one hostile value in one field at a time, then reload the surface. Loading five at once hides which value caused which break.
- Cover at minimum: a 120 character name, an 80 character unbroken filename, null, an empty string, a whitespace only string, an absent payload key, zero, 9999999, 1234567890.12, a negative amount, 0.0001, an Arabic title string, a mixed Hebrew and Latin title, a CJK title with no spaces, a name with an apostrophe and accents, and a 1000 row list.
- For user text fields, also run a short pass with strings from the Big List of Naughty Strings, a public list of inputs known to cause trouble as user data. Use it for display and handling checks here. Full input validation rules belong to a security review, not to this audit.
- Treat an absent key as its own case. Null, empty, and missing are three different payloads and they break different code paths.

## 3. Add pseudolocalization and resize passes

- Run one pass with pseudolocalized strings before any real translation exists. Pseudolocalization replaces a string with an accented, bracketed, longer version, so truncation, string concatenation, and hardcoded text show up early. Brackets mark where a string starts and ends, so a missing closing bracket means the string was cut or built from pieces.
- Generate one string at a time: `python3 bin/stress_tool.py pseudo "Confirm booking"` prints a bracketed version padded by 40 percent by default. Published pseudolocalization guides use roughly 30 to 40 percent expansion. Treat that range as a test heuristic, not as a claim about any one language.
- Repeat the audit at 320 CSS pixels and at 200 percent text resize. A surface that only works at full desktop width has not passed.

## 4. Walk the states, not just the data

- Test empty, loading, and error states on their own, then in sequence: loading then empty, loading then error, error then a retry that succeeds, and a slow load interrupted by navigation.
- Check overflow state by state: a skeleton placeholder that fits means nothing if the loaded title pushes the same card off screen.
- Check counts and badges in every state. Zero is a real result and gets its own wording. A loading state must not flash a zero or an empty message before data arrives.

## 5. Record, score, and gate

- Record every break as one row in a CSV with columns id, surface, fixture, severity, evidence, fix. Evidence is a screenshot or recording plus the fixture id and the viewport or zoom level. A break with no evidence does not enter the score.
- Score with the severity scale in `references/scoring-and-fixes.md`: S1 blocker at 10 points, S2 major at 5, S3 minor at 2, S4 note at 1. S1 means data loss, a blocked task, or a wrong value read as right.
- Run the gate: `python3 bin/stress_tool.py score report.csv`. The tool rejects rows with no evidence or no fix, prints the score, and exits 1 when any S1 exists.
- Apply the gate plainly: any S1 fails release for that surface, any S2 holds it unless a named owner records an accepted risk with a date, and S3 or S4 items get filed.

## 6. Fix and retest

- Fix with the patterns in `references/scoring-and-fixes.md`. Common ones: `overflow-wrap: break-word` for unbroken strings, `min-width: 0` on flex and grid children that will not shrink, the ellipsis trio plus a way to reach the full value, one display rule per field for null and missing values, and `dir="auto"` or a `bdi` element for user strings inserted into a page with a different direction.
- Retest the exact fixture that broke, at the exact size where it broke, plus one normal value to check the fix did not break the common case.
- Keep the hostile fixtures in the regular test data after the audit. A fix that only lives in a one time manual pass will regress.

# Output Contract

Deliver, for each audited surface:

1. The surface name, the viewports and zoom levels tested, and the fixture ids loaded.
2. A break report CSV with columns id, surface, fixture, severity, evidence, fix, plus the tool output showing counts per severity, the total score, and the gate verdict.
3. For each break: the screenshot or recording reference, what the user would see or lose, and the specific fix applied or proposed.
4. A retest result per fixed break: pass or fail, with the fixture id and size retested.
5. A short list of what was not tested, for example a locale, a browser, or a surface left out of scope. Do not present a partial audit as a full one.

# Operating Rules

1. Never invent limits, ratios, or standards. WCAG numbers in this skill come from W3C guidance for criteria 1.4.4 and 1.4.10. Pseudolocalization expansion is a heuristic from published guides, stated as a range.
2. Never truncate a number on the left, and never let a truncated label stand as the only copy of a value. Every clipped text needs a path to the full text.
3. Never render a missing value as the literal words null, undefined, or NaN, and never treat zero as missing.
4. Keep hostile data in test accounts and local builds only. Do not put hostile fixtures into production data, shared demos, or another person and their account.
5. One hostile value at a time during diagnosis. Combined loads are fine for a final soak pass after single value breaks are recorded.
6. A screenshot from a different viewport, a different fixture, or a staging build presented as release evidence invalidates that row. Retake it.
