1 | import { TSESLint } from '@typescript-eslint/experimental-utils';
|
2 | import * as parser from '@typescript-eslint/parser';
|
3 | import { readFileSync } from 'fs';
|
4 | import rule, { Options } from '../src/rules/config';
|
5 |
|
6 | const ruleTester = new TSESLint.RuleTester({
|
7 | parserOptions: {
|
8 | ecmaVersion: 6,
|
9 | sourceType: 'module',
|
10 | ecmaFeatures: {},
|
11 | |
12 |
|
13 |
|
14 |
|
15 | project: './tests/fixture-project/tsconfig.json',
|
16 | warnOnUnsupportedTypeScriptVersion: false,
|
17 | },
|
18 | parser: require.resolve('@typescript-eslint/parser'),
|
19 | });
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | const tslintRulesConfig: Options = [
|
25 | {
|
26 | rules: {
|
27 | semicolon: [true, 'always'],
|
28 | },
|
29 | },
|
30 | ];
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | const tslintRulesDirectoryConfig: Options = [
|
36 | {
|
37 | rulesDirectory: ['./tests/test-tslint-rules-directory'],
|
38 | rules: {
|
39 | 'always-fail': {
|
40 | severity: 'error',
|
41 | },
|
42 | },
|
43 | },
|
44 | ];
|
45 |
|
46 | ruleTester.run('tslint/config', rule, {
|
47 | valid: [
|
48 | {
|
49 | code: 'var foo = true;',
|
50 | options: tslintRulesConfig,
|
51 | filename: './tests/fixture-project/1.ts',
|
52 | },
|
53 | {
|
54 | filename: './tests/test-project/file-spec.ts',
|
55 | code: readFileSync('./tests/test-project/file-spec.ts', 'utf8').replace(
|
56 | /\n/g,
|
57 | ' ',
|
58 | ),
|
59 | parserOptions: {
|
60 | project: `${__dirname}/test-project/tsconfig.json`,
|
61 | },
|
62 | options: tslintRulesConfig,
|
63 | },
|
64 | {
|
65 | code: 'throw "should be ok because rule is not loaded";',
|
66 | options: tslintRulesConfig,
|
67 | filename: './tests/fixture-project/2.ts',
|
68 | },
|
69 | ],
|
70 |
|
71 | invalid: [
|
72 | {
|
73 | options: [{ lintFile: './tests/test-project/tslint.json' }],
|
74 | code: 'throw "err" // no-string-throw',
|
75 | output: 'throw new Error("err") // no-string-throw',
|
76 | filename: './tests/fixture-project/3.ts',
|
77 | errors: [
|
78 | {
|
79 | messageId: 'failure',
|
80 | data: {
|
81 | message:
|
82 | 'Throwing plain strings (not instances of Error) gives no stack traces',
|
83 | ruleName: 'no-string-throw',
|
84 | },
|
85 | },
|
86 | ],
|
87 | },
|
88 | {
|
89 | code: 'var foo = true // semicolon',
|
90 | options: tslintRulesConfig,
|
91 | output: 'var foo = true; // semicolon',
|
92 | filename: './tests/fixture-project/4.ts',
|
93 | errors: [
|
94 | {
|
95 | messageId: 'failure',
|
96 | data: {
|
97 | message: 'Missing semicolon',
|
98 | ruleName: 'semicolon',
|
99 | },
|
100 | line: 1,
|
101 | column: 15,
|
102 | },
|
103 | ],
|
104 | },
|
105 | {
|
106 | code: 'var foo = true // fail',
|
107 | options: tslintRulesDirectoryConfig,
|
108 | output: 'var foo = true // fail',
|
109 | filename: './tests/fixture-project/5.ts',
|
110 | errors: [
|
111 | {
|
112 | messageId: 'failure',
|
113 | data: {
|
114 | message: 'failure',
|
115 | ruleName: 'always-fail',
|
116 | },
|
117 | line: 1,
|
118 | column: 1,
|
119 | },
|
120 | ],
|
121 | },
|
122 | {
|
123 | filename: './tests/test-project/source.ts',
|
124 | code: readFileSync('./tests/test-project/source.ts', 'utf8').replace(
|
125 | /\n/g,
|
126 | ' ',
|
127 | ),
|
128 | parserOptions: {
|
129 | project: `${__dirname}/test-project/tsconfig.json`,
|
130 | },
|
131 | options: [
|
132 | {
|
133 | rulesDirectory: [
|
134 | `${__dirname}/../../../node_modules/tslint/lib/rules`,
|
135 | ],
|
136 | rules: { 'restrict-plus-operands': true },
|
137 | },
|
138 | ],
|
139 | errors: [
|
140 | {
|
141 | messageId: 'failure',
|
142 | data: {
|
143 | message:
|
144 | 'Operands of \'+\' operation must either be both strings or both numbers or both bigints, but found 1 + "2". Consider using template literals.',
|
145 | ruleName: 'restrict-plus-operands',
|
146 | },
|
147 | },
|
148 | ],
|
149 | },
|
150 | ],
|
151 | });
|
152 |
|
153 | describe('tslint/error', () => {
|
154 | function testOutput(code: string, config: TSESLint.Linter.Config): void {
|
155 | const linter = new TSESLint.Linter();
|
156 | linter.defineRule('tslint/config', rule);
|
157 | linter.defineParser('@typescript-eslint/parser', parser);
|
158 |
|
159 | expect(() => linter.verify(code, config)).toThrow(
|
160 | 'You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.',
|
161 | );
|
162 | }
|
163 |
|
164 | it('should error on missing project', () => {
|
165 | testOutput('foo;', {
|
166 | rules: {
|
167 | 'tslint/config': [2, tslintRulesConfig],
|
168 | },
|
169 | parser: '@typescript-eslint/parser',
|
170 | });
|
171 | });
|
172 |
|
173 | it('should error on default parser', () => {
|
174 | testOutput('foo;', {
|
175 | parserOptions: {
|
176 | project: `${__dirname}/test-project/tsconfig.json`,
|
177 | },
|
178 | rules: {
|
179 | 'tslint/config': [2, tslintRulesConfig],
|
180 | },
|
181 | });
|
182 | });
|
183 |
|
184 | it('should not crash if there are no tslint rules specified', () => {
|
185 | const linter = new TSESLint.Linter();
|
186 | jest.spyOn(console, 'warn').mockImplementation();
|
187 | linter.defineRule('tslint/config', rule);
|
188 | linter.defineParser('@typescript-eslint/parser', parser);
|
189 | expect(() =>
|
190 | linter.verify(
|
191 | 'foo;',
|
192 | {
|
193 | parserOptions: {
|
194 | project: `${__dirname}/test-project/tsconfig.json`,
|
195 | },
|
196 | rules: {
|
197 | 'tslint/config': [2, {}],
|
198 | },
|
199 | parser: '@typescript-eslint/parser',
|
200 | },
|
201 | `${__dirname}/test-project/extra.ts`,
|
202 | ),
|
203 | ).not.toThrow();
|
204 |
|
205 | expect(console.warn).toHaveBeenCalledWith(
|
206 | expect.stringContaining(
|
207 | `Tried to lint ${__dirname}/test-project/extra.ts but found no valid, enabled rules for this file type and file path in the resolved configuration.`,
|
208 | ),
|
209 | );
|
210 | jest.resetAllMocks();
|
211 | });
|
212 | });
|