@checkdigit/eslint-plugin 6.6.0-PR.75-b2d2 → 6.6.0-PR.75-1cd1
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/dist-cjs/index.cjs +99257 -92
- package/dist-cjs/metafile.json +10519 -134
- package/dist-mjs/ast/format.mjs +2 -2
- package/dist-mjs/ast/tree.mjs +22 -4
- package/dist-mjs/index.mjs +7 -5
- package/dist-mjs/no-fixture-headers.mjs +98 -0
- package/dist-mjs/no-fixture.mjs +108 -49
- package/dist-types/ast/tree.d.ts +3 -1
- package/dist-types/index.d.ts +2 -1
- package/dist-types/no-fixture-headers.d.ts +4 -0
- package/dist-types/no-fixture.d.ts +1 -1
- package/package.json +1 -1
- package/src/ast/format.ts +1 -1
- package/src/ast/tree.ts +24 -3
- package/src/index.ts +3 -1
- package/src/no-fixture-headers.ts +134 -0
- package/src/no-fixture.ts +180 -91
package/src/no-fixture.ts
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/* eslint-disable no-console */
|
|
10
|
-
|
|
11
9
|
import type {
|
|
12
10
|
AwaitExpression,
|
|
13
11
|
CallExpression,
|
|
@@ -18,8 +16,8 @@ import type {
|
|
|
18
16
|
SimpleCallExpression,
|
|
19
17
|
VariableDeclaration,
|
|
20
18
|
} from 'estree';
|
|
21
|
-
import type
|
|
22
|
-
import {
|
|
19
|
+
import { type Rule, type Scope, SourceCode } from 'eslint';
|
|
20
|
+
import { getEnclosingScopeNode, getEnclosingStatement, getParent } from './ast/tree';
|
|
23
21
|
import { strict as assert } from 'node:assert';
|
|
24
22
|
import getDocumentationUrl from './get-documentation-url';
|
|
25
23
|
import { getIndentation } from './ast/format';
|
|
@@ -33,10 +31,12 @@ interface FixtureCallInformation {
|
|
|
33
31
|
requestBody?: Expression;
|
|
34
32
|
requestHeaders?: { name: Expression; value: Expression }[];
|
|
35
33
|
assertions?: Expression[][];
|
|
34
|
+
inlineStatementNode?: Node;
|
|
35
|
+
inlineBodyReference?: MemberExpression;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// recursively analyze the fixture/supertest call chain to collect information of request/response
|
|
39
|
-
function analyzeFixtureCall(call: SimpleCallExpression, results: FixtureCallInformation) {
|
|
39
|
+
function analyzeFixtureCall(call: SimpleCallExpression, results: FixtureCallInformation, sourceCode: SourceCode) {
|
|
40
40
|
const parent = getParent(call);
|
|
41
41
|
assert.ok(parent, 'parent should exist for fixture/supertest call node');
|
|
42
42
|
|
|
@@ -47,11 +47,18 @@ function analyzeFixtureCall(call: SimpleCallExpression, results: FixtureCallInfo
|
|
|
47
47
|
results.rootNode = parent;
|
|
48
48
|
} else if (parent.type === 'AwaitExpression') {
|
|
49
49
|
results.fixtureNode = call;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
results.rootNode =
|
|
50
|
+
const enclosingStatement = getEnclosingStatement(parent);
|
|
51
|
+
assert.ok(enclosingStatement);
|
|
52
|
+
const awaitParent = getParent(parent);
|
|
53
|
+
if (awaitParent?.type === 'MemberExpression') {
|
|
54
|
+
results.rootNode = parent;
|
|
55
|
+
results.inlineStatementNode = enclosingStatement;
|
|
56
|
+
if (awaitParent.property.type === 'Identifier' && awaitParent.property.name === 'body') {
|
|
57
|
+
results.inlineBodyReference = awaitParent;
|
|
58
|
+
}
|
|
59
|
+
} else if (enclosingStatement.type === 'VariableDeclaration') {
|
|
60
|
+
results.variableDeclaration = enclosingStatement;
|
|
61
|
+
results.rootNode = enclosingStatement;
|
|
55
62
|
} else {
|
|
56
63
|
results.rootNode = parent;
|
|
57
64
|
}
|
|
@@ -77,10 +84,10 @@ function analyzeFixtureCall(call: SimpleCallExpression, results: FixtureCallInfo
|
|
|
77
84
|
nextCall = setRequestHeaderCall;
|
|
78
85
|
}
|
|
79
86
|
} else {
|
|
80
|
-
throw new Error(`Unexpected expression in fixture/supertest call ${
|
|
87
|
+
throw new Error(`Unexpected expression in fixture/supertest call ${sourceCode.getText(parent)}`);
|
|
81
88
|
}
|
|
82
89
|
if (nextCall) {
|
|
83
|
-
analyzeFixtureCall(nextCall, results);
|
|
90
|
+
analyzeFixtureCall(nextCall, results, sourceCode);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
|
|
@@ -91,8 +98,9 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
91
98
|
bodyReferences: MemberExpression[];
|
|
92
99
|
headersReferences: MemberExpression[];
|
|
93
100
|
statusReferences: MemberExpression[];
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
destructuringBodyVariable?: Scope.Variable;
|
|
102
|
+
destructuringHeadersVariable?: Scope.Variable;
|
|
103
|
+
destructuringHeadersReferences?: MemberExpression[] | undefined;
|
|
96
104
|
} = {
|
|
97
105
|
bodyReferences: [],
|
|
98
106
|
headersReferences: [],
|
|
@@ -102,6 +110,7 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
102
110
|
if (fixtureInformation.variableDeclaration) {
|
|
103
111
|
const responseVariables = scopeManager.getDeclaredVariables(fixtureInformation.variableDeclaration);
|
|
104
112
|
for (const responseVariable of responseVariables) {
|
|
113
|
+
const scope = responseVariable.scope;
|
|
105
114
|
const identifier = responseVariable.identifiers[0];
|
|
106
115
|
assert.ok(identifier);
|
|
107
116
|
const identifierParent = getParent(identifier);
|
|
@@ -109,53 +118,61 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
109
118
|
if (identifierParent.type === 'VariableDeclarator') {
|
|
110
119
|
// e.g. const response = ...
|
|
111
120
|
results.variable = responseVariable;
|
|
121
|
+
const responseReferences = responseVariable.references.map((responseReference) =>
|
|
122
|
+
getParent(responseReference.identifier),
|
|
123
|
+
);
|
|
112
124
|
// e.g. response.body
|
|
113
|
-
results.bodyReferences =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
node.property.name === 'body',
|
|
122
|
-
);
|
|
125
|
+
results.bodyReferences = responseReferences.filter(
|
|
126
|
+
(node): node is MemberExpression =>
|
|
127
|
+
node !== null &&
|
|
128
|
+
node !== undefined &&
|
|
129
|
+
node.type === 'MemberExpression' &&
|
|
130
|
+
node.property.type === 'Identifier' &&
|
|
131
|
+
node.property.name === 'body',
|
|
132
|
+
);
|
|
123
133
|
// e.g. response.headers / response.header / response.get()
|
|
124
|
-
results.headersReferences =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
(node.property.name === 'header' || node.property.name === 'headers' || node.property.name === 'get'),
|
|
133
|
-
);
|
|
134
|
+
results.headersReferences = responseReferences.filter(
|
|
135
|
+
(node): node is MemberExpression =>
|
|
136
|
+
node !== null &&
|
|
137
|
+
node !== undefined &&
|
|
138
|
+
node.type === 'MemberExpression' &&
|
|
139
|
+
node.property.type === 'Identifier' &&
|
|
140
|
+
(node.property.name === 'header' || node.property.name === 'headers' || node.property.name === 'get'),
|
|
141
|
+
);
|
|
134
142
|
// e.g. response.status / response.statusCode
|
|
135
|
-
results.statusReferences =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
(node.property.name === 'status' || node.property.name === 'statusCode'),
|
|
144
|
-
);
|
|
143
|
+
results.statusReferences = responseReferences.filter(
|
|
144
|
+
(node): node is MemberExpression =>
|
|
145
|
+
node !== null &&
|
|
146
|
+
node !== undefined &&
|
|
147
|
+
node.type === 'MemberExpression' &&
|
|
148
|
+
node.property.type === 'Identifier' &&
|
|
149
|
+
(node.property.name === 'status' || node.property.name === 'statusCode'),
|
|
150
|
+
);
|
|
145
151
|
} else if (
|
|
146
152
|
// body reference through destruction/renaming, e.g. "const { body } = ..."
|
|
147
153
|
identifierParent.type === 'Property' &&
|
|
148
154
|
identifierParent.key.type === 'Identifier' &&
|
|
149
155
|
identifierParent.key.name === 'body'
|
|
150
156
|
) {
|
|
151
|
-
results.
|
|
157
|
+
results.destructuringBodyVariable = responseVariable;
|
|
152
158
|
} else if (
|
|
153
159
|
// header reference through destruction/renaming, e.g. "const { headers } = ..."
|
|
154
160
|
identifierParent.type === 'Property' &&
|
|
155
161
|
identifierParent.key.type === 'Identifier' &&
|
|
156
162
|
identifierParent.key.name === 'headers'
|
|
157
163
|
) {
|
|
158
|
-
results.
|
|
164
|
+
results.destructuringHeadersVariable = responseVariable;
|
|
165
|
+
results.destructuringHeadersReferences = scope.set
|
|
166
|
+
.get(responseVariable.name)
|
|
167
|
+
?.references.map((reference) => reference.identifier)
|
|
168
|
+
.map(getParent)
|
|
169
|
+
.filter(
|
|
170
|
+
(parent): parent is MemberExpression =>
|
|
171
|
+
parent?.type === 'MemberExpression' &&
|
|
172
|
+
parent.property.type === 'Identifier' &&
|
|
173
|
+
parent.property.name !== 'get' &&
|
|
174
|
+
getParent(parent)?.type !== 'CallExpression',
|
|
175
|
+
);
|
|
159
176
|
} else {
|
|
160
177
|
throw new Error(`Unknown response variable reference: ${responseVariable.name}`);
|
|
161
178
|
}
|
|
@@ -174,12 +191,13 @@ function isValidPropertyName(name: unknown) {
|
|
|
174
191
|
return typeof name === 'string' && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/u.test(name);
|
|
175
192
|
}
|
|
176
193
|
|
|
194
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
177
195
|
function createResponseAssertions(
|
|
178
196
|
fixtureCallInformation: FixtureCallInformation,
|
|
179
197
|
sourceCode: SourceCode,
|
|
180
|
-
|
|
198
|
+
responseVariableName: string,
|
|
199
|
+
destructuringResponseHeadersVariable: Scope.Variable | undefined,
|
|
181
200
|
) {
|
|
182
|
-
// [TODO:] make sure status assertion is ordered as the first
|
|
183
201
|
let statusAssertion: string | undefined;
|
|
184
202
|
const nonStatusAssertions: string[] = [];
|
|
185
203
|
for (const expectArguments of fixtureCallInformation.assertions ?? []) {
|
|
@@ -190,20 +208,32 @@ function createResponseAssertions(
|
|
|
190
208
|
(assertionArgument.type === 'MemberExpression' &&
|
|
191
209
|
assertionArgument.object.type === 'Identifier' &&
|
|
192
210
|
assertionArgument.object.name === 'StatusCodes') ||
|
|
193
|
-
assertionArgument.type === 'Literal'
|
|
211
|
+
assertionArgument.type === 'Literal' ||
|
|
212
|
+
sourceCode.getText(assertionArgument).includes('StatusCodes.')
|
|
194
213
|
) {
|
|
195
214
|
// status code assertion
|
|
196
|
-
statusAssertion = `assert.equal(${
|
|
215
|
+
statusAssertion = `assert.equal(${responseVariableName}.status, ${sourceCode.getText(assertionArgument)})`;
|
|
197
216
|
} else if (assertionArgument.type === 'ArrowFunctionExpression') {
|
|
198
|
-
// callback assertion
|
|
199
|
-
|
|
217
|
+
// callback assertion using arrow function
|
|
218
|
+
let functionBody = sourceCode.getText(assertionArgument.body);
|
|
219
|
+
|
|
220
|
+
const [originalResponseArgument] = assertionArgument.params;
|
|
221
|
+
assert.ok(originalResponseArgument?.type === 'Identifier');
|
|
222
|
+
const originalResponseArgumentName = originalResponseArgument.name;
|
|
223
|
+
if (originalResponseArgumentName !== responseVariableName) {
|
|
224
|
+
functionBody = functionBody.replace(
|
|
225
|
+
new RegExp(`\\b${originalResponseArgumentName}\\b`, 'ug'),
|
|
226
|
+
responseVariableName,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
nonStatusAssertions.push(`assert.ok(${functionBody})`);
|
|
200
230
|
} else if (assertionArgument.type === 'Identifier') {
|
|
201
|
-
// callback assertion
|
|
202
|
-
nonStatusAssertions.push(`assert.ok(${sourceCode.getText(assertionArgument)}(${
|
|
231
|
+
// callback assertion using function reference
|
|
232
|
+
nonStatusAssertions.push(`assert.ok(${sourceCode.getText(assertionArgument)}(${responseVariableName}))`);
|
|
203
233
|
} else if (assertionArgument.type === 'ObjectExpression' || assertionArgument.type === 'CallExpression') {
|
|
204
234
|
// body deep equal assertion
|
|
205
235
|
nonStatusAssertions.push(
|
|
206
|
-
`assert.deepEqual(await ${
|
|
236
|
+
`assert.deepEqual(await ${responseVariableName}.json(), ${sourceCode.getText(assertionArgument)})`,
|
|
207
237
|
);
|
|
208
238
|
} else {
|
|
209
239
|
throw new Error(`Unexpected Supertest assertion argument: ".expect(${sourceCode.getText(assertionArgument)})`);
|
|
@@ -212,13 +242,17 @@ function createResponseAssertions(
|
|
|
212
242
|
// header assertion
|
|
213
243
|
const [headerName, headerValue] = expectArguments;
|
|
214
244
|
assert.ok(headerName && headerValue);
|
|
245
|
+
const headersReference =
|
|
246
|
+
destructuringResponseHeadersVariable !== undefined
|
|
247
|
+
? destructuringResponseHeadersVariable.name
|
|
248
|
+
: `${responseVariableName}.headers`;
|
|
215
249
|
if (headerValue.type === 'Literal' && headerValue.value instanceof RegExp) {
|
|
216
250
|
nonStatusAssertions.push(
|
|
217
|
-
`assert.ok(${
|
|
251
|
+
`assert.ok(${headersReference}.get(${sourceCode.getText(headerName)}).match(${sourceCode.getText(headerValue)}))`,
|
|
218
252
|
);
|
|
219
253
|
} else {
|
|
220
254
|
nonStatusAssertions.push(
|
|
221
|
-
`assert.equal(${
|
|
255
|
+
`assert.equal(${headersReference}.get(${sourceCode.getText(headerName)}), ${sourceCode.getText(headerValue)})`,
|
|
222
256
|
);
|
|
223
257
|
}
|
|
224
258
|
}
|
|
@@ -236,18 +270,15 @@ function getResponseVariableNameToUse(
|
|
|
236
270
|
) {
|
|
237
271
|
if (fixtureCallInformation.variableDeclaration) {
|
|
238
272
|
const firstDeclaration = fixtureCallInformation.variableDeclaration.declarations[0];
|
|
239
|
-
// [TODO:] double check if it works for destruction/rename declaration
|
|
240
273
|
if (firstDeclaration && firstDeclaration.id.type === 'Identifier') {
|
|
241
274
|
return firstDeclaration.id.name;
|
|
242
275
|
}
|
|
243
276
|
}
|
|
244
277
|
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
);
|
|
248
|
-
scopeManager.
|
|
249
|
-
assert.ok(closestFunctionExpression);
|
|
250
|
-
const scope = scopeManager.acquire(closestFunctionExpression);
|
|
278
|
+
const enclosingScopeNode = getEnclosingScopeNode(fixtureCallInformation.rootNode);
|
|
279
|
+
scopeManager.getDeclaredVariables(fixtureCallInformation.rootNode);
|
|
280
|
+
assert.ok(enclosingScopeNode);
|
|
281
|
+
const scope = scopeManager.acquire(enclosingScopeNode);
|
|
251
282
|
assert.ok(scope !== null);
|
|
252
283
|
let scopeVariables = scopeVariablesMap.get(scope);
|
|
253
284
|
if (!scopeVariables) {
|
|
@@ -268,6 +299,15 @@ function getResponseVariableNameToUse(
|
|
|
268
299
|
return responseVariableNameToUse;
|
|
269
300
|
}
|
|
270
301
|
|
|
302
|
+
function isResponseBodyRedefinition(responseBodyReference: MemberExpression): boolean {
|
|
303
|
+
const parent = getParent(responseBodyReference);
|
|
304
|
+
return parent?.type === 'VariableDeclarator' && parent.id.type === 'Identifier';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function getResponseBodyRetrievalText(responseVariableName: string) {
|
|
308
|
+
return `await ${responseVariableName}.json()`;
|
|
309
|
+
}
|
|
310
|
+
|
|
271
311
|
const rule: Rule.RuleModule = {
|
|
272
312
|
meta: {
|
|
273
313
|
type: 'suggestion',
|
|
@@ -278,17 +318,19 @@ const rule: Rule.RuleModule = {
|
|
|
278
318
|
messages: {
|
|
279
319
|
preferNativeFetch: 'Prefer native fetch API over customized fixture API.',
|
|
280
320
|
unknownError:
|
|
281
|
-
'Unknown error occurred: {{ error }}. Please manually convert the fixture API call to fetch API call.',
|
|
321
|
+
'Unknown error occurred in file "{{fileName}}": {{ error }}. Please manually convert the fixture API call to fetch API call.',
|
|
282
322
|
},
|
|
283
323
|
fixable: 'code',
|
|
284
324
|
schema: [],
|
|
285
325
|
},
|
|
326
|
+
// eslint-disable-next-line max-lines-per-function
|
|
286
327
|
create(context) {
|
|
287
328
|
const sourceCode = context.sourceCode;
|
|
288
329
|
const scopeManager = sourceCode.scopeManager;
|
|
289
330
|
const scopeVariablesMap = new Map<Scope.Scope, string[]>();
|
|
290
331
|
|
|
291
332
|
return {
|
|
333
|
+
// eslint-disable-next-line max-lines-per-function
|
|
292
334
|
'CallExpression[callee.object.object.name="fixture"][callee.object.property.name="api"]': (
|
|
293
335
|
fixtureCall: CallExpression,
|
|
294
336
|
) => {
|
|
@@ -302,15 +344,16 @@ const rule: Rule.RuleModule = {
|
|
|
302
344
|
assert.ok(urlArgumentNode !== undefined);
|
|
303
345
|
|
|
304
346
|
const fixtureCallInformation = {} as FixtureCallInformation;
|
|
305
|
-
analyzeFixtureCall(fixtureCall, fixtureCallInformation);
|
|
347
|
+
analyzeFixtureCall(fixtureCall, fixtureCallInformation, sourceCode);
|
|
306
348
|
|
|
307
349
|
const {
|
|
308
350
|
variable: responseVariable,
|
|
309
351
|
bodyReferences: responseBodyReferences,
|
|
310
352
|
headersReferences: responseHeadersReferences,
|
|
311
353
|
statusReferences: responseStatusReferences,
|
|
312
|
-
|
|
313
|
-
|
|
354
|
+
destructuringBodyVariable: destructuringResponseBodyVariable,
|
|
355
|
+
destructuringHeadersVariable: destructuringResponseHeadersVariable,
|
|
356
|
+
// destructuringHeadersReferences: destructuringResponseHeadersReferences,
|
|
314
357
|
} = analyzeResponseReferences(fixtureCallInformation, scopeManager);
|
|
315
358
|
|
|
316
359
|
// convert url from `/sample-service/v1/ping` to `${BASE_PATH}/ping`
|
|
@@ -346,17 +389,30 @@ const rule: Rule.RuleModule = {
|
|
|
346
389
|
scopeVariablesMap,
|
|
347
390
|
);
|
|
348
391
|
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
392
|
+
const isResponseBodyVariableRedefinitionNeeded =
|
|
393
|
+
destructuringResponseBodyVariable !== undefined ||
|
|
394
|
+
fixtureCallInformation.inlineBodyReference !== undefined ||
|
|
395
|
+
(responseBodyReferences.length > 0 && !responseBodyReferences.some(isResponseBodyRedefinition));
|
|
396
|
+
const redefineResponseBodyVariableName = `${responseVariableNameToUse}Body`;
|
|
397
|
+
|
|
398
|
+
const isResponseVariableRedefinitionNeeded =
|
|
399
|
+
(responseVariable === undefined && fixtureCallInformation.assertions !== undefined) ||
|
|
400
|
+
isResponseBodyVariableRedefinitionNeeded;
|
|
352
401
|
|
|
353
|
-
const responseBodyHeadersVariableRedefineLines =
|
|
402
|
+
const responseBodyHeadersVariableRedefineLines = isResponseVariableRedefinitionNeeded
|
|
354
403
|
? [
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
404
|
+
// eslint-disable-next-line no-nested-ternary
|
|
405
|
+
...(destructuringResponseBodyVariable
|
|
406
|
+
? [
|
|
407
|
+
`const ${destructuringResponseBodyVariable.name} = ${getResponseBodyRetrievalText(responseVariableNameToUse)}`,
|
|
408
|
+
]
|
|
409
|
+
: isResponseBodyVariableRedefinitionNeeded
|
|
410
|
+
? [
|
|
411
|
+
`const ${redefineResponseBodyVariableName} = ${getResponseBodyRetrievalText(responseVariableNameToUse)}`,
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
414
|
+
...(destructuringResponseHeadersVariable
|
|
415
|
+
? [`const ${destructuringResponseHeadersVariable.name} = ${responseVariableNameToUse}.headers`]
|
|
360
416
|
: []),
|
|
361
417
|
]
|
|
362
418
|
: [];
|
|
@@ -365,15 +421,16 @@ const rule: Rule.RuleModule = {
|
|
|
365
421
|
fixtureCallInformation,
|
|
366
422
|
sourceCode,
|
|
367
423
|
responseVariableNameToUse,
|
|
424
|
+
destructuringResponseHeadersVariable,
|
|
368
425
|
);
|
|
369
426
|
|
|
370
427
|
// add variable declaration if needed
|
|
371
428
|
const fetchCallText = `fetch(${fetchUrlArgumentText}, ${fetchRequestArgumentLines})`;
|
|
372
|
-
const fetchStatementText = !
|
|
429
|
+
const fetchStatementText = !isResponseVariableRedefinitionNeeded
|
|
373
430
|
? fetchCallText
|
|
374
431
|
: `const ${responseVariableNameToUse} = await ${fetchCallText}`;
|
|
375
432
|
|
|
376
|
-
const nodeToReplace =
|
|
433
|
+
const nodeToReplace = isResponseVariableRedefinitionNeeded
|
|
377
434
|
? fixtureCallInformation.rootNode
|
|
378
435
|
: fixtureCallInformation.fixtureNode;
|
|
379
436
|
const appendingAssignmentAndAssertionText = [
|
|
@@ -386,18 +443,35 @@ const rule: Rule.RuleModule = {
|
|
|
386
443
|
context.report({
|
|
387
444
|
node: fixtureCall,
|
|
388
445
|
messageId: 'preferNativeFetch',
|
|
446
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
389
447
|
*fix(fixer) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
448
|
+
if (fixtureCallInformation.inlineStatementNode) {
|
|
449
|
+
const preInlineDeclaration = [
|
|
450
|
+
fetchStatementText,
|
|
451
|
+
`${appendingAssignmentAndAssertionText};\n${indentation}`,
|
|
452
|
+
].join(``);
|
|
453
|
+
yield fixer.insertTextBefore(fixtureCallInformation.inlineStatementNode, preInlineDeclaration);
|
|
454
|
+
} else {
|
|
455
|
+
yield fixer.replaceText(nodeToReplace, fetchStatementText);
|
|
456
|
+
|
|
457
|
+
const needEndingSemiColon = sourceCode.getText(nodeToReplace).endsWith(';');
|
|
458
|
+
yield fixer.insertTextAfter(
|
|
459
|
+
nodeToReplace,
|
|
460
|
+
needEndingSemiColon ? `${appendingAssignmentAndAssertionText};` : appendingAssignmentAndAssertionText,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
397
463
|
|
|
398
464
|
// handle response body references
|
|
399
465
|
for (const responseBodyReference of responseBodyReferences) {
|
|
400
|
-
yield fixer.replaceText(
|
|
466
|
+
yield fixer.replaceText(
|
|
467
|
+
responseBodyReference,
|
|
468
|
+
isResponseBodyVariableRedefinitionNeeded || !isResponseBodyRedefinition(responseBodyReference)
|
|
469
|
+
? redefineResponseBodyVariableName
|
|
470
|
+
: getResponseBodyRetrievalText(responseVariableNameToUse),
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (fixtureCallInformation.inlineBodyReference) {
|
|
474
|
+
yield fixer.replaceText(fixtureCallInformation.inlineBodyReference, redefineResponseBodyVariableName);
|
|
401
475
|
}
|
|
402
476
|
|
|
403
477
|
// handle response headers references
|
|
@@ -414,9 +488,22 @@ const rule: Rule.RuleModule = {
|
|
|
414
488
|
const headerNameNode = parent.arguments[0];
|
|
415
489
|
headerName = sourceCode.getText(headerNameNode);
|
|
416
490
|
}
|
|
417
|
-
assert.ok(headerName);
|
|
491
|
+
assert.ok(headerName !== undefined);
|
|
418
492
|
yield fixer.replaceText(parent, `${responseVariableNameToUse}.headers.get(${headerName})`);
|
|
419
493
|
}
|
|
494
|
+
// if (destructuringResponseHeadersVariable !== undefined) {
|
|
495
|
+
// for (const destructuringResponseHeadersReference of destructuringResponseHeadersReferences ?? []) {
|
|
496
|
+
// const headerNameNode = destructuringResponseHeadersReference.property;
|
|
497
|
+
// const headerName = destructuringResponseHeadersReference.computed
|
|
498
|
+
// ? sourceCode.getText(headerNameNode)
|
|
499
|
+
// : `'${sourceCode.getText(headerNameNode)}'`;
|
|
500
|
+
|
|
501
|
+
// yield fixer.replaceText(
|
|
502
|
+
// destructuringResponseHeadersReference,
|
|
503
|
+
// `${destructuringResponseHeadersVariable.name}.get(${headerName})`,
|
|
504
|
+
// );
|
|
505
|
+
// }
|
|
506
|
+
// }
|
|
420
507
|
|
|
421
508
|
// convert response.statusCode to response.status
|
|
422
509
|
for (const responseStatusReference of responseStatusReferences) {
|
|
@@ -428,7 +515,7 @@ const rule: Rule.RuleModule = {
|
|
|
428
515
|
}
|
|
429
516
|
}
|
|
430
517
|
|
|
431
|
-
// handle direct return without await
|
|
518
|
+
// handle direct return statement without await, e.g. "return fixture.api.get(...);"
|
|
432
519
|
if (
|
|
433
520
|
fixtureCallInformation.rootNode.type === 'ReturnStatement' &&
|
|
434
521
|
fixtureCallInformation.assertions !== undefined
|
|
@@ -441,12 +528,14 @@ const rule: Rule.RuleModule = {
|
|
|
441
528
|
},
|
|
442
529
|
});
|
|
443
530
|
} catch (error) {
|
|
444
|
-
|
|
531
|
+
// eslint-disable-next-line no-console
|
|
532
|
+
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
|
445
533
|
context.report({
|
|
446
534
|
node: fixtureCall,
|
|
447
535
|
messageId: 'unknownError',
|
|
448
536
|
data: {
|
|
449
|
-
|
|
537
|
+
fileName: context.filename,
|
|
538
|
+
error: error instanceof Error ? error.toString() : JSON.stringify(error),
|
|
450
539
|
},
|
|
451
540
|
});
|
|
452
541
|
}
|