| JSON Reporter Protocol |
| ====================== |
| |
| The test runner supports a JSON reporter which provides a machine-readable |
| representation of the test runner's progress. This reporter is intended for use |
| by IDEs and other tools to present a custom view of the test runner's operation |
| without needing to parse output intended for humans. |
| |
| Note that the test runner is highly asynchronous, and users of this protocol |
| shouldn't make assumptions about the ordering of events beyond what's explicitly |
| specified in this document. It's possible for events from multiple tests to be |
| intertwined, for a single test to emit an error after it completed successfully, |
| and so on. |
| |
| ## Usage |
| |
| Pass the `--reporter json` command-line flag to the test runner to activate the |
| JSON reporter. |
| |
| dart test --reporter json <path-to-test-file> |
| |
| You may also use the `--file-reporter` option to enable the JSON reporter output |
| to a file, in addition to another reporter writing to stdout. |
| |
| dart test --file-reporter json:reports/tests.json <path-to-test-file> |
| |
| The JSON stream will be emitted via standard output. It will be a stream of JSON |
| objects, separated by newlines. |
| |
| See `json_reporter.schema.json` for a formal description of the protocol schema. |
| See `test/runner/json_reporter_test.dart` for some sample output. |
| |
| ## Compatibility |
| |
| The protocol emitted by the JSON reporter is considered part of the public API |
| of the `test` package, and is subject to its [semantic versioning][semver] |
| restrictions. In particular: |
| |
| [semver]: https://dart.dev/tools/pub/versioning#semantic-versions |
| |
| * No new feature will be added to the protocol without increasing the test |
| package's minor version number. |
| |
| * No breaking change will be made to the protocol without increasing the test |
| package's major version number. |
| |
| The following changes are not considered breaking. This is not necessarily a |
| comprehensive list. |
| |
| * Adding a new attribute to an existing object. |
| |
| * Adding a new type of any object with a `type` parameter. |
| |
| * Adding new test state values. |
| |
| ## Reading this Document |
| |
| Each major type of JSON object used by the protocol is described by a class. |
| Classes have names which are referred to in this document, but are not used as |
| part of the protocol. Classes have typed attributes, which refer to the types |
| and names of attributes in the JSON objects. If an attribute's type is another |
| class, that refers to a nested object. The special type `List<...>` indicates a |
| JSON list of the given type. |
| |
| Classes can "extend" one another, meaning that the subclass has all the |
| attributes of the superclass. Concrete subclasses can be distinguished by the |
| specific value of their `type` attribute. Classes may be abstract, indicating |
| that only their subclasses will ever be used. |
| |
| ## Events |
| |
| ### Event |
| |
| ``` |
| abstract class Event { |
| // The type of the event. |
| // |
| // This is always one of the subclass types listed below. |
| String type; |
| |
| // The time (in milliseconds) that has elapsed since the test runner started. |
| int time; |
| } |
| ``` |
| |
| This is the root class of the protocol. All root-level objects emitted by the |
| JSON reporter will be subclasses of `Event`. |
| |
| ### StartEvent |
| |
| ``` |
| class StartEvent extends Event { |
| String type = "start"; |
| |
| // The version of the JSON reporter protocol being used. |
| // |
| // This is a semantic version, but it reflects only the version of the |
| // protocol—it's not identical to the version of the test runner itself. |
| String protocolVersion; |
| |
| // The version of the test runner being used. |
| // |
| // This is null if for some reason the version couldn't be loaded. |
| String? runnerVersion; |
| |
| // The pid of the VM process running the tests. |
| int pid; |
| } |
| ``` |
| |
| A single start event is emitted before any other events. It indicates that the |
| test runner has started running. |
| |
| ### AllSuitesEvent |
| |
| ``` |
| class AllSuitesEvent extends Event { |
| String type = "allSuites"; |
| |
| /// The total number of suites that will be loaded. |
| int count; |
| } |
| ``` |
| |
| A single suite count event is emitted once the test runner knows the total |
| number of suites that will be loaded over the course of the test run. Because |
| this is determined asynchronously, its position relative to other events (except |
| `StartEvent`) is not guaranteed. |
| |
| ### SuiteEvent |
| |
| ``` |
| class SuiteEvent extends Event { |
| String type = "suite"; |
| |
| /// Metadata about the suite. |
| Suite suite; |
| } |
| ``` |
| |
| A suite event is emitted before any `GroupEvent`s for groups in a given test |
| suite. This is the only event that contains the full metadata about a suite; |
| future events will refer to the suite by its opaque ID. |
| |
| ### DebugEvent |
| |
| ``` |
| class DebugEvent extends Event { |
| String type = "debug"; |
| |
| /// The suite for which debug information is reported. |
| int suiteID; |
| |
| /// The HTTP URL for the Dart Observatory, or `null` if the Observatory isn't |
| /// available for this suite. |
| String? observatory; |
| |
| /// The HTTP URL for the remote debugger for this suite's host page, or `null` |
| /// if no remote debugger is available for this suite. |
| String? remoteDebugger; |
| } |
| ``` |
| |
| A debug event is emitted after (although not necessarily directly after) a |
| `SuiteEvent`, and includes information about how to debug that suite. It's only |
| emitted if the `--debug` flag is passed to the test runner. |
| |
| Note that the `remoteDebugger` URL refers to a remote debugger whose protocol |
| may differ based on the browser the suite is running on. You can tell which |
| protocol is in use by the `Suite.platform` field for the suite with the given |
| ID. Since the same browser instance is used for multiple suites, different |
| suites may have the same `host` URL, although only one suite at a time will be |
| active when `--pause-after-load` is passed. |
| |
| ### GroupEvent |
| |
| ``` |
| class GroupEvent extends Event { |
| String type = "group"; |
| |
| /// Metadata about the group. |
| Group group; |
| } |
| ``` |
| |
| A group event is emitted before any `TestStartEvent`s for tests in a given |
| group. This is the only event that contains the full metadata about a group; |
| future events will refer to the group by its opaque ID. |
| |
| This includes the implicit group at the root of each suite, which has a `null` |
| name. However, it does *not* include implicit groups for the virtual suites |
| generated to represent loading test files. |
| |
| If the group is skipped, a single `TestStartEvent` will be emitted for a test |
| within the group, followed by a `TestDoneEvent` marked as skipped. The |
| `group.metadata` field should *not* be used for determining whether a group is |
| skipped. |
| |
| ### TestStartEvent |
| |
| ``` |
| class TestStartEvent extends Event { |
| String type = "testStart"; |
| |
| // Metadata about the test that started. |
| Test test; |
| } |
| ``` |
| |
| An event emitted when a test begins running. This is the only event that |
| contains the full metadata about a test; future events will refer to the test by |
| its opaque ID. |
| |
| If the test is skipped, its `TestDoneEvent` will have `skipped` set to `true`. |
| The `test.metadata` should *not* be used for determining whether a test is |
| skipped. |
| |
| ### MessageEvent |
| |
| ``` |
| class MessageEvent extends Event { |
| String type = "print"; |
| |
| // The ID of the test that printed a message. |
| int testID; |
| |
| // The type of message being printed. |
| String messageType; |
| |
| // The message that was printed. |
| String message; |
| } |
| ``` |
| |
| A `MessageEvent` indicates that a test emitted a message that should be |
| displayed to the user. The `messageType` field indicates the precise type of |
| this message. Different message types should be visually distinguishable. |
| |
| A message of type "print" comes from a user explicitly calling `print()`. |
| |
| A message of type "skip" comes from a test, or a section of a test, being |
| skipped. A skip message shouldn't be considered the authoritative source that a |
| test was skipped; the `TestDoneEvent.skipped` field should be used instead. |
| |
| ### ErrorEvent |
| |
| ``` |
| class ErrorEvent extends Event { |
| String type = "error"; |
| |
| // The ID of the test that experienced the error. |
| int testID; |
| |
| // The result of calling toString() on the error object. |
| String error; |
| |
| // The error's stack trace, in the stack_trace package format. |
| String stackTrace; |
| |
| // Whether the error was a TestFailure. |
| bool isFailure; |
| } |
| ``` |
| |
| A `ErrorEvent` indicates that a test encountered an uncaught error. Note |
| that this may happen even after the test has completed, in which case it should |
| be considered to have failed. |
| |
| If a test is asynchronous, it may encounter multiple errors, which will result |
| in multiple `ErrorEvent`s. |
| |
| ### TestDoneEvent |
| |
| ``` |
| class TestDoneEvent extends Event { |
| String type = "testDone"; |
| |
| // The ID of the test that completed. |
| int testID; |
| |
| // The result of the test. |
| String result; |
| |
| // Whether the test's result should be hidden. |
| bool hidden; |
| |
| // Whether the test (or some part of it) was skipped. |
| bool skipped; |
| } |
| ``` |
| |
| An event emitted when a test completes. The `result` attribute indicates the |
| result of the test: |
| |
| * `"success"` if the test had no errors. |
| |
| * `"failure"` if the test had a `TestFailure` but no other errors. |
| |
| * `"error"` if the test had an error other than a `TestFailure`. |
| |
| If the test encountered an error, the `TestDoneEvent` will be emitted after the |
| corresponding `ErrorEvent`. |
| |
| The `hidden` attribute indicates that the test's result should be hidden and not |
| counted towards the total number of tests run for the suite. This is true for |
| virtual tests created for loading test suites, `setUpAll()`, and |
| `tearDownAll()`. Only successful tests will be hidden. |
| |
| Note that it's possible for a test to encounter an error after completing. In |
| that case, it should be considered to have failed, but no additional |
| `TestDoneEvent` will be emitted. If a previously-hidden test encounters an |
| error after completing, it should be made visible. |
| |
| ### DoneEvent |
| |
| ``` |
| class DoneEvent extends Event { |
| String type = "done"; |
| |
| // Whether all tests succeeded (or were skipped). |
| // |
| // Will be `null` if the test runner was close before all tests completed |
| // running. |
| bool? success; |
| } |
| ``` |
| |
| An event indicating the result of the entire test run. This will be the final |
| event emitted by the reporter. |
| |
| ## Other Classes |
| |
| ### Test |
| |
| ``` |
| class Test { |
| // An opaque ID for the test. |
| int id; |
| |
| // The name of the test, including prefixes from any containing groups. |
| String name; |
| |
| // The ID of the suite containing this test. |
| int suiteID; |
| |
| // The IDs of groups containing this test, in order from outermost to |
| // innermost. |
| List<int> groupIDs; |
| |
| // The (1-based) line on which the test was defined, or `null`. |
| int? line; |
| |
| // The (1-based) column on which the test was defined, or `null`. |
| int? column; |
| |
| // The URL for the file in which the test was defined, or `null`. |
| String? url; |
| |
| // The (1-based) line in the original test suite from which the test |
| // originated. |
| // |
| // Will only be present if `root_url` is different from `url`. |
| int? root_line; |
| |
| // The (1-based) line on in the original test suite from which the test |
| // originated. |
| // |
| // Will only be present if `root_url` is different from `url`. |
| int? root_column; |
| |
| // The URL for the original test suite in which the test was defined. |
| // |
| // Will only be present if different from `url`. |
| String? root_url; |
| |
| // This field is deprecated and should not be used. |
| Metadata metadata; |
| } |
| ``` |
| |
| A single test case. The test's ID is unique in the context of this test run. |
| It's used elsewhere in the protocol to refer to this test without including its |
| full representation. |
| |
| Most tests will have at least one group ID, representing the implicit root |
| group. However, some may not; these should be treated as having no group |
| metadata. |
| |
| The `line`, `column`, and `url` fields indicate the location the `test()` |
| function was called to create this test. They're treated as a unit: they'll |
| either all be `null` or they'll all be non-`null`. The URL is always absolute, |
| and may be a `package:` URL. |
| |
| ### Suite |
| |
| ``` |
| class Suite { |
| // An opaque ID for the group. |
| int id; |
| |
| // The platform on which the suite is running. |
| String platform; |
| |
| // The path to the suite's file, or `null` if that path is unknown. |
| String? path; |
| } |
| ``` |
| |
| A test suite corresponding to a loaded test file. The suite's ID is unique in |
| the context of this test run. It's used elsewhere in the protocol to refer to |
| this suite without including its full representation. |
| |
| A suite's platform is one of the platforms that can be passed to the |
| `--platform` option, or `null` if there is no platform (for example if the file |
| doesn't exist at all). Its path is either absolute or relative to the root of |
| the current package. |
| |
| ### Group |
| |
| ``` |
| class Group { |
| // An opaque ID for the group. |
| int id; |
| |
| // The name of the group, including prefixes from any containing groups. |
| String name; |
| |
| // The ID of the suite containing this group. |
| int suiteID; |
| |
| // The ID of the group's parent group, unless it's the root group. |
| int? parentID; |
| |
| // The number of tests (recursively) within this group. |
| int testCount; |
| |
| // The (1-based) line on which the group was defined, or `null`. |
| int? line; |
| |
| // The (1-based) column on which the group was defined, or `null`. |
| int? column; |
| |
| // The URL for the file in which the group was defined, or `null`. |
| String? url; |
| |
| // This field is deprecated and should not be used. |
| Metadata metadata; |
| } |
| ``` |
| |
| A group containing test cases. The group's ID is unique in the context of this |
| test run. It's used elsewhere in the protocol to refer to this group without |
| including its full representation. |
| |
| The implicit group at the root of each test suite has `null` `name` and |
| `parentID` attributes. |
| |
| The `line`, `column`, and `url` fields indicate the location the `group()` |
| function was called to create this group. They're treated as a unit: they'll |
| either all be `null` or they'll all be non-`null`. The URL is always absolute, |
| and may be a `package:` URL. |
| |
| ### Metadata |
| |
| ``` |
| class Metadata { |
| bool skip; |
| |
| // The reason the tests was skipped, or `null` if it wasn't skipped. |
| String? skipReason; |
| } |
| ``` |
| |
| The metadata class is deprecated and should not be used. |
| |
| ## Remote Debugger APIs |
| |
| When running browser tests with `--pause-after-load`, the test package embeds a |
| few APIs in the JavaScript context of the host page. These allow tools to |
| control the debugging process in the same way a user might do from the command |
| line. They can be accessed by connecting to the remote debugger using the |
| [`DebugEvent.remoteDebugger`](#DebugEvent) URL. |
| |
| All APIs are defined as methods on the top-level `dartTest` object. The |
| following methods are available: |
| |
| ### `resume()` |
| |
| Calling `resume()` when the test runner is paused causes it to resume running |
| tests. If the test runner is not paused, it won't do anything. When |
| `--pause-after-load` is passed, the test runner will pause after loading each |
| suite but before any tests are run. |
| |
| This gives external tools a chance to use the remote debugger protocol to set |
| breakpoints before tests have begun executing. They can start the test runner |
| with `--pause-after-load`, connect to the remote debugger using the |
| [`DebugEvent.remoteDebugger`](#DebugEvent) URL, set breakpoints, then call |
| `dartTest.resume()` in the host frame when they're finished. |
| |
| ### `restartCurrent()` |
| |
| Calling `restartCurrent()` when the test runner is running a test causes it to |
| re-run that test once it completes its current run. It's intended to be called |
| when the browser is paused, as at a breakpoint. |