Repository navigation
Test Plan Linking
The Test Plan Linking feature allows users to add existing test cases to a test plan. This is one of the most complex features in TestLink due to the need to handle:
- Test case filtering (by keywords, platform, execution type, etc.)
- Test case versioning (only one version can be linked per plan)
- Complex database queries joining multiple tables
- Tree-based UI for organizing test suites and test cases
- Bulk operations (add/remove multiple at once)
PHP Controllers:
-
lib/plan/planAddTC.php- Main controller
Templates:
-
gui/templates/tl-classic/plan/planAddTC_m1.tpl- UI template -
gui/templates/dashio/plan/planAddTC.tpl- Dashboard theme UI
Classes:
-
lib/functions/testproject.class.php- Test project operations -
lib/functions/testcase.class.php- Test case operations -
lib/plan/testplan.class.php- Test plan operations
Critical issue to understand:
- One test case can have MULTIPLE VERSIONS
- A test plan links to EXACTLY ONE version of a test case
- Different versions can have different properties:
- Different execution type (MANUAL vs AUTOMATED)
- Different steps
- Different keywords
- Different status
This scenario reproduces a complex filtering bug:
-
Test Suite A - create TC1 - Version 1 - exec type MANUAL
-
Create TC2 - Version 1 - exec type AUTO
-
Create TC3 - Version 1 - exec type MANUAL
-
Add TC1 Version 1 to test plan
-
Apply filter on exec type AUTO
-
Tree shows Test Suite A with 1 element
-
Click folder → RIGHT pane shows:
- TC2 - Version 1 (NOT assigned to plan) ✓ Correct
-
Edit TC1 → Create Version 2 - exec type AUTO
-
Add TC1 Version 2 to test plan
-
Apply filter on exec type AUTO
-
Tree shows Test Suite A with 2 elements
-
RIGHT pane shows:
- TC2 - Version 1 (NOT assigned) - Correct
- TC1 - Version 2 (NOT assigned) - WRONG!
This is WRONG because:
- TC1 IS linked to plan, but with VERSION 1
- Version 2 is shown only because it matches the AUTO filter
- Creates confusion about what's actually linked
-
Common Attributes (exist on all versions):
- Keyword ID
- Test case ID
- Test case name
-
Version-Level Attributes (can differ between versions):
- Execution type (MANUAL vs AUTOMATED)
- Steps
- Keywords (can differ per version in some cases)
Current implementation:
- Applies ALL filters together
- For version-level attributes, filters on LAST ACTIVE version
- This is NOT the same as version linked to plan
- Creates inconsistency between tree counter and displayed items
From code comment in planAddTC.php (line 218):
- Version-level attribute filters should NOT be applied to already-linked items
- This keeps displayed items consistent with actual links
- Two-step filtering:
- First: Get items already linked to plan (ignore version filters)
- Second: Apply version-level filters to unlinked items
Key tables:
-- Test cases table
CREATE TABLE testcases (
id INT PRIMARY KEY,
name VARCHAR(255),
parent_id INT, -- Test suite
...
);
-- Test case versions
CREATE TABLE tcversions (
id INT PRIMARY KEY,
tcid INT, -- Links to testcases
version INT,
execution_type INT, -- 1=MANUAL, 2=AUTOMATED
...
);
-- Test plan test cases (the link table)
CREATE TABLE test_plan_test_cases (
testplan_id INT,
testcaseid INT, -- Points to testcases (not tcversions!)
tcversionid INT, -- The specific version linked
platform_id INT,
...
);
-- Keywords
CREATE TABLE keywords (
id INT PRIMARY KEY,
keyword VARCHAR(255),
...
);
-- Test case keywords
CREATE TABLE testcase_keywords (
tcid INT,
keywordid INT,
...
);// In planAddTC.php
$sql = "SELECT DISTINCT tc.id, tc.name, tcv.id AS version_id,
tcv.execution_type, kw.keyword_id
FROM testcases tc
JOIN tcversions tcv ON tc.id = tcv.tcid
LEFT JOIN testcase_keywords kw ON tc.id = kw.tcid
WHERE tc.parent_id = " . $suite_id;
// Then filter based on user selections
if ($filter_execution_type) {
// Only filter for UNLINKED versions
// For linked versions, show the actual linked version
}Tree shows test suites (folders) containing test cases. When filtering:
- Count based on filters
- But keep consistency for already-linked items
// Insert into test_plan_test_cases
$sql = "INSERT INTO test_plan_test_cases
(testplan_id, testcaseid, tcversionid, platform_id)
VALUES (" . $plan_id . ", " . $tc_id . ", " . $version_id . ", " . $platform_id . ")";-
Mixing test case ID with version ID:
- test_plan_test_cases stores BOTH
- testcaseid = test case ID
- tcversionid = version ID
- Queries must join correctly
-
Filter inconsistency:
- Don't apply version-level filters to linked items
- Use two-step filtering
-
Multi-version confusion:
- One plan = one version per test case
- Don't show multiple versions as if they're separate items
-
Keyword filtering:
- Keywords can be at test case level
- Always use OR logic (test case with ANY selected keyword)
$sql = "SELECT DISTINCT tc.id, tc.name, tcv.id AS tcvid, tcv.execution_type
FROM testcases tc
JOIN tcversions tcv ON tc.id = tcv.tcid AND tcv.version =
(SELECT MAX(version) FROM tcversions WHERE tcid = tc.id)
LEFT JOIN test_plan_test_cases tptc
ON tc.id = tptc.testcaseid AND tptc.testplan_id = " . $plan_id . "
WHERE tptc.id IS NULL
AND tc.parent_id = " . $suite_id;$sql = "SELECT DISTINCT tc.id, tc.name, tcv.id AS tcvid
FROM testcases tc
JOIN tcversions tcv ON tc.id = tcv.tcid
JOIN test_plan_test_cases tptc
ON tc.id = tptc.testcaseid AND tcv.id = tptc.tcversionid
WHERE tptc.testplan_id = " . $plan_id
AND tc.parent_id = " . $suite_id;- Tree display: Shows test suites and nested test cases
- Filter controls: Allow filtering by keyword, exec type, etc.
-
Left/Right panes:
- Left: Tree of available test cases
- Right: Actions (Add/Remove buttons)
- Counters: Show number of test cases before/after filter
- Drag and drop: Some implementations support D&D from tree to plan
- Don't fetch all test cases at once
- Use pagination for large result sets
- Cache static data (keywords, execution types)
- Index heavily used filter columns
- Test case filtering and search
- Test plan management
- Platform assignment per test case
- Bulk operations (add/remove multiple)
Key test scenarios:
- Add new test case to empty plan
- Add test case with specific keyword
- Add test case with specific exec type
- Filter combined with existing links
- Handle test case with multiple versions
- Remove test case from plan
- Verify counts match displayed items
- Upload-on-execution support
- Enhanced filtering
- Performance improvements
- Developer-Guide for architecture overview and common tasks
- Test-Execution for execution workflows