Results By Issues Modernized - sebiboga/testlink-upgraded GitHub Wiki
The Results by Issues reports show test cases with linked bugs from the issue tracker. Two variants share one screen: "Latest Generation" (only latest executions) and "All Executions" (every execution with bugs). It replaces the legacy 1.9.20 lib/results/resultsBugs.php HTML view with a modern Dashio-styled page backed by a plain-PHP REST BFF.
Path: ASIDE menu → Reports → Results / Issues by Test Plan / Results / Issues by Test Plan - All Builds
URL: gui/templates/results/resultsBugs.html?tproject_id=<id>&tplan_id=<id>&type=0|1
BFF API: api/reports/index.php — GET ?action=results_bugs&type=0|1
Rights: testplan_metrics (same right the legacy controller enforces)
Tracking issue: #763
Two report variants share one screen:
| Variant |
type param |
Title | Behavior |
|---|---|---|---|
| Latest Generation | 0 |
Results / Issues by Test Plan | Shows only latest generation executions with bugs |
| All Executions | 1 |
Results / Issues by Test Plan - All Builds | Shows all executions with bugs (may include duplicates) |
The "All Executions" variant shows a warning hint that some test cases may appear multiple times.
| Element | Description |
|---|---|
| Header | Teal banner with "Results by Issues" title, "for test plan" label, plan name, locale switcher |
| Toolbar | Test project name, report type dropdown (Latest Generation / All Executions) |
| Summary cards | Four cards: Open Bugs, Resolved Bugs, Total Bugs, TCs with Bugs |
| Report body | DataTable with Test Suite, Test Case, and Bugs columns; grouped by test suite |
| Footer | Generation timestamp, elapsed time, and explanatory italic info paragraph ("This report shows all bugs linked to test cases during execution.") |
| Legacy behavior | Modernized behavior |
|---|---|
| `resultsBugs.php?type=0 | 1` loads via Smarty |
| PHP template builds HTML table | DataTables jQuery plugin renders the table |
Data from getLTCVNewGeneration() (type=0) or getAllExecutionsWithBugs() (type=1) |
Same backend calls via BFF |
Bug links via get_bugs_for_exec()
|
Same backend call via BFF |
Legacy echoed link_to_bts (an HTML fragment from buildViewBugLink()) straight into the cell |
BFF returns that fragment as bugs[].link (unchanged, for HTML consumers) and the bare tracker URL as bugs[].url; the screen anchors on url behind a scheme guard — Fixes #1837
|
| ExtTable with group-by | DataTable with sorting, pagination, search |
resultsBugs.tpl:70 renders <p class="italic">{$labels.info_bugs_per_tc_report}</p> below the table (report description) |
Modern footer renders the equivalent italic info paragraph via rb.infoReport (<p class="info">) next to the generated-on line — Refs #1271
|
| Type | Description |
|---|---|
| Latest Generation (default) | Only shows test cases from their most recent execution — no duplicates |
| All Executions | Shows every execution that has linked bugs — test cases may appear multiple times across builds |
All labels use the rb.* i18n key namespace (14 keys per bundle). The screen uses the shared TLi18n module for locale loading and switching. Keys are defined in all 10 locale bundles (en.json, ro.json, etc.).
The footer report-description paragraph uses rb.infoReport — added for all 10 locales in Refs #1271 (en: "This report shows all bugs linked to test cases during execution."; legacy text sourced from locale/en_GB/strings.txt:1834 $TLS_info_bugs_per_tc_report; de/es/fr/ja/pt/zh reuse the legacy translations, ro/it/ru are newly authored).
Column headers reuse existing i18n keys: title_test_suite_name, title_test_case_title, title_test_case_bugs.
Returns test cases with linked bugs for a test plan.
Parameters:
-
tproject_id(int, required) -
tplan_id(int, required) -
type(int, optional):0for latest generation (default),1for all executions
Response (200):
{
"status": "ok",
"tproject_id": 1,
"tplan_id": 2,
"tproject_name": "Test Project",
"tplan_name": "Test Plan 1",
"type": 0,
"verbose_type": "latest",
"title_key": "link_report_total_bugs",
"bug_interface_on": true,
"total_open_bugs": 5,
"total_resolved_bugs": 3,
"total_bugs": 8,
"total_cases_with_bugs": 6,
"rows": [
{
"tc_id": 10,
"tc_name": "Login Test",
"full_external_id": "TP-1",
"tsuite_name": "Authentication",
"external_id": 1,
"bugs": [
{
"bug_id": "BUG-123",
"link": "<div title=\"Access issue tracking system\" style=\"background: #ffa0a0;\"><a href='https://bts.example.com/BUG-123' target='_blank'>BUG-123 : [New issue]</a></div>",
"url": "https://bts.example.com/BUG-123",
"is_resolved": false,
"build_name": "Build 1"
}
]
}
],
"has_data": true,
"elapsed_time": 0.01
}Rights check: BFF validates testplan_metrics right (global + contextual per-project/per-plan) before any data query.
Empty states:
- No test plan configured → HTTP 400 error
- No issue tracker enabled → "No linked bugs found for this report."
- Issue tracker enabled but no bugs found → Same empty state message
The legacy report footer always renders an explanatory italic line between the result table and the generated-on line
(resultsBugs.tpl:70 → <p class="italic">{$labels.info_bugs_per_tc_report}</p>,
text: "This report shows all bugs linked to test cases during execution."). The modern screen renders the same line:
- Modern:
#footerInfoHTML appends<p class="info">withrb.infoReportafter theGenerated on: … | Elapsed seconds: Nline (resultsBugs.html:207-212). - The paragraph is escaped via the screen's
esc()helper and translated withTLi18n.t('rb.infoReport')in all 10 locales. - Mirrors the established sibling pattern from
resultsMatrix.html(rsm.infoReport) andabsoluteLatest.html(alx.infoReport).
| File | Purpose |
|---|---|
gui/templates/results/resultsBugs.html |
Standalone HTML+JS+CSS screen (~230 lines) |
api/reports/index.php |
BFF API — results_bugs action (lines ~3820–3971) |
lib/general/asideMenu.php |
ASIDE link switch for link_report_total_bugs / link_report_total_bugs_all_exec
|
gui/templates/i18n/*.json |
i18n locale bundles (16 rb.* keys per bundle, incl. rb.infoReport — Refs #1271 and rb.executionHistory / rb.testCaseDesign — Refs #1269) |
lib/results/resultsBugs.php |
Legacy controller (still exists but no longer linked from ASIDE) |
tmp/fixtures_1271.php |
Reproducible fixture: RB1271 project/plan, 2 executions with linked bugs via local mantisdb tracker |
tmp/fixtures_1269.php |
Reproducible fixture for the TC icon pair: RB1269 project/plan, 3 executions, 2 with linked bugs (Refs #1269) |
lib/results/resultsBugs.php:89-96 — for every test case that carries at least one linked bug
the controller built the Test Case cell as two icon links prepended to PREFIX-id: name:
$exec_history_link = "<a href=\"javascript:openExecHistoryWindow({$tc_id});\">" .
"<img title=\"" . $l18n['execution_history'] . "\" src=\"{$img['history']}\" /></a> ";
$edit_link = "<a href=\"javascript:openTCEditWindow({$tc_id});\">" .
"<img title=\"" . $l18n['design'] . "\" src=\"{$img['edit']}\" /></a> ";with the labels initialized at lib/results/resultsBugs.php:73. The row was therefore clickable
twice: the execution history of that test case and its design.
gui/templates/results/resultsBugs.html rendered the Test Case column as inert plain text — the
BFF already shipped tc_id on every row (api/reports/index.php, rows[].tc_id) and the JS
never used it, so a user could not reach the execution history or the design of a test case with bugs.
- New
tcCell(row)ingui/templates/results/resultsBugs.htmlrenders the pair with the Dashio icon set (fa-clock-rotate-left,fa-pen-to-square, teal#4ECDC4) followed byPREFIX-id: name, matching the sibling modernized reportresultsMatrix.html:346-360. - Targets (both already modernized screens):
-
/gui/templates/execute/execHistory.html?tcase_id=<tc_id>&tproject_id=<project>— popuphistory_popup(900x650) -
/gui/templates/testcases/tcEdit.html?tcase_id=<tc_id>&tproject_id=<project>— popuptcEdit_<tc_id>(900x700), contract fromtplanWithCF.html:128 - The legacy window helpers (
gui/javascript/testlink_library.js:1726/:913) do not exist in the modern shell, andopenExecHistoryWindow()concatenated&tproject_id=+ an undeclared variable (→tproject_id=undefined, documented attcNotRunAnyPlatform.html:139-144), so both popups are addressed directly andtproject_idis always sent.
-
- The renderer branches on
type !== 'display': the icons exist only in the display cell, so DataTables sorting and the column filter keep working on the plain text. - Legacy dead markup
"<!-- 0000000001 -->"(zero-paddedexternal_idas an HTML comment) is intentionally not ported; the value is still delivered asrows[].external_id. - i18n:
rb.executionHistory+rb.testCaseDesignin all 10 locale bundles. -
Bonus fix: the old renderer read
row.name, but the BFF emitstc_name— the test case name was rendered as an empty string (RB-1:).tcCell()readsrow.tc_name || row.name.
12/12 test cases PASS (suite ## Task — Issue #1269 in tmp/TLU_Test_Cases.md), fixture
tmp/fixtures_1269.php (project RB1269, plan RB Plan, 3 executions, local mantis_bug_table
so the DB-API tracker resolves links and resolved state without any remote service).
History popup shows RB-1 - Execution History / Executions(1); design popup shows
Edit Test Case - login broken. No new Error/Warning entries in the Event Viewer.
Every bug in the Bugs column carried an href that was the issue tracker's HTML fragment,
percent-encoded and resolved against the current directory, so clicking a bug landed on a TestLink
404 (fetch(anchor.href) → status: 404) — the report's main column was unusable. No console
error and no Event Viewer row, so the defect was silent.
pre-fix : link " 101" url="http://localhost:8082/gui/templates/results/%3Cdiv%20%20title=%22Access%20issue%20tracking%20system%22…%3C/div%3E"
post-fix: link " 101" url="http://mantis.local/view_bug.php?bug_id=101"
-
issueTrackerInterface::buildViewBugLink()returns an HTML fragment, not a URL (issueTrackerInterface.class.php:365builds the anchor,:437-442wraps it in a status-coloured<div>);lib/functions/exec.inc.php:449-453stores it aslink_to_bts. Legacy screens echoed it as HTML (lib/results/resultsBugs.php:204) — correct there. -
api/reports/index.php(action=results_bugs) copied the fragment into a neutrally namedlinkfield and exposed no URL, so the field looked like one. -
renderBugLinks()escaped the fragment intohref.
The bare URL was always available but never left the tracker object:
issueTrackerInterface::buildViewBugURL() (issueTrackerInterface.class.php:485-488), inherited or
overridden by every tracker.
-
BFF
api/reports/index.php: newbugViewUrl($its, $bugId, $linkHtml)helper returns the bare tracker URL viabuildViewBugURL()— guarded byis_object()+method_exists()+try/catch(Throwable)— and falls back to extracting thehreffrom the fragment, then to''. Each bug entry now carriesbugs[].urlnext to the untouchedbugs[].link. -
Screen
gui/templates/results/resultsBugs.html: newsafeHttpUrl()accepts onlyhttp://,https://,//host/…,/path;renderBugLinks()anchors onb.urlthrough it, addsrel="noopener"and degrades to a<span class="bug-link">(no href) when no usable URL exists. Resolved styling (.bug-link.resolved) is unchanged.
Suite ## Regression — Issue #1837 in tmp/TLU_Test_Cases.md; fixture php tmp/fixtures_1269.php
(project RB1269, plan RB Plan, local mantis_bug_table). Live URLs for type=0 and type=1,
resolved/open styling kept, bugs[].link unchanged, 11 client + 7 PHP guard cases, console and Event
Viewer clean, sibling resultsByStatus (which deliberately echoes the fragment as HTML) unaffected.
A code review subagent's BLOCKER findings were checked against the committed code and did not
reproduce (they described an ENT_HTML5 decode, a new URL() guard and a $bug['links']
shadowing loop, none of which are in this diff). Its one valid suggestion was folded in:
bugViewUrl() applies the same scheme allow-list server-side before publishing bugs[].url, so
the API can never ship javascript:/data:/markup to a future consumer.
Detail page: Bugfix-Issue-1837-resultsBugs-BugLink-Href.md.