Skip to content

Commit 944cb50

Browse files
authored
Fix integration test and rule bugs caught by it (#2688)
1 parent e11b084 commit 944cb50

11 files changed

+175
-90
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@
9797
"listr2": "^8.3.3",
9898
"lodash-es": "^4.17.21",
9999
"markdownlint-cli": "^0.45.0",
100-
"memoize": "^10.1.0",
101100
"nano-spawn": "^1.0.2",
102101
"node-style-text": "^0.0.8",
103102
"npm-package-json-lint": "^8.0.0",

rules/prefer-class-fields.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {isSemicolonToken} from '@eslint-community/eslint-utils';
12
import getIndentString from './utils/get-indent-string.js';
23

34
const MESSAGE_ID_ERROR = 'prefer-class-fields/error';
@@ -86,9 +87,19 @@ const create = context => {
8687
yield removeFieldAssignment(node, sourceCode, fixer);
8788

8889
if (existingProperty) {
89-
yield existingProperty.value
90-
? fixer.replaceText(existingProperty.value, propertyValue)
91-
: fixer.insertTextAfter(existingProperty.key, ` = ${propertyValue}`);
90+
if (existingProperty.value) {
91+
yield fixer.replaceText(existingProperty.value, propertyValue);
92+
return;
93+
}
94+
95+
const text = ` = ${propertyValue}`;
96+
const lastToken = sourceCode.getLastToken(existingProperty);
97+
if (isSemicolonToken(lastToken)) {
98+
yield fixer.insertTextBefore(lastToken, text);
99+
return;
100+
}
101+
102+
yield fixer.insertTextAfter(existingProperty, `${text};`);
92103
return;
93104
}
94105

rules/prefer-string-replace-all.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ function getPatternReplacement(node) {
6060
return `\\u{${codePoint.toString(16)}}`;
6161
}
6262

63+
if (kind === 'octal') {
64+
return `\\u{${codePoint.toString(16)}}`;
65+
}
66+
6367
let character = raw;
6468
if (kind === 'identifier') {
6569
character = character.slice(1);

test/integration/run-eslint.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,4 @@ async function runEslint(project) {
155155
}
156156

157157
export default runEslint;
158+
export {UnicornEslintFatalError, UnicornIntegrationTestError};

test/integration/test.js

Lines changed: 39 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import spawn from 'nano-spawn';
88
import styleText from 'node-style-text';
99
import {outdent} from 'outdent';
1010
import {isCI} from 'ci-info';
11-
import memoize from 'memoize';
1211
import YAML from 'yaml';
1312
import allProjects from './projects.js';
14-
import runEslint from './run-eslint.js';
13+
import runEslint, {UnicornIntegrationTestError} from './run-eslint.js';
1514

1615
if (isCI) {
1716
const CI_CONFIG_FILE = new URL('../../.github/workflows/main.yml', import.meta.url);
@@ -61,51 +60,34 @@ if (projects.length === 0) {
6160
process.exit(0);
6261
}
6362

64-
const getBranch = memoize(async dirname => {
65-
const {stdout} = await spawn('git', ['branch', '--show-current'], {cwd: dirname});
66-
return stdout;
67-
});
63+
const execute = async project => {
64+
if (!fs.existsSync(project.location)) {
65+
await spawn('git', [
66+
'clone',
67+
project.repository,
68+
'--single-branch',
69+
'--depth',
70+
'1',
71+
project.location,
72+
], {stdout: 'inherit', stderr: 'inherit'});
73+
}
6874

69-
const execute = project => new Listr(
70-
[
71-
{
72-
title: 'Cloning',
73-
skip: () => fs.existsSync(project.location) ? 'Project already downloaded.' : false,
74-
task: () => spawn('git', [
75-
'clone',
76-
project.repository,
77-
'--single-branch',
78-
'--depth',
79-
'1',
80-
project.location,
81-
], {stdout: 'inherit', stderr: 'inherit'}),
82-
},
83-
{
84-
title: 'Running eslint',
85-
task: () => runEslint(project),
86-
},
87-
].map(({title, task, skip}) => ({
88-
title: `${project.name} / ${title}`,
89-
skip,
90-
task,
91-
})),
92-
{exitOnError: false},
93-
);
75+
await runEslint(project);
76+
};
9477

95-
async function printEslintError(eslintError) {
96-
const {message, project} = eslintError;
78+
function printEslintError(error) {
79+
const {message, project, errors} = error;
9780

9881
console.log();
9982
console.error(
10083
styleText.red.bold.underline(`[${project.name}]`),
10184
message,
10285
);
10386

104-
project.branch ??= await getBranch(project.location);
105-
for (const error of eslintError.errors) {
106-
let file = path.relative(project.location, error.eslintFile.filePath);
87+
for (const error of errors) {
88+
let file = path.relative(project.location, error.eslintFile.filePath).replaceAll('\\', '/');
10789
if (project.repository) {
108-
file = `${project.repository}/blob/${project.branch}/${file}`;
90+
file = `${project.repository}/blob/HEAD/${file}`;
10991
}
11092

11193
if (typeof error.eslintMessage.line === 'number') {
@@ -119,33 +101,30 @@ async function printEslintError(eslintError) {
119101
}
120102
}
121103

122-
async function printListrError(listrError) {
123-
process.exitCode = 1;
104+
async function printTestError(error) {
105+
process.exitCode ||= 1;
124106

125-
if (!listrError.errors) {
126-
console.error(listrError);
107+
if (!(error instanceof UnicornIntegrationTestError)) {
108+
console.error(error);
127109
return;
128110
}
129111

130-
for (const error of listrError.errors) {
131-
if (error.name !== 'UnicornIntegrationTestError') {
132-
console.error(error);
133-
continue;
134-
}
135-
136-
// eslint-disable-next-line no-await-in-loop
137-
await printEslintError(error);
138-
}
112+
printEslintError(error);
139113
}
140114

141-
try {
142-
await new Listr(
143-
projects.map(project => ({title: project.name, task: () => execute(project)})),
144-
{
145-
renderer: isCI ? 'verbose' : 'default',
146-
concurrent: true,
115+
await new Listr(
116+
projects.map(project => ({
117+
title: project.name,
118+
async task() {
119+
try {
120+
await execute(project);
121+
} catch (error) {
122+
await printTestError(error);
123+
}
147124
},
148-
).run();
149-
} catch (error) {
150-
await printListrError(error);
151-
}
125+
})),
126+
{
127+
renderer: isCI ? 'verbose' : 'default',
128+
concurrent: true,
129+
},
130+
).run();

test/prefer-class-fields.js

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import outdent from 'outdent';
2-
import {getTester} from './utils/test.js';
2+
import {getTester, parsers} from './utils/test.js';
33

44
const {test} = getTester(import.meta);
55

6-
const MESSAGE_ID = 'prefer-class-fields/error';
7-
86
test.snapshot({
97
valid: [
108
'class Foo {bar = 1}',
@@ -164,7 +162,12 @@ test.snapshot({
164162
],
165163
});
166164

167-
test.typescript({
165+
test.snapshot({
166+
testerOptions: {
167+
languageOptions: {
168+
parser: parsers.typescript,
169+
},
170+
},
168171
valid: [
169172
outdent`
170173
class Foo {
@@ -178,22 +181,20 @@ test.typescript({
178181
`,
179182
],
180183
invalid: [
181-
{
182-
code: outdent`
183-
class MyError extends Error {
184-
constructor(message: string) {
185-
this.name = "MyError";
186-
}
187-
}
188-
`,
189-
errors: [{messageId: MESSAGE_ID}],
190-
output: outdent`
191-
class MyError extends Error {
192-
constructor(message: string) {
193-
}
194-
name = "MyError";
195-
}
196-
`,
197-
},
184+
outdent`
185+
class MyError extends Error {
186+
constructor(message: string) {
187+
this.name = "MyError";
188+
}
189+
}
190+
`,
191+
outdent`
192+
class MyError extends Error {
193+
name: string;
194+
constructor(message: string) {
195+
this.name = "MyError";
196+
}
197+
}
198+
`,
198199
],
199200
});

test/prefer-string-replace-all.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ test.snapshot({
109109
String.raw`foo.replace(/\n/g, _)`,
110110
String.raw`foo.replace(/\u{20}/gu, _)`,
111111
String.raw`foo.replace(/\u{20}/gv, _)`,
112+
String.raw`foo.replace(/\1/g, _)`,
112113

113114
'foo.replaceAll(/a]/g, _)',
114115
String.raw`foo.replaceAll(/\r\n\u{1f600}/gu, _)`,

test/snapshots/prefer-class-fields.js.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,71 @@ Generated by [AVA](https://avajs.dev).
597597
5 | static// comment;␊
598598
6 | }␊
599599
`
600+
601+
## invalid(1): class MyError extends Error { constructor(message: string) { this.name = "MyError"; } }
602+
603+
> Input
604+
605+
`␊
606+
1 | class MyError extends Error {␊
607+
2 | constructor(message: string) {␊
608+
3 | this.name = "MyError";␊
609+
4 | }␊
610+
5 | }␊
611+
`
612+
613+
> Output
614+
615+
`␊
616+
1 | class MyError extends Error {␊
617+
2 | constructor(message: string) {␊
618+
3 | }␊
619+
4 | name = "MyError";␊
620+
5 | }␊
621+
`
622+
623+
> Error 1/1
624+
625+
`␊
626+
1 | class MyError extends Error {␊
627+
2 | constructor(message: string) {␊
628+
> 3 | this.name = "MyError";␊
629+
| ^^^^^^^^^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊
630+
4 | }␊
631+
5 | }␊
632+
`
633+
634+
## invalid(2): class MyError extends Error { name: string; constructor(message: string) { this.name = "MyError"; } }
635+
636+
> Input
637+
638+
`␊
639+
1 | class MyError extends Error {␊
640+
2 | name: string;␊
641+
3 | constructor(message: string) {␊
642+
4 | this.name = "MyError";␊
643+
5 | }␊
644+
6 | }␊
645+
`
646+
647+
> Output
648+
649+
`␊
650+
1 | class MyError extends Error {␊
651+
2 | name: string = "MyError";␊
652+
3 | constructor(message: string) {␊
653+
4 | }␊
654+
5 | }␊
655+
`
656+
657+
> Error 1/1
658+
659+
`␊
660+
1 | class MyError extends Error {␊
661+
2 | name: string;␊
662+
3 | constructor(message: string) {␊
663+
> 4 | this.name = "MyError";␊
664+
| ^^^^^^^^^^^^^^^^^^^^^^ Prefer class field declaration over \`this\` assignment in constructor for static values.␊
665+
5 | }␊
666+
6 | }␊
667+
`
181 Bytes
Binary file not shown.

test/snapshots/prefer-string-replace-all.js.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,28 @@ Generated by [AVA](https://avajs.dev).
883883
| ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊
884884
`
885885

886-
## invalid(42): foo.replaceAll(/a]/g, _)
886+
## invalid(42): foo.replace(/\1/g, _)
887+
888+
> Input
889+
890+
`␊
891+
1 | foo.replace(/\\1/g, _)␊
892+
`
893+
894+
> Output
895+
896+
`␊
897+
1 | foo.replaceAll('\\u{1}', _)␊
898+
`
899+
900+
> Error 1/1
901+
902+
`␊
903+
> 1 | foo.replace(/\\1/g, _)␊
904+
| ^^^^^^^ Prefer \`String#replaceAll()\` over \`String#replace()\`.␊
905+
`
906+
907+
## invalid(43): foo.replaceAll(/a]/g, _)
887908

888909
> Input
889910
@@ -904,7 +925,7 @@ Generated by [AVA](https://avajs.dev).
904925
| ^^^^^ This pattern can be replaced with 'a]'.␊
905926
`
906927

907-
## invalid(43): foo.replaceAll(/\r\n\u{1f600}/gu, _)
928+
## invalid(44): foo.replaceAll(/\r\n\u{1f600}/gu, _)
908929

909930
> Input
910931
@@ -925,7 +946,7 @@ Generated by [AVA](https://avajs.dev).
925946
| ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\n\\u{1f600}'.␊
926947
`
927948

928-
## invalid(44): foo.replaceAll(/\r\n\u{1f600}/gv, _)
949+
## invalid(45): foo.replaceAll(/\r\n\u{1f600}/gv, _)
929950

930951
> Input
931952
@@ -946,7 +967,7 @@ Generated by [AVA](https://avajs.dev).
946967
| ^^^^^^^^^^^^^^^^^ This pattern can be replaced with '\\r\\n\\u{1f600}'.␊
947968
`
948969

949-
## invalid(45): foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _)
970+
## invalid(46): foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _)
950971

951972
> Input
952973
@@ -967,7 +988,7 @@ Generated by [AVA](https://avajs.dev).
967988
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This pattern can be replaced with a string literal.␊
968989
`
969990

970-
## invalid(46): foo.replace(/(?!a)+/g, "")
991+
## invalid(47): foo.replace(/(?!a)+/g, "")
971992

972993
> Input
973994

0 commit comments

Comments
 (0)