Test cases for search functionality: search, filter, and sort in one pass

Most test cases for search functionality stop at 'type a word, see results.' The bugs are in the empty state, the special characters, the filter combinations, and the state that doesn't survive a reload. Here's how to test all of it in plain English.

playbooksearchno-codeqa
monito

Test cases for search functionality: search, filter, and sort in one pass

playbooksearchno-codeqa
July 29, 2026

Search is the feature everyone demos and nobody tests properly. The demo is "type a product name, see the product" — one query, one obvious result, applause. The real thing is a small system: an input with debounce and maybe autocomplete, a results list with an empty state and a no-results state, a set of filters that combine in ways nobody enumerated, a sort order, pagination, and a URL that's supposed to carry all of it so a shared link or a page reload lands you back on the same results. Every one of those seams is where a real user's search quietly returns the wrong thing.

Search the web for test cases for search functionality and you get the same 100-row spreadsheet on a dozen sites: "verify search with valid keyword," "verify search with invalid keyword," "verify case insensitivity." Those aren't wrong, they're just the demo written down. They test the input box. They don't test the system — the filter that stops applying after you paginate, the sort that resets when you reload, the special character that 500s the search endpoint, the empty state that renders undefined for a user with no history. This is the playbook for the whole system, in plain English, no scripts.

What "testing search" actually has to cover

The input box is the part that works. Here's the surface that doesn't:

  1. The three list states, not one. Results, no results (a valid query that matches nothing), and empty (no query yet, or a cleared query). Teams test the first, occasionally the second, and almost never the third — which is why the initial search screen so often ships with a broken or placeholder-filled empty state.
  2. Input the search box didn't expect. Special characters (%, &, #, quotes, a lone backslash), a query that's only spaces, an extremely long string, a leading/trailing space, and — if your backend speaks SQL or a query DSL — the characters that mean something to it. The bar isn't "does it find results," it's "does it fail safely and predictably."
  3. Filters that combine. One filter is easy. The bug is in the intersection: apply two filters that together match nothing (does it show the no-results state, or the unfiltered list?), apply a filter then change the search term (does the filter persist or silently drop?), clear one filter of several (do the rest stay applied?).
  4. Sort, and sort-plus-filter. Sorting works in isolation. Then you filter, and the sort resets to default without telling anyone; or you sort, paginate, and page two comes back in the original order.
  5. State that survives the URL. Search + filters + sort + page should live in the URL (or wherever you keep them) so that a reload, a shared link, and the browser back button all reproduce the exact same results. This is the single most under-tested property of any search UI, and the one users notice immediately when it breaks.
  6. Debounce and race conditions. Type fast, and the results for an earlier keystroke can arrive after the results for a later one and overwrite them — the last-response-wins race that shows results for "ca" when the box says "cat." Autocomplete has the same problem, one layer up.
  7. Pagination consistency. Page two of a filtered, sorted search is still filtered and sorted. The result you saw on page one doesn't reappear on page two because the underlying set shifted mid-browse.

Most teams cover item 1 (the results case) and ship. Items 3, 5, and 6 arrive as bug reports phrased as "search is weird sometimes," which is the least actionable sentence in your inbox because it depends entirely on the sequence of clicks. The prompts below drive those sequences on purpose.

Prompt 1: the three states and the input you didn't expect

Test the search on https://staging.yourapp.com/search.

1. Search for a term you KNOW has matches. Confirm relevant results
   appear and the result count (if shown) is plausible.
2. Search for a random string that should match NOTHING (e.g.
   "zzxqwplk"). Confirm a proper "no results" state appears — a clear
   message, not a blank page, not the previous results left on screen,
   not an error.
3. Clear the search entirely. Confirm the empty state is intentional
   (a prompt to search, or a sensible default list) — not broken,
   blank, or showing placeholder text like "undefined" or "NaN".
4. Now try inputs the box probably didn't plan for, one at a time, and
   report exactly what each does:
   - special characters: %  &  #  "  '  \
   - a query of only spaces
   - a very long string (paste 500+ characters)
   - a term with a leading and trailing space

For every case in step 4, report whether the app handled it
gracefully, returned a confusing result, or triggered a 4xx/5xx in the
network tab. Flag any input that produced a server error.

Step 4 is where the security-adjacent bugs surface: a search box that passes a lone backslash or a quote straight into a query and gets back a 500 is telling you the input isn't being escaped, and that's worth knowing before someone less friendly finds it. The agent watches the network tab, so an error the UI swallowed into a generic "no results" still gets reported as the server error it actually was — a distinction a screenshot can't make and a "verify invalid keyword" checklist row never captures.

Prompt 2: filters that combine, and the state that has to persist

On https://staging.yourapp.com/search, test filters in combination
with search — not one at a time.

1. Run a search that returns results. Apply one filter. Confirm the
   results narrow correctly and the result count drops.
2. Apply a SECOND filter that, combined with the first, should match
   nothing. Confirm you get the proper no-results state — NOT the
   unfiltered list quietly coming back.
3. With filters applied, change the search term. Report whether the
   filters stay applied or silently reset.
4. Clear ONE filter while others remain. Confirm only that filter is
   removed and the rest still apply.
5. Now reload the page with search + filters active. Confirm the search
   term, every filter, and the result set all come back exactly as they
   were. Then copy the URL, open it in a fresh tab, and confirm it
   reproduces the same results.

Report any filter that drops when it shouldn't, any combination that
falls back to the unfiltered list, and any state (search, filters) that
does NOT survive the reload or the shared URL.

Step 5 is the highest-leverage check here. A search UI that keeps its state in component memory instead of the URL looks perfect until a user reloads, shares a link, or hits back — and then it's a support ticket that says "the link I sent showed different results than what I saw." Testing it means actually reloading and actually opening the URL in a new context, which is exactly the kind of multi-step, stateful sequence that's tedious to do by hand every release and trivial to describe to an agent once. It's the same state-survives-navigation property that multi-step forms live or die on.

Prompt 3: sort, pagination, and the debounce race

On https://staging.yourapp.com/search, test sorting, pagination, and
fast typing together.

1. Run a search with enough results to paginate. Sort by each available
   option and confirm the order actually changes and looks correct.
2. Apply a sort, then go to page two. Confirm page two is still in the
   sorted order (not the default order) and still respects any active
   filter.
3. Note the first result on page one, go to page two and back. Confirm
   page one is unchanged — no items shuffled or duplicated across pages.
4. Type a multi-character query FAST, character by character with no
   pause (e.g. "c", "a", "t"). Confirm the final results match the
   final query ("cat"), not an earlier keystroke. Watch for results
   flickering to a stale set and staying there.
5. If there's autocomplete/typeahead, do the same: type fast, then
   confirm the suggestions match the final input and selecting one runs
   the expected search.

Report any sort that resets on pagination, any item that appears on two
pages or vanishes between them, and any case where the results or
suggestions lag behind the query (the last-response-wins race).

Step 4 is the debounce race, and it's genuinely hard to catch by hand because it depends on typing faster than the responses come back — you have to get unlucky in exactly the right way. An agent types deterministically and reads the final rendered results against the final query, so the race that shows "ca" results under a "cat" query surfaces as a reported mismatch instead of a bug someone half-remembers seeing once. This is the same class of non-deterministic, timing-dependent UI issue that makes streaming and async interfaces so hard to pin down.

Reading the results

Each run comes back as a Monito Session: a screenshot timeline of every query, filter, and page, the network log with the actual search requests and their responses, console output, and the agent's reasoning where it judged something like "is this empty state intentional or broken." For search specifically, the network log is where the input-handling bugs live — the 500 on a special character, the request that fired without the filter attached — and the screenshot timeline is where you see the state that didn't survive the reload. Neither is a single pass/fail bit, which is the point: "search is weird sometimes" becomes "the second filter drops when you change the query, here's the request that proves it."

None of the prompts names a filter's CSS class, a sort dropdown's ID, or a results-container selector — only what search is for. Restyle the filters, swap the pagination for infinite scroll, rename everything: the intent is identical, so the prompts are too. That's why describing intent beats scripting steps for anything as redesign-prone as a search interface, and why an agent reading the rendered page finds what a selector-bound script walks past.

The one to run right now

If you run a single check today, run the filters-and-persistence test — it catches the two bugs users notice fastest (a filter combination that silently un-filters, and state that dies on reload):

On https://staging.yourapp.com/search, run a search that returns
results, apply two filters that together should match nothing, and
confirm you get a real "no results" state rather than the unfiltered
list. Then reload the page and confirm the search term and both filters
come back exactly as they were, and that copying the URL into a fresh
tab reproduces the same results. Report any filter that drops, any
fallback to the unfiltered list, and any state that doesn't survive the
reload or the shared link. Capture screenshots and any console or
network errors.

Save it as a Test Scenario, pin it to your staging Project, and wire it into CI against every preview deploy that touches search, filtering, or the results page. A full run is typically 8–13 credits — roughly $0.08–$0.13 — and your first run is free. Point it at staging, paste the prompt, and read the network log. If the impossible filter combination shows an honest empty state and the reload brings everything back, you've got a search that behaves the way users assume it already does.