-
-
Notifications
You must be signed in to change notification settings - Fork 36.5k
test_runner: extend tag filter with boolean expression DSL #63054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -491,8 +491,8 @@ added: | |
|
|
||
| Tags annotate tests and suites with arbitrary string labels. The | ||
| [`--experimental-test-tag-filter`][] CLI flag (or the `testTagFilters` | ||
| option on [`run()`][]) selects tests whose tag set contains every | ||
| provided filter value. | ||
| option on [`run()`][]) selects tests by a boolean expression over those | ||
| labels. | ||
|
|
||
| Tags are an alternative to encoding metadata into test names. They are | ||
| useful for cross-cutting axes such as subsystem, speed bucket, flakiness, | ||
|
|
@@ -525,37 +525,89 @@ describe('database', { tags: ['db'] }, () => { | |
| }); | ||
| ``` | ||
|
|
||
| Tag values must be non-empty strings. Tags are matched case-insensitively; | ||
| the canonical form is lowercase. Duplicates within a single `tags` array | ||
| are collapsed on the lowercased form, preserving the first-seen | ||
| declaration order. | ||
| Tag values must be non-empty strings that contain no whitespace, no | ||
| operator characters (`& | ! ( ) *`), and are not the reserved words | ||
| `'and'`, `'or'`, or `'not'` in any casing. Tags are matched | ||
| case-insensitively; the canonical form is lowercase. Duplicates within a | ||
| single `tags` array are collapsed on the lowercased form, preserving the | ||
| first-seen declaration order. | ||
|
|
||
| Hooks (`before`, `after`, `beforeEach`, `afterEach`) do not declare their | ||
| own tags. They run as part of their owning suite, which carries the | ||
| suite's tags. | ||
|
|
||
| ### Filtering by tag | ||
| ### Filtering syntax | ||
|
|
||
| Each [`--experimental-test-tag-filter`][] value is a literal tag name. A | ||
| test runs only when its tag set contains that name. The flag may be | ||
| specified more than once; tests must match **every** filter to run. The | ||
| same applies to the `testTagFilters` array on [`run()`][]. Filters are | ||
| case-insensitive and AND'd with [`--test-name-pattern`][], | ||
| [`--test-skip-pattern`][], and `.only` filtering. | ||
| The filter expression supports: | ||
|
|
||
| Untagged tests are excluded under any non-empty filter, since the filter | ||
| requires the tag to be present. | ||
| * Identifiers—any non-whitespace, non-operator characters. A literal | ||
| identifier matches a tag of the same value (case-insensitive). | ||
| * `*` wildcards inside an identifier match any sequence of characters. | ||
| A bare `*` matches any tagged test. | ||
| * Boolean operators with two equivalent forms: | ||
| * `and` / `&&` | ||
| * `or` / `||` | ||
| * `not` / `!` | ||
| * Parentheses for grouping. | ||
|
Comment on lines
+543
to
+551
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not too keen on creating a new language for that. Let's either use JS (e.g.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably better if we have this conversation at: #63054 (comment)? Having it in JS means we have a problem (can't serialize functions, so |
||
|
|
||
| ### Reading tags from inside a test | ||
| The word forms (`and`, `or`, `not`) require whitespace separation; the | ||
| punctuation forms do not. | ||
|
|
||
| #### Operator precedence | ||
|
|
||
| The expression is evaluated with the standard precedence | ||
| `not > and > or`. Binary operators are left-associative. | ||
|
|
||
| | Expression | Equivalent grouping | | ||
| | -------------- | ------------------- | | ||
| | `a or b and c` | `a or (b and c)` | | ||
| | `not a and b` | `(not a) and b` | | ||
|
|
||
| Use parentheses to override: | ||
|
|
||
| | Expression | Selects | | ||
| | ------------------------------ | ------------------------------------------ | | ||
| | `(unit or smoke) and not slow` | unit-or-smoke tests that are not also slow | | ||
| | `db && !flaky` | db tests that are not flaky | | ||
| | `*` | every tagged test | | ||
|
|
||
| #### Untagged tests | ||
|
|
||
| Untagged tests behave as if they have an empty tag set. As a result: | ||
|
|
||
| | Filter expression | Untagged test | Why | | ||
| | ------------------------ | ------------- | ------------------------------------------------ | | ||
| | `db` | excluded | Positive match against an empty tag set is false | | ||
| | `*` | excluded | The bare wildcard requires at least one tag | | ||
| | `db or unit` | excluded | Both branches are false against an empty tag set | | ||
| | `not flaky` | included | Negation against an empty tag set is true | | ||
| | `not flaky and not slow` | included | Both negations are true against an empty tag set | | ||
| | `db or not flaky` | included | The negated branch is true | | ||
|
|
||
| For example, `--experimental-test-tag-filter='not flaky'` runs every test | ||
| that is not tagged `flaky`, including all untagged tests. | ||
|
|
||
| #### Composing multiple filters | ||
|
|
||
| [`--experimental-test-tag-filter`][] may be specified more than once on the | ||
| command line. Multiple expressions compose by AND—a test must satisfy | ||
| every expression to run. The same applies to passing an array to | ||
| `testTagFilters` on [`run()`][]. The tag filter is also AND'd with | ||
| [`--test-name-pattern`][], [`--test-skip-pattern`][], and `.only` | ||
| filtering. | ||
|
|
||
| #### Reading tags from inside a test | ||
|
|
||
| The [`TestContext`][] object exposes the test's tags as a frozen array | ||
| through [`context.tags`][], so tests can branch on their own metadata. | ||
|
|
||
| ### Errors | ||
| #### Errors | ||
|
|
||
| A tag value that violates the validation rules above throws | ||
| `ERR_INVALID_ARG_VALUE` at the registration site, before any test runs. | ||
| A non-array `tags` value throws `ERR_INVALID_ARG_TYPE`. | ||
| A non-array `tags` value throws `ERR_INVALID_ARG_TYPE`. A malformed | ||
| filter expression on the CLI causes the test runner to exit with a | ||
| non-zero status before running any test files. | ||
|
|
||
| ## Extraneous asynchronous activity | ||
|
|
||
|
|
@@ -830,7 +882,7 @@ test runner functionality: | |
|
|
||
| * `--test` - Prevented to avoid recursive test execution | ||
| * `--experimental-test-coverage` - Managed by the test runner | ||
| * `--experimental-test-tag-filter` - Filter values are validated by the parent | ||
| * `--experimental-test-tag-filter` - Filter expressions are validated by the parent | ||
| process and re-emitted to child processes | ||
| * `--watch` - Watch mode is handled at the parent level | ||
| * `--experimental-default-config-file` - Config file loading is handled by the parent | ||
|
|
@@ -1746,10 +1798,11 @@ changes: | |
| For each test that is executed, any corresponding test hooks, such as | ||
| `beforeEach()`, are also run. | ||
| **Default:** `undefined`. | ||
| * `testTagFilters` {string|string\[]} A tag name, or an array of tag names, | ||
| used to filter tests by their declared tags. Tests must contain every | ||
| listed tag to run. Equivalent to passing [`--experimental-test-tag-filter`][] | ||
| on the command line. See [Test tags][]. **Default:** `undefined`. | ||
| * `testTagFilters` {string|string\[]} A boolean expression, or an array of | ||
| boolean expressions, used to filter tests by their declared tags. | ||
| Multiple expressions compose by AND. Equivalent to passing | ||
| [`--experimental-test-tag-filter`][] on the command line. See | ||
| [Test tags][]. **Default:** `undefined`. | ||
| * `timeout` {number} A number of milliseconds the test execution will | ||
| fail after. | ||
| If unspecified, subtests inherit this value from their parent. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure adding quotes helps here, to me at least that seems more confusing (e.g. git docs do not do that: https://git-scm.com/docs/git-log)