Write TestRail Cases
Core principle: A test case exists to verify a business rule, not to document a click path. Navigation is the means; assertions about the resulting state are the point. A case whose expected results could be true even if the feature were broken is a failed case.
Mandatory Workflow (follow in this exact order)
Detect the application area from the story (see Area Detection below) and read the matching context file in
references/context/. Never skip this. If the area cannot be determined, ask the user.1b. Assess context quality. After reading the context file, check if enrichment is needed (see Lightweight Context Enrichment below). Trigger enrichment automatically if ANY of these are true:
- Context file does not exist
- File header says DRAFT
- File has fewer than 10 Key Business Rules
- File has no "Exact UI Texts" section or fewer than 3 confirmed toasts
- File has more than 5 items in "Known Gaps / Items to Verify"
Read
references/examples.md— golden cases from the team's TestRail. New cases must match their verification depth and style.Read the User Story / Jira ticket (fetch via Jira MCP if an ID is given). Extract: Summary, Description, Acceptance Criteria, Development Team (→
custom_dev_team), and the ticket key(s) that belong inrefs.refs= whatever work item(s) this case verifies. In this team's real cases that is very often NOT a story — it may be a Bug the case regression-covers, a Tech-Debt/automation ticket, or several related tickets across different Jira projects (e.g.UIF-525, UIF-526, MODFIN-391, MODFIN-421). Include every ticket the user explicitly gave and every ticket the case directly verifies. Do NOT auto-add unrelated links Jira happens to show (unrelated parent epics, sibling subtasks, review tasks pulled in only by enrichment). If there is no story at all and the case exists to cover a bug, the bug key is the correctrefs— do not invent a story-style key.Map the story to the context file's "Key Business Rules" section: list every rule the story touches or changes. Each touched rule is a scenario candidate. Each acceptance criterion is a scenario candidate.
Perform Scenario Analysis and present the scenario list to the user for confirmation (format below). Wait for confirmation.
Generate cases, one per confirmed scenario, following the Writing Guidelines and Step Patterns.
Self-review every case against the Self-review gate before showing it. Fix violations silently.
Ask for the TestRail Section ID, show the posting preview, wait for explicit confirmation, then post via API.
Lightweight Context Enrichment
Runs automatically when triggered by step 1b. This is NOT a full
build-app-contextrun — it is fast and targeted to the specific story. Do not write anything to disk.
What to fetch
GitHub — max 3 files, in priority order:
translations/<ui-module>/en_US.json— extract values for translation keys that contain the story's main feature keywords (e.g. if story is about "batch allocation", extract keys containing "batch", "allocation", "budget"). Use the GitHub repo from the area detection table.package.json→stripes.permissionSets— extract allpermissionName/displayNamevalues as candidate Capability Set names.- One Cypress fragment file from
stripes-testing/cypress/support/fragments/<area>/that matches the story's feature — extract UI label strings and selector names.
Jira — story + directly linked issues only:
- The story itself is already fetched in step 3 — reuse it.
- Fetch all issues directly linked to the story: subtasks, related bugs, parent epic (one API call each).
- Extract acceptance criteria from each linked issue → additional business rule candidates.
- Issues fetched purely for enrichment are a context source — do not reflexively dump all of them into
refs.refsstays what step 3 set: the ticket(s) the user gave plus the item(s) the case directly verifies (which may be a bug, a tech-debt ticket, or several related keys — see step 3). The rule is "what does this case verify", not "story-key-only" and not "every link Jira shows".
What NOT to fetch
- All closed stories for the component — that is
build-app-contextscope. - All bugs for the component.
- More than 3 GitHub files.
- Any file larger than 150 KB.
How to use enriched data
Add extracted strings to in-memory context for this session only.
Do NOT write to the context file on disk — context files are read-only for this skill.
In the Scenario Analysis output, add a note:
⚠️ Context was enriched from GitHub/Jira for this session (context file triggered enrichment). Run
build-app-contextto make the enrichment permanent.If enrichment also returns zero useful data (GitHub unreachable, Jira returns nothing) — proceed with what the context file has and note it in Scenario Analysis:
⚠️ Context enrichment attempted but returned no additional data. Cases are based on the existing context file only — verify domain-specific details on first execution.
Area Detection and Context Files
Context files live in references/context/. Determine the area from (in priority order): the apps and entities actually described in the story text, the Development Team field, and only then the Jira project prefix as a hint. If the story content contradicts the prefix mapping, trust the story content. Confirm your detection in one line ("Story describes — loading context") rather than asking, unless detection is ambiguous — then ask.
The story itself is the source of truth. If the user's instructions contradict the story (e.g. "this is not an ECS story" while the story has an ecs label, mentions tenants/affiliations, or lives in a consortia module), do not silently follow either side — flag the conflict in one line and ask which to trust before generating. The same applies to ECS Enabled: derive it from the story's labels and text, not from assumptions.
| Area | Context file | Jira prefixes / keywords |
|---|---|---|
| Agreements | references/context/agreements.md |
ERM, UIAG; "agreement", "agreement line", "SAS" |
| Bulk Edit | references/context/bulk-edit.md |
UIBULKED, MODBULKOPS; "bulk edit", "identifier", "in-app", "commit changes", "preview of records matched", "errors accordion", "reason for error", "bulk edit profile", "query builder", "matched records", "suppress from discovery" |
| Check In | references/context/check-in.md |
UICHKIN, CIRC; "check in", "return", "backdate" |
| Check Out | references/context/check-out.md |
UICHKOUT, CIRC; "check out", "loan", "patron block" |
| Orders | references/context/orders.md |
UIOR, MODORDERS; "purchase order", "POL", "PO line", "encumbrance", "fund distribution", "order template", "open order", "unopen", "reopen", "claiming", "donor information", "bindery active", "routing list", "acquisitions unit" |
| Invoices | references/context/invoices.md |
UINV, MODINVOICE, MODINVOSTO; "invoice", "invoice line", "voucher", "approve invoice", "pay invoice", "cancel invoice", "pending payment", "lock total", "release encumbrance", "batch group", "adjustment", "pro rate", "export to accounting", "vendor invoice number", "approve and pay in one click", "update order status", "duplicate invoice" |
| Receiving | references/context/receiving.md |
UIREC; "piece", "receive", "receiving title" |
| Finance | references/context/finance.md |
UIF, MODFIN; "fund", "budget", "ledger", "fiscal year", "allocation", "encumbrance", "transfer", "rollover", "expense class", "acquisition unit" |
| Inventory | references/context/inventory.md |
UIIN, MODINV; "instance", "holdings", "item" |
| Requests | references/context/requests.md |
UIREQ; "request", "hold", "page", "recall", "pickup" |
| Mediated Requests | references/context/mediated-requests.md |
UIREQMED, MODREQMED; "mediated request", "secure tenant", "secure request", "interim service point" |
| Users | references/context/users.md |
UIU, MODUSERS; "patron", "user record", "custom fields", "proxy", "service points", "profile picture" |
| Fees/Fines | references/context/fees-fines.md |
UIU, MODFEE, MODFEESFINES; "fee", "fine", "fee/fine", "charge", "pay", "waive", "refund", "transfer account", "manual charge", "overdue fine", "lost item fee", "fee/fine owner", "payment method", "bursar" |
| Loans | references/context/loans.md |
UIU, CIRC; "loan", "loan details", "renew", "change due date", "declare lost", "declared lost", "claim returned", "claimed returned", "anonymization", "loan comments" |
| Organizations | references/context/organizations.md |
UIORG, MODORGS; "organization", "vendor", "vendor record", "interface", "integration", "EDI", "account number", "accounting code", "contact people", "donor", "banking information" |
| Circulation Settings | references/context/circulation-settings.md |
UICIRC, CIRCSET; "loan policy", "request policy", "overdue fine policy", "lost item fee policy", "circulation rules", "fixed due date schedule", "staff slips", "loan anonymization", "title level request", "TLR", "cancellation reasons", "closed library due date" |
| Patron Notices | references/context/patron-notices.md |
UICIRC; "patron notice", "notice template", "notice policy", "triggering event", "loan notice", "request notice", "fee/fine notice", "notice token" |
| Circulation Log | references/context/circulation-log.md |
UICIRCLOG, MODAUD; "circulation log", "circ log", "circ action", "log event", "logged" |
| Course Reserves | references/context/course-reserves.md |
UICR, MODCOURSE; "course", "course reserve", "reserve item", "instructor", "crosslist", "cross-listed", "registrar id" |
| Data Import | references/context/data-import.md |
UIDATIMP, MODDATAIMP, MODDICORE, MODSOURMAN, MODSOURCE, MODELINKS; "import", "job profile", "match profile", "action profile", "field mapping profile", "data import", "MARC import", "EDIFACT import", "LDR 05", "MARC Holdings", "file extension" |
| Data Export | references/context/data-export.md |
UIDEXP, MDEXP; "export", "mapping profile", ".mrc" |
| MARC Authority | references/context/marc-authority.md |
UIMARCAUTH, MODELINKS; "authority", "quickMARC" |
| MARC Bib (quickMARC) | references/context/marc-bib-quickmarc.md |
UIQM, MODSOURCE; "quickMARC", "MARC bib", "MARC bibliographic", "LDR", "leader", "008 field", "derive", "controlled field", "linking authority" |
| eHoldings | references/context/eholdings.md |
UIEH; "package", "title", "provider", "KB", "EBSCO" |
| Licenses | references/context/licenses.md |
UILIC, MODLIC, ERM; "license", "amendment", "term", "core document", "supplementary document" |
| Lists | references/context/lists.md |
UILISTS, MODLISTS, MODFQM; "list", "FQM", "query" |
| OAI-PMH | references/context/oai-pmh.md |
MODOAIPMH; "harvest", "OAI" |
| Consortium Manager | references/context/consortium-manager.md |
UICONSET (ui-consortia-settings), MODCON; "consortium", "ECS", "affiliation", "shared setting", "central tenant", "member tenant", "select members", "confirm share to all", "confirm member libraries", "authorization roles", "authorization policies", "data import logs", "data export logs" |
A story can span two areas (e.g. closing an order affects Finance) — read both context files.
Some areas are API/protocol-tested, not UI-tested. OAI-PMH, and API-flagged cases in MARC validation (
API | ...sections), assert HTTP requests and XML/JSON response fields rather than toasts, modals, and panes. For these, the same rigor applies but to a different surface: the "steps" are request URLs/params (e.g.verb=ListRecords&metadataPrefix=marc21_withholdings&from=<date>) and the "expected results" are response contents and exact field mappings (e.g. holdings → MARC952subfields), not UI strings. Don't force a UI navigation/toast shape onto these; follow the protocol/field-mapping detail in the context file. Execution Type may beKarateorBackend Componentrather thanManualfor such cases.
If a context file for the detected area does not exist, notify the user:
"I don't have a context file for this area yet. I'll generate cases based on the User Story alone — results may be less accurate for domain-specific preconditions."
How to use the context file
- Key Business Rules section is the scenario source. For each rule the story touches, decide: does this story change the rule, depend on it, or risk breaking it? Touched rules become scenarios.
- Use the domain model to write realistic preconditions: which records must exist, in what status, with what values (e.g. an encumbrance requires a Fund, a Budget in an active Fiscal Year, an Open PO with a POL fund distribution).
- Use documented side effects to build verification steps: if the context file says an action touches N entities (e.g. PO Close → encumbrance released, budget Available restored, PO status + reason for closure, POL statuses), the case verifies ALL of them.
- Use exact terminology from the context file (statuses, field names, transaction types) — never invent UI labels.
Context file protection rule
Context files in references/context/ are read-only for this skill.
Never modify, overwrite, or delete context files when running write-testrail-cases.
If a context file appears outdated, notify the user and suggest running build-app-context to refresh it.
Scenario Analysis
Before generating cases, present:
I've read the story (refs: XXX-123) and the <area> context. Business rules touched: <rule numbers/short names>.
Here are the scenarios I plan to cover:
Happy path:
1. User can successfully [main action] with valid data → Critical Path
Business-rule verification:
2. [action] releases/creates/updates [entity] with [exact state] → Critical Path
UI verification:
3. [page/modal] shows all required elements on open → Extended
Capability boundaries:
4. User without [Capability Set] cannot [action] → Critical Path
Negative / edge cases:
5. [action] fails when [condition] → Extended
Does this coverage look complete? Any scenarios to add or remove before I write the cases?
For feature stories on top of finance/rollover/templates, bias the scenario list toward the team's journey shape (see "Journey vs atomic cases"). Prefer a handful of
Functionaljourney cases that each bundle several related ACs over one atomic case per AC, and explicitly include an "Inferred integration scenarios" group proposing the cross-feature / over-time journeys the ACs don't spell out (fiscal-year rollover, create-from-template, new-FY-begins, sibling-gating-off). Present these clearly so the user can confirm scope before generation:Inferred integration journeys (not in the ACs — from domain knowledge, confirm scope): J1. Create + edit an order carrying the feature across a fiscal-year rollover → Functional / Critical Path / Journey J2. Create an order from a template that has the feature preconfigured → Functional / Critical Path / Journey J3. Feature behavior when a new fiscal year begins → Functional / Extended / Journey
Wait for the user's confirmation or corrections before proceeding.
Journey vs atomic cases
Choose the case granularity to match the story type:
- Lifecycle / workflow stories (a process with checkpoints: request lifecycle with notices, order open→receive→pay, import job stages) → write one journey case per flow variant, walking through the whole lifecycle and verifying every checkpoint along the way (like the team's mediated-request notice cases: create → notice → transit → arrival → pickup → notice → cancel → notice in a single case). Set
User Journey = Yesonly if the target area actually uses that flag (see the House Style table's journey column — most areas leave itNoeven for large cases). Flow variants (Page vs Hold, item-level vs title-level) become separate journey cases, not separate per-checkpoint cases. - Feature / element stories (a new column, modal, validation, setting) → atomic cases per scenario,
User Journey = No— but size each case to the area's median (a Fees&Fines element case is ~2 steps; a Bulk Edit one is large).
Never split one lifecycle into per-checkpoint stubs — an executor would have to rebuild the same preconditions N times, and intermediate state transitions would go unverified.
Match the target area's house style — do NOT impose one global shape. A measured audit of the real corpus (see "House Style by Area" table below) shows the team's Type, typical case size, and journey-flag usage vary strongly by area, so the right shape is whatever that area actually does, not a universal "journey-first" or "atomic" rule. Concretely:
- Case granularity / size follows the area's median. Some areas are short and atomic by norm (Fees&Fines median ~2 steps, Course Reserves / Circulation Settings ~3); others are large and workflow-shaped (Bulk Edit median ~14, Patron Notices ~12, Data Import ~10); acquisitions sit around 7–8. Write to the local median: don't emit 14 one-AC stubs where the area writes bundled workflow cases, and don't force a giant journey where the area writes tight atomic checks. When a feature genuinely spans a workflow (the UIOR-1530 Payment-terms example — the team wrote 4 large bundled cases C1385639/C1395029/C1404901/C1404902, not 13 one-AC stubs), bundle several related ACs into one case; when the story is a small element on a short-case area, stay atomic.
Typeis per-area (resolve IDs viaget_case_types). Acquisitions & a few others skew Functional (Orders 94%, Invoices 95%, Finance 94%, Organizations 85%, Mediated 94%, eHoldings 62%). Most circulation / ERM / export areas skew Other (Loans 85%, Fees&Fines 79%, Bulk Edit 94%, Agreements 89%, License 98%, Data Export 93%, OAI-PMH 84%, Circulation Log/Settings, Course Reserves, Lists). Several are ~50/50 (Check-in, Check-out, Data Import, Users, Requests). Pick the Type that dominates the target area's row below.User Journeyflag is rarely set — defaultNo. Across the corpus it is 0–3% in most areas (Orders 0%, Bulk Edit 0%, Finance 2%), with only a few modest exceptions (Check-in ~18%, Organizations ~15%, Circulation Settings ~10%). Do NOT setUser Journey = Yesjust because a case walks several steps — the team writes large bundled cases and still leaves the flagNo. Set itYesonly in the handful of areas that actually use it, or when the case is an explicit end-to-end lifecycle in a circulation area.Still think like a senior QA — infer integration scenarios the ACs don't spell out (fiscal-year rollover, create-from-template, sibling-gating-off, record-open side effects), derived from the context file's domain knowledge, and propose them in Scenario Analysis marked as inferred. That instinct is area-independent even though the case shape is not.
refs may span the whole feature, not just the one story — when a case genuinely exercises sibling FE/BE tickets (e.g. UIOR-1530 cases also drive UIOR-1528 + MODORDERS-1428), list all of them in
refs, not only the story you were asked about.
House Style by Area (measured from the real TestRail corpus, 2026-07-23)
Match the target area's row: use the dominant
Type, aim near the median step count, and only setUser Journey = Yeswhere the flag column is non-trivial. Percentages are share of that area's cases; resolve Type IDs viaget_case_types(Functional=6, Other=7).
| Area | Dominant Type | Median steps | Journey flag | Shape note |
|---|---|---|---|---|
| Orders | Functional (94%) | 8 | ~0% | Functional, mid-size; bundle workflow ACs |
| Invoices | Functional (95%) | 8 | ~1% | Functional, mid-size |
| Finance | Functional (94%) | 7 | ~2% | Functional, exact money values |
| Organizations | Functional (85%) | 8 | ~15% | Functional; journey flag sometimes used |
| Mediated Requests | Functional (94%) | 6 | ~0% | Functional, ECS-by-default |
| eHoldings | Functional (62%) | 6 | ~9% | Lean Functional |
| Loans | Other (85%) | 6 | ~0% | Other, short/atomic |
| Fees&Fines | Other (79%) | 2 | ~1% | Other, very short atomic cases |
| Bulk Edit | Other (94%) | 14 | ~0% | Other, large multi-step cases |
| Agreements | Other (89%) | 8 | ~9% | Other |
| License | Other (98%) | 7 | ~3% | Other |
| Data Export | Other (93%) | 6 | ~0% | Other |
| Lists | Other (65%) | 6 | ~0% | Other/Functional mix, lean Other |
| Circulation Settings | Other (69%) | 3 | ~10% | Other, short |
| Circulation Log | Other (82%) | 5 | ~8% | Other |
| Course Reserves | Other (61%) | 3 | ~0% | Other, short |
| OAI-PMH | Other (84%) | 8 | ~0% | Other, API/protocol assertions |
| Check-in | ~50/50 | 6 | ~18% | Mixed Type; journey flag used |
| Check-out | Func 51% / Other 43% | 6 | ~6% | Mixed Type |
| Data Import | Func 52% / Other 47% | 10 | ~1% | Mixed Type, large cases |
| Users | Func 52% / Other 46% | 6 | ~3% | Mixed Type |
| Requests | Func 53% / Other 45% | 5 | ~1% | Mixed Type |
| Patron Notices | Other 53% / Func 40% | 12 | ~2% | Lean Other, large cases |
| Inventory | Functional (67%) | 5 | ~0% | Functional, compact atomic; per-variant (record type / OS) |
| Receiving | Functional (99%) | 8 | ~0% | Almost pure Functional; piece flow + Inventory/Finance side effects |
For any area not in this table, sample the area's TestRail section directly and match what you see.
Watch for terse titles that hide a journey. A short scenario name like "item with at least one open request" or "check in special-status item" often means walk every outcome that condition produces in one case, not just the first. The team's real case for "item with an open request" (C7148) is a single
User Journey = Yescase that checks in the item at a non-pickup service point (→ In transit, transit slip, reprint), then switches to the pickup service point and checks it in again (→ Awaiting pickup, hold slip, reprint) — both fulfillment outcomes plus slip reprints in one case. When a condition can resolve to multiple end states, default to a journey covering all of them and confirm in Scenario Analysis, rather than emitting an atomic case for just one branch.
Verify effects at their real destination
Assert the end effect where it actually lands, not at a convenient proxy:
- Notices → check the mailbox from preconditions: exact subject + body contains the configured item/title/patron tokens. The Circulation log "Notice sent" entry may be an additional check, never the only one.
- Exports → download and check the produced file, not only the log row.
- Finance effects → the transaction and budget values, not only a toast.
Scenario Coverage Checklist
For every User Story, consider:
- Primary happy path (valid data, expected flow, correct end state)
- Every Key Business Rule the story touches — verified by explicit state assertions
- UI element verification (buttons, labels, columns, counts — especially for new pages)
- Edit and duplicate flows (where applicable)
- Delete flow including: Cancel button, confirm delete, verify removal from list
- Capability boundaries: restricted user cannot perform actions outside their Capability Sets — and the case also asserts what the restricted user still CAN see/do
- Negative cases: invalid input, wrong profile, empty files, referenced records blocking deletion
- Edge cases: repeated UUIDs, records referenced in multiple places, boundary values
- Status field changes (exact before/after values)
- Toast messages: exact text with placeholders — never just "success toast"
- Modal element verification: key elements are verified inline on first open; full modal inventory is used only when the modal itself is under test
- Interactive modal buttons (Swap, Recalculate): dedicated scenario verifying fields, errors, and button states update after clicking
- Multi-tenant / ECS behavior (only if applicable to the story)
- Drag and drop / reordering (if the story involves record order)
- Multi-tab behavior (only if the story explicitly covers concurrent editing — rare pattern, see end of Step Patterns)
- Inline edit flows: Save/Cancel appear inline, Save disabled until change made, row returns to read-only on Cancel
- Multi-select dropdowns: all options shown, dropdown stays open after selection, Save enables/disables correctly
- Search & filter pane: all accordions with correct initial expanded/collapsed state; Search/Reset buttons disabled by default
- Pagination: paginator appears when results exceed page limit; total count matches header
- Date range filters: From/To fields appear on accordion expand; results filter correctly
- Load/performance (if story involves bulk operations — mark as Extended)
- Every entry point for the touched action, and every selection state. When an action is reachable from more than one place in the UI (e.g. an
Actionsmenu on more than one tab, a per-row ellipsis menu, a detail-pageActionsmenu, a bulk-selection toolbar), the team tests it from all of them, not just the first one found — different entry points can expose different bugs (wrong button state, wrong selection scope). Likewise, when an action supports selecting zero / one / multiple / mixed-valid-and-invalid rows, each selection state is its own scenario candidate: none selected (control disabled), a single valid item, a single invalid item (control disabled or blocked), a mix of valid+invalid (alert / "deselect to continue"), and multiple valid items (does the action apply to each independently, or split/aggregate across them — e.g. an amount divided across multiple selected records). Check the context file for a documented entry-point list before assuming one flow covers the feature; if the context file doesn't document this for the touched action, ask or verify in a real corpus case rather than guessing at one path. (Learned from validating Fees/Fines Waive against real case C465 — seefees-fines.md"Cover every entry point for each action".)
Test Case Structure
Every test case has these sections (in order):
Metadata — Type, Priority, Release, Test Group, References, and other classification fields. Preconditions — numbered list: all users with their Capability Sets, required data setup with exact values, and starting system state. Steps — actions with a concrete expected result for each step.
Template
Title: [Actor/Subject] [verb] [object] [condition]
Type: Other
Priority: [Critical | High | Medium | Low — choose based on impact]
Release: Umbrellaleaf
Test Group: [Smoke | Critical Path | Extended — required]
User Journey: No [set to Yes for journey/lifecycle cases — see "Journey vs atomic cases"]
Multi-Tenant: No
Bug Created: No
Unstable: No
ECS Enabled: No [set to Yes only when the story explicitly tests ECS/cross-tenant behavior]
ECS Unsupported: No [fixed default — never ask]
Capabilities Ready: Yes
Execution Type: Manual [required]
Dev Team: [Value from "Development Team" field in the User Story]
Customer Name: [All | MOBIUS/GALILEO | LOC — only if the story targets a specific customer]
Labels: AI [always added to every case]
References: [Jira ticket IDs, comma-separated, e.g. MODFIN-273, UIF-657]
Preconditions:
1. User with following Capability Sets is logged in:
- Data - [Module] [Resource] - [Action]
- Procedural - [Module] [Resource] - [Action]
2. [Required records with exact values, closely related facts grouped into one entry where natural, e.g. "Two Fiscal years exist. One has an assigned Acquisition unit; Fund A-FY-current: Allocated = 200.00, Encumbered = 50.00, Available = 150.00"]
3. [More data as needed — one numbered entry per record/state or tightly related group of facts, so steps can reference "Preconditions #3"]
4. User is on [Starting app/page]
Steps:
1. Action: [verb] [object] on [location] — navigation/setup clicks may be grouped as a bullet list inside one step when they share a single verification point
Expected: [Observable UI or system result — prose by default; bullet list only for a true inventory of independently-checkable items]
2. Action: [verb] [entity] from Preconditions #N
Expected: [Column-by-column / field-by-field assertion with exact values]
Title Format Rules
Use a plain descriptive sentence — no prefixes, no labels.
Format: [Actor/Subject] [verb] [object] [condition]
Do's ✓
User with Edit capability set can create locked mapping profileMapping profile Status column shows Locked after lock checkbox is enabledDelete mapping profile modal shows all required elementsHoldings export fails when submitted file contains invalid UUIDsSearch filters mapping profiles by name and FOLIO record typeUser can create a multi-select custom field← always include actor ("User can...")User can check out requested item in Central tenant← ECS case: no prefix needed, tenant context is in the title
Don'ts ✗
— too vague, no subject or conditionVerify mapping profile creation— missing actor; rewrite asCreate a text field custom fieldUser can create a text field custom field— don't start with "Verify that"Verify that final custom field can be removed— no prefixesNegative: Holdings export with invalid UUIDs— no pipe-style prefixesECS | Delete locked mapping profile— no prefixesLoad testing - Export 500k records— don't start with "Test that"Test that user can create profile
ECS title exception: You may use
[ECS <Area>]prefix only when the test is exclusively ECS-specific and the area label adds essential disambiguation — e.g.[ECS Loans] User can check out requested item in Central tenant. Use sparingly; prefer embedding the tenant context in the title sentence itself.
Note: the Title is the one place actor naming ("User can...") is used. Steps below use imperative style instead — see Writing Guidelines.
Writing Guidelines
Write every test case as if it will be executed by a QA engineer who has never seen this feature before. Steps must be precise enough that any team member — Firebird, Spitfire, Vega, Volaris, Thunderjet — can execute the case without guessing. Same level of detail regardless of team or area.
Preconditions
Target environment is Eureka, so Capability Sets are the source of truth for a new case's access requirements — always include them. Note, however, that a large share of the team's real cases list both the legacy Okapi permission and the Eureka capability set, frequently as a two-column table ("Permission name for Okapi env" / "Capabilities/Sets for Eureka env"), e.g.:
| Permission name for Okapi env. | Capabilities/Sets for Eureka env. |
| Requests: All permissions | data - UI-Requests - manage |
This dual listing is an accepted team convention during the Okapi→Eureka transition — do not treat the presence of a legacy-permission column as an error, and preserve it if the user's source material or the section's existing cases use it. When generating from scratch with no such precedent, a clean Eureka-capability-set list alone is fine. Never rely on a legacy permission instead of a capability set.
- Number every precondition (1., 2., 3., ...) so steps can reference them as "Preconditions #N". The list must be flat: each user, each data record, and the starting page are separate top-level numbered items — never nest a data record or login state as a sub-bullet under another item. Sub-bullets are allowed only for listing Capability Sets under a user entry. Before finalizing, check that every "Preconditions #N" referenced in steps resolves to an actual top-level item N.
- Grouping facts within one entry is expected, not just tolerated. Real team-authored preconditions routinely pack several closely related facts into a single numbered item (e.g. "Two Fiscal years exist. One has an assigned Acquisition unit" or "Fund A-FY-current: Allocated = 200.00, Encumbered = 50.00, Available = 150.00" as one line covering three values). Split into separate numbered items only when the sub-facts belong to genuinely different records/entities, or when a step needs to reference one specific fact by number independently of the others. Don't force one-value-per-line where the team would naturally write one sentence.
- List required Capability Sets for the logged-in user, e.g.:
Data - UI-Data-Export Settings - Edit Data - UI-Data-Export Settings Lock - Edit - List required data with exact values for everything a step will later assert as a number or fixed status: monetary amounts, statuses, counts, relationships. If a step will verify
Available = 200.00, the precondition must establish the startingAvailable = 150.00 (Allocated 200.00 − Encumbered 50.00). For circulation/inventory flows where the team uses symbolic identifiers (service point S,<barcode>), keep those symbolic in preconditions too — see the two-part value rule under "Steps — Business-Logic Verification". Don't mix invented concrete barcodes into an otherwise symbolic case. - List the starting system state (which app or page the user is on).
- Default to a single, unnamed user ("User with following Capability Sets is logged in...", "Staff user is on..."). This matches how the team actually writes cases — most real cases never name an actor at all. Introduce named actors (User A / User B) only when the scenario genuinely requires two distinct roles active at once — e.g. a proxy and a sponsor, a requester and staff processing the request, an admin setting something up for a restricted user to then be tested — and even then, name them by role where possible ("Admin user" / "Restricted user") rather than defaulting to letters. (Actor names, when used, live in Preconditions only — see Steps below for step phrasing.)
- For ECS cases: always specify which tenant each data record belongs to (Central or member tenant name), which service points are configured, and what affiliation the user starts with:
1. Active Ledger exists in Central tenant for the current fiscal year; Fund A (Allocated = 200.00) exists in member tenant (College), related to the Ledger above 2. User is logged in with affiliation set to Central tenant 3. Item with barcode 12345 exists in member tenant, status Available; pickup service point SP-Central is configured in Central tenant
Steps — Case Shape (match the golden examples)
- Entry: grouped navigation step(s) from the app entry point, each with a real verification (pane opened, exact counts like "3 members selected", buttons with states).
- First-open context: when the case lands on its target pane/modal for the first time, verify the key elements relevant to the scenario — columns, labels, button states, counts. A full element-by-element inventory step is only needed when the pane/modal itself is the subject under test.
- Business assertions: per-row / per-field verification steps — the core of the case.
- Interaction + re-verification (when the scenario involves changing state): perform the change, then re-assert the new exact state (new counts, rows appearing/disappearing).
Sizing: a typical case is 5–10 steps (team median is 9). A case with 3 or fewer steps is a self-review failure unless the scenario is genuinely a single trivial assertion — if it feels too small, the navigation and context verification are probably missing, or the scenario should be merged with a sibling.
Preconditions hold only data and state that exists before the test starts (records, users, configuration, login, starting app). Any action the user performs in the UI during the test — selecting members, switching tabs, opening panes, applying filters — is a step with its own expected result, never a precondition. "User is on with all 3 members selected" is wrong: member selection is a step (and per the golden examples, the selection modal itself gets verified).
Steps — Business-Logic Verification (the most important rules)
- Every case must verify the business rule outcome, not just the UI mechanics of triggering it. The trigger flow (menus, modals, Submit) is compressed; the state verification is detailed.
- For quantitative assertions: absolute values, never relative. Where a step verifies a specific number — money, counts, percentages, budget buckets — write
Encumbered = 0.00,Available = 200.00 (restored to full allocation), never "increased by 50.00" or "decreased accordingly". The executor must be able to compare a number on screen with a number in the case. Finance/Orders/Invoices cases are largely this kind, and the team writes concrete amounts in preconditions too (real example C648502: "Fund A having current budget with money allocation $100 … Allowable expenditure percentage 110%"). But use concreteness only where the exact number is the point. When only a threshold or direction is under test, the team writes it relatively — real example C380517 (insufficient-funds) uses "enter any value exceeding money allocation for Fund B" rather than a specific figure, because the exact amount is irrelevant to what's being verified. Match the number's role: exact when asserted, symbolic/relative when only the boundary matters. - For non-quantitative / circulation-style flows: symbolic placeholders are the team's norm — do not invent fake concrete values. Check in, Check out, Requests, Loans, Inventory cases typically use abstract identifiers the executor fills in at run time:
service point S/S1,<barcode>,<title>,<material type>, "Item with at least one open request". Real example (C7148 preconditions): "User with Check In permissions … with service points S and S1 assigned. Item with at least one open request, with top request with pickup service point S." Do NOT fabricate a specific barcode like12345or a named service point likeSP-Awhen the team would writeS/<barcode>— invented concrete values just add noise the executor has to ignore. Use concrete values here only when the exact value is itself under test (e.g. a barcode with a trailing space, a specific fee amount). - Verify all entities the action touches, per the context file. Example — closing a PO must assert: encumbrance transaction status, budget values, PO status and "Reason for closure" on the PO record, POL receipt/payment statuses.
- Prefer explicit column-level verification for business-critical tables. Never "transaction is displayed" for transaction or budget-value tables — assert exact values such as
Type = "Encumbrance",Source = <PO number>,Amount = $50.00,Status = "Released". A concise summary assertion is acceptable for navigation/lookup tables where it still proves the intended behavior. - Reference preconditions by number in steps: "the unlocked mapping profile from Preconditions #3".
- Toast messages: exact text with placeholders, e.g.
"<amount> was successfully allocated to the budget <Fund-FY>". Never "success toast is displayed". If the exact text is unknown and not in the context file or examples, write the most likely exact text and flag it:(verify exact wording on first execution).
Steps — Granularity and Format
- Navigation/setup actions may be grouped into one step as a bullet list when they share a single verification point (see examples.md, Example 1, step 1). Verification/assertion actions are never grouped — one assertion target per step.
- **Brea
…(truncated)