lint-text.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import fs from 'fs';
  2. import path from 'path';
  3. import test from 'ava';
  4. import pify from 'pify';
  5. import fn from '..';
  6. process.chdir(__dirname);
  7. const readFile = pify(fs.readFile);
  8. const hasRule = (results, ruleId) => results[0].messages.some(x => x.ruleId === ruleId);
  9. test('.lintText()', t => {
  10. const {results} = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`);
  11. t.true(hasRule(results, 'semi'));
  12. });
  13. test('default `ignores`', t => {
  14. const result = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  15. filename: 'node_modules/ignored/index.js'
  16. });
  17. t.is(result.errorCount, 0);
  18. t.is(result.warningCount, 0);
  19. });
  20. test('`ignores` option', t => {
  21. const result = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  22. filename: 'ignored/index.js',
  23. ignores: ['ignored/**/*.js']
  24. });
  25. t.is(result.errorCount, 0);
  26. t.is(result.warningCount, 0);
  27. });
  28. test('`ignores` option without cwd', t => {
  29. const result = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  30. filename: 'ignored/index.js',
  31. ignores: ['ignored/**/*.js']
  32. });
  33. t.is(result.errorCount, 0);
  34. t.is(result.warningCount, 0);
  35. });
  36. test('respect overrides', t => {
  37. const result = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  38. filename: 'ignored/index.js',
  39. ignores: ['ignored/**/*.js'],
  40. overrides: [
  41. {
  42. files: ['ignored/**/*.js'],
  43. ignores: []
  44. }
  45. ]
  46. });
  47. t.is(result.errorCount, 1);
  48. t.is(result.warningCount, 0);
  49. });
  50. test('overriden ignore', t => {
  51. const result = fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  52. filename: 'unignored.js',
  53. overrides: [
  54. {
  55. files: ['unignored.js'],
  56. ignores: ['unignored.js']
  57. }
  58. ]
  59. });
  60. t.is(result.errorCount, 0);
  61. t.is(result.warningCount, 0);
  62. });
  63. test('`ignores` option without filename', t => {
  64. t.throws(() => {
  65. fn.lintText(`'use strict'\nconsole.log('unicorn');\n`, {
  66. ignores: ['ignored/**/*.js']
  67. });
  68. }, /The `ignores` option requires the `filename` option to be defined./);
  69. });
  70. test('JSX support', t => {
  71. const {results} = fn.lintText('const app = <div className="appClass">Hello, React!</div>;\n');
  72. t.true(hasRule(results, 'no-unused-vars'));
  73. });
  74. test('plugin support', t => {
  75. const {results} = fn.lintText('var React;\nReact.render(<App/>);\n', {
  76. plugins: ['react'],
  77. rules: {'react/jsx-no-undef': 'error'}
  78. });
  79. t.true(hasRule(results, 'react/jsx-no-undef'));
  80. });
  81. test('prevent use of extended native objects', t => {
  82. const {results} = fn.lintText('[].unicorn();\n');
  83. t.true(hasRule(results, 'no-use-extend-native/no-use-extend-native'));
  84. });
  85. test('extends support', t => {
  86. const {results} = fn.lintText('var React;\nReact.render(<App/>);\n', {
  87. extends: 'xo-react'
  88. });
  89. t.true(hasRule(results, 'react/jsx-no-undef'));
  90. });
  91. test('extends support with `esnext` option', t => {
  92. const {results} = fn.lintText('import path from \'path\';\nlet React;\nReact.render(<App/>);\n', {
  93. extends: 'xo-react'
  94. });
  95. t.true(hasRule(results, 'react/jsx-no-undef'));
  96. });
  97. test('disable style rules when `prettier` option is enabled', t => {
  98. const withoutPrettier = fn.lintText('(a) => {}\n', {}).results;
  99. // `arrow-parens` is enabled
  100. t.true(hasRule(withoutPrettier, 'arrow-parens'));
  101. // `prettier/prettier` is disabled
  102. t.false(hasRule(withoutPrettier, 'prettier/prettier'));
  103. const withPrettier = fn.lintText('(a) => {}\n', {prettier: true}).results;
  104. // `arrow-parens` is disabled by `eslint-config-prettier`
  105. t.false(hasRule(withPrettier, 'arrow-parens'));
  106. // `prettier/prettier` is enabled
  107. t.true(hasRule(withPrettier, 'prettier/prettier'));
  108. });
  109. test('extends `react` support with `prettier` option', t => {
  110. const {results} = fn.lintText('<Hello name={ firstname } />;\n', {extends: 'xo-react', prettier: true});
  111. // `react/jsx-curly-spacing` is disabled by `eslint-config-prettier`
  112. t.false(hasRule(results, 'react/jsx-curly-spacing'));
  113. // `prettier/prettier` is enabled
  114. t.true(hasRule(results, 'prettier/prettier'));
  115. });
  116. test('always use the latest ECMAScript parser so esnext syntax won\'t throw in normal mode', t => {
  117. const {results} = fn.lintText('async function foo() {}\n\nfoo();\n');
  118. t.is(results[0].errorCount, 0);
  119. });
  120. test('regression test for #71', t => {
  121. const {results} = fn.lintText(`const foo = { key: 'value' };\nconsole.log(foo);\n`, {
  122. extends: path.join(__dirname, 'fixtures/extends.js')
  123. });
  124. t.is(results[0].errorCount, 0, results[0]);
  125. });
  126. test('lintText() - overrides support', async t => {
  127. const cwd = path.join(__dirname, 'fixtures/overrides');
  128. const bar = path.join(cwd, 'test/bar.js');
  129. const barResults = fn.lintText(await readFile(bar, 'utf8'), {filename: bar, cwd}).results;
  130. t.is(barResults[0].errorCount, 0, barResults[0]);
  131. const foo = path.join(cwd, 'test/foo.js');
  132. const fooResults = fn.lintText(await readFile(foo, 'utf8'), {filename: foo, cwd}).results;
  133. t.is(fooResults[0].errorCount, 0, fooResults[0]);
  134. const index = path.join(cwd, 'test/index.js');
  135. const indexResults = fn.lintText(await readFile(bar, 'utf8'), {filename: index, cwd}).results;
  136. t.is(indexResults[0].errorCount, 0, indexResults[0]);
  137. });
  138. test('do not lint gitignored files if filename is given', async t => {
  139. const cwd = path.join(__dirname, 'fixtures/gitignore');
  140. const ignoredPath = path.resolve('fixtures/gitignore/test/foo.js');
  141. const ignored = await readFile(ignoredPath, 'utf-8');
  142. const {results} = fn.lintText(ignored, {filename: ignoredPath, cwd});
  143. t.is(results[0].errorCount, 0);
  144. });
  145. test('lint gitignored files if filename is not given', async t => {
  146. const ignoredPath = path.resolve('fixtures/gitignore/test/foo.js');
  147. const ignored = await readFile(ignoredPath, 'utf-8');
  148. const {results} = fn.lintText(ignored);
  149. t.true(results[0].errorCount > 0);
  150. });
  151. test('do not lint gitignored files in file with negative gitignores', async t => {
  152. const cwd = path.join(__dirname, 'fixtures/negative-gitignore');
  153. const ignoredPath = path.resolve('fixtures/negative-gitignore/bar.js');
  154. const ignored = await readFile(ignoredPath, 'utf-8');
  155. const {results} = fn.lintText(ignored, {filename: ignoredPath, cwd});
  156. t.is(results[0].errorCount, 0);
  157. });
  158. test('multiple negative patterns should act as positive patterns', async t => {
  159. const cwd = path.join(__dirname, 'fixtures', 'gitignore-multiple-negation');
  160. const filename = path.join(cwd, '!!!unicorn.js');
  161. const text = await readFile(filename, 'utf-8');
  162. const {results} = fn.lintText(text, {filename, cwd});
  163. t.is(results[0].errorCount, 0);
  164. });
  165. test('lint negatively gitignored files', async t => {
  166. const cwd = path.join(__dirname, 'fixtures/negative-gitignore');
  167. const glob = path.posix.join(cwd, '*');
  168. const {results} = await fn.lintFiles(glob, {cwd});
  169. t.true(results[0].errorCount > 0);
  170. });