Search Test Cases - sebiboga/testlink-upgraded GitHub Wiki
Path: ASIDE menu > Search > Quick search / Search Test Cases
URL: gui/templates/search/searchView.html?tproject_id=<id>&tplan_id=<id>
BFF API: api/search/index.php (session-based auth, JSON I/O)
Prerequisite: view_tc grant on the current test project (same visibility
rule as legacy getMenuVisibility()); the Search section is hidden otherwise.
Replaces: legacy quick-search form lib/search/searchForm.php +
gui/templates/dashio/search/searchForm.tpl, results from
lib/testcases/tcSearch.php (ExtJS grid).

Searches test cases in the CURRENT test project only, combining all filled
criteria with a logical AND (LIKE '%value%' semantics on text fields) β
exactly like TestLink 1.9.20's quick search.
Notices shown above the form:
- all criteria are combined with AND;
- search is restricted to the selected test project;
- the test project prefix typed in the Test Case ID field is ignored
(a bare number like
3is auto-completed toSFP-3).
| Criterion | Behavior |
|---|---|
| Test Case ID | full external id (SFP-2) or bare number (2); unknown id β warning "Test case does not exist." |
| Version | exact match on tcversion.version |
| Title |
LIKE on test case name |
| Created by / Edited by |
LIKE on author/updater login, first name or last name |
| Summary / Preconditions | LIKE |
| Steps / Expected results |
LIKE across tcsteps rows |
| Creation date from/to | range on tcversions.creation_ts (to-date includes 23:59:59); dates sent in the project's configured format |
| Modification date from/to | range on tcversions.modification_ts
|
| Importance | High/Medium/Low β shown only when the project has test priority enabled |
| Status | Draft β¦ Final domain (localized server-side, same source as legacy) |
| Keyword | exact keyword id β shown only when the project has keywords |
| Custom field + value | design-time custom fields linked to test cases; date/datetime values converted like legacy, everything else LIKE
|
| Requirement document ID |
LIKE on req_doc_id via req_coverage β shown only when requirements are enabled |
Result cap: if more than testcase_cfg.search.max_qty_for_display (200)
distinct test cases match, the search refuses to display and warns
"Too many results - please refine the search criteria." (legacy behavior).

The legacy grid (lib/testcases/tcSearch.php:339-351) grouped the hits per test
suite, so the modern results table is grouped too:
- every suite path gets a collapsible teal header with its item count
(
Suite Alpha (3 Items), singular(1 Item)) β the legacygroupTextTpl '{text} (N Items)'ofexttable.class.php:591; - clicking a header collapses/expands that group; the toolbar button
Expand/Collapse Groups collapses/expands all of them and reports
Groups collapsed/Groups expanded; - the Test Suite column is hidden while grouping (legacy
hideGroupedColumn=true); the toolbar toggle Show all Columns / Hide Test Suite column brings it back (legacy disabled that button, so the port adds it as a superset); - default order is suite path (so groups never split) and test case
descending inside each group, per legacy
sortDirection='DESC'; sorting by another column keeps the groups intact; -
orderMulti: falsemirrors legacyallowMultiSort=false; nothing is stored inlocalStorage(storeTableState=falsein legacy); - the no-CDN fallback table (see #799) renders the same group headers.
No BFF change was needed: api/search/index.php already returns path per row.
New i18n keys sv.grid.* (7) exist in all 10 bundles.

DataTable with columns:
- Test Suite β full path of the parent suite;
-
Test Case β clickable
PREFIX-extid [vN] :: Name; opens the test case editor popup (archiveData.php?edit=testcase&id=β¦&tcversion_id=β¦); - Summary β HTML stripped to plain text;
- Version;
- actions: edit popup + execution history popup
(
execHistory.php?tcase_id=β¦).
A match counter is displayed next to the results heading and in the footer. When nothing matches, an empty-state message is shown instead of the table.

| Situation | Message |
|---|---|
| TC ID given but not found | "Test case does not exist." |
| Project has no test cases | "Test project is empty (no test cases)." |
| No rows match | "No test cases match the given criteria." |
| > 200 matches | "Too many results - please refine the search criteria." |
- Menu entry visible with
view_tc(mapped to rightmgt_view_tc) β admins, guests, testers, designers etc.; hidden for<no rights>users. - BFF enforces the same rule: HTTP 403
No permissionwithout it, HTTP 401 when not authenticated.
All labels/titles/placeholders/messages use the client-side TLi18n module
with keys under the search.* namespace, present in all locale bundles:
en, ro, de, fr, es, it, pt, ru, ja, zh. A locale switcher is available in the
header.
- BFF endpoints:
-
GET api/search/index.php?action=context&tproject_id=Nβ prefix, keywords, custom fields, status domain, feature flags, max result count; -
GET api/search/index.php?action=search&tproject_id=N&β¦β runs the search, returns{count, rows[], warning}where each row carries testcase_id, tcversion_id, full external id, version, summary and suite path.
-
- SQL mirrors legacy
tcSearch.php: nodes_hierarchy join tcversions (+ LEFT JOIN steps), optional joins for keywords / custom field values / requirement coverage / author & updater users;COUNT(DISTINCT NH_TC.id)first, then the row fetch capped at the display limit. - All user input is escaped through
$db->prepare_string()before being placed into LIKE clauses (same hardening as the 1.9.20 security fixes).
URL: gui/templates/search/searchQuickView.html?tproject_id=<id>&tplan_id=<id>
BFF API: same api/search/index.php (action=tcQuickSearch)
The ASIDE highlight logic (syncAsideActiveLink() in main.tpl) matches menu
links by pathname. While both Search items shared one screen, BOTH were
highlighted at the same time. Each screen now has its own file, so exactly one
item is active β consistent with every other ASIDE section.
A focused mini-form for fast lookups inside the current test project:
- single input field accepting either a title fragment (
LIKE %value%) or a test case ID (full external ID likeP2-1, or bare number auto-completed with the project prefix); - results table (external ID, version, title, path) rendered as a DataTable;
- clicking a result opens the modernized TC viewer
(
gui/templates/testcases/tcView.html), not the legacy popup; - empty input β toast error; no matches β inline "no results" message;
- self-contained page: own CSS, TLi18n localization, Dashio header/toolbar.
lib/functions/common.php builds both URIs:
$actions->tcSearch = "/gui/templates/search/searchView.html?{$ctx}";
$actions->tcQuickSearch = "/gui/templates/search/searchQuickView.html?{$ctx}";

The quick Search Test Cases screen now appends a "Generated by TestLink on β¦" timestamp to the results footer after a successful search, matching the legacy behavior. The timestamp is formatted server-side in the user's TestLink locale (including the time component) and shown only when a search runs and produces no warning; it is hidden on warnings, empty results, and after Reset.
Changes
-
api/search/index.php(action=search): addsgenerated_on(locale-awaretlStrftime(timestamp_format, locales_timestamp_format[$locale]) andgenerated_on_isoto the response payload (Refs #1093). -
gui/templates/search/searchView.html: renders a hidden#footerGeneratedblock that displayscommon.generatedBy+ the server-formatted timestamp when results exist without warning; cleared on reset. Uses existing i18n keycommon.generatedBy.
Screenshots
- Results footer with generated-on timestamp (success case)

The quick search screen dropped the legacy full search criteria form
(tcSearch.php?doAction=userInput / lib/testcases/tcSearchForm.php) β the
modern screen had only the single quick-text box. The gap is now closed with an
expandable Advanced criteria panel (#btnAdv toggle β #advPanel) that
re-implements EVERY legacy criterion on the same BFF (api/search/index.php,
which already parsed all of them):
| Group | Fields |
|---|---|
| Identification | Test Case ID (project prefix ignored β hint shown), Version |
| Text | Title, Summary, Preconditions, Steps, Expected results, Jolly (OR) |
| Meta | Importance (only when the project enables test priority), Status, Created by, Edited by |
| Dates | Creation from/to, Modification from/to (project date format via fmtDate()) |
| Taxonomy | Keyword, Custom field + value, Requirement document ID (both gated by project settings) |

- All criteria combine with a logical AND; an empty form β toast "Type what you are looking for.".
-
Jolly (OR) supersedes Title/Summary/Preconditions/Steps/Expected results
exactly like legacy
tcSearch.php:93-121β a hint appears under the note as soon as Jolly is typed. - An explicit advanced Title (or Test Case ID) wins over the quick box; the conflict raises a localized toast ("Quick search text ignoredβ¦") instead of silently dropping the input.
- Enter inside any advanced input triggers the search.
-
Deep links:
?name=β¦&importance=β¦&creation_date_from=β¦prefill their fields AND auto-open the panel, so a filter can never be active-but-invisible. -
resetForm()clears all 20 fields; the panel toggle stays localized (aria-expanded+ rotating caret, no label desync on locale switch).
-
gui/templates/search/searchQuickView.htmlβ panel HTML/CSS,buildAdvancedParams(),toggleAdvanced(),fmtDate(), deep-link prefill, Enter-to-search. -
api/search/index.phpβ unchanged (already supported every filter). - i18n:
search.advancedCriteria,search.quickIgnoredTc,search.quickIgnoredName,search.jollySupersedes, reusedsearch.prefixIgnored/search.filterModeAndin all bundles.
The quick-search results were a flat DataTable: the suite path was one plain
column, there was no grouping, no collapsible group headers, no grid toolbar and
the default order was the suite path ascending β while legacy
lib/testcases/tcSearch.php:339-353 (buildExtTable) configured the results
ExtTable with setGroupByColumnName('test_suite'), setSortByColumnName('test_case')
-
sortDirection='DESC',showToolbar=trueandallowMultiSort=false.
The gap is closed on gui/templates/search/searchQuickView.html with the same
port already done for searchView.html (#1092):
| Legacy (1.9.20) | Modern (2.0.1) |
|---|---|
setGroupByColumnName('test_suite') |
DataTables RowGroup dataSrc:'path'
|
groupTextTpl '{text} (N Items)' (exttable.class.php:591) |
teal tr.dtrg-group header Alpha (2 Items) / Alpha/A1 (1 Item)
|
| ExtJS group-header click toggles that group | click a group header β that group expands/collapses (presence-based collapsedGroups key deleted on expand) |
hideGroupedColumn=true (exttable.class.php:55) |
Test Suite column hidden while grouped + Show all Columns / Hide Test Suite column toolbar toggle |
showToolbar=true β Expand/Collapse Groups + Reset Filters (inc_ext_table.tpl:187-206, :292-302) |
#gridToolbar with Expand/Collapse Groups + Reset Filters + the column toggle |
setSortByColumnName('test_case'), DESC, allowMultiSort=false
|
order: [[path,'asc'],[testcase,'desc']], orderMulti:false, pinGroupOrder() keeps the group column primary |
| β | plain-<tbody> fallback still renders the group headers when the DataTables CDN is down (Refs #799) |
BFF: unchanged β api/search/index.php already returns path per row.
i18n: sv.grid.resetFilters + sv.grid.filtersCleared added to all 10 locale
bundles (the other sv.grid.* keys exist since #1092); coverage gate 9/9 green.
Test suite: Task β Issue #1102 in tmp/TLU_Test_Cases.md β 8 cases, all PASS.
Screenshots


The quick-search results block used to close with the match count only
(#footerInfo, e.g. "5 matches"), while legacy closed it with a
"Generated by TestLink on <date>" stamp
(gui/templates/dashio/testcases/tcSearchResults.tpl:39):
{if !isset($gui->warning_msg) || $gui->warning_msg == ''}
... tables ...
{lang_get s='generated_by_TestLink_on'} {$smarty.now|date_format:$gsmarty_timestamp_format}
{else} ... warning box only ... {/if}The stamp is back on gui/templates/search/searchQuickView.html:
| Legacy (1.9.20) | Modern (2.0.1) |
|---|---|
generated_by_TestLink_on + $smarty.now|date_format:$gsmarty_timestamp_format
|
#footerGenerated / #footerGenOn with common.generatedBy + the server-formatted generated_on from api/search/index.php action=search (locale timestamp format incl. time β same payload #1093 added; tooltip = generated_on_iso) |
rendered only when the search ran with an empty warning_msg
|
renderGeneratedFooter(r) shows it only when rows exist AND r.warning is empty AND generated_on is present β hidden on no_records_found / testcase_does_not_exists / any warning and on empty results, cleared by Reset, refreshed on every re-search |
label localized via lang_get
|
label from the existing i18n key common.generatedBy (all 10 bundles) β ?locale=de renders Generiert von TestLink am 08/10/2026 13:40:52
|
BFF: unchanged β action=search has returned generated_on /
generated_on_iso since issue #1093.
i18n: no new keys; bash ai/verify_i18n_coverage.sh PASS (9/9).
Test suite: Task β Issue #1103 in tmp/TLU_Test_Cases.md β 9 cases, all PASS
(match / no-match / re-search timestamp refresh / missing-TC warning / Reset /
de locale / advanced-criteria path / #1102 grid regression / Event Viewer clean).
