Test case review workflow plugin for Kiwi TCMS: group test cases into review requests, assign reviewers, collect votes, and track KPIs.
Project description
kiwitcms-review
Test case review workflow plugin for Kiwi TCMS.
Group test cases into a structured Review Request, assign reviewers, collect per-reviewer votes and per-case decisions, notify by email, track KPIs, and report on completed sessions — all without modifying the Kiwi TCMS core codebase.
Table of contents
- Features
- Screenshots
- Installation
- Configuration
- Permissions
- XML-RPC / JSON-RPC API
- How the plugin hooks into Kiwi
- Development
- Compatibility
- License
Features
- Review Request — group one or more
TestCaseobjects into a single review cycle with title, description (markdown), due date, and a state machine - Per-reviewer votes —
Approved/Needs changes/Rejected, with optional markdown comments - Per-case decisions — track approval status for each case in the request, with inline one-click decision buttons
- Automatic state transitions — request transitions to
Approved/Rejected/Changes requestedonce votes are in, via a pure state machine - Email notifications — reviewers notified on assignment, requester notified on votes and state changes (via Kiwi's existing
mailtohelper) - Dashboard "Pending my review" widget — injected into the Kiwi dashboard via JS, no core template edits
- "Send for review" button — injected into TestCase and TestPlan detail pages; opens a Bootstrap modal with title / due date / reviewer typeahead / description
- Per-case status badge — injected next to the TestCase title showing the latest review decision with a link to the parent request
- TestCase + TestPlan browser — on the review detail page, tab through all cases or plans to add them to the review in bulk
- XML-RPC & JSON-RPC API — 13 methods covering create / filter / cancel / vote / metrics / add-case / add-reviewer etc.
- Statistics dashboard with 4 charts — state distribution (donut), 30-day activity (area spline), top reviewers (bar), top requesters (bar)
- KPI overview — open requests, overdue, approval rate, 30-day counters
- Review time overview — average / median / fastest / slowest time-to-decision
- Per-session reporting — vote breakdown, time-to-decision, reviewer participation
- Configurable status filter — by default only
PROPOSEDtest cases can be submitted; override viaREVIEW_ALLOWED_CASE_STATUSESsetting - Full audit trail —
django-simple-historyshadow tables on every model
Screenshots
Dashboard integration
Injects a "Pending my review" card into the Kiwi dashboard listing every open review request assigned to the current user.
Review list
Filterable list of all review requests with colour-coded state badges.
New review request
Markdown-enabled form with date picker, reviewer selectpicker, and a proper breadcrumb.
Review detail
KPI strip, reviewer-and-votes table, cases-under-review table with inline per-case decision buttons, and a markdown vote form.
TestCase / TestPlan browser
Bulk-add cases either by searching or by drilling into a test plan.
Statistics dashboard
KPI cards, review-time overview, and four C3.js charts summarising the whole review process.
Per-session report
Drill into any request to see the full metrics — participation, time to decision, per-case counts, and per-reviewer activity.
TestCase detail — injected button and badge
The plugin grafts a status badge (next to the case title) and a "Send for review" button onto every TestCase detail page without touching the core template.
Send-for-review modal
Click the button to open a modal with username typeahead and a date picker, all wired to the plugin's JSON-RPC API.
Installation
From PyPI (once published)
pip install kiwitcms-review
./manage.py migrate tcms_review
./manage.py collectstatic --noinput
From source (editable install)
git clone https://github.com/veenone/kiwi-tcms-review-workflow.git
pip install -e kiwi-tcms-review-workflow
./manage.py migrate tcms_review
./manage.py collectstatic --noinput
That's it. Kiwi auto-discovers the plugin via the kiwitcms.plugins entry point and:
- adds
tcms_reviewtoINSTALLED_APPS - mounts the URLs at
/reviews/ - adds the menu items to the MORE menu
- registers the XML-RPC methods (done in the plugin's
apps.py::ready()) - injects the client-side bundle into every HTML response (via a response middleware, also registered in
ready())
Restart your Kiwi process after install.
Configuration
The plugin ships with a sensible default workflow configured out of the box. No settings file edits are required — on first install, the migration seeds the recommended status transitions into the database, and admins can edit them via the Kiwi admin UI.
Admin UI (recommended)
After install, two admin pages become available under Admin → Test Case Review:
| Page | Purpose |
|---|---|
admin/tcms_review/reviewconfig/ |
Singleton config — edit the Allowed case statuses JSON list here |
admin/tcms_review/reviewstatustransition/ |
Add / edit / toggle status-transition rules |
Default rules seeded on first install (migration 0005_seed_default_transitions):
| Source status | Decision | Target status |
|---|---|---|
PROPOSED |
Approved | CONFIRMED |
PROPOSED |
Needs changes | NEED_UPDATE |
PROPOSED |
Rejected | DISABLED |
CONFIRMED |
Needs changes | NEED_UPDATE |
Uncheck Is active on any row to temporarily disable a rule without deleting it. All edits are recorded by django-simple-history so you can audit config changes alongside the reviews themselves.
Django settings fallback
If the DB has no rows (e.g. during a migration, or if you roll back 0005_seed_default_transitions), the plugin falls back to these Django settings:
| Setting | Default | Purpose |
|---|---|---|
REVIEW_ALLOWED_CASE_STATUSES |
["PROPOSED"] |
List of TestCaseStatus.name values that can be submitted for review |
REVIEW_STATUS_TRANSITIONS |
{} |
Auto-transition map for TestCase.case_status |
Example in tcms_settings_dir/review.py or your own local_settings.py:
REVIEW_ALLOWED_CASE_STATUSES = ["PROPOSED", "CONFIRMED"]
REVIEW_STATUS_TRANSITIONS = {
("PROPOSED", "approved"): "CONFIRMED",
("PROPOSED", "needs_changes"): "NEED_UPDATE",
("PROPOSED", "rejected"): "DISABLED",
("CONFIRMED", "needs_changes"): "NEED_UPDATE",
}
Resolution order: DB rows → Django settings → plugin-shipped defaults.
Permissions
On first migrate, four Django permissions are created and auto-granted to the built-in Tester group:
| Permission | Purpose |
|---|---|
tcms_review.add_reviewrequest |
Create review requests |
tcms_review.change_reviewrequest |
Be a reviewer, cast votes, cancel, add cases |
tcms_review.view_reviewrequest |
Browse list and detail pages, call read-only RPC methods |
tcms_review.delete_reviewrequest |
Admin only |
Some operations have additional object-level guards beyond the permission:
- Voting — the user must be in the request's
reviewersM2M and the request must not be cancelled - Cancelling — only the original requester can cancel
XML-RPC / JSON-RPC API
All methods are registered under MODERNRPC_METHODS_MODULES and available at /xml-rpc/ and /json-rpc/. Same authentication model as Kiwi core RPC methods.
ReviewRequest
| Method | Permission | Purpose |
|---|---|---|
create(values) |
add_reviewrequest |
Create a new request. values validated through the same form the HTML view uses. |
filter(query) |
view_reviewrequest |
List requests matching the query dict. |
filter_canonical(query) |
view_reviewrequest |
List IDs only. Mirrors Bug.filter_canonical. |
add_case(request_id, case_id) |
change_reviewrequest |
Attach a test case. Enforces REVIEW_ALLOWED_CASE_STATUSES. |
remove_case(request_id, case_id) |
change_reviewrequest |
Detach a test case. |
add_reviewer(request_id, user_id) |
change_reviewrequest |
Add a reviewer. |
remove_reviewer(request_id, user_id) |
change_reviewrequest |
Remove a reviewer, triggers state recalculation. |
cancel(request_id) |
change_reviewrequest + owner-only |
Cancel the request. |
metrics(request_id) |
view_reviewrequest |
Returns {state, votes, items, participation, time_to_decision_seconds}. |
ReviewVote
| Method | Permission | Purpose |
|---|---|---|
cast(request_id, decision, comment) |
change_reviewrequest + reviewer guard |
Cast or revise the caller's vote; triggers state recompute. |
filter(query) |
view_reviewrequest |
List votes matching the query. |
ReviewItem
| Method | Permission | Purpose |
|---|---|---|
set_decision(item_id, decision, comment) |
change_reviewrequest |
Update the per-case decision. |
filter(query) |
view_reviewrequest |
List items matching the query. Used internally by the per-case badge injector. |
Example — Python
from tcms_api import TCMS
rpc = TCMS().exec
request = rpc.ReviewRequest.create({
"title": "Payment flow audit",
"description": "Please review the new payment-flow test cases.",
"due_date": "2026-05-01 17:00",
"reviewers": [12, 34],
})
rpc.ReviewRequest.add_case(request["id"], 100)
rpc.ReviewRequest.add_case(request["id"], 101)
rpc.ReviewVote.cast(request["id"], "approved", "LGTM")
metrics = rpc.ReviewRequest.metrics(request["id"])
How the plugin hooks into Kiwi
The plugin is a self-contained Django app that piggybacks on Kiwi's existing plugin contract. It never edits any Kiwi core file.
| Concern | Mechanism |
|---|---|
INSTALLED_APPS |
Auto — any module listed under kiwitcms.plugins entry point is appended (Kiwi's settings/common.py) |
| URL routing | Auto — each plugin's urls.py is mounted at ^<entry-point-name>/ (Kiwi's urls.py:62). We use reviews → /reviews/ |
| MORE menu | Auto — each plugin's menu.MENU_ITEMS is appended |
MODERNRPC_METHODS_MODULES |
Manual — we register methods directly into the modernrpc registry from apps.py::ready(), because modernrpc's ready() runs before plugin ready() and would otherwise miss our methods |
| Middleware | Manual — apps.py::ready() appends InjectReviewBundleMiddleware to settings.MIDDLEWARE |
| Dashboard widget | JS injection from inject.js — detects body id / URL, grafts a DOM node |
| Send-for-review button | JS injection — same mechanism |
| Per-case status badge | JS injection — calls /reviews/json/case/<pk>/latest/ on TestCase detail pages |
Reuses Kiwi's public tcms.core.utils.mailto.mailto() helper |
Two-jQuery rule — Kiwi ships two jQuery instances ($ with Bootstrap plugins, jQuery plain). The plugin's IIFE wrappers MUST close with })($).
Development
Run the unit tests
python -m unittest tcms_review.tests.test_state_machine
The state machine tests are pure Python and require no Django setup. DB-dependent tests are in test_models.py, test_views.py, test_permissions.py, test_signals.py, test_reports.py, and test_api.py — run them against a Kiwi dev install:
./manage.py test tcms_review --settings=tcms.settings.test
Build the wheel
pip install -e ".[dev]"
python -m build
Produces dist/kiwitcms_review-<version>-py3-none-any.whl and a source tarball.
Upload to PyPI
python -m twine upload dist/*
Lint
No enforced lint yet. The code follows black/flake8 conventions.
Compatibility
| Version | |
|---|---|
| Kiwi TCMS | ≥ 12.0 (tested against 15.3) |
| Python | ≥ 3.9 |
| Django | ≥ 3.2 (tested against 5.2) |
django-simple-history |
≥ 3.0 |
django-modern-rpc |
≥ 1.0 |
License
GPL-2.0-or-later. Matches Kiwi TCMS.
Author
Achmad Fienan Rahardianto — veenone@gmail.com
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kiwitcms_review-0.7.4.tar.gz.
File metadata
- Download URL: kiwitcms_review-0.7.4.tar.gz
- Upload date:
- Size: 1.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7633d0636d75ee5f3c2cf99353f7d3273da4cb36a00ec495c26d396c670c33fb
|
|
| MD5 |
aae1a13de16b70e77c2248c82f989ca9
|
|
| BLAKE2b-256 |
487050680a100117a0cf1cde1e042649f7eeaa98db319e74f46bb679d5f9ac88
|
File details
Details for the file kiwitcms_review-0.7.4-py3-none-any.whl.
File metadata
- Download URL: kiwitcms_review-0.7.4-py3-none-any.whl
- Upload date:
- Size: 196.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9771772f2d710fd240620c5843d51e71fee82a44a3f73dc768aec450b7b34f06
|
|
| MD5 |
303f79cc1af8c75d7e84b8c44b8cbe1e
|
|
| BLAKE2b-256 |
3bd77b7372062f7136127a1c4eaa484686a8bd147e28931b57627106a9d4065b
|