@checkdigit/eslint-plugin 6.6.0-PR.75-a3df → 6.6.0-PR.75-f33d
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 +92926 -28
- package/dist-cjs/metafile.json +10446 -132
- package/dist-mjs/ast/format.mjs +2 -2
- package/dist-mjs/index.mjs +7 -5
- package/dist-mjs/no-fixture-headers.mjs +98 -0
- package/dist-mjs/no-fixture.mjs +26 -13
- 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/index.ts +3 -1
- package/src/no-fixture-headers.ts +134 -0
- package/src/no-fixture.ts +70 -39
package/src/no-fixture.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
SimpleCallExpression,
|
|
17
17
|
VariableDeclaration,
|
|
18
18
|
} from 'estree';
|
|
19
|
-
import type
|
|
19
|
+
import { type Rule, type Scope, SourceCode } from 'eslint';
|
|
20
20
|
import { getEnclosingScopeNode, getEnclosingStatement, getParent } from './ast/tree';
|
|
21
21
|
import { strict as assert } from 'node:assert';
|
|
22
22
|
import getDocumentationUrl from './get-documentation-url';
|
|
@@ -36,7 +36,7 @@ interface FixtureCallInformation {
|
|
|
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
|
|
|
@@ -84,10 +84,10 @@ function analyzeFixtureCall(call: SimpleCallExpression, results: FixtureCallInfo
|
|
|
84
84
|
nextCall = setRequestHeaderCall;
|
|
85
85
|
}
|
|
86
86
|
} else {
|
|
87
|
-
throw new Error(`Unexpected expression in fixture/supertest call ${
|
|
87
|
+
throw new Error(`Unexpected expression in fixture/supertest call ${sourceCode.getText(parent)}`);
|
|
88
88
|
}
|
|
89
89
|
if (nextCall) {
|
|
90
|
-
analyzeFixtureCall(nextCall, results);
|
|
90
|
+
analyzeFixtureCall(nextCall, results, sourceCode);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -100,6 +100,7 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
100
100
|
statusReferences: MemberExpression[];
|
|
101
101
|
destructuringBodyVariable?: Scope.Variable;
|
|
102
102
|
destructuringHeadersVariable?: Scope.Variable;
|
|
103
|
+
destructuringHeadersReferences?: MemberExpression[] | undefined;
|
|
103
104
|
} = {
|
|
104
105
|
bodyReferences: [],
|
|
105
106
|
headersReferences: [],
|
|
@@ -109,6 +110,7 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
109
110
|
if (fixtureInformation.variableDeclaration) {
|
|
110
111
|
const responseVariables = scopeManager.getDeclaredVariables(fixtureInformation.variableDeclaration);
|
|
111
112
|
for (const responseVariable of responseVariables) {
|
|
113
|
+
const scope = responseVariable.scope;
|
|
112
114
|
const identifier = responseVariable.identifiers[0];
|
|
113
115
|
assert.ok(identifier);
|
|
114
116
|
const identifierParent = getParent(identifier);
|
|
@@ -116,39 +118,36 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
116
118
|
if (identifierParent.type === 'VariableDeclarator') {
|
|
117
119
|
// e.g. const response = ...
|
|
118
120
|
results.variable = responseVariable;
|
|
121
|
+
const responseReferences = responseVariable.references.map((responseReference) =>
|
|
122
|
+
getParent(responseReference.identifier),
|
|
123
|
+
);
|
|
119
124
|
// e.g. response.body
|
|
120
|
-
results.bodyReferences =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
node.property.name === 'body',
|
|
129
|
-
);
|
|
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
|
+
);
|
|
130
133
|
// e.g. response.headers / response.header / response.get()
|
|
131
|
-
results.headersReferences =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
(node.property.name === 'header' || node.property.name === 'headers' || node.property.name === 'get'),
|
|
140
|
-
);
|
|
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
|
+
);
|
|
141
142
|
// e.g. response.status / response.statusCode
|
|
142
|
-
results.statusReferences =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
(node.property.name === 'status' || node.property.name === 'statusCode'),
|
|
151
|
-
);
|
|
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
|
+
);
|
|
152
151
|
} else if (
|
|
153
152
|
// body reference through destruction/renaming, e.g. "const { body } = ..."
|
|
154
153
|
identifierParent.type === 'Property' &&
|
|
@@ -163,6 +162,17 @@ function analyzeResponseReferences(fixtureInformation: FixtureCallInformation, s
|
|
|
163
162
|
identifierParent.key.name === 'headers'
|
|
164
163
|
) {
|
|
165
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
|
+
);
|
|
166
176
|
} else {
|
|
167
177
|
throw new Error(`Unknown response variable reference: ${responseVariable.name}`);
|
|
168
178
|
}
|
|
@@ -186,6 +196,7 @@ function createResponseAssertions(
|
|
|
186
196
|
fixtureCallInformation: FixtureCallInformation,
|
|
187
197
|
sourceCode: SourceCode,
|
|
188
198
|
responseVariableName: string,
|
|
199
|
+
destructuringResponseHeadersVariable: Scope.Variable | undefined,
|
|
189
200
|
) {
|
|
190
201
|
let statusAssertion: string | undefined;
|
|
191
202
|
const nonStatusAssertions: string[] = [];
|
|
@@ -231,13 +242,17 @@ function createResponseAssertions(
|
|
|
231
242
|
// header assertion
|
|
232
243
|
const [headerName, headerValue] = expectArguments;
|
|
233
244
|
assert.ok(headerName && headerValue);
|
|
245
|
+
const headersReference =
|
|
246
|
+
destructuringResponseHeadersVariable !== undefined
|
|
247
|
+
? destructuringResponseHeadersVariable.name
|
|
248
|
+
: `${responseVariableName}.headers`;
|
|
234
249
|
if (headerValue.type === 'Literal' && headerValue.value instanceof RegExp) {
|
|
235
250
|
nonStatusAssertions.push(
|
|
236
|
-
`assert.ok(${
|
|
251
|
+
`assert.ok(${headersReference}.get(${sourceCode.getText(headerName)}).match(${sourceCode.getText(headerValue)}))`,
|
|
237
252
|
);
|
|
238
253
|
} else {
|
|
239
254
|
nonStatusAssertions.push(
|
|
240
|
-
`assert.equal(${
|
|
255
|
+
`assert.equal(${headersReference}.get(${sourceCode.getText(headerName)}), ${sourceCode.getText(headerValue)})`,
|
|
241
256
|
);
|
|
242
257
|
}
|
|
243
258
|
}
|
|
@@ -255,7 +270,6 @@ function getResponseVariableNameToUse(
|
|
|
255
270
|
) {
|
|
256
271
|
if (fixtureCallInformation.variableDeclaration) {
|
|
257
272
|
const firstDeclaration = fixtureCallInformation.variableDeclaration.declarations[0];
|
|
258
|
-
// [TODO:] double check if it works for destruction/rename declaration
|
|
259
273
|
if (firstDeclaration && firstDeclaration.id.type === 'Identifier') {
|
|
260
274
|
return firstDeclaration.id.name;
|
|
261
275
|
}
|
|
@@ -316,6 +330,7 @@ const rule: Rule.RuleModule = {
|
|
|
316
330
|
const scopeVariablesMap = new Map<Scope.Scope, string[]>();
|
|
317
331
|
|
|
318
332
|
return {
|
|
333
|
+
// eslint-disable-next-line max-lines-per-function
|
|
319
334
|
'CallExpression[callee.object.object.name="fixture"][callee.object.property.name="api"]': (
|
|
320
335
|
fixtureCall: CallExpression,
|
|
321
336
|
) => {
|
|
@@ -329,7 +344,7 @@ const rule: Rule.RuleModule = {
|
|
|
329
344
|
assert.ok(urlArgumentNode !== undefined);
|
|
330
345
|
|
|
331
346
|
const fixtureCallInformation = {} as FixtureCallInformation;
|
|
332
|
-
analyzeFixtureCall(fixtureCall, fixtureCallInformation);
|
|
347
|
+
analyzeFixtureCall(fixtureCall, fixtureCallInformation, sourceCode);
|
|
333
348
|
|
|
334
349
|
const {
|
|
335
350
|
variable: responseVariable,
|
|
@@ -338,6 +353,7 @@ const rule: Rule.RuleModule = {
|
|
|
338
353
|
statusReferences: responseStatusReferences,
|
|
339
354
|
destructuringBodyVariable: destructuringResponseBodyVariable,
|
|
340
355
|
destructuringHeadersVariable: destructuringResponseHeadersVariable,
|
|
356
|
+
// destructuringHeadersReferences: destructuringResponseHeadersReferences,
|
|
341
357
|
} = analyzeResponseReferences(fixtureCallInformation, scopeManager);
|
|
342
358
|
|
|
343
359
|
// convert url from `/sample-service/v1/ping` to `${BASE_PATH}/ping`
|
|
@@ -405,6 +421,7 @@ const rule: Rule.RuleModule = {
|
|
|
405
421
|
fixtureCallInformation,
|
|
406
422
|
sourceCode,
|
|
407
423
|
responseVariableNameToUse,
|
|
424
|
+
destructuringResponseHeadersVariable,
|
|
408
425
|
);
|
|
409
426
|
|
|
410
427
|
// add variable declaration if needed
|
|
@@ -426,6 +443,7 @@ const rule: Rule.RuleModule = {
|
|
|
426
443
|
context.report({
|
|
427
444
|
node: fixtureCall,
|
|
428
445
|
messageId: 'preferNativeFetch',
|
|
446
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
429
447
|
*fix(fixer) {
|
|
430
448
|
if (fixtureCallInformation.inlineStatementNode) {
|
|
431
449
|
const preInlineDeclaration = [
|
|
@@ -470,9 +488,22 @@ const rule: Rule.RuleModule = {
|
|
|
470
488
|
const headerNameNode = parent.arguments[0];
|
|
471
489
|
headerName = sourceCode.getText(headerNameNode);
|
|
472
490
|
}
|
|
473
|
-
assert.ok(headerName);
|
|
491
|
+
assert.ok(headerName !== undefined);
|
|
474
492
|
yield fixer.replaceText(parent, `${responseVariableNameToUse}.headers.get(${headerName})`);
|
|
475
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
|
+
// }
|
|
476
507
|
|
|
477
508
|
// convert response.statusCode to response.status
|
|
478
509
|
for (const responseStatusReference of responseStatusReferences) {
|