Skip to main content

CLI Tool

TestPlanIt provides a command-line interface (CLI) for importing test results and managing test data from the terminal or CI/CD pipelines. The CLI is available as standalone binaries that require no Node.js or npm installation.

Supported Formats​

The CLI supports importing test results from 7 different formats:

FormatDescriptionFile Extensions
junitJUnit XML.xml
testngTestNG XML.xml
xunitxUnit XML.xml
nunitNUnit XML.xml
mstestMSTest TRX.trx, .xml
mochaMocha JSON.json
cucumberCucumber JSON.json

The CLI can auto-detect the format based on file content, or you can specify it explicitly with the -F/--format option.

Installation​

Download the appropriate binary for your platform from the CLI releases:

PlatformBinary
Linux (x64)testplanit-linux-x64
macOS (Apple Silicon)testplanit-macos-arm64
macOS (Intel)testplanit-macos-x64
Windows (x64)testplanit-windows-x64.exe

Linux (x64):

curl -L -o testplanit https://github.com/testplanit/testplanit/releases/download/cli-v1.0.0/testplanit-linux-x64
chmod +x testplanit
sudo mv testplanit /usr/local/bin/

macOS (Apple Silicon):

curl -L -o testplanit https://github.com/testplanit/testplanit/releases/download/cli-v1.0.0/testplanit-macos-arm64
chmod +x testplanit
sudo mv testplanit /usr/local/bin/

macOS (Intel):

curl -L -o testplanit https://github.com/testplanit/testplanit/releases/download/cli-v1.0.0/testplanit-macos-x64
chmod +x testplanit
sudo mv testplanit /usr/local/bin/

Windows (PowerShell):

Invoke-WebRequest -Uri "https://github.com/testplanit/testplanit/releases/download/cli-v1.0.0/testplanit-windows-x64.exe" -OutFile "testplanit.exe"
.\testplanit.exe --help

Configuration​

Setting Up​

Before using the CLI, configure your TestPlanIt instance URL and API token:

# Set URL and token
testplanit config set --url https://your-testplanit-instance.com --token tpi_your_token

# View current configuration
testplanit config show

# Clear stored configuration
testplanit config clear

# Show config file location
testplanit config path

Environment Variables​

Environment variables take precedence over stored configuration, making them ideal for CI/CD:

VariableDescription
TESTPLANIT_URLTestPlanIt instance URL
TESTPLANIT_TOKENAPI token
TESTPLANIT_RUN_IDSet by TestPlanIt when it dispatches a job; run plan, run complete, run finish and import -r default to it
TESTPLANIT_EXECUTION_IDSet by TestPlanIt when it dispatches a job; run plan and run finish default to it
TESTPLANIT_URL=https://demo.testplanit.com \
TESTPLANIT_TOKEN=tpi_xxx \
testplanit import results.xml -p 1 -n "Build"

Creating an API Token​

  1. Navigate to your user profile in TestPlanIt
  2. Go to the API Tokens section
  3. Click Create Token
  4. Give it a descriptive name and optional expiration date
  5. Copy the token immediately (it won't be shown again)

See the API Tokens documentation for more details.

Commands​

Import Test Results​

Import test results from various formats to create or update a test run:

testplanit import <files...> --project <id|name> --name <name> [options]

Required Parameters​

ParameterDescription
<files...>Test result files or glob patterns (e.g., ./results/*.xml)
-p, --project <value>Target project (ID or exact name)
-n, --name <name>Name for the test run (required unless appending to existing run with -r)

Optional Parameters​

ParameterDescription
-F, --format <format>File format: auto, junit, testng, xunit, nunit, mstest, mocha, cucumber (default: auto-detect)
-s, --state <value>Workflow state (ID or exact name)
-c, --config <value>Configuration (ID or exact name)
-m, --milestone <value>Milestone (ID or exact name)
-f, --folder <value>Parent folder for test cases (ID or exact name)
-t, --tags <values>Tags (comma-separated IDs or names; use quotes for names with commas)
-r, --test-run <value>Existing test run to append results (ID or exact name)
-d, --attachments-dir <path>Base directory for resolving attachment paths (default: directory of test result file)
--no-attachmentsSkip uploading attachments from test results
-a, --run-attachments <files...>Files to attach to the test run (e.g., test plans, reports)
--case-matcher <mode>Link results to existing cases by ID: off, name, property, auto (default: off)
--case-id-format <preset>Case-ID pattern in the test name when matching by name: brackets, c, tc (default: brackets)
note

For project, state, config, milestone, folder, and test run options, the CLI looks up entities by exact name match. If no match is found, an error is returned. For tags, if a tag name doesn't exist, it will be created automatically.

Attachments​

The CLI supports two types of attachments:

  1. Test Result Attachments: Screenshots, videos, and other files referenced in your test results (JUnit, NUnit, MSTest, Cucumber). These are automatically detected and uploaded, then linked to the specific test case results.

  2. Test Run Attachments: Files you want to attach to the test run itself, such as test plans, requirements documents, or coverage reports. Use the -a option to specify these files.

FormatAttachment SupportPath Format
JUnit[[ATTACHMENT|path]] in system-outRelative to XML file
NUnit<attachment><filePath> elementsAbsolute paths
MSTestResultFiles/ResultFile elementsRelative to test run directory
Cucumberembeddings arrayEmbedded base64 or file paths
TestNGNot supported-
xUnitNot supported-
MochaNot supported-

Use -d, --attachments-dir to specify a custom base directory for resolving relative attachment paths. By default, paths are resolved relative to the test result file's directory.

Use --no-attachments to skip uploading test result attachments entirely.

Linking results to existing cases by ID​

By default the importer matches each result to a case by name and class name. If your automation renames a test, the importer no longer recognizes it and creates a duplicate case. To prevent this, assign each automated test the ID of the case it belongs to, and turn on case-ID matching with --case-matcher. When a result carries a recognized ID, it links to that case regardless of the test name, so renames stop creating duplicates.

There are two ways to carry the ID, controlled by --case-matcher:

  • name — the ID is embedded in the test name; the pattern is chosen with --case-id-format (see below).
  • property — the ID is carried in a JUnit <property> named test_id (or testplanit_case_id), following the convention used by TestRail and Xray (see below).
  • auto — try the test_id property first, then fall back to the name pattern.

Name patterns (--case-id-format):

PresetMatchesExample test name
brackets (default)[123], [C123], [123, 456][123] user can log in
cC123C123 user can log in
tcTC-123, TC123TC-123 user can log in

Property form (--case-matcher property or auto):

<testcase name="user can log in" classname="auth.LoginTests">
<properties>
<property name="test_id" value="123"/>
</properties>
</testcase>

Notes:

  • A single test may reference multiple cases ([123, 456] or value="123,456"); each referenced case receives the same result.
  • Tests without an ID still import normally — they are matched/created by name and class name as before.
  • If a referenced case ID does not exist in the target project, that result is skipped (never auto-created under a wrong name) and reported as a warning at the end of the import.
  • The ID is the numeric TestPlanIt case ID; a leading C or # is tolerated.
  • Property matching requires a reporter that writes per-<testcase> properties (e.g. pytest's record_property, the Playwright JUnit reporter with embedAnnotationsAsProperties, NUnit [Property]). Frameworks that only emit suite-level properties (e.g. Maven Surefire) should use the name-based pattern instead.

Examples​

# Import a single JUnit file (auto-detected)
testplanit import results.xml -p 1 -n "Build 123"

# Import with explicit format
testplanit import results.xml -p 1 -n "Build 123" -F junit

# Import TestNG results
testplanit import testng-results.xml -p 1 -n "TestNG Suite" -F testng

# Import Mocha JSON results
testplanit import mocha-report.json -p 1 -n "Mocha Tests" -F mocha

# Import Cucumber JSON results
testplanit import cucumber-report.json -p 1 -n "BDD Tests" -F cucumber

# Import multiple files with glob pattern
testplanit import "./test-results/*.xml" -p 1 -n "CI Build"

# Link results to existing cases by an ID in the test name (e.g. "[123] login")
testplanit import results.xml -p 1 -n "Build 123" --case-matcher name

# Use the TestRail-style C-prefix pattern (e.g. "C123 login")
testplanit import results.xml -p 1 -n "Build 123" --case-matcher name --case-id-format c

# Link by a test_id property in the XML, falling back to the name pattern
testplanit import results.xml -p 1 -n "Build 123" --case-matcher auto

# Import with IDs
testplanit import results.xml \
--project 1 \
--name "Nightly Build" \
--format junit \
--state 5 \
--config 2 \
--milestone 3 \
--folder 10 \
--tags 1,2,3

# Import with names (exact match required)
testplanit import results.xml \
--project "My Project" \
--name "Nightly Build" \
--state "In Progress" \
--config "Chrome on Windows" \
--milestone "Sprint 5" \
--folder "API Tests" \
--tags "regression,smoke,critical"

# Mix IDs and names
testplanit import results.xml -p 1 -n "Build 123" \
--state "Completed" \
--tags '1,"new tag",smoke'

# Append results to an existing test run (no -n needed)
testplanit import more-results.xml \
-p "My Project" \
--test-run "Build 123"

# Import with attachments from a custom directory
testplanit import results.xml -p 1 -n "Build" -d ./test-artifacts

# Import without uploading attachments
testplanit import results.xml -p 1 -n "Build" --no-attachments

# Attach files to the test run (test plans, reports, etc.)
testplanit import results.xml -p 1 -n "Build" \
-a ./test-plan.pdf ./coverage-report.html

# Combine test result attachments with test run attachments
testplanit import results.xml -p 1 -n "Build" \
-d ./screenshots \
-a ./test-plan.pdf ./requirements.docx

Run Commands​

For a job that TestPlanIt dispatched (see Automated Execution), or any job reporting into a run created up front:

# Create one run up front and let every shard, machine and retry attach to it
export TESTPLANIT_RUN_ID=$(testplanit run create -p 9 -n "Nightly #12")

# The run's automated cases, as JSON
testplanit run plan

# One selector per line, for a shell shim to feed a runner filter
testplanit run plan --format lines --selector-field fullName > plan.txt

# Mark the run complete once every job has reported
testplanit run complete

# Report the execution's outcome (needed for generic-webhook targets, which TestPlanIt cannot poll)
testplanit run finish --conclusion success
CommandOptions
run create-n, --name <name> (required), -p, --project <id|name> (default $TESTPLANIT_PROJECT_ID), --type REGULAR|JUNIT|TESTNG|XUNIT|NUNIT|MSTEST|MOCHA|CUCUMBER (default REGULAR), --config <id|name>, --milestone <id|name>, --tags <a,b> (missing tags are created). Prints only the new run id to stdout
run plan-r, --run <id> (default $TESTPLANIT_RUN_ID), --execution <id> (default $TESTPLANIT_EXECUTION_ID; applies that execution's case subset and ref), -F, --format json|lines, --selector-field selector|fullName|title|className|id, -o, --output <file>
run complete-r, --run <id>, -p, --project <id> (read from the run when omitted)
run finish-r, --run <id>, --execution <id>, --conclusion success|failure|cancelled (required), --message <text>

--format lines prints selector.fullName (class name and test name) by default. The CLI never produces framework-specific filter syntax; a short script in your repository turns the lines into the --grep, -k or --tests expression your runner takes.

CI/CD Integration​

Triggered from TestPlanIt​

When TestPlanIt dispatches the job, TESTPLANIT_RUN_ID and TESTPLANIT_EXECUTION_ID arrive as workflow inputs or pipeline variables. Export them into the environment, read the plan, run the tests, import the results, complete the run:

on:
workflow_dispatch:
inputs:
TESTPLANIT_RUN_ID: { required: false }
TESTPLANIT_EXECUTION_ID: { required: false }
TESTPLANIT_PROJECT_ID: { required: false }
TESTPLANIT_URL: { required: false }
TESTPLANIT_PLAN_URL: { required: false }

jobs:
test:
runs-on: ubuntu-latest
env:
TESTPLANIT_URL: ${{ inputs.TESTPLANIT_URL }}
TESTPLANIT_TOKEN: ${{ secrets.TESTPLANIT_TOKEN }}
TESTPLANIT_RUN_ID: ${{ inputs.TESTPLANIT_RUN_ID }}
TESTPLANIT_EXECUTION_ID: ${{ inputs.TESTPLANIT_EXECUTION_ID }}
steps:
- uses: actions/checkout@v4
- run: npx @testplanit/cli run plan --format lines --selector-field fullName > plan.txt
- run: ./scripts/run-testplanit-plan.sh plan.txt
- if: always()
run: npx @testplanit/cli import ./test-results/*.xml -p ${{ inputs.TESTPLANIT_PROJECT_ID }}

TestPlanIt polls a GitHub Actions or GitLab CI job for its outcome, so nothing else is needed. A job started through a generic webhook (Jenkins, for example) ends with testplanit run finish --conclusion success|failure. Reserve run complete for runs that hold nothing but automated cases; it completes the whole run, manual cases included. The step-by-step guides are under Automated Execution.

GitHub Actions​

Using the standalone binary (no Node.js required):

name: Test and Import Results

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run Tests
run: npm test

- name: Download TestPlanIt CLI
run: |
curl -L -o testplanit https://github.com/testplanit/testplanit/releases/latest/download/testplanit-linux-x64
chmod +x testplanit

- name: Import Test Results to TestPlanIt
if: always()
env:
TESTPLANIT_URL: ${{ secrets.TESTPLANIT_URL }}
TESTPLANIT_TOKEN: ${{ secrets.TESTPLANIT_TOKEN }}
run: |
./testplanit import ./test-results/*.xml \
-p 1 \
-n "Build ${{ github.run_number }} - ${{ github.sha }}"

GitLab CI​

stages:
- test
- report

test:
stage: test
script:
- npm test
artifacts:
reports:
junit: test-results/*.xml
paths:
- test-results/

import-results:
stage: report
needs: [test]
when: always
script:
- curl -L -o testplanit https://github.com/testplanit/testplanit/releases/latest/download/testplanit-linux-x64
- chmod +x testplanit
- ./testplanit import ./test-results/*.xml -p 1 -n "Pipeline $CI_PIPELINE_ID"
variables:
TESTPLANIT_URL: $TESTPLANIT_URL
TESTPLANIT_TOKEN: $TESTPLANIT_TOKEN

Jenkins Pipeline​

pipeline {
agent any

stages {
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}

stage('Import to TestPlanIt') {
steps {
withCredentials([
string(credentialsId: 'testplanit-url', variable: 'TESTPLANIT_URL'),
string(credentialsId: 'testplanit-token', variable: 'TESTPLANIT_TOKEN')
]) {
sh '''
curl -L -o testplanit https://github.com/testplanit/testplanit/releases/latest/download/testplanit-linux-x64
chmod +x testplanit
./testplanit import ./target/surefire-reports/*.xml \
-p 1 \
-n "Build ${BUILD_NUMBER}"
'''
}
}
}
}
}

Azure DevOps​

trigger:
- main

pool:
vmImage: 'ubuntu-latest'

steps:
- script: npm test
displayName: 'Run Tests'

- script: |
curl -L -o testplanit https://github.com/testplanit/testplanit/releases/latest/download/testplanit-linux-x64
chmod +x testplanit
./testplanit import ./test-results/*.xml \
-p 1 \
-n "Build $(Build.BuildNumber)"
displayName: 'Import Results to TestPlanIt'
env:
TESTPLANIT_URL: $(TESTPLANIT_URL)
TESTPLANIT_TOKEN: $(TESTPLANIT_TOKEN)
condition: always()

CircleCI​

version: 2.1

jobs:
test-and-import:
docker:
- image: cimg/node:18.0
steps:
- checkout
- run:
name: Run Tests
command: npm test
- run:
name: Import Results
command: |
curl -L -o testplanit https://github.com/testplanit/testplanit/releases/latest/download/testplanit-linux-x64
chmod +x testplanit
./testplanit import ./test-results/*.xml \
-p 1 \
-n "Build ${CIRCLE_BUILD_NUM}"
when: always

workflows:
test:
jobs:
- test-and-import

Troubleshooting​

Common Issues​

"TestPlanIt URL is not configured"

Run testplanit config set --url <your-url> or set the TESTPLANIT_URL environment variable.

"API token is not configured"

Run testplanit config set --token <your-token> or set the TESTPLANIT_TOKEN environment variable.

"Invalid API token" or "API token has been revoked"

Your token may be expired or revoked. Create a new token in your TestPlanIt user profile.

"No matching files found to import"

Check that your file patterns match existing files. Use quotes around glob patterns to prevent shell expansion: "./results/*.xml"

If you specify a format explicitly, files with unexpected extensions will be skipped. For example, -F mocha expects .json files.

Debug Mode​

Enable debug output by setting the DEBUG environment variable:

DEBUG=1 testplanit import results.xml -p 1 -n "Test"