eslint-plugin-test-governance 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -1
- package/README.md +65 -25
- package/dist/rules/marker-matches-state.js +1 -1
- package/dist/rules/no-orphaned-marker.js +1 -1
- package/dist/rules/require-ticket.d.ts +1 -1
- package/dist/rules/require-ticket.js +24 -1
- package/dist/utils/analyze.d.ts +21 -2
- package/dist/utils/analyze.js +358 -34
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +6 -0
- package/dist/utils/frameworks.d.ts +9 -0
- package/dist/utils/frameworks.js +2 -0
- package/dist/utils/options.d.ts +17 -0
- package/dist/utils/options.js +58 -14
- package/dist/utils/schema.js +6 -0
- package/dist/utils/tickets.d.ts +18 -0
- package/dist/utils/tickets.js +145 -8
- package/docs/rules/require-ticket.md +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,49 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.11.0] - 2026-11-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Ticket presets for six more trackers: `openproject` (classic `#4821` work package IDs, per-project IDs
|
|
14
|
+
such as `AGILE-1`, and work package links once `host` is set), `bugzilla` (bug numbers, and
|
|
15
|
+
`show_bug.cgi?id=4821` links once `host` is set), `phabricator` (task monograms such as `T4821`, and
|
|
16
|
+
`/T4821` links once `host` is set; Phorge shares the format), `rally` (formatted IDs `US4821`, `DE4821`,
|
|
17
|
+
`TA4821`, `TC4821`), `servicenow` (record numbers such as `STRY0010001` for the `prefixes` you list) and
|
|
18
|
+
`plane` (work-item IDs such as `WEB-142`, and work-item links on `app.plane.so` or a `host`). The
|
|
19
|
+
`rally` preset rejects zero-padded IDs, which is how MITRE ATT&CK writes its technique IDs (`TA0006`).
|
|
20
|
+
- Tickets from test annotations: with `annotations` (default `{ type: 'issue' }`), a valid ticket in
|
|
21
|
+
Playwright's `annotation` of that type or Vitest's `meta` key of that name satisfies the ticket
|
|
22
|
+
requirement, on tests and describes; an invalid one is reported, and `annotations: false` turns it
|
|
23
|
+
off. Comments stay the form that works everywhere; Jest, Mocha, Jasmine, node:test and Cypress have
|
|
24
|
+
no annotations.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- A `host` value with a scheme, port or path, such as `https://jira.example.com` or `tracker.io:8080`, is
|
|
29
|
+
now a configuration error on every preset that takes `host`, not only on `notion`, `redmine` and
|
|
30
|
+
`youtrack`. Such a value never matched a URL hostname, so it already rejected every link silently.
|
|
31
|
+
|
|
32
|
+
## [0.10.1] - 2026-10-31
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Options that belong to another preset, such as `prefixes` on `trello`, are reported even when a value next
|
|
37
|
+
to them is malformed, so one run shows both the misplaced option and the bad value.
|
|
38
|
+
- A placeholder with several `*` characters, such as `*a*a*a*a*b`, matches in one pass instead of
|
|
39
|
+
backtracking on a long ticket.
|
|
40
|
+
- A details object written as a literal with a spread or a computed key, `{ ...base, tag: '@new' }` or
|
|
41
|
+
`{ ['tag']: '@new' }`, keeps its tags and options; a key or spread that can't be read, such as an object
|
|
42
|
+
imported from another file, is reported with `reportUnreadableTags` instead of being ignored.
|
|
43
|
+
- A tag read from an array element, `TAGS[1]`, or from a constant inside a template, `` `${TAG}` ``, is
|
|
44
|
+
checked like the constant itself.
|
|
45
|
+
- A runtime skip method kept in a local variable, `const { skip } = t`, `const s = testInfo.skip` or
|
|
46
|
+
`const p = pending`, is checked like the method called on the context, in every framework.
|
|
47
|
+
- `node:test`'s `test.describe` and `test.it` are read after `import { test } from 'node:test'`, as they were
|
|
48
|
+
after `require()` or a default import. A state member kept in a variable, `const s = it.skip` or
|
|
49
|
+
`const { skip } = it`, is checked like `it.skip(...)` in every framework. The same holds for a skip called
|
|
50
|
+
on a required module used inline, `require('node:test').test.skip(...)`.
|
|
51
|
+
|
|
9
52
|
## [0.10.0] - 2026-10-30
|
|
10
53
|
|
|
11
54
|
### Added
|
|
@@ -396,7 +439,9 @@ First release.
|
|
|
396
439
|
- Support for ESLint 9 and 10 flat config, JavaScript and TypeScript test files, `.extend()`
|
|
397
440
|
fixtures, describe blocks, runtime skips and tags in titles or the `tag` option.
|
|
398
441
|
|
|
399
|
-
[Unreleased]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.
|
|
442
|
+
[Unreleased]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.11.0...HEAD
|
|
443
|
+
[0.11.0]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.10.1...v0.11.0
|
|
444
|
+
[0.10.1]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.10.0...v0.10.1
|
|
400
445
|
[0.10.0]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.9.0...v0.10.0
|
|
401
446
|
[0.9.0]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.8.1...v0.9.0
|
|
402
447
|
[0.8.1]: https://github.com/Huldoser/eslint-plugin-test-governance/compare/v0.8.0...v0.8.1
|
package/README.md
CHANGED
|
@@ -242,6 +242,7 @@ and markers. To turn a rule off, set it to `'off'` as usual.
|
|
|
242
242
|
| `allowBlankLine` | `boolean` | `false` | Let blank lines separate a marker from its test. |
|
|
243
243
|
| `allowNotes` | `boolean` | `false` | Allow free text after the tickets, e.g. `// SKIP: TRADE-123 flaky on CI`. Applies to markers and to `FIXME` / `TODO` comments. |
|
|
244
244
|
| `comments` | `false \| { keywords?: string[] }` | `{ keywords: ['FIXME', 'TODO'] }` | Comment keywords that must start with a ticket anywhere in a test file, e.g. `// TODO: TRADE-123`. Your keywords replace the default ones. `false` allows free-form comments. |
|
|
245
|
+
| `annotations` | `false \| { type?: string }` | `{ type: 'issue' }` | Accept the ticket from a test annotation as well: Playwright's `annotation` of the given type or Vitest's `meta` key of that name. One valid annotation covers every state the test is in, and a describe's covers its tests. See [Tickets from annotations](#tickets-from-annotations). |
|
|
245
246
|
| `testFunctions` | `string[]` | `['test']` | Names treated as the framework's `test`. Jest and Vitest default to `['test', 'it']`, Mocha, Cypress and WebdriverIO to `['it', 'specify', 'test']`, and Jasmine to `['it']`. Your list replaces the default one, so include those names if you still need them. See below. |
|
|
246
247
|
| `reportUnreadableTags` | `boolean` | `false` | When a tag state is on, report titles that aren't static text, and tags or details that can't be read in the file, such as ones imported from another module, since their tags can't be checked. |
|
|
247
248
|
|
|
@@ -274,37 +275,48 @@ a `customStates` entry.
|
|
|
274
275
|
|
|
275
276
|
### Ticket presets
|
|
276
277
|
|
|
277
|
-
| Preset | Accepts
|
|
278
|
-
| -------------- |
|
|
279
|
-
| `any` | `PROJ-123`, `#123`, `owner/repo#123`, any http(s) URL
|
|
280
|
-
| `jira` | `TRADE-123`, `https://<host>/browse/TRADE-123` (also under a path, as in `/jira/browse/TRADE-123`), board URLs with `selectedIssue=TRADE-123`
|
|
281
|
-
| `github` | `#123`, `owner/repo#123`, `https://github.com/owner/repo/issues/123` (or `/pull/`)
|
|
282
|
-
| `gitlab` | `#123`, `group/project#123`, `https://gitlab.com/group/project/-/issues/123`
|
|
283
|
-
| `linear` | `RISK-123`, `https://linear.app/<workspace>/issue/RISK-123/...`
|
|
284
|
-
| `azure-devops` | `AB#123`, `#123`, `https://dev.azure.com/<org>/<project>/_workitems/edit/123`
|
|
285
|
-
| `trello` | card links, `https://trello.com/c/nqPiDKmw` with or without the `/9-title` after it
|
|
286
|
-
| `notion` | Unique IDs such as `TASK-123`, `https://www.notion.so/TASK-123`, page links that end in the page ID (also `https://app.notion.com/p/...`), database links with `?p=<page ID>`
|
|
287
|
-
| `asana` | task links, `https://app.asana.com/0/<project>/<task>` and the newer `https://app.asana.com/1/<workspace>/.../task/<task>`
|
|
288
|
-
| `clickup` | `CU-86abc123`, `#86abc123`, custom IDs such as `DEV-123`, `https://app.clickup.com/t/86abc123` or `/t/<workspace>/DEV-123`, view links with `?p=<task>`
|
|
289
|
-
| `redmine` | `#123`, `#123-4` and `#123#note-4` (a note), `##123`; with `host`, `https://<host>/issues/123` (also under a path)
|
|
290
|
-
| `monday` | item links, `https://<account>.monday.com/boards/<board>/pulses/<item>`, and custom keys such as `TIX-001`
|
|
291
|
-
| `youtrack` | `KT-123`, `https://<host>/issue/KT-123/...` or `/projects/KT/issues/KT-123` (also under a path, as in `/youtrack/issue/KT-123`)
|
|
292
|
-
| `basecamp` | to-do and card links, `https://3.basecamp.com/<account>/buckets/<project>/todos/<id>` or `/card_tables/cards/<id>`
|
|
293
|
-
| `shortcut` | `sc-123`, `[sc-123]`, `https://app.shortcut.com/<workspace>/story/123/...`
|
|
294
|
-
| `
|
|
295
|
-
| `
|
|
296
|
-
| `
|
|
297
|
-
|
|
298
|
-
`
|
|
299
|
-
|
|
300
|
-
|
|
278
|
+
| Preset | Accepts | Options |
|
|
279
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
280
|
+
| `any` | `PROJ-123`, `#123`, `owner/repo#123`, any http(s) URL | |
|
|
281
|
+
| `jira` | `TRADE-123`, `https://<host>/browse/TRADE-123` (also under a path, as in `/jira/browse/TRADE-123`), board URLs with `selectedIssue=TRADE-123` | `projects`; `host` (any host by default) |
|
|
282
|
+
| `github` | `#123`, `owner/repo#123`, `https://github.com/owner/repo/issues/123` (or `/pull/`) | `host` for GitHub Enterprise (`github.com` by default) |
|
|
283
|
+
| `gitlab` | `#123`, `group/project#123`, `https://gitlab.com/group/project/-/issues/123` | `host` for self-managed (`gitlab.com` by default) |
|
|
284
|
+
| `linear` | `RISK-123`, `https://linear.app/<workspace>/issue/RISK-123/...` | `teams` |
|
|
285
|
+
| `azure-devops` | `AB#123`, `#123`, `https://dev.azure.com/<org>/<project>/_workitems/edit/123` | `host` for Azure DevOps Server (`dev.azure.com` and `*.visualstudio.com` by default) |
|
|
286
|
+
| `trello` | card links, `https://trello.com/c/nqPiDKmw` with or without the `/9-title` after it | |
|
|
287
|
+
| `notion` | Unique IDs such as `TASK-123`, `https://www.notion.so/TASK-123`, page links that end in the page ID (also `https://app.notion.com/p/...`), database links with `?p=<page ID>` | `prefixes`; `host` for a custom domain (`notion.so`, `www.notion.so`, `app.notion.com` and `*.notion.site` by default) |
|
|
288
|
+
| `asana` | task links, `https://app.asana.com/0/<project>/<task>` and the newer `https://app.asana.com/1/<workspace>/.../task/<task>` | |
|
|
289
|
+
| `clickup` | `CU-86abc123`, `#86abc123`, custom IDs such as `DEV-123`, `https://app.clickup.com/t/86abc123` or `/t/<workspace>/DEV-123`, view links with `?p=<task>` | `prefixes` for custom IDs |
|
|
290
|
+
| `redmine` | `#123`, `#123-4` and `#123#note-4` (a note), `##123`; with `host`, `https://<host>/issues/123` (also under a path) | `host`, needed for links |
|
|
291
|
+
| `monday` | item links, `https://<account>.monday.com/boards/<board>/pulses/<item>`, and custom keys such as `TIX-001` | `prefixes` for custom keys |
|
|
292
|
+
| `youtrack` | `KT-123`, `https://<host>/issue/KT-123/...` or `/projects/KT/issues/KT-123` (also under a path, as in `/youtrack/issue/KT-123`) | `projects`; `host` for Server or a custom domain (`youtrack.jetbrains.com`, `*.youtrack.cloud` and `*.myjetbrains.com` by default) |
|
|
293
|
+
| `basecamp` | to-do and card links, `https://3.basecamp.com/<account>/buckets/<project>/todos/<id>` or `/card_tables/cards/<id>` | |
|
|
294
|
+
| `shortcut` | `sc-123`, `[sc-123]`, `https://app.shortcut.com/<workspace>/story/123/...` | |
|
|
295
|
+
| `openproject` | `#4821` and per-project IDs such as `AGILE-1`; with `host`, `https://<host>/projects/<identifier>/work_packages/AGILE-1` (also `/work_packages/4821` and the short `/wp/4821`) | `host`, needed for links |
|
|
296
|
+
| `bugzilla` | bug numbers `4821`; with `host`, `https://<host>/show_bug.cgi?id=4821`, also under a path as in `/bugzilla/show_bug.cgi` | `host`, needed for links |
|
|
297
|
+
| `phabricator` | task monograms `T4821`; with `host`, `https://<host>/T4821`. Phorge, Phabricator's maintained fork, shares the format | `host`, needed for links |
|
|
298
|
+
| `rally` | formatted IDs `US4821`, `DE4821`, `TA4821`, `TC4821`, never zero-padded (so MITRE ATT&CK's `TA0006` isn't one) | |
|
|
299
|
+
| `servicenow` | record numbers `STRY0010001` for the prefixes you list, any digit count | `prefixes`, required: record-number prefixes such as `STRY` |
|
|
300
|
+
| `plane` | `WEB-142`; `https://app.plane.so/<workspace>/<project>/issues/WEB-142` (or a UUID), and the same on a `host` for self-hosted | `projects`; `host` for self-hosted (`app.plane.so` by default) |
|
|
301
|
+
| `url` | links to the hosts you list, and nothing else | `hosts`, required: host names such as `tasks.example.com`, with no scheme, port or path; `*.example.com` matches its subdomains |
|
|
302
|
+
| `numeric` | `4821` | `minLength` (1), `maxLength` (20) |
|
|
303
|
+
| `pattern` | anything matching your regex, anchored at both ends | `pattern`, `flags` |
|
|
304
|
+
|
|
305
|
+
`projects` (Jira, YouTrack, Plane), `teams` (Linear) and `prefixes` (Notion, ClickUp, monday.com,
|
|
306
|
+
ServiceNow) are keys as they appear in tickets, in uppercase: `TRADE` for `TRADE-123`, `STRY` for
|
|
307
|
+
`STRY0010001`. YouTrack project IDs may also start with a digit, as `4DM`. Without them, any key passes;
|
|
308
|
+
ServiceNow's list is required. `host` is a host name such as `jira.example.com`, or `*.example.com` for
|
|
309
|
+
every subdomain, as in `{ preset: 'jira', host: '*.atlassian.net' }`. Every preset that takes `host`
|
|
310
|
+
requires a plain host name: a value with a scheme, port or path never matched a link, so it is an error.
|
|
311
|
+
Each preset
|
|
301
312
|
takes only its own options, so `host` on the `any` preset fails with a clear message instead of being
|
|
302
313
|
ignored. A `pattern` always matches the whole ticket, so the `g` and `y` flags make no difference and are
|
|
303
314
|
ignored.
|
|
304
315
|
|
|
305
316
|
A preset rejects links that aren't one ticket in its tracker, such as a Trello board, an Asana project or
|
|
306
317
|
a Notion database view. Redmine runs on your own server, and `/issues/123` is also GitHub's path, so its
|
|
307
|
-
links pass only once `host` is set.
|
|
318
|
+
links pass only once `host` is set. The same holds for OpenProject, Bugzilla and Phabricator, which also
|
|
319
|
+
run on their own servers.
|
|
308
320
|
|
|
309
321
|
For a tracker without a preset, use `url` with its host, for example
|
|
310
322
|
`{ preset: 'url', hosts: ['www.wrike.com'] }` for Wrike links such as
|
|
@@ -329,6 +341,34 @@ testGovernance.configure({
|
|
|
329
341
|
The built-in presets are written to run in linear time. A `pattern` preset runs your regex as given,
|
|
330
342
|
so keep it free of nested quantifiers.
|
|
331
343
|
|
|
344
|
+
### Tickets from annotations
|
|
345
|
+
|
|
346
|
+
A comment never reaches a test report. Playwright and Vitest can carry the ticket in data that does, so
|
|
347
|
+
`annotations` accepts it from there as well:
|
|
348
|
+
|
|
349
|
+
```js
|
|
350
|
+
test.skip(
|
|
351
|
+
'rejects a margin order above the buying power',
|
|
352
|
+
{
|
|
353
|
+
annotation: { type: 'issue', description: 'TRADE-123' },
|
|
354
|
+
},
|
|
355
|
+
async () => {},
|
|
356
|
+
);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
```js
|
|
360
|
+
// Vitest: the ticket is the `issue` key of `meta`.
|
|
361
|
+
test.skip('rejects a margin order above the buying power', { meta: { issue: 'TRADE-123' } }, () => {});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The `type` names Playwright's annotation type and Vitest's `meta` key; `'issue'` by default, matched
|
|
365
|
+
exactly. The value must be the ticket and nothing else, as in a marker. One valid annotation covers every
|
|
366
|
+
state the test is in, and a describe's annotation covers its tests. An annotation of another type is
|
|
367
|
+
left alone, and an annotation whose ticket isn't static text is reported with `reportUnreadableTags`.
|
|
368
|
+
`annotations: false` turns it off. A marker comment still works everywhere, and when both are written,
|
|
369
|
+
both must be valid. Jest, Mocha, Jasmine, node:test and Cypress have no annotations, so for them it is
|
|
370
|
+
comments only.
|
|
371
|
+
|
|
332
372
|
### Which functions count as `test`
|
|
333
373
|
|
|
334
374
|
In Playwright files:
|
|
@@ -21,7 +21,7 @@ export default createRule({
|
|
|
21
21
|
create(context, analysis, options) {
|
|
22
22
|
const { sourceCode } = context;
|
|
23
23
|
for (const subject of analysis.subjects) {
|
|
24
|
-
const missing = missingStates(subject);
|
|
24
|
+
const missing = missingStates(subject, options);
|
|
25
25
|
if (missing.length === 0)
|
|
26
26
|
continue;
|
|
27
27
|
const stray = strayMarkers(subject, options);
|
|
@@ -31,7 +31,7 @@ export default createRule({
|
|
|
31
31
|
}
|
|
32
32
|
for (const subject of analysis.subjects) {
|
|
33
33
|
// A stray marker next to a missing one is a mismatch, reported by marker-matches-state.
|
|
34
|
-
if (missingStates(subject).length > 0)
|
|
34
|
+
if (missingStates(subject, options).length > 0)
|
|
35
35
|
continue;
|
|
36
36
|
const name = describeSubject(subject).replace(/^This/, 'this');
|
|
37
37
|
for (const marker of strayMarkers(subject, options)) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("../utils/create-rule.ts").Rule<"missingTicket" | "invalidTicket" | "placeholderTicket" | "extraText" | "removeExtraText" | "missingMarker" | "markerCase" | "dynamicTitle" | "dynamicTags">;
|
|
1
|
+
declare const _default: import("../utils/create-rule.ts").Rule<"missingTicket" | "invalidTicket" | "placeholderTicket" | "extraText" | "removeExtraText" | "missingMarker" | "markerCase" | "dynamicTitle" | "dynamicTags" | "invalidAnnotation" | "unreadAnnotation">;
|
|
2
2
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appliesTo, describeSubject, evaluate, headLoc } from "../utils/analyze.js";
|
|
1
|
+
import { annotationTicketsOf, appliesTo, describeSubject, evaluate, hasAnnotationTicket, headLoc, unreadAnnotationsOf, } from "../utils/analyze.js";
|
|
2
2
|
import { createRule } from "../utils/create-rule.js";
|
|
3
3
|
export default createRule({
|
|
4
4
|
meta: {
|
|
@@ -20,6 +20,8 @@ export default createRule({
|
|
|
20
20
|
removeExtraText: 'Remove `{{text}}`.',
|
|
21
21
|
dynamicTitle: "This title isn't static text, so its tags can't be checked. Use a string or template literal.",
|
|
22
22
|
dynamicTags: "These tags can't be read in this file, so they can't be checked. Use strings, or constants declared in this file.",
|
|
23
|
+
invalidAnnotation: "'{{ticket}}' is not a valid ticket for the '{{state}}' state. Expected {{expected}}.",
|
|
24
|
+
unreadAnnotation: "This annotation's ticket isn't static text, so it can't be checked. Use a string, or a constant declared in this file.",
|
|
23
25
|
},
|
|
24
26
|
},
|
|
25
27
|
create(context, analysis, options) {
|
|
@@ -28,16 +30,37 @@ export default createRule({
|
|
|
28
30
|
const reported = new Set();
|
|
29
31
|
const reportOnce = (key) => !reported.has(key) && Boolean(reported.add(key));
|
|
30
32
|
for (const subject of analysis.subjects) {
|
|
33
|
+
const needsTicket = subject.states.some((s) => s.required);
|
|
31
34
|
if (options.reportUnreadableTags && hasTagStates) {
|
|
32
35
|
if (subject.dynamicTitle)
|
|
33
36
|
context.report({ node: subject.node.arguments[0], messageId: 'dynamicTitle' });
|
|
34
37
|
for (const node of subject.unreadTags)
|
|
35
38
|
context.report({ node, messageId: 'dynamicTags' });
|
|
36
39
|
}
|
|
40
|
+
// An annotation whose ticket can't be read may be the one that was supposed to cover the test.
|
|
41
|
+
if (options.reportUnreadableTags && needsTicket) {
|
|
42
|
+
for (const node of unreadAnnotationsOf(subject)) {
|
|
43
|
+
if (reportOnce(`annotation-unread:${node.range[0]}`)) {
|
|
44
|
+
context.report({ node, messageId: 'unreadAnnotation' });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
for (const { state, required } of subject.states) {
|
|
38
49
|
if (!required)
|
|
39
50
|
continue;
|
|
40
51
|
const result = evaluate(subject, state);
|
|
52
|
+
// Annotations are checked whenever a ticket is needed, whatever the markers say.
|
|
53
|
+
for (const ticket of annotationTicketsOf(subject)) {
|
|
54
|
+
if (state.ticket.check(ticket.text) !== 'ok' && reportOnce(`annotation:${ticket.node.range[0]}`)) {
|
|
55
|
+
context.report({
|
|
56
|
+
node: ticket.node,
|
|
57
|
+
messageId: 'invalidAnnotation',
|
|
58
|
+
data: { ticket: ticket.text, state: state.name, expected: state.ticket.expected },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (result.kind === 'missing' && hasAnnotationTicket(subject, state, options))
|
|
63
|
+
continue;
|
|
41
64
|
if (result.kind !== 'ok' &&
|
|
42
65
|
result.kind !== 'missing' &&
|
|
43
66
|
!reportOnce(`${result.marker.comment.range[0]}:${state.name}`)) {
|
package/dist/utils/analyze.d.ts
CHANGED
|
@@ -72,6 +72,13 @@ export interface Subject {
|
|
|
72
72
|
skipped: boolean;
|
|
73
73
|
ownSkip: boolean;
|
|
74
74
|
markers: Marker[];
|
|
75
|
+
/** Tickets read from annotations of the configured type on this call, with the node to report at. */
|
|
76
|
+
annotationTickets: {
|
|
77
|
+
text: string;
|
|
78
|
+
node: Node;
|
|
79
|
+
}[];
|
|
80
|
+
/** Annotation values of the configured type on this call whose ticket can't be read. */
|
|
81
|
+
unreadTickets: Node[];
|
|
75
82
|
dynamicTitle: boolean;
|
|
76
83
|
/** For a runtime function such as Jasmine's `pending()`, its name; messages call the others by their state. */
|
|
77
84
|
functionName?: string;
|
|
@@ -112,8 +119,20 @@ export declare function analyze(sourceCode: SourceCode, options: ResolvedOptions
|
|
|
112
119
|
/** Whether `subject` is in `stateName` at all, required or not, including inherited and runtime states. */
|
|
113
120
|
export declare function appliesTo(subject: Subject, stateName: string): boolean;
|
|
114
121
|
export declare function evaluate(subject: Subject, state: StateDef): Evaluation;
|
|
115
|
-
/**
|
|
116
|
-
export declare function
|
|
122
|
+
/** Annotation tickets that can cover `subject`: its own and those of enclosing describes and tests. */
|
|
123
|
+
export declare function annotationTicketsOf(subject: Subject): {
|
|
124
|
+
text: string;
|
|
125
|
+
node: Node;
|
|
126
|
+
}[];
|
|
127
|
+
/** Annotation values whose ticket can't be read, `subject`'s own and those of enclosing describes and tests. */
|
|
128
|
+
export declare function unreadAnnotationsOf(subject: Subject): Node[];
|
|
129
|
+
/** Whether a valid annotation ticket covers `state` for `subject`. */
|
|
130
|
+
export declare function hasAnnotationTicket(subject: Subject, state: StateDef, options: ResolvedOptions): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Required states on `subject` that have no ticket at all: no marker, not even a broken one, and no
|
|
133
|
+
* valid annotation.
|
|
134
|
+
*/
|
|
135
|
+
export declare function missingStates(subject: Subject, options: ResolvedOptions): StateDef[];
|
|
117
136
|
/**
|
|
118
137
|
* Whether a marker reads as an ordinary comment rather than a ticket reference, such as
|
|
119
138
|
* `// FIXME: this breaks on slow machines`: it starts with something that is not a ticket and goes on
|