eslint-plugin-test-governance 0.3.0 → 0.4.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 +76 -1
- package/README.md +166 -64
- package/dist/index.d.ts +14 -12
- package/dist/index.js +22 -12
- package/dist/rules/marker-matches-state.d.ts +1 -2
- package/dist/rules/marker-matches-state.js +7 -7
- package/dist/rules/no-conflicting-states.d.ts +1 -2
- package/dist/rules/no-conflicting-states.js +12 -20
- package/dist/rules/no-orphaned-marker.d.ts +1 -2
- package/dist/rules/no-orphaned-marker.js +15 -10
- package/dist/rules/require-ticket-in-comments.d.ts +1 -2
- package/dist/rules/require-ticket-in-comments.js +13 -18
- package/dist/rules/require-ticket.d.ts +1 -2
- package/dist/rules/require-ticket.js +18 -10
- package/dist/utils/analyze.d.ts +17 -14
- package/dist/utils/analyze.js +370 -153
- package/dist/utils/constants.d.ts +5 -3
- package/dist/utils/constants.js +4 -3
- package/dist/utils/create-rule.d.ts +9 -10
- package/dist/utils/create-rule.js +5 -11
- package/dist/utils/distance.d.ts +0 -1
- package/dist/utils/distance.js +0 -1
- package/dist/utils/errors.d.ts +0 -1
- package/dist/utils/errors.js +0 -1
- package/dist/utils/fix.d.ts +0 -3
- package/dist/utils/fix.js +0 -8
- package/dist/utils/frameworks.d.ts +59 -0
- package/dist/utils/frameworks.js +102 -0
- package/dist/utils/options.d.ts +26 -11
- package/dist/utils/options.js +13 -9
- package/dist/utils/schema.d.ts +1 -2
- package/dist/utils/schema.js +2 -2
- package/dist/utils/tickets.d.ts +0 -1
- package/dist/utils/tickets.js +4 -4
- package/dist/utils/validate.d.ts +0 -1
- package/dist/utils/validate.js +3 -3
- package/docs/rules/marker-matches-state.md +18 -10
- package/docs/rules/no-conflicting-states.md +14 -8
- package/docs/rules/no-orphaned-marker.md +31 -17
- package/docs/rules/require-ticket-in-comments.md +26 -22
- package/docs/rules/require-ticket.md +107 -47
- package/package.json +7 -10
|
@@ -26,27 +26,27 @@ Incorrect:
|
|
|
26
26
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
27
27
|
|
|
28
28
|
```js
|
|
29
|
-
test('
|
|
29
|
+
test('places a limit order @new @unstable', async () => {});
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
33
33
|
|
|
34
34
|
```js
|
|
35
|
-
// SKIP:
|
|
36
|
-
// NEW:
|
|
37
|
-
test.skip('
|
|
35
|
+
// SKIP: TRADE-9
|
|
36
|
+
// NEW: TRADE-9
|
|
37
|
+
test.skip('places a limit order @new', async () => {});
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
41
41
|
|
|
42
42
|
```js
|
|
43
|
-
test('
|
|
43
|
+
test('exports the trade history as CSV @New', async () => {});
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
47
47
|
|
|
48
48
|
```js
|
|
49
|
-
test('
|
|
49
|
+
test('shows dividend payments in the account history @unstabel', async () => {});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
Correct:
|
|
@@ -54,9 +54,15 @@ Correct:
|
|
|
54
54
|
<!-- example: valid settings={"lifecycleTags":true} -->
|
|
55
55
|
|
|
56
56
|
```js
|
|
57
|
-
test('
|
|
57
|
+
test('places a limit order @unstable', async () => {});
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## Options
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
This rule has no options of its own. It reads the shared options described in the
|
|
63
|
+
[README](../../README.md#options). These change what it reports:
|
|
64
|
+
|
|
65
|
+
- `lifecycleTags`, or `states` for `new` and `unstable`: the `@new` checks run when the `new` state
|
|
66
|
+
is on, and `@new` together with `@unstable` is reported when both are on.
|
|
67
|
+
- `customStates`: the tags of custom states get the same case and typo checks.
|
|
68
|
+
- `framework` and `testFunctions`: which functions count as the framework's `test`.
|
|
@@ -22,14 +22,18 @@ one from this.
|
|
|
22
22
|
A `FIXME:` or `TODO:` comment that starts with a valid ticket is treated by where it sits:
|
|
23
23
|
|
|
24
24
|
- Above a helper or any other code that isn't a test, it is a tracked work comment and is allowed.
|
|
25
|
-
- Above a test, describe or skip call that is not in the `fixme` state,
|
|
26
|
-
and is reported, since `// FIXME:
|
|
27
|
-
marker forgotten. Move a work comment into the test body, or onto the line of code it
|
|
25
|
+
- Above a test, describe or skip call that is not in the `fixme` state, a `FIXME:` comment reads as a
|
|
26
|
+
leftover marker and is reported, since `// FIXME: TRADE-12` above a test usually means the test was
|
|
27
|
+
fixed and the marker forgotten. Move a work comment into the test body, or onto the line of code it
|
|
28
|
+
is about.
|
|
29
|
+
- Above a Jest or Vitest test that is not a todo, a `TODO:` comment is a tracked work comment about
|
|
30
|
+
the test and is allowed. `TODO` is the everyday keyword for such comments, so there it is far more
|
|
31
|
+
often a note than the forgotten marker of a `test.todo`.
|
|
28
32
|
|
|
29
33
|
Other markers that read as a sentence, such as `// SKIP: this one is flaky`, are left alone too: the rule
|
|
30
34
|
counts a comment as prose when its first word is not a valid ticket and more text follows.
|
|
31
35
|
|
|
32
|
-
Files
|
|
36
|
+
Files without tests, such as application code, are not checked.
|
|
33
37
|
|
|
34
38
|
The rule offers a suggestion to delete the comment.
|
|
35
39
|
|
|
@@ -40,16 +44,16 @@ Incorrect:
|
|
|
40
44
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
41
45
|
|
|
42
46
|
```js
|
|
43
|
-
// NEW:
|
|
44
|
-
test('
|
|
47
|
+
// NEW: TRADE-80 promoted last sprint
|
|
48
|
+
test('saves a watchlist', async () => {});
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
<!-- example: invalid -->
|
|
48
52
|
|
|
49
53
|
```js
|
|
50
|
-
test('
|
|
51
|
-
// SKIP:
|
|
52
|
-
await page.goto('/
|
|
54
|
+
test('places a limit order', async ({ page }) => {
|
|
55
|
+
// SKIP: TRADE-81
|
|
56
|
+
await page.goto('/orders');
|
|
53
57
|
});
|
|
54
58
|
```
|
|
55
59
|
|
|
@@ -58,27 +62,37 @@ Correct:
|
|
|
58
62
|
<!-- example: valid settings={"lifecycleTags":true} -->
|
|
59
63
|
|
|
60
64
|
```js
|
|
61
|
-
test('
|
|
65
|
+
test('saves a watchlist', async () => {});
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
<!-- example: valid -->
|
|
65
69
|
|
|
66
70
|
```js
|
|
67
|
-
test('
|
|
68
|
-
// FIXME: the
|
|
69
|
-
await page.goto('/
|
|
71
|
+
test('places a limit order', async ({ page }) => {
|
|
72
|
+
// FIXME: the price field sometimes loses focus on slow machines
|
|
73
|
+
await page.goto('/orders');
|
|
70
74
|
});
|
|
71
75
|
```
|
|
72
76
|
|
|
73
77
|
<!-- example: valid -->
|
|
74
78
|
|
|
75
79
|
```js
|
|
76
|
-
// FIXME:
|
|
77
|
-
test.fixme('
|
|
78
|
-
await page.goto('/
|
|
80
|
+
// FIXME: TRADE-81
|
|
81
|
+
test.fixme('places a limit order', async ({ page }) => {
|
|
82
|
+
await page.goto('/orders');
|
|
79
83
|
});
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
## Options
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
This rule has no options of its own. It reads the shared options described in the
|
|
89
|
+
[README](../../README.md#options). These change what it reports:
|
|
90
|
+
|
|
91
|
+
- `lifecycleTags`, `states` and `customStates`: which keywords are markers. `// NEW:` is a marker
|
|
92
|
+
only while the `new` state is on, so with `lifecycleTags` off it is an ordinary comment.
|
|
93
|
+
- `ticket` and `placeholders`: whether a comment starts with a valid ticket, which separates a
|
|
94
|
+
leftover marker from a sentence or a work comment.
|
|
95
|
+
- `allowBlankLine`: whether a marker separated from the code below it by a blank line still belongs
|
|
96
|
+
to that code.
|
|
97
|
+
- `framework` and `testFunctions`: which functions count as the framework's `test`, and so which
|
|
98
|
+
files are test files.
|
|
@@ -13,12 +13,12 @@ A `// TODO: clean this up` comment is a promise nobody tracks. This rule makes e
|
|
|
13
13
|
source of truth and nothing is forgotten in a comment.
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
// TODO:
|
|
17
|
-
// FIXME:
|
|
18
|
-
// TODO(
|
|
19
|
-
// TODO(alice): refactor this
|
|
20
|
-
// TODO: refactor this
|
|
21
|
-
// FIXME:
|
|
16
|
+
// TODO: TRADE-512 ✓
|
|
17
|
+
// FIXME: TRADE-77, TRADE-78 ✓ several tickets, separated by commas
|
|
18
|
+
// TODO(TRADE-512) ✓ the ticket can go in parentheses
|
|
19
|
+
// TODO(alice): refactor this ✖ a name is not a ticket
|
|
20
|
+
// TODO: refactor this ✖ no ticket
|
|
21
|
+
// FIXME: TRADE-77 flaky locator ✖ only tickets are allowed (allowNotes: true allows the note)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
How comments are read:
|
|
@@ -26,13 +26,14 @@ How comments are read:
|
|
|
26
26
|
- The keyword starts the comment (or a line of a block comment). `TODO` and `FIXME` in capitals
|
|
27
27
|
count with or without a colon. Other casings, such as `todo:`, need the colon, so a sentence that
|
|
28
28
|
happens to start with "Todo" is left alone.
|
|
29
|
-
- The tickets can also go in parentheses
|
|
30
|
-
teams already use. Anything after the parentheses is a note, as it would be after `TODO:
|
|
29
|
+
- The tickets can also go in parentheses after the keyword, `TODO(TRADE-512)` or `TODO (TRADE-512)`, a
|
|
30
|
+
style many teams already use. Anything after the parentheses is a note, as it would be after `TODO: TRADE-512`.
|
|
31
31
|
- The tickets follow the same rules as markers: the shared ticket format, placeholders such as `TBD`
|
|
32
32
|
rejected, and no text after the tickets unless `allowNotes` is set.
|
|
33
|
-
- A `// FIXME:` marker directly above a `test.fixme(...)
|
|
34
|
-
[`require-ticket`](require-ticket.md) instead, so it is
|
|
35
|
-
|
|
33
|
+
- A `// FIXME:` marker directly above a `test.fixme(...)`, or a `// TODO:` marker above a Jest or
|
|
34
|
+
Vitest `test.todo(...)`, is checked by [`require-ticket`](require-ticket.md) instead, so it is
|
|
35
|
+
reported once.
|
|
36
|
+
- Only test files (a test framework import or a call to a test function) are checked.
|
|
36
37
|
Application code is left alone, even if your config applies the plugin to it.
|
|
37
38
|
|
|
38
39
|
## Examples
|
|
@@ -42,9 +43,9 @@ Incorrect:
|
|
|
42
43
|
<!-- example: invalid -->
|
|
43
44
|
|
|
44
45
|
```js
|
|
45
|
-
test('
|
|
46
|
+
test('places a limit order', async ({ page }) => {
|
|
46
47
|
// FIXME: this locator breaks on mobile
|
|
47
|
-
await page.click('#
|
|
48
|
+
await page.click('#buy');
|
|
48
49
|
});
|
|
49
50
|
```
|
|
50
51
|
|
|
@@ -52,7 +53,7 @@ test('checkout', async ({ page }) => {
|
|
|
52
53
|
|
|
53
54
|
```js
|
|
54
55
|
// TODO: TBD
|
|
55
|
-
test('
|
|
56
|
+
test('cancels an open order', async ({ page }) => {});
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
Correct:
|
|
@@ -60,17 +61,17 @@ Correct:
|
|
|
60
61
|
<!-- example: valid -->
|
|
61
62
|
|
|
62
63
|
```js
|
|
63
|
-
test('
|
|
64
|
-
// FIXME:
|
|
65
|
-
await page.click('#
|
|
64
|
+
test('places a limit order', async ({ page }) => {
|
|
65
|
+
// FIXME: TRADE-81
|
|
66
|
+
await page.click('#buy');
|
|
66
67
|
});
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
<!-- example: valid settings={"comments":{"keywords":["FIXME"]}} -->
|
|
70
71
|
|
|
71
72
|
```js
|
|
72
|
-
// TODO: add a
|
|
73
|
-
test('
|
|
73
|
+
// TODO: add a test for cancelled orders once the API is stable
|
|
74
|
+
test('places a limit order', async ({ page }) => {});
|
|
74
75
|
```
|
|
75
76
|
|
|
76
77
|
## Options
|
|
@@ -78,7 +79,10 @@ test('checkout', async ({ page }) => {});
|
|
|
78
79
|
This rule has no options of its own. It reads the shared options described in the
|
|
79
80
|
[README](../../README.md#options):
|
|
80
81
|
|
|
81
|
-
- `comments`: the keywords to check, `{ keywords: ['FIXME', 'TODO'] }` by default.
|
|
82
|
-
`{ keywords: ['FIXME'] }`
|
|
83
|
-
`
|
|
82
|
+
- `comments`: the keywords to check, `{ keywords: ['FIXME', 'TODO'] }` by default. Your list
|
|
83
|
+
replaces the default one: `{ keywords: ['FIXME'] }` allows free-form `TODO`s, and
|
|
84
|
+
`{ keywords: ['FIXME', 'TODO', 'HACK'] }` checks `HACK` too. `comments: false` turns the check
|
|
85
|
+
off, and so does turning the rule off in your config.
|
|
84
86
|
- `ticket`, `placeholders` and `allowNotes` work as they do for markers.
|
|
87
|
+
- `framework` and `testFunctions`: which functions count as the framework's `test`, and so which
|
|
88
|
+
files are test files.
|
|
@@ -13,51 +13,59 @@ at the ticket that explains it, so disabled tests show up in planning instead of
|
|
|
13
13
|
|
|
14
14
|
A test is in a state when it is:
|
|
15
15
|
|
|
16
|
-
| State | Put there by
|
|
17
|
-
| -------------- |
|
|
18
|
-
| `skip` | `test.skip('title', fn)`, `test.describe.skip`, `test.skip()` in a body, `testInfo.skip()` | `// SKIP:` | yes |
|
|
19
|
-
| `fixme` | `test.fixme(...)`, `test.describe.fixme`, `testInfo.fixme()`
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
| State | Put there by | Marker | On by default |
|
|
17
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -------------------------- |
|
|
18
|
+
| `skip` | `test.skip('title', fn)`, `test.describe.skip`, `test.skip()` in a body, `testInfo.skip()`, `test.step.skip`, `step.skip()` in a step | `// SKIP:` | yes |
|
|
19
|
+
| `fixme` | `test.fixme(...)`, `test.describe.fixme`, `testInfo.fixme()` | `// FIXME:` | yes |
|
|
20
|
+
| `todo` | Jest's and Vitest's `test.todo` | `// TODO:` | yes, in Jest and Vitest |
|
|
21
|
+
| `new` | the `@new` tag | `// NEW:` | with `lifecycleTags: true` |
|
|
22
|
+
| `unstable` | the `@unstable` tag | `// UNSTABLE:` | with `lifecycleTags: true` |
|
|
23
|
+
| `fail`, `slow` | `test.fail(...)`, `test.slow()`, `testInfo.fail()`, `testInfo.slow()` | `// FAIL:`, `// SLOW:` | no |
|
|
24
|
+
| custom | any tag you list in `customStates` | your keyword | when configured |
|
|
25
|
+
|
|
26
|
+
In Jest and Vitest, `xit`, `xdescribe`, Vitest's `{ skip: true }` and `context.skip()` skip a test
|
|
27
|
+
too, and `test.failing` and `test.fails` put it in the `fail` state. The
|
|
28
|
+
[README](../../README.md#jest-and-vitest) lists every form.
|
|
29
|
+
|
|
30
|
+
Tags are read from the title (`'places a limit order @new'`) and from `{ tag: '@new' }` or `{ tag: ['@new', '@smoke'] }`.
|
|
26
31
|
Tags on a `describe` apply to every test inside it.
|
|
27
32
|
|
|
28
33
|
## Marker format
|
|
29
34
|
|
|
30
35
|
```text
|
|
31
|
-
// SKIP:
|
|
32
|
-
// SKIP:
|
|
33
|
-
/* FIXME: https://github.com/acme/
|
|
36
|
+
// SKIP: TRADE-123
|
|
37
|
+
// SKIP: TRADE-123, TRADE-124
|
|
38
|
+
/* FIXME: https://github.com/acme/trading-engine/issues/4821 */
|
|
34
39
|
```
|
|
35
40
|
|
|
36
41
|
- The marker goes in the comment block directly above the test, describe or `test.skip()` call.
|
|
37
42
|
Other comments, such as `eslint-disable-next-line`, may sit in the same block. A blank line ends
|
|
38
43
|
the block unless `allowBlankLine` is set.
|
|
39
|
-
- The keyword is uppercase and followed by a colon. `// skip:
|
|
44
|
+
- The keyword is uppercase and followed by a colon. `// skip: TRADE-1` is reported with a hint.
|
|
40
45
|
- After the colon come one or more tickets separated by commas, and nothing else. The ticket is the
|
|
41
46
|
source of truth, so the reason for a skip belongs in the ticket, not in the comment.
|
|
42
|
-
`// SKIP:
|
|
47
|
+
`// SKIP: TRADE-123 flaky on CI` is reported, with a suggestion that removes the extra text. Teams
|
|
43
48
|
that want short notes can set `allowNotes: true`.
|
|
44
49
|
- A marker on a skipped or tagged `describe` covers every test inside it.
|
|
45
50
|
- A `test.skip()` inside a test body can have its marker above the call or above the test.
|
|
46
|
-
-
|
|
51
|
+
- A skipped step, `test.step.skip(...)` or `step.skip()` in its body, can have its marker above the
|
|
52
|
+
call, above the step or above a skipped test around it. A `test.skip()` inside a step skips the
|
|
53
|
+
whole test, so its marker goes above the call or above the test.
|
|
54
|
+
- Ticket IDs in test titles (`'TRADE-52: closes all positions'`) are never read as tickets.
|
|
47
55
|
|
|
48
56
|
How ticket lists are read:
|
|
49
57
|
|
|
50
|
-
| Marker
|
|
51
|
-
|
|
|
52
|
-
| `// SKIP:
|
|
53
|
-
| `// SKIP:
|
|
54
|
-
| `// SKIP: https://acme.atlassian.net/browse/
|
|
55
|
-
| `// SKIP:
|
|
56
|
-
| `// SKIP:
|
|
57
|
-
| `// SKIP:
|
|
58
|
-
| `// SKIP:
|
|
59
|
-
| `// SKIP:
|
|
60
|
-
| `// SKIP: flaky on CI`
|
|
58
|
+
| Marker | Result |
|
|
59
|
+
| --------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
60
|
+
| `// SKIP: TRADE-1, TRADE-2` or `// SKIP: TRADE-1,TRADE-2` | two tickets |
|
|
61
|
+
| `// SKIP: TRADE-1 , TRADE-2 ` | two tickets; extra spaces are fine |
|
|
62
|
+
| `// SKIP: https://acme.atlassian.net/browse/TRADE-1?focusedCommentId=5#comment-5` | one ticket; query strings and fragments are part of a URL |
|
|
63
|
+
| `// SKIP: TRADE-1 flaky on CI`, `// SKIP: TRADE-1: flaky` | extra text `flaky on CI` / `: flaky` |
|
|
64
|
+
| `// SKIP: TRADE-1.` or `// SKIP: TRADE-1:` | one ticket; a full stop or colon at the end is fine |
|
|
65
|
+
| `// SKIP: TRADE-1,` | extra text `,` |
|
|
66
|
+
| `// SKIP: TRADE-1 and TRADE-2` | extra text `and TRADE-2`; separate tickets with commas |
|
|
67
|
+
| `// SKIP: TRADE-1;TRADE-2`, `// SKIP: TRADE-1/TRADE-2` | invalid ticket, since only commas separate tickets |
|
|
68
|
+
| `// SKIP: flaky on CI` | invalid ticket `flaky`: the first word must be a ticket |
|
|
61
69
|
|
|
62
70
|
In a block comment, only the marker line is checked, so a JSDoc block can explain the skip on other
|
|
63
71
|
lines. Extra text is reported only once the tickets on that line are valid.
|
|
@@ -66,11 +74,34 @@ Conditional skips such as `test.skip(browserName === 'webkit', 'Not supported')`
|
|
|
66
74
|
permanent platform limit, so they need no ticket unless you set `requireTicketForConditional: true`.
|
|
67
75
|
A runtime skip under an `if`, a `switch` case, a ternary or `&&` counts as conditional too, since
|
|
68
76
|
`if (!enabled) test.skip()` does the same as `test.skip(!enabled)`. So does a skip in a `catch` block,
|
|
69
|
-
such as `catch { test.skip(true, '
|
|
77
|
+
such as `catch { test.skip(true, 'Market data feed is down') }`, and a skip after an early exit such as
|
|
70
78
|
`if (ready) return;` or `if (!ok) throw error;` earlier in the same function.
|
|
71
79
|
|
|
72
80
|
If one test or describe has several markers for the same state, each must be valid: in
|
|
73
|
-
`// SKIP:
|
|
81
|
+
`// SKIP: TRADE-1` followed by `// SKIP: nope`, the second line is reported.
|
|
82
|
+
|
|
83
|
+
## Dynamic titles
|
|
84
|
+
|
|
85
|
+
Tags can only be read from a title that is text: a string or a template literal such as
|
|
86
|
+
`` `buys ${symbol} @new` ``. A title built another way, such as `'buys ' + symbol` or a variable, may
|
|
87
|
+
hide a tag. With `reportDynamicTitles: true`, the rule reports those titles whenever a tag state
|
|
88
|
+
(`@new`, `@unstable` or a custom state) is on, so no tagged test slips past.
|
|
89
|
+
|
|
90
|
+
<!-- example: invalid settings={"lifecycleTags":true,"reportDynamicTitles":true} -->
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
for (const symbol of ['AAPL', 'MSFT']) {
|
|
94
|
+
test('buys ' + symbol, async ({ page }) => {});
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
<!-- example: valid settings={"lifecycleTags":true,"reportDynamicTitles":true} -->
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
for (const symbol of ['AAPL', 'MSFT']) {
|
|
102
|
+
test(`buys ${symbol}`, async ({ page }) => {});
|
|
103
|
+
}
|
|
104
|
+
```
|
|
74
105
|
|
|
75
106
|
## Examples
|
|
76
107
|
|
|
@@ -79,34 +110,40 @@ Incorrect:
|
|
|
79
110
|
<!-- example: invalid -->
|
|
80
111
|
|
|
81
112
|
```js
|
|
82
|
-
test.skip('
|
|
113
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
83
114
|
```
|
|
84
115
|
|
|
85
116
|
<!-- example: invalid -->
|
|
86
117
|
|
|
87
118
|
```js
|
|
88
119
|
// SKIP: flaky on CI
|
|
89
|
-
test.skip('
|
|
120
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
90
121
|
```
|
|
91
122
|
|
|
92
123
|
<!-- example: invalid -->
|
|
93
124
|
|
|
94
125
|
```js
|
|
95
126
|
// SKIP: TODO
|
|
96
|
-
test.skip('
|
|
127
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
97
128
|
```
|
|
98
129
|
|
|
99
130
|
<!-- example: invalid -->
|
|
100
131
|
|
|
101
132
|
```js
|
|
102
|
-
// SKIP:
|
|
103
|
-
test.skip('
|
|
133
|
+
// SKIP: TRADE-481 broker sandbox is down
|
|
134
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
104
135
|
```
|
|
105
136
|
|
|
106
137
|
<!-- example: invalid settings={"lifecycleTags":true} -->
|
|
107
138
|
|
|
108
139
|
```js
|
|
109
|
-
test('one
|
|
140
|
+
test('closes all positions with one click @new', async ({ page }) => {});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
<!-- example: invalid settings={"framework":"jest"} -->
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
it.todo('caps position size at 2% of equity');
|
|
110
147
|
```
|
|
111
148
|
|
|
112
149
|
Correct:
|
|
@@ -114,24 +151,24 @@ Correct:
|
|
|
114
151
|
<!-- example: valid -->
|
|
115
152
|
|
|
116
153
|
```js
|
|
117
|
-
// SKIP:
|
|
118
|
-
test.skip('
|
|
154
|
+
// SKIP: TRADE-481
|
|
155
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
119
156
|
```
|
|
120
157
|
|
|
121
158
|
<!-- example: valid -->
|
|
122
159
|
|
|
123
160
|
```js
|
|
124
|
-
// FIXME:
|
|
125
|
-
test.describe.fixme('
|
|
126
|
-
test('
|
|
127
|
-
test('
|
|
161
|
+
// FIXME: TRADE-12
|
|
162
|
+
test.describe.fixme('risk limits', () => {
|
|
163
|
+
test('stops trading at the daily loss limit', async () => {});
|
|
164
|
+
test('caps position size at 2% of equity', async () => {});
|
|
128
165
|
});
|
|
129
166
|
```
|
|
130
167
|
|
|
131
168
|
<!-- example: valid -->
|
|
132
169
|
|
|
133
170
|
```js
|
|
134
|
-
test('
|
|
171
|
+
test('draws the price chart', async ({ page, browserName }) => {
|
|
135
172
|
test.skip(browserName === 'webkit', 'Layout differs on WebKit by design');
|
|
136
173
|
});
|
|
137
174
|
```
|
|
@@ -139,18 +176,41 @@ test('opens the dashboard', async ({ page, browserName }) => {
|
|
|
139
176
|
<!-- example: valid settings={"allowNotes":true} -->
|
|
140
177
|
|
|
141
178
|
```js
|
|
142
|
-
// SKIP:
|
|
143
|
-
test.skip('
|
|
179
|
+
// SKIP: TRADE-481 broker sandbox is down
|
|
180
|
+
test.skip('rejects a margin order above the buying power', async ({ page }) => {});
|
|
144
181
|
```
|
|
145
182
|
|
|
146
183
|
<!-- example: valid settings={"lifecycleTags":true} -->
|
|
147
184
|
|
|
148
185
|
```js
|
|
149
|
-
// NEW:
|
|
150
|
-
test('one
|
|
186
|
+
// NEW: TRADE-77
|
|
187
|
+
test('closes all positions with one click', { tag: '@new' }, async ({ page }) => {});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
<!-- example: valid settings={"framework":"vitest"} -->
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
import { describe, test } from 'vitest';
|
|
194
|
+
|
|
195
|
+
describe('risk limits', () => {
|
|
196
|
+
// TODO: TRADE-90
|
|
197
|
+
test.todo('caps position size at 2% of equity');
|
|
198
|
+
|
|
199
|
+
test.skipIf(process.env.CI)('streams live quotes', () => {});
|
|
200
|
+
});
|
|
151
201
|
```
|
|
152
202
|
|
|
153
203
|
## Options
|
|
154
204
|
|
|
155
205
|
This rule has no options of its own. It reads the shared options described in the
|
|
156
|
-
[README](../../README.md#options), usually set once with `testGovernance.configure({...})`.
|
|
206
|
+
[README](../../README.md#options), usually set once with `testGovernance.configure({...})`. These
|
|
207
|
+
change what it reports:
|
|
208
|
+
|
|
209
|
+
- `ticket` and `placeholders`: which tickets are accepted.
|
|
210
|
+
- `lifecycleTags`, `states` and `customStates`: which states need a marker, what the marker is
|
|
211
|
+
called, and, for a state with its own `ticket`, which tickets it accepts.
|
|
212
|
+
- `requireTicketForConditional`: conditional skips need a ticket too.
|
|
213
|
+
- `allowBlankLine`: blank lines may separate a marker from its test.
|
|
214
|
+
- `allowNotes`: text may follow the tickets.
|
|
215
|
+
- `reportDynamicTitles`: report titles whose tags can't be read (see [Dynamic titles](#dynamic-titles)).
|
|
216
|
+
- `framework` and `testFunctions`: which functions count as the framework's `test`.
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-test-governance",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ESLint rules that require a ticket comment above skipped, fixme and tagged Playwright tests.",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "ESLint rules that require a ticket comment above skipped, fixme, todo and tagged Playwright, Jest and Vitest tests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
7
7
|
"eslintplugin",
|
|
8
8
|
"eslint-plugin",
|
|
9
9
|
"playwright",
|
|
10
|
+
"jest",
|
|
11
|
+
"vitest",
|
|
10
12
|
"testing"
|
|
11
13
|
],
|
|
12
14
|
"homepage": "https://github.com/Huldoser/eslint-plugin-test-governance#readme",
|
|
@@ -35,7 +37,6 @@
|
|
|
35
37
|
"types": "./dist/index.d.ts",
|
|
36
38
|
"files": [
|
|
37
39
|
"dist",
|
|
38
|
-
"!dist/**/*.map",
|
|
39
40
|
"docs",
|
|
40
41
|
"CHANGELOG.md"
|
|
41
42
|
],
|
|
@@ -45,8 +46,8 @@
|
|
|
45
46
|
"scripts": {
|
|
46
47
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
|
|
47
48
|
"typecheck": "tsc -p tsconfig.json",
|
|
48
|
-
"test": "
|
|
49
|
-
"test:coverage": "
|
|
49
|
+
"test": "node --test 'tests/**/*.test.ts'",
|
|
50
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-branches=100 --test-coverage-functions=100 --test-coverage-lines=100 'tests/**/*.test.ts'",
|
|
50
51
|
"test:pack": "node scripts/pack-smoke.mjs",
|
|
51
52
|
"docs": "npm run build && eslint-doc-generator",
|
|
52
53
|
"docs:check": "npm run build && eslint-doc-generator --check",
|
|
@@ -63,15 +64,11 @@
|
|
|
63
64
|
"@types/node": "^26.6.3",
|
|
64
65
|
"@typescript-eslint/parser": "^8.71.0",
|
|
65
66
|
"@typescript-eslint/utils": "^8.71.0",
|
|
66
|
-
"@vitest/coverage-v8": "^5.0.2",
|
|
67
67
|
"eslint": "^10.11.0",
|
|
68
|
-
"eslint-config-prettier": "^10.1.8",
|
|
69
68
|
"eslint-doc-generator": "^3.7.1",
|
|
70
69
|
"eslint-plugin-eslint-plugin": "^7.6.2",
|
|
71
|
-
"globals": "^17.13.0",
|
|
72
70
|
"prettier": "^3.9.9",
|
|
73
71
|
"typescript": "6.0",
|
|
74
|
-
"typescript-eslint": "^8.71.0"
|
|
75
|
-
"vitest": "^5.0.2"
|
|
72
|
+
"typescript-eslint": "^8.71.0"
|
|
76
73
|
}
|
|
77
74
|
}
|