Skip to content

Commit 3f5751e

Browse files
feat(no-conditional-in-test): only report optional chaining when allowOptionalChaining is false (#1934)
1 parent 2cbd92b commit 3f5751e

File tree

3 files changed

+155
-35
lines changed

3 files changed

+155
-35
lines changed

docs/rules/no-conditional-in-test.md

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ devoted to it.
1212
## Rule details
1313

1414
This rule reports on any use of a conditional statement such as `if`, `switch`,
15-
ternary expressions, and optional chaining.
15+
and ternary expressions.
1616

1717
Examples of **incorrect** code for this rule:
1818

@@ -36,10 +36,6 @@ it('bar', () => {
3636
expect(fixtures.length).toBeGreaterThan(-1);
3737
});
3838

39-
it('baz', () => {
40-
const value = obj?.bar;
41-
});
42-
4339
it('qux', async () => {
4440
const promiseValue = () => {
4541
return something instanceof Promise
@@ -81,11 +77,47 @@ const promiseValue = something => {
8177
return something instanceof Promise ? something : Promise.resolve(something);
8278
};
8379

84-
it('baz', () => {
85-
const value = obj!.bar;
86-
});
87-
8880
it('qux', async () => {
8981
await expect(promiseValue()).resolves.toBe(1);
9082
});
9183
```
84+
85+
## Options
86+
87+
```json
88+
{
89+
"jest/no-conditional-in-test": [
90+
"error",
91+
{
92+
"allowOptionalChaining": true
93+
}
94+
]
95+
}
96+
```
97+
98+
### `allowOptionalChaining`
99+
100+
Default: `true`
101+
102+
When set to `false`, optional chaining (`?.`) inside test bodies will be
103+
reported as a conditional.
104+
105+
Examples of **incorrect** code when `allowOptionalChaining` is `false`:
106+
107+
```js
108+
it('foo', () => {
109+
const value = obj?.bar;
110+
});
111+
112+
it('bar', () => {
113+
obj?.foo();
114+
});
115+
```
116+
117+
Examples of **correct** code when `allowOptionalChaining` is `false`:
118+
119+
```js
120+
it('foo', () => {
121+
const value = obj!.bar;
122+
});
123+
```

src/rules/__tests__/no-conditional-in-test.test.ts

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -954,46 +954,106 @@ ruleTester.run('optional chaining', rule, {
954954
valid: [
955955
'const x = obj?.foo',
956956
dedent`
957-
const foo = obj?.bar;
958-
959957
it('foo', () => {
960-
expect(foo).toBe(undefined);
961-
});
958+
const value = obj?.bar;
959+
})
962960
`,
963961
dedent`
964-
describe('foo', () => {
965-
const val = obj?.bar;
962+
it('foo', () => {
963+
obj?.foo?.bar;
966964
})
967965
`,
968966
dedent`
969-
describe('foo', () => {
970-
beforeEach(() => {
971-
const val = obj?.bar;
972-
});
967+
it('foo', () => {
968+
obj?.foo();
973969
})
974970
`,
975971
dedent`
976-
describe('foo', () => {
977-
afterEach(() => {
978-
const val = obj?.bar;
979-
});
972+
it('foo', () => {
973+
obj?.[key];
980974
})
981975
`,
982976
dedent`
983-
const values = something.map(thing => thing?.foo);
977+
test('foo', () => {
978+
obj?.bar;
979+
})
980+
`,
981+
dedent`
982+
it('is valid', () => {
983+
const values = something.map(thing => thing?.foo);
984984
985-
it('valid', () => {
986985
expect(values).toStrictEqual(['foo']);
987986
});
988987
`,
989-
dedent`
990-
describe('valid', () => {
988+
],
989+
invalid: [],
990+
});
991+
992+
ruleTester.run('optional chaining with allowOptionalChaining=false', rule, {
993+
valid: [
994+
{
995+
code: 'const x = obj?.foo',
996+
options: [{ allowOptionalChaining: false }],
997+
},
998+
{
999+
code: dedent`
1000+
const foo = obj?.bar;
1001+
1002+
it('foo', () => {
1003+
expect(foo).toBe(undefined);
1004+
});
1005+
`,
1006+
options: [{ allowOptionalChaining: false }],
1007+
},
1008+
{
1009+
code: dedent`
1010+
describe('foo', () => {
1011+
const val = obj?.bar;
1012+
})
1013+
`,
1014+
options: [{ allowOptionalChaining: false }],
1015+
},
1016+
{
1017+
code: dedent`
1018+
describe('foo', () => {
1019+
beforeEach(() => {
1020+
const val = obj?.bar;
1021+
});
1022+
})
1023+
`,
1024+
options: [{ allowOptionalChaining: false }],
1025+
},
1026+
{
1027+
code: dedent`
1028+
describe('foo', () => {
1029+
afterEach(() => {
1030+
const val = obj?.bar;
1031+
});
1032+
})
1033+
`,
1034+
options: [{ allowOptionalChaining: false }],
1035+
},
1036+
{
1037+
code: dedent`
9911038
const values = something.map(thing => thing?.foo);
992-
it('still valid', () => {
1039+
1040+
it('valid', () => {
9931041
expect(values).toStrictEqual(['foo']);
9941042
});
995-
});
996-
`,
1043+
`,
1044+
options: [{ allowOptionalChaining: false }],
1045+
},
1046+
{
1047+
code: dedent`
1048+
describe('valid', () => {
1049+
const values = something.map(thing => thing?.foo);
1050+
it('still valid', () => {
1051+
expect(values).toStrictEqual(['foo']);
1052+
});
1053+
});
1054+
`,
1055+
options: [{ allowOptionalChaining: false }],
1056+
},
9971057
],
9981058
invalid: [
9991059
{
@@ -1002,6 +1062,7 @@ ruleTester.run('optional chaining', rule, {
10021062
const value = obj?.bar;
10031063
})
10041064
`,
1065+
options: [{ allowOptionalChaining: false }],
10051066
errors: [
10061067
{
10071068
messageId: 'conditionalInTest',
@@ -1016,6 +1077,7 @@ ruleTester.run('optional chaining', rule, {
10161077
obj?.foo?.bar;
10171078
})
10181079
`,
1080+
options: [{ allowOptionalChaining: false }],
10191081
errors: [
10201082
{
10211083
messageId: 'conditionalInTest',
@@ -1030,6 +1092,7 @@ ruleTester.run('optional chaining', rule, {
10301092
obj?.foo();
10311093
})
10321094
`,
1095+
options: [{ allowOptionalChaining: false }],
10331096
errors: [
10341097
{
10351098
messageId: 'conditionalInTest',
@@ -1044,6 +1107,7 @@ ruleTester.run('optional chaining', rule, {
10441107
obj?.[key];
10451108
})
10461109
`,
1110+
options: [{ allowOptionalChaining: false }],
10471111
errors: [
10481112
{
10491113
messageId: 'conditionalInTest',
@@ -1058,6 +1122,7 @@ ruleTester.run('optional chaining', rule, {
10581122
obj?.bar;
10591123
})
10601124
`,
1125+
options: [{ allowOptionalChaining: false }],
10611126
errors: [
10621127
{
10631128
messageId: 'conditionalInTest',
@@ -1072,6 +1137,7 @@ ruleTester.run('optional chaining', rule, {
10721137
obj?.bar;
10731138
})
10741139
`,
1140+
options: [{ allowOptionalChaining: false }],
10751141
errors: [
10761142
{
10771143
messageId: 'conditionalInTest',
@@ -1086,6 +1152,7 @@ ruleTester.run('optional chaining', rule, {
10861152
obj?.bar;
10871153
})
10881154
`,
1155+
options: [{ allowOptionalChaining: false }],
10891156
errors: [
10901157
{
10911158
messageId: 'conditionalInTest',
@@ -1100,6 +1167,7 @@ ruleTester.run('optional chaining', rule, {
11001167
obj?.bar;
11011168
})
11021169
`,
1170+
options: [{ allowOptionalChaining: false }],
11031171
errors: [
11041172
{
11051173
messageId: 'conditionalInTest',
@@ -1114,6 +1182,7 @@ ruleTester.run('optional chaining', rule, {
11141182
obj?.bar;
11151183
})
11161184
`,
1185+
options: [{ allowOptionalChaining: false }],
11171186
errors: [
11181187
{
11191188
messageId: 'conditionalInTest',
@@ -1128,6 +1197,7 @@ ruleTester.run('optional chaining', rule, {
11281197
obj?.bar;
11291198
})
11301199
`,
1200+
options: [{ allowOptionalChaining: false }],
11311201
errors: [
11321202
{
11331203
messageId: 'conditionalInTest',
@@ -1144,6 +1214,7 @@ ruleTester.run('optional chaining', rule, {
11441214
})
11451215
})
11461216
`,
1217+
options: [{ allowOptionalChaining: false }],
11471218
errors: [
11481219
{
11491220
messageId: 'conditionalInTest',
@@ -1160,6 +1231,7 @@ ruleTester.run('optional chaining', rule, {
11601231
expect(values).toStrictEqual(['foo']);
11611232
});
11621233
`,
1234+
options: [{ allowOptionalChaining: false }],
11631235
errors: [
11641236
{
11651237
messageId: 'conditionalInTest',

src/rules/no-conditional-in-test.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import type { TSESTree } from '@typescript-eslint/utils';
22
import { createRule, isTypeOfJestFnCall } from './utils';
33

4-
export default createRule({
4+
interface RuleOptions {
5+
allowOptionalChaining?: boolean;
6+
}
7+
8+
export default createRule<[RuleOptions], 'conditionalInTest'>({
59
name: __filename,
610
meta: {
711
docs: {
@@ -11,10 +15,20 @@ export default createRule({
1115
conditionalInTest: 'Avoid having conditionals in tests',
1216
},
1317
type: 'problem',
14-
schema: [],
18+
schema: [
19+
{
20+
type: 'object',
21+
properties: {
22+
allowOptionalChaining: {
23+
type: 'boolean',
24+
},
25+
},
26+
additionalProperties: false,
27+
},
28+
],
1529
},
16-
defaultOptions: [],
17-
create(context) {
30+
defaultOptions: [{ allowOptionalChaining: true }],
31+
create(context, [{ allowOptionalChaining }]) {
1832
let inTestCase = false;
1933

2034
const maybeReportConditional = (node: TSESTree.Node) => {
@@ -41,7 +55,9 @@ export default createRule({
4155
SwitchStatement: maybeReportConditional,
4256
ConditionalExpression: maybeReportConditional,
4357
LogicalExpression: maybeReportConditional,
44-
ChainExpression: maybeReportConditional,
58+
...(!allowOptionalChaining && {
59+
ChainExpression: maybeReportConditional,
60+
}),
4561
};
4662
},
4763
});

0 commit comments

Comments
 (0)