Test Case Export Modernized - sebiboga/testlink-upgraded GitHub Wiki

Test Case / Suite / Project Export โ€” Modernized Screen

Modernization of the legacy export screen (lib/testcases/tcExport.php, gui/templates/dashio/testcases/tcExport.tpl) โ€” GitHub issue #803.

The legacy Smarty screen is replaced by a standalone Dashio page (gui/templates/testcases/tcExport.html) backed by a plain-PHP REST BFF API (api/testcasesexport/index.php). The Test Case Viewer toolbar Export button now opens the modern screen instead of submitting the legacy form.

Path: Test Spec โ†’ Test Case Viewer โ†’ toolbar Export URL: gui/templates/testcases/tcExport.html?tproject_id=..&testcase_id=..&tcversion_id=.. BFF API: GET /api/testcasesexport/?action=info + POST /api/testcasesexport/?action=export Rights: any authenticated session (parity with legacy export, which is read-only). Tracking issue: #803


Export modes

The screen mirrors the three export contexts of the legacy tcExport.php:

Mode Trigger Filename pattern
Test case testcase_id + tcversion_id set <name>.version<v>.testcase.xml
Test suite (children) containerID set, no recursion <suite>.testsuite-children-testcases.xml
Test suite (deep) containerID set + useRecursion=1 <suite>.testsuite-deep.xml
Test project (deep) useRecursion=1, root container <project>.testproject-deep.xml

The mode/title/default filename are resolved by the BFF info route using the same logic as the legacy initializeGui() and init_args().

Screen layout

Section Description
Heading Export title + breadcrumb context (test project name)
File name Text input, pre-filled with the default filename; editable
Format Type select, populated dynamically from the BFF types map โ€” XML and Markdown. The default filename extension follows the selection (.xml / .md). A view file format documentation hint link (tcx.fileFormatsDoc โ†’ docs/tl-file-formats.pdf) sits below the select
Options Checkboxes: external ID, with project prefix, summary, preconditions, steps, requirements, custom fields, keywords, attachments โ€” plus Export skeleton (structure only) for suite/project modes
Action Export (downloads the selected format) and Cancel

Prefix mirroring: unchecking Include external ID disables and unchecks With project prefix (external prefix only applies when external ID is on).

Markdown format (MD)

The Markdown type produces a structured document that round-trips through the modern MD importer (api/testcasesimport/?action=import_md):

# <project name>
**Project:** <project name>
**Prefix:** <prefix>

## <suite name> (Suite ID: N)
### TC-<tcase-node-id>: <title>
- **ExternalID:** TLU-190          (prefix + glue + tc_external_id)
- **Importance:** High | Medium | Low
- **Preconditions:** ...           (single bullet line, newlines flattened)
- **Steps:**
     1. Do something
        *Expected:* something happened
- **Steps:**_none_                 (when a TC has no steps)

Format rules (kept symmetric with the MD parser):

  • Importance labels use the TestLink canonical mapping High=3 / Medium=2 / Low=1.
  • The exported block always reflects the latest test case version (the ExternalID, title, preconditions and steps all come from the same latest version).
  • ExternalID is omitted when the case has no external id.
  • TC-<N> in the heading is the test case node id โ€” used only for display and for the internalID hit-criteria on re-import; duplicate detection prefers the ExternalID line.

Re-importing the exported document with hit_criteria=externalid + action_on_hit=update_last_version updates each existing test case in place by its ExternalID and never creates duplicates.

Export flow

  1. On load the page calls GET /api/testcasesexport/?action=info to resolve the context (mode, project name, default filename, supported types, grants).
  2. The user edits options and clicks Export.
  3. The page POSTs action=export with the option flags and reads the response as a blob (XMLHttpRequest responseType='blob'), then triggers a browser download via an object URL.
  4. Any JSON error payload (unauthenticated, nothing to export, invalid input) is detected from the blob content-type and shown as a localized toast.

Error & edge states

Case Result
Empty file name local toast tcx.errEmptyFilename, no request sent
Nothing to export (empty suite) warn box tcx.noTestcasesToExport shown, Export still active
Project with no suites warn box tcx.noTestsuitesToExport
Not authenticated HTTP 401 {"status":"error","message":"Not authenticated"}
Nothing generated (0 bytes) HTTP 400 "Nothing to export for the given selection"

Skeleton export โ€” "Export skeleton" option (#1326)

Legacy lib/testcases/tcExport.php:145 reads the exportSkel request flag and, for every export that is NOT a single test case, sets $opt['skeleton'] = 1 before calling exportTestSuiteDataToXML(). The exporter honours that with $topt['excludeTC'], producing a structure-only XML: the <testsuite id name><node_order/><details/> tree with NO <testcase> elements. In the tl-classic template the flag was a second submit button exportSkel ("Export skeleton"), which the Dashio theme dropped while the controller feature stayed live โ€” so the modern tcExport.html could not reach it (gap, [#1326]).

The BFF already implemented the flag (api/testcasesexport/index.php:320-322, byte-identical output to legacy); the gap was the missing UI affordance plus doExport() never sending the parameter. Implemented:

  • A "Export skeleton" checkbox (#exportSkel, labels/hint via tcx.exportSkel / tcx.exportSkelHint).
  • Visibility gated to the suite/project export modes (test-suite deep, suite children, whole project); hidden for the single-testcase mode (legacy contract: exportSkel is a no-op for one-TC exports) and gated to the XML type (Selecting Markdown hides + unchecks it).
  • doExport() appends exportSkel=1 to the POST body when checked.

Verified end-to-end on the TP1326 fixture: checkbox on โ†’ POST exportSkel=1 โ†’ 322-byte structure-only XML (byte-identical to legacy); off โ†’ full 2291-byte XML with test cases.

Export skeleton option

Suite-level export launchers (#1325)

Legacy 1.9.20 reached the suite export from containerView.tpl (test project control panel, btn_export_all_testsuites) and from the test-suite control panel (include/containerViewTestSuiteTextButtons.inc.tpl โ€” btn_export_testsuite โ†’ deep suite export + btn_export_tc โ†’ children test cases). Modern parity was completed with two suite-context launch points:

Launch point Location Deep (recursion) Children (suite_tc)
Test Specification suite card testSpec.html showSuiteView() Export Test Suite button Export Test Cases button
Test Suite Viewer toolbar suiteView.html Export Test Suite button Export Test Cases button

Both open tcExport.html?tproject_id=<project>&containerID=<suite_id>[&useRecursion=1] in a popup; the BFF resolves the mode (testsuite vs suite_tc), default filename (<suite>.testsuite-deep.xml vs <suite>.testsuite-children-testcases.xml) and export options exactly as the legacy screen did. No BFF change was needed โ€” the export API already handled both suite modes. New i18n keys: tspec.exportSuite, tspec.exportSuiteCases, suvw.exportSuite, suvw.exportSuiteCases (all locales).

Screenshots (suite launchers #1325)

Test Specification suite card โ€” Export Test Suite / Export Test Cases

Test Suite Viewer toolbar โ€” Export Test Suite / Export Test Cases

Backend notes

  • The BFF reuses the exact legacy export methods โ€” testcase::exportTestCaseDataToXML() and testsuite::exportTestSuiteDataToXML() โ€” so the generated XML is byte-for-byte identical to the legacy screen.
  • The MD export path uses the new markdownTcExport class (lib/functions/markdown_tc_export.class.php), streaming a .md attachment with Content-Type: text/markdown; charset=utf-8.
  • nothingTodo is computed with a countExportChildren() helper that mirrors the legacy $check_children logic (excludes testplan/requirement/build/platform nodes).
  • Two pre-existing legacy E_WARNINGs in testsuite.class.php::exportTestSuiteDataToXML() ($$tsuiteData typo leaving $tsuiteData undefined at the project root, and an uninitialized $attachXML) were fixed (lib/functions/testsuite.class.php) so the recursive export no longer pollutes the Event Viewer.

API endpoints

Method Endpoint Description
GET /api/testcasesexport/?action=info export context: mode, names, default filename, types, grants
POST /api/testcasesexport/?action=export stream the file as a download (XML or Markdown, per exportType)

All routes require a valid session; POST is protected by the shared same-origin CSRF guard (_guard.php).

i18n

All labels, titles, placeholders and messages use client-side TLi18n keys under the tcx.* namespace (32 keys) present in all 10 locale bundles (gui/templates/i18n/{en,ro,de,es,fr,it,ja,pt,ru,zh}.json). Bundle consistency is enforced by tools/lint_i18n.py. Type labels use tcx.type.xml / tcx.type.md; both were added to the 8 bundles that lacked them (previously the dropdown fell back to the backend label), together with tcx.fileFormatsDoc (file-format docs link).

SCREEN-COMPARE parity verification (Refs #1323)

Parity run of the modern screen vs legacy lib/testcases/tcExport.php (2026-09-09). Outcome: XML output is byte-for-byte identical to legacy for the single test-case (835 B) and the project-deep (1061 B) exports; all four modes produce the same default filenames and checkbox defaults; Markdown works in every mode (superset โ€” legacy recursion was XML-only); empty-suite shows the localized warn box (superset โ€” legacy hid the form). Two gaps were fixed in-run (file-format documentation link; tcx.type.* keys in 8 bundles). One gap remains open: the suite-level export has no modern launcher (legacy containerView.tpl โ†’ tcExport.php?containerID=<suite>); tracked in #1325. The skeleton export gap is closed (see above, #1326). Cleanup of the legacy files: #1324.

Screenshots

Project mode โ€” export all test suites

Test case mode โ€” single test case export

Empty suite โ€” no test cases to export warning

Export screen โ€” format dropdown closed

Export screen โ€” format dropdown open (XML + Markdown)


TestLink 2.0.1 ยท Test Case / Suite / Project Export ยท Refs #803 ยท MD round-trip #842 / #852 / #853 ยท SCREEN-COMPARE #1323 (gaps #1324 #1325) ยท skeleton #1326

โš ๏ธ **GitHub.com Fallback** โš ๏ธ