Repository navigation
Test Specification Editor
Path: ASIDE → Test Specification
URL: gui/templates/testcases/testSpec.html?tproject_id=<id>[&tcase_id=<id>][&locale=<loc>]
BFF API: api/testcases/index.php (action=tree, action=get;
POST create, update, delete, create_version, suite_create,
suite_update, suite_delete — session-based auth, JSON I/O)
Rights: mgt_view_tc to browse; mgt_modify_tc for every write action;
editing an executed version additionally requires
testproject_edit_executed_testcases (or canEditExecuted config), same as
legacy tcEdit.php.
Replaces: legacy workarea launcher
lib/general/frmWorkArea.php?feature=editTc → tree frame +
lib/testcases/tcEdit.php editor forms.

Single-page replacement of the 1.9.20 Test Specification two-frame layout:
- Left pane — test case tree: full project subtree (suites at any depth + test cases) rendered from one BFF call; suite count pills; expand/collapse toggles; live name filter box; inactive test cases dimmed.
-
Right pane — context panel, three states:
- Suite selected: path, sub-suite / test-case counters, "New Test Case Here", "New Sub-Suite", "Rename Suite", "Delete Suite".
- Test case selected: identity header (name, version badge, external id badge, executed/inactive badges), meta grid (status, importance, execution type, internal id), summary and preconditions blocks, numbered steps table, assigned keywords chips and the action bar.
- Editor form (create or edit): name, status (workflow domain draft..final), importance, execution type, summary, preconditions, steps editor (add/remove rows, auto renumbering), project keywords checkboxes.
| Button | Behavior |
|---|---|
| New Test Case | opens create form targeted at the selected suite |
| New Test Suite | modal → POST suite_create under selection (or project root) |
| Rename Suite | modal prefilled → POST suite_update
|
| Delete Suite | confirm modal (warns when non-empty) → POST suite_delete
|
| Edit Test Case | pre-filled form → POST update (latest version, legacy doUpdate semantics) |
| Create New Version | confirm modal → POST create_version, view reloads on VER n+1 |
| Full Viewer | opens the modernized read-only tcView.html?tcase_id=… in a new tab |
| Delete Test Case | confirm modal → POST delete (removes ALL versions) |


| Situation | Message |
|---|---|
| Not authenticated | BFF returns HTTP 401 JSON error |
| Missing right on write action | HTTP 403 "Requires permission: modify test cases" |
| Executed latest version without special right | HTTP 403, save blocked (legacy parity) |
| Empty name on save/create/rename | toast "Name is required" |
| Unknown node id (deep link) | panel shows the BFF error text |
All labels/toasts/placeholders come from the tspec.* block — 62 keys added
to all 10 locale bundles (en, de, es, fr, it, ja, pt, ro, ru, zh),
verified valid JSON. Runtime switch via ?locale= verified (e.g. RO).
- Wrong column name
tcexternalid(real schema:tc_external_id) broke the tree aggregates and get action — SQL error was captured by the Event Viewer. - Create form crashed on
Object.keys(undefined)when no keywords were assigned yet (guard added). - TC name/suite parent were read from
tcversionswhich has nonamecolumn — get action now joinsnodes_hierarchytwice. - PHP 8 warning "Undefined property: stdClass::$canEditExecuted" logged in the Event Viewer on every context/update call — now null-safe.
All four fixed and re-verified; Event Viewer clean afterwards.
Gap: the 1.9.20 design-time editor rendered a Status select
(attributesLinear.inc.tpl, <select name="tc_status"> bound to
$gui->domainTCStatus) and persisted it via setStatus; the modernized
screen had dropped the field entirely (create/update never wrote status,
detail view hid it).
Implemented (already modernized minus this gap):
-
Editor form (create + edit) shows a Status select with the full
workflow domain: Draft (default), Ready for review, Review in progress,
Rework, Obsolete, Future, Final. Domain + order come from the BFF
(
statusDomain, derived from$tlCfg->testCaseStatus); labels are the existingtcview.status1..7i18n keys (all locales). -
Persistence: BFF
create/updatereadstatusfrom the POST body, validate it against the domain and persist it through the existingtestcase::create(options['status'])/testcase::update($attrib['status'])path (sametcversions.statuscolumn the legacysetStatus()wrote;modification_ts/updater_idare updated byupdate()as usual). Absent / invalid value on create falls back to Draft (1); on update the current value is kept. - Detail view adds a Status meta-item with the current workflow label.



Verified live (project "Demo Project", TC "Login with valid credentials"):
create with "Ready for review" → tcversions.status=2; edit to "Final" →
tcversions.status=7 with refreshed modification_ts; detail view badge
tracks the value; RO locale renders all 7 workflow labels (Ciornă … Final).
Event Viewer: no new ERROR/WARNING rows. Test suite: Task — Issue #908
in tmp/TLU_Test_Cases.md (PASS).
Gap: the 1.9.20 design-time editor rendered an Estimated Execution
Duration (min) input (attributesLinear.inc.tpl, gated as mandatory by
$tlCfg->testcase_cfg->estimated_execution_duration->required, persisted via
setEstimatedExecDuration → tcversions.estimated_exec_duration); the
modernized screen had no field and the BFF silently dropped the value on
create/update.
Implemented:
-
BFF (
api/testcases/index.php): newisDurationRequired()helper reads the config flag;context/get/keywordsresponses exposeestimateDurationRequired;create/updateacceptestimated_execution_duration, validate it (is_numeric; 400Invalid estimated duration), enforce it when configured as required (400Estimated execution duration is required), and persist it throughtestcase::create/updateoptions (estimatedExecDuration). Empty string on update writes NULL — the legacy always-write semantics. -
Editor (
testSpec.html): create + edit form gain the decimal input#tcDurationInput(red*+requiredattribute only when the config flag is set);saveForm()blocks non-numeric values (toast) and empty-when-required (toast); the detail view meta-grid shows the value ("… min") when set. -
i18n:
tspec.estimatedDuration,tspec.estimatedDurationHint,tspec.errDurationInvalid,tspec.errDurationRequiredin all 10 bundles.


Verified live (project "TestSpec Fixture", suite "Suite Alpha"):
create with 7.5 → 7.50; edit 42.75 → 55 → view + DB updated; abc blocked
client-side and 400 server-side; with required='required' (temp config) empty
save blocked both client + server; clearing → NULL; tcView.html shows the
value. Event Viewer: no new ERROR/WARNING rows. Test suite:
Task — Issue #911 in tmp/TLU_Test_Cases.md (12/12 PASS).
The legacy per-version controls (freeze/unfreeze, activate/deactivate, delete-this-version) and the version dropdown are ported to the modern screen.
BFF (api/testcases/index.php):
-
getaccepts optionaltcversion_id(edit/view a SPECIFIC version; default latest). A requested version that does not exist returns 404 "Test case version not found" instead of the old silent fallback +E_WARNING("Trying to access array offset on false" — root cause:fetchFirstRow()returns booleanfalsewhen no rows,is_null()never fired; all BFF row checks now useempty()). -
GET version_list→versions[](tcversion_id,version,active,is_open,has_been_executed,is_latest) + grants (mgt_modify_tc,testcase_freeze,delete_frozen_tcversion,testproject_delete_executed_testcases). -
POST delete_version→ legacy gates (frozen ⇒ needsdelete_frozen_tcversion; executed ⇒testproject_delete_executed_testcases/canDeleteExecuted); when the last version is removed the now-empty test-casenodes_hierarchyrow is deleted too (no ghost nodes in the tree). -
POST freeze/unfreeze(needstestcase_freeze) →tcversions.is_open. -
POST activate/deactivate→tcversions.active(legacysetActiveAttrsemantics: toggles the viewed tcversion, mirrored bytestcase.class.php).
UI (gui/templates/testcases/testSpec.html):
version selector in the viewer + editor (Ver. N (current|latest), ❄ frozen marker,
"(inactive)"), per-version Freeze/Unfreeze, Activate/Deactivate (hint "latest only" on
old versions of a multi-version TC), Delete-this-version, askConfirm OK-button label
now dynamic. Editor save sends tcversion_id so updates hit exactly the edited version.
i18n: 20 new tspec.* keys across all 9 locale bundles.

Verified live (project "Version Lifecycle Test", MultiVer Demo v1=21/v2=23): get-by-version,
404-on-missing-version, freeze→403-delete-frozen, unfreeze, deactivate/activate with correct
"latest only" hint, per-version edit precision, delete v1→1 left, delete last→ghost node removed.
Event Viewer: no new ERROR/WARNING rows after the fix. Test suite: Task — Issue #914 in
tmp/TLU_Test_Cases.md (15/15 PASS).