Skip to content

Discovering Tests, Tasks and Suites

Before you can run, filter, or report on a Robot Framework project you usually want to know what's actually in it: which suites live in which directories, what tags each test carries, which .robot files Robot would even pick up. robotcode discover answers those questions without executing a single test.

It applies the same configuration and profile pipeline that robotcode robot would use to run the project — so what discover reports is exactly what robot would see — and surfaces the result either as a human-readable tree on the terminal or as structured data for scripts, CI pipelines, and editors.

Who this is for:

  • Developers opening an unfamiliar project who want a one-shot overview before drilling in.
  • CI/CD pipelines that need a stable inventory of tests / tags / files for sharding, reporting dashboards, or build-time validation.
  • Editor and IDE integrations — the JSON output is what the RobotCode VS Code extension consumes to build its Test Explorer view.
  • AI-driven workflows — coding agents (Claude Code, Cursor, Copilot, …), inventory analyses, test-selection assistants. Letting the agent grep through a tree of .robot files is wasteful; discover returns just the slice the agent asked for (a tag list, a file list, the test tree under one path).
  • Test-selection scripts that build -bl/--by-longname lists for robotcode robot — pre-filtering tests for sharded CI matrices, regression sets, or scheduled smoke runs.

Typical things you can do with it:

  • get a tree view of every suite, sub-suite, test and task the active profile would discover
  • list just the tests (or just the tasks) one per line, with source paths and tags
  • list every distinct tag in the project with the tests it tags
  • list the metadata names and values in use — of tests and tasks (Robot Framework 7.5+) and of suites — with the items that carry them
  • list the .robot and .resource files of the project, honouring .gitignore / .robotignore
  • check the Robot Framework version, Python version and environment the tooling is running under

The eight subcommands share the same configuration, search, and output-format pipeline — once you've learned one the others follow:

Subcommand Use it when you want to …
all … see the whole tree (workspace → suite → sub-suite → test/task)
tests … list tests one per line (typed test)
tasks … list tasks one per line (typed task, RPA mode)
suites … list suites only — no per-test rows
tags … build a tag → [tests] index for triage or dashboards
metadata … see which metadata names and values are in use, and where (Robot Framework 7.5+)
files … list every .robot / .resource file not excluded by .gitignore / .robotignore
info … check the active Robot / Python / RobotCode versions and environment

This guide is split in two:

  1. Part 1 — Using the commands walks through every subcommand from the terminal perspective: what it does, what shows up on the screen, and what filters and flags are available.
  2. Part 2 — JSON reference documents the structured output for scripts, CI pipelines, and editor integrations — the JSON schema of every subcommand plus jq-based recipes.

For the exhaustive option list see the auto-generated CLI reference.

Terminal window
# Workspace tree — every suite, every test, with tags
robotcode discover all
# Flat list of tests, one per line
robotcode discover tests
# Just the test names matching "Login", in the current profile's scope
robotcode discover tests --search Login
# A tag → tests dictionary
robotcode discover tags
# Metadata names and values of tests and suites (Robot Framework 7.5+)
robotcode discover metadata --values
# Every test for issue 4409 (Robot Framework 7.5+)
robotcode discover tests --by-test-metadata Issue:4409
# Which source files would Robot even parse?
robotcode discover files
# Active Robot / Python / RobotCode versions + environment
robotcode discover info

If you don't pass paths or filters, discover falls back to the default paths from your active robot.toml profile — the same logic robotcode robot uses.

discover runs the same configuration pipeline as robotcode robot:

  1. Profile resolution. robot.toml is loaded and combined with any --profile selectors. The resulting profile contributes the default paths (paths = [...]), variables, library paths, and any other Robot Framework settings.
  2. Argument layering. Positional ROBOT_OPTIONS_AND_ARGS you pass on the command line are appended after the profile's settings — Robot's normal precedence rules apply. So robotcode discover tests --include smoke ./tests/login picks up the profile's paths unless you give explicit paths (then those win), and adds --include smoke on top of any profile-level includes.
  3. Static parsing only. discover never executes a keyword. It walks the suite tree the same way Robot's --dryrun would but stops earlier — at parse time. Library imports, variable files, listeners, and pre-run modifiers are loaded only as far as needed to resolve the tree.

If you want to discover something outside your profile (a one-off file, a specific directory), pass it as a positional argument:

Terminal window
robotcode discover tests path/to/suite.robot
robotcode discover all path/to/dir/ path/to/other/

You can also pass any standard robot argument through — --include, --exclude, --suite, --test, --variable, --pythonpath, … See Robot-native filters below.

The default TEXT output is meant for humans reading in a terminal — colourised, paginated. For pipelines, scripts, editor integrations, and AI agents, the global -f/--format flag — set before the subcommand — switches to a stable structured format:

Terminal window
robotcode discover all # default: human-readable TEXT
robotcode --format json discover all # compact JSON, one line
robotcode --format json_indent discover all # pretty-printed JSON
robotcode --format toml discover all # TOML

If you only ever look at terminal output, the following sections cover everything you need. For the JSON shape of any subcommand, jump to the JSON reference.

Pager and colour handling is identical to results — see Pager and colour for the details.

all prints the complete hierarchy the active profile resolves to: the workspace root, every suite and sub-suite below it, and every test or task inside those suites. It's the closest thing to "show me what's in this project".

Terminal window
robotcode discover all
robotcode discover all ./tests/login # restrict to a path
robotcode discover all --include smoke # restrict to a tag

Sample output:

# All
- **MyProject** (`.`)
- **MyProject.Tests** (`tests`)
- **MyProject.Tests.Checkout** (`tests/checkout`)
- **MyProject.Tests.Checkout.Test Checkout** (`tests/checkout/test_checkout.robot`)
- **MyProject.Tests.Checkout.Test Checkout.Empty Cart** (`tests/checkout/test_checkout.robot:8`)
- **MyProject.Tests.Login** (`tests/login`)
- **MyProject.Tests.Login.Test Login** (`tests/login/test_login.robot`)
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- _Tags:_ `regression`, `smoke`
- _Metadata:_ Author: Hans Müller, Issue: 4409
- **MyProject.Tests.Login.Test Login.Good Password** (`tests/login/test_login.robot:22`)
- _Tags:_ `smoke`
- _Metadata:_ Issue: 4410
…
## Statistics
- _Suites:_ 8
- _Suites with tests:_ 3
- _Tests:_ 4
Flag Effect
--show-tags / --no-show-tags Show or hide the Tags: line under each test/task. Default: on.
--show-metadata / --no-show-metadata Show or hide a Metadata: line (name: value, …) under each test/task that has [Metadata] (Robot Framework 7.5+). Default: on.
--full-paths / --no-full-paths Absolute source paths. Default: relative to the project root.
--search TEXT / --search-regex PATTERN Prune the tree to tests matching the pattern; surviving tests keep their full ancestor chain. See Search.
-bl NAME / -ebl NAME Include/exclude tests, tasks or suites by exact long name. See Robot-native filters.
any standard robot flag --include, --exclude, --suite, --test, --variable, --pythonpath, … passed through to the discovery pipeline.

A test or task that has [Metadata] is followed by one line listing all its entries (hide it with --no-show-metadata) — in the raw markdown output - _Metadata:_ Issue: 4409, Owner Team: core. Names keep the spelling from the source file; the lines of a multi-line value are joined with a space. Test metadata exists since Robot Framework 7.5; with an older version installed, --help does not list the flag and passing it has no effect. The same flag is available on tests and tasks, where it is off by default, like --show-tags.

A diagnostics footer is added to TEXT output when parsing emits warnings or errors (deprecated section headers, duplicate test names, unparseable files). In JSON they land in the diagnostics field — see diagnostics.

tests collapses the hierarchy: one row per test, with full long name and source location. The natural input for scripts that need a list of test identifiers.

Terminal window
robotcode discover tests
robotcode discover tests --show-tags # add a `Tags: ...` line per test
robotcode discover tests --show-metadata # add a `Metadata: ...` line per test (RF 7.5+)
robotcode discover tests --include smoke # filter by tag
robotcode discover tests --search "Login" # substring search
robotcode discover tests path/to/suite.robot # one suite only

Sample output:

# Tests
- **MyProject.Tests.Checkout.Test Checkout.Empty Cart** (`tests/checkout/test_checkout.robot:8`)
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- **MyProject.Tests.Login.Test Login.Good Password** (`tests/login/test_login.robot:22`)
…
## Statistics
- _Suites:_ 8
- _Suites with tests:_ 3
- _Tests:_ 4
Flag Effect
--show-tags / --no-show-tags Include a Tags: line per test. Default: off (tests are always one-line in TEXT).
--show-metadata / --no-show-metadata Include a Metadata: line (name: value, …) per test that has [Metadata] (Robot Framework 7.5+). Default: off.
--full-paths / --no-full-paths Absolute source paths.
--search TEXT / --search-regex PATTERN Filter by name/source/body/tags.
-bl NAME / -ebl NAME Long-name include/exclude.
any standard robot flag Pass-through.

Tasks defined with *** Tasks *** are intentionally not in this list — use tasks for those.

tasks is the RPA twin of tests: one row per task. The TEXT output and flag set mirror tests; only the heading differs (# Tasks).

Terminal window
robotcode discover tasks
robotcode discover tasks --show-tags

In a mixed-mode suite (rare — Robot doesn't recommend it), tests shows the *** Test Cases *** half and tasks shows the *** Tasks *** half. A project where the active robot.toml profile sets rpa = true puts everything under tasks.

suites lists every suite the active profile resolves, one per line, no per-test detail. Useful when you want to know which suites exist without scrolling past hundreds of tests, or to build a suite-by-suite shard plan for CI.

Terminal window
robotcode discover suites
robotcode discover suites --include smoke # only suites with a smoke test

Sample output:

# Suites
- **MyProject** (`.`)
- **MyProject.Tests** (`tests`)
- **MyProject.Tests.Checkout** (`tests/checkout`)
- **MyProject.Tests.Checkout.Test Checkout** (`tests/checkout/test_checkout.robot`)
- **MyProject.Tests.Login** (`tests/login`)
- **MyProject.Tests.Login.Test Login** (`tests/login/test_login.robot`)
- **MyProject.Tests.Search** (`tests/search`)
- **MyProject.Tests.Search.Test Search** (`tests/search/test_search.robot`)
## Statistics
- _Suites:_ 8
- _Suites with tests:_ 3
- _Tests:_ 4

A suite with no surviving tests after filtering disappears from the list — --include smoke against a suite where nothing is tagged smoke will drop that suite.

tags builds an index of every distinct tag in the discovered suite tree. The default TEXT output is the tag list alone; --tests and --tasks expand each tag with the tests / tasks it covers.

Terminal window
robotcode discover tags # just the tags
robotcode discover tags --tests # tag → tests, indented
robotcode discover tags -i smoke # tags appearing in the smoke subset
robotcode discover tags --not-normalized # keep the original tag spelling

Sample output (--tests):

# Tags
- **regression**
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- **smoke**
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- **MyProject.Tests.Login.Test Login.Good Password** (`tests/login/test_login.robot:22`)
## Statistics
- _Suites:_ 8
- _Suites with tests:_ 3
- _Tests:_ 4
Flag Effect
--normalized / --not-normalized Whether tag keys are normalised (lowercase, no whitespace, no underscore). Default: on. Bug 1 / bug_1 / BUG1 collapse to a single bug1 entry; with --not-normalized the three variants stay separate.
--tests / --no-tests Add the tests carrying each tag. Default: off.
--tasks / --no-tasks Add the tasks carrying each tag. Default: off.
--full-paths Absolute source paths in the expanded child rows.
--search TEXT / --search-regex PATTERN Filter the underlying test set first; only tags surviving on at least one matching test appear.
-bl / -ebl / any robot flag Pass-through.

The --normalized default matches Robot Framework's own tag-matching semantics: --include "bug 1" matches tests tagged bug_1 or BUG1. Disable it (--not-normalized) when you want to audit tag hygiene — three distinct dict keys for Bug 1 / bug_1 / BUG1 make accidental variants pop.

metadata builds an index of the metadata in the discovered suite tree, in two sections: the [Metadata] of tests and tasks (Robot Framework 7.5+), and the Metadata of suites, including metadata you give Robot with --metadata NAME:VALUE. The default TEXT output lists the names; --values adds the values of each name, and --tests, --tasks and --suites add the items that carry them.

Terminal window
robotcode discover metadata # the names in use
robotcode discover metadata --values # … with their values
robotcode discover metadata --values --tests # … and the tests under each value
robotcode discover metadata -i smoke --values # the metadata of the smoke subset

Sample output (--values --tests --suites, raw markdown):

# Metadata
## Tests and Tasks
- **Author**
- Hans Müller
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- **Issue**
- 4409
- **MyProject.Tests.Login.Test Login.Bad Password** (`tests/login/test_login.robot:14`)
- 4410
- **MyProject.Tests.Login.Test Login.Good Password** (`tests/login/test_login.robot:22`)
## Suites
- **Version**
- 1.2
- **MyProject.Tests.Login.Test Login** (`tests/login/test_login.robot`)

How the index is built:

  • The test section lists exactly the entries --by-test-metadata matches: every line of a multi-line value is a value of its own, while the cells of one [Metadata] row stay one value. Every value it shows can be selected with --by-test-metadata Name:Value.
  • Suite metadata is shown in its own section, but not selectable: --by-test-metadata matches a test's own metadata only.
  • Names that Robot Framework treats as the same — case, spaces and underscores are ignored — are one name, shown in the first spelling found; values are grouped the same way. Names and values are sorted.
  • Values are shown as written; variables in them are not replaced.
  • Robot's filters, -bl/-ebl, -btm/-ebtm and --search narrow the discovered tests and suites first, as for tags. When nothing is left, the output says (no metadata matched).
Flag Effect
--values / --no-values Add the values of each name. Default: off.
--tests / --no-tests Add the tests under each value, or under each name without --values. Default: off.
--tasks / --no-tasks Add the tasks, like --tests. Default: off.
--suites / --no-suites Add the suites under the values of the suite section. Default: off.
--full-paths Absolute source paths in the item rows.
--search TEXT / --search-regex PATTERN Filter the underlying tests first.
-bl / -ebl / -btm / -ebtm / any robot flag Pass-through.

files lists every .robot and .resource file in the given paths (or the profile's default paths), honouring .gitignore / .robotignore. Files and folders whose names start with _ are listed too, although Robot Framework skips them when it collects suites.

Terminal window
robotcode discover files # respects profile paths
robotcode discover files ./tests ./resources # explicit directories
robotcode discover files --search "checkout" # filename substring filter

Sample output:

# Files
- `tests/login/test_login.robot`
- `tests/checkout/test_checkout.robot`
- `tests/search/test_search.robot`
_Total:_ 3 file(s)

Files under directories ignored by .gitignore / .robotignore are excluded automatically. Use this when you need to feed a list of files into another tool (linter, formatter, custom pre-commit hook).

Flag Effect
--full-paths / --no-full-paths Absolute paths. Default: relative to the project root.
--search TEXT / --search-regex PATTERN Filter by path/filename substring or regex.

info reports the versions and platform robotcode is running under — useful for bug reports, CI metadata, and "does this build have the right Robot version" checks.

Terminal window
robotcode discover info
robotcode --format json discover info

TEXT output:

# Info
- _Robot Framework:_ 7.5
- _RobotCode:_ 2.7.0
- _Python:_ 3.13.12
- _Executable:_ .venv/bin/python
- _Machine:_ x86_64
- _Platform:_ linux
- _System:_ Linux
…

Set ROBOT_OPTIONS, ROBOT_SYSLOG_FILE, ROBOT_SYSLOG_LEVEL or ROBOT_INTERNAL_TRACES and they're echoed back under robotEnv, so you can verify the run environment matches expectations.

Every discover subcommand (except info and files) passes through to Robot Framework's own filtering machinery. Anything you'd write on a robot command line works here:

Flag Effect
-i / --include TAG_PATTERN Include tests with a matching tag. Supports Robot's AND/OR/NOT syntax and globs (*, ?).
-e / --exclude TAG_PATTERN Exclude tests with a matching tag.
-s / --suite NAME Limit to suites whose name matches the glob.
-t / --test NAME Limit to tests whose name matches the glob (--task is the RPA alias).
-bl / --by-longname NAME Exact long-name include — no glob expansion.
-ebl / --exclude-by-longname NAME Exact long-name exclude.
-btm / --by-test-metadata PATTERN Tests/tasks whose [Metadata] matches the pattern (Robot Framework 7.5+) — see Selecting by test metadata.
-ebtm / --exclude-by-test-metadata PATTERN Exclusion side of --by-test-metadata (Robot Framework 7.5+).
--variable NAME:VALUE, --pythonpath PATH, … Anything else Robot accepts.

Filters compose — --include smoke --exclude wip means "smoke and not wip".

  • -s / -t are glob matches.
  • -bl / -ebl are exact matches against the full long name. They mirror robotcode robot --by-longname so you can hand the same names to robot and discover.

Use -bl when you have a precise name (often pasted from a failure list or a build log); use -s/-t when you want pattern matching.

-btm/--by-test-metadata PATTERN and -ebtm/--exclude-by-test-metadata PATTERN (Robot Framework 7.5+) select tests and tasks by their [Metadata]: the issue a test verifies, who wrote or reviewed it, and other data you keep there rather than in tags. Both are repeatable. Several --by-test-metadata patterns select the tests matching any of them, --exclude-by-test-metadata removes every test matching one, and both narrow every other selection (--include, --suite, -bl, --search, …). They work the same with robotcode robot, robotcode robot-debug, every discover subcommand except files and info, and every results subcommand.

Terminal window
robotcode discover tests --by-test-metadata Issue:4409 # every test for issue 4409
robotcode discover tests -btm "Author:Hans* OR Reviewer:Hans*" # written or reviewed by Hans
robotcode discover tests -btm "Author:Hans* NOT Reviewer:*" # by Hans, not reviewed yet
robotcode discover tests -ebtm "Owner:*" # tests without an owner
robotcode robot -i smoke -btm Issue:4409 # run the smoke tests for issue 4409

A pattern is one or more terms NAME:VALUE, combined like a tag pattern:

  • A test is matched by its entries, Name:Value, one for every line of every metadata value. A term matches when it matches one of the entries.
  • *, ? and […] work as in tag patterns: Issue:44*, *:4409 (4409 in any metadata), Owner:* (any Owner, also one without a value). For a literal [, write [[].
  • Case, spaces and underscores are ignored on both sides, so names compare the way Robot Framework compares metadata names: owner_team:core matches Owner Team: core.
  • AND, OR and NOT combine terms. As in Robot's tag patterns, AND binds before OR and NOT last: A OR B AND C means A OR (B AND C), and A NOT B NOT C matches when A matches and neither B nor C does. A pattern that starts with NOT matches every test that doesn't match the rest.
  • An operator is only a word of its own, in upper case with whitespace or the start or end of the pattern around it. So Issue:CORE-123, Issue:ANDROID-5 and Author:NORBERT stay literal, while Robot Framework 7.5 would read the same text as a tag pattern as Issue:C OR E-123 and so on.
  • To match an operator word inside a value, write it in lower case: Title:rock and roll matches Rock AND Roll.

Every line of a value is an entry of its own, so write several values of one name as continuation lines:

*** Test Cases ***
Transfer Money
[Metadata] Issue 4409
... 5769
Transfer 100 EUR

Issue:4409 and Issue:5769 both select this test. Several cells in one row ([Metadata] Issue 4409 5769) stay one value: Robot Framework joins them keeping the separator of the source, so they can't be told apart from spaces inside a value. Issue:4409 doesn't select such a test; Issue:4409* does.

  • Only a test's own [Metadata]. A suite's Metadata, including metadata given with --metadata, is not part of its tests' entries; discover metadata lists it in its own section.
  • The values as the model holds them. Variables are replaced only when a test runs, so robot and discover see ${BUILD} in [Metadata] Build ${BUILD}, while results sees the value the run used — as with tags.

A term without : (Issue) or an operator without a term on one side (Issue:4409 AND) makes the pattern invalid. The command fails with an error that names the pattern and doesn't run or list anything.

The selection is done by the pre-run modifiers robotcode.modifiers.ByTestMetadata and robotcode.modifiers.ExcludedByTestMetadata from the robotcode-modifiers package. They also work with plain robot; separate the pattern from the modifier name with ;, because the pattern contains ::

Terminal window
robot --prerunmodifier "robotcode.modifiers.ByTestMetadata;Issue:4409" tests

In robot.toml, a profile selects by metadata through pre-run-modifiers; RobotCode uses ; there on its own:

[profiles.issue-4409]
pre-run-modifiers = { "robotcode.modifiers.ByTestMetadata" = ["Issue:4409"] }

Given an invalid pattern, the modifiers report the error and select no test. Robot Framework would otherwise skip a modifier it cannot create and run every test.

--search and --search-regex are mutually exclusive (passing both is a usage error). They prune the discovered tree to tests matching the pattern; surviving tests keep their full ancestor chain so discover all --search Login still shows MyProject → Tests → Login → Test Login → Bad Password with sibling suites pruned.

The search applies across:

  • the test's name and full long name
  • the test's source path
  • the test's [Documentation], [Template] and [Timeout]
  • the test's tags (Robot's normalisation rules apply)
  • the test's [Metadata] names and values (Robot Framework 7.5+)
  • every keyword call's name, arguments and assigned variables inside the test body
  • FOR/WHILE/IF conditions, VAR/RETURN values, EXCEPT patterns, GROUP names
  • any ancestor suite's Documentation or Metadata — a hit on a suite-level field keeps every test underneath it
Flag Semantics
--search TEXT Case-insensitive substring match. No metacharacters; [ and * are literal.
--search-regex PATTERN Python regex, case-sensitive by default. Prefix with (?i) for case-insensitive matching.

Tag targets are matched with normalisation (lowercase, whitespace and underscores ignored), so --search "bug 1" matches tests tagged bug_1, Bug1, or BUG 1. All other targets are matched literally.

Terminal window
robotcode discover tests --search "Login"
robotcode discover all --search-regex 'Login.*(Bad|Good)'
robotcode discover tests --search-regex '(?i)checkout'
robotcode discover tags --search smoke
robotcode discover files --search "_keywords"

Matches are marked as inline code in the TEXT output so they're easy to scan. Structured output (JSON/TOML) is unchanged — searching only filters, it doesn't add markup.

--search works on these too:

  • tags filters the underlying tests first; tag keys whose tests are all dropped disappear from the dictionary. So discover tags --search Login is "what tags are involved in the Login subset".
  • files matches the filename / path itself — no body inspection. So discover files --search "_keywords" is "every file whose path contains _keywords".

An unparseable regex (e.g. [unclosed) yields a usage error pinpointing where the pattern failed to compile. The command exits non-zero before parsing any suite.

  • TEXT output is markdown. On a coloured TTY the markdown is rendered to themed ANSI via rich (and paged if longer than your terminal); on pipes or with --no-color the raw markdown goes through verbatim — paste it into a PR description, a Slack message, or feed it straight to an LLM.
  • Quote your globs. --suite "*.Login.*" and --test "Test ?" need quotes so the shell doesn't expand them against the local filesystem first.
  • -bl is for exact names, -t is for patterns. When you copy a test long-name out of another tool's output, -bl is usually what you want — no need to escape glob characters.
  • Pre-filter before piping into robot. robotcode -f json discover tests --include smoke | jq -r '.items[].longname' gives you a stable list of long names you can hand back to robotcode robot -bl ... for sharded CI runs.
  • Parse errors don't stop discovery. Files with syntax errors still appear in the tree; their problems land in the diagnostics field. Pipe through --format json and check .diagnostics for build-time gating.

When you set -f json (or -f json_indent for pretty-printed, or -f toml) between robotcode and the subcommand, every discover command emits structured data instead of the terminal-formatted text. This is what CI pipelines, scripts, editor integrations, and AI agents consume.

Terminal window
robotcode --format json discover all
robotcode --format json discover tests --include smoke
robotcode --format json discover tags --tests

A few rules hold across every subcommand:

  • camelCase keys. relSource, supportsParseInclude, robotVersionString, etc.
  • Optional fields are omitted, not null. A null literal never appears. If a field has no value (a test with no tags), the key is simply absent.
  • Empty array ≠ missing field. A field listed in the schema may be [] if the section "exists but is empty"; a field absent entirely means the section wasn't computed for this run. // [] in jq smooths over the difference.
  • Stability. Fields are appended over time, never renamed or removed in place. Existing consumers keep working.

Every subcommand that returns tests/tasks/suites uses the same TestItem schema:

{
"type": "test",
"id": "/abs/path/tests/login/test_login.robot;MyProject.Tests.Login.Test Login.Bad Password;14",
"name": "Bad Password",
"longname": "MyProject.Tests.Login.Test Login.Bad Password",
"lineno": 14,
"uri": "file:///abs/path/tests/login/test_login.robot",
"relSource": "tests/login/test_login.robot",
"source": "/abs/path/tests/login/test_login.robot",
"tags": ["regression", "smoke"],
"metadata": { "Author": "Hans Müller", "Issue": "4409" },
"range": {
"start": { "line": 13, "character": 0 },
"end": { "line": 13, "character": 0 }
},
"children": [ /* nested TestItems for type=suite/workspace */ ],
"error": "Parse error message (when this item failed to parse)",
"rpa": false
}

Field notes:

  • type is one of "workspace", "suite", "test", "task". workspace is the synthetic root that wraps the actual project directory.
  • id is a stable identifier (source;longname;lineno for tests/tasks; source;longname for suites). Editor integrations use this to track which TestItem corresponds to which on-disk artefact across reloads.
  • lineno is 1-based; range.start.line / range.end.line are 0-based (LSP convention). Both refer to the same line of source.
  • uri is a file:// URI of the source file — same format the Language Server Protocol uses.
  • tags are normalised (Bug 1, bug_1, Bug1 all come through as "bug1") and only present when the item has at least one tag.
  • metadata is the [Metadata] of a test or task (Robot Framework 7.5+) as a name → value object. Names keep the spelling from the source file and values come through verbatim, the lines of a multi-line value joined with \n. A [Metadata] setting with nothing after it is no metadata and is left out. The key is absent when the test has no metadata and on Robot Framework versions older than 7.5; suite and workspace items never carry it. The --show-metadata flag is render-only for TEXT mode — it doesn't affect the JSON.
  • description is declared in the schema, but discover never sets it, so the key does not appear in the output. A test's [Documentation] is not part of the item.
  • children is the nested-tree field — present on the workspace item and on the suite items of discover all ([] when a suite is empty), absent on test / task items. discover suites lists its suites without it.
  • range follows the LSP Range shape so editors can highlight the source span.

All four subcommands wrap their items in a ResultItem:

{
"items": [ /* TestItem(s) */ ],
"diagnostics": { /* see below */ },
"filtersApplied": { "search": "Login" },
"supportsParseInclude": true
}

Per subcommand:

  • all — items has exactly one entry: the workspace root, whose children is the suite tree.
  • tests / tasks — items is a flat list of test / task entries respectively.
  • suites — items is a flat list of suite entries (no children — drill into the source files separately if you want per-suite tests).

filtersApplied is optional and absent when unused. diagnostics is always present — an empty object {} when nothing fired.

supportsParseInclude is true when the project's Robot Framework version supports the --parseinclude (-I) option (RF ≥ 6.1). It is a property of the discovery environment, not of any single item, so it lives on the result envelope. Editor integrations use it to decide whether to pass -I when running a subset of tests. Omitted (treat as false) for RF < 6.1.

{
"tags": {
"bug1": [ /* TestItems carrying this tag */ ],
"smoke": [ /* … */ ]
},
"filtersApplied": { "search": "Login" }
}

Field notes:

  • tags is always an object — possibly empty {} if no tag survived the filter chain.
  • Keys are tag names; normalised by default, original spellings preserved with --not-normalized.
  • Each value is an array of TestItem objects (the tests / tasks carrying that tag).
  • A test with n tags appears in n entries.
{
"metadata": {
"Issue": {
"4409": [ /* TestItems of the tests and tasks with this value */ ],
"4410": [ /* … */ ]
}
},
"suiteMetadata": {
"Version": { "1.2": [ /* suite TestItems */ ] }
}
}

Field notes:

  • metadata holds the test and task metadata (Robot Framework 7.5+), suiteMetadata the suite metadata. Both are always objects, possibly empty {}; on Robot Framework versions older than 7.5, metadata is always {}.
  • Keys are names in the first spelling found. Below each name, every value — one per line of a multi-line value — maps to the items that have it; an item is listed once per value.
  • Items are TestItems. Suite items carry no metadata field. The JSON doesn't depend on --values, --tests, --tasks or --suites.
  • Every test for an issue: robotcode -f json discover metadata | jq -r '.metadata.Issue["4409"][]?.longname'.
[
"tests/login/test_login.robot",
"tests/login/resources/login_keywords.resource",
"tests/checkout/test_checkout.robot"
]

files is a plain JSON array of path strings — relative to the project root by default, absolute with --full-paths. No wrapper object, no per-file metadata. If you need richer info, use discover all and walk the tree.

{
"robotVersionString": "7.5",
"robotEnv": {
"ROBOT_OPTIONS": "--include smoke"
},
"robotcodeVersionString": "2.7.0",
"pythonVersionString": "3.13.12",
"executable": ".venv/bin/python",
"machine": "x86_64",
"processor": "x86_64",
"platform": "linux",
"system": "Linux",
"systemVersion": "#1 SMP …"
}

Field notes:

  • robotEnv echoes back any of ROBOT_OPTIONS, ROBOT_SYSLOG_FILE, ROBOT_SYSLOG_LEVEL, ROBOT_INTERNAL_TRACES that were set, and is absent (or {}) when none of them are.

When parsing a file produces a warning or error (deprecated syntax, duplicate test names, unparseable file …), discover all/tests/tasks/suites reports it as an LSP-style diagnostic keyed by the source's file:// URI:

"diagnostics": {
"file:///abs/path/parse_error.robot": [
{
"range": { "start": { "line": 13, "character": 0 },
"end": { "line": 13, "character": 0 } },
"message": "Singular section headers like '*** Keyword ***' are deprecated. Use plural format like '*** Keywords ***' instead.",
"severity": 2,
"code": "discover",
"source": "robotcode.discover"
}
]
}

Field notes:

  • severity follows LSP's enum: 1 = Error, 2 = Warning, 3 = Information, 4 = Hint.
  • A file whose parser failed entirely still contributes whatever tests Robot could recover; the unrecoverable parts come through here.
  • diagnostics is an empty object {} when nothing fired.

Every subcommand that accepts search flags includes a filtersApplied field when one was set:

"filtersApplied": {
"search": "Login"
}
"filtersApplied": {
"search-regex": "(?i)login"
}

The Robot-native filters (--include, --exclude, --suite, --test, -bl, -ebl) are not echoed here — they're handed straight to Robot Framework's filter pipeline and the effect is visible in the surviving items set.

A grab-bag of jq-based recipes. Pin -f json between robotcode and the subcommand to lock the format.

Terminal window
# List of suite long names → fed into a CI matrix
robotcode --format json discover suites \
| jq -r '.items[].longname'
# Long names of every smoke test
robotcode --format json discover tests --include smoke \
| jq -r '.items[].longname'
Terminal window
robotcode --format json discover all \
| jq -e '(.diagnostics // {}) | map(.[] | select(.severity == 1)) | length == 0'

List every file the test pipeline would touch

Section titled “List every file the test pipeline would touch”
Terminal window
robotcode --format json discover files | jq -r '.[]'
Terminal window
# tag → count of tests carrying it
robotcode --format json discover tags \
| jq '.tags | map_values(length)'
# Tags with fewer than 3 tests (candidates for cleanup)
robotcode --format json discover tags \
| jq '.tags | to_entries | map(select(.value | length < 3)) | from_entries'
Terminal window
robotcode --format json discover info | jq -r .robotVersionString
Terminal window
# Every test whose body calls `Open Browser`
robotcode --format json discover tests --search "Open Browser" \
| jq -r '.items[].longname'

For the per-flag reference, see the auto-generated CLI reference.

Released under the Apache-2.0. Copyright © 2022-present Daniel Biehl