SearchSource.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. function os() {
  7. const data = _interopRequireWildcard(require('os'));
  8. os = function () {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function path() {
  14. const data = _interopRequireWildcard(require('path'));
  15. path = function () {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _micromatch() {
  21. const data = _interopRequireDefault(require('micromatch'));
  22. _micromatch = function () {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _jestConfig() {
  28. const data = require('jest-config');
  29. _jestConfig = function () {
  30. return data;
  31. };
  32. return data;
  33. }
  34. function _jestRegexUtil() {
  35. const data = require('jest-regex-util');
  36. _jestRegexUtil = function () {
  37. return data;
  38. };
  39. return data;
  40. }
  41. function _jestResolveDependencies() {
  42. const data = _interopRequireDefault(require('jest-resolve-dependencies'));
  43. _jestResolveDependencies = function () {
  44. return data;
  45. };
  46. return data;
  47. }
  48. function _jestSnapshot() {
  49. const data = require('jest-snapshot');
  50. _jestSnapshot = function () {
  51. return data;
  52. };
  53. return data;
  54. }
  55. function _jestUtil() {
  56. const data = require('jest-util');
  57. _jestUtil = function () {
  58. return data;
  59. };
  60. return data;
  61. }
  62. function _interopRequireDefault(obj) {
  63. return obj && obj.__esModule ? obj : {default: obj};
  64. }
  65. function _getRequireWildcardCache() {
  66. if (typeof WeakMap !== 'function') return null;
  67. var cache = new WeakMap();
  68. _getRequireWildcardCache = function () {
  69. return cache;
  70. };
  71. return cache;
  72. }
  73. function _interopRequireWildcard(obj) {
  74. if (obj && obj.__esModule) {
  75. return obj;
  76. }
  77. if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
  78. return {default: obj};
  79. }
  80. var cache = _getRequireWildcardCache();
  81. if (cache && cache.has(obj)) {
  82. return cache.get(obj);
  83. }
  84. var newObj = {};
  85. var hasPropertyDescriptor =
  86. Object.defineProperty && Object.getOwnPropertyDescriptor;
  87. for (var key in obj) {
  88. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  89. var desc = hasPropertyDescriptor
  90. ? Object.getOwnPropertyDescriptor(obj, key)
  91. : null;
  92. if (desc && (desc.get || desc.set)) {
  93. Object.defineProperty(newObj, key, desc);
  94. } else {
  95. newObj[key] = obj[key];
  96. }
  97. }
  98. }
  99. newObj.default = obj;
  100. if (cache) {
  101. cache.set(obj, newObj);
  102. }
  103. return newObj;
  104. }
  105. function _defineProperty(obj, key, value) {
  106. if (key in obj) {
  107. Object.defineProperty(obj, key, {
  108. value: value,
  109. enumerable: true,
  110. configurable: true,
  111. writable: true
  112. });
  113. } else {
  114. obj[key] = value;
  115. }
  116. return obj;
  117. }
  118. const regexToMatcher = testRegex => {
  119. const regexes = testRegex.map(testRegex => new RegExp(testRegex));
  120. return path =>
  121. regexes.some(regex => {
  122. const result = regex.test(path); // prevent stateful regexes from breaking, just in case
  123. regex.lastIndex = 0;
  124. return result;
  125. });
  126. };
  127. const toTests = (context, tests) =>
  128. tests.map(path => ({
  129. context,
  130. duration: undefined,
  131. path
  132. }));
  133. const hasSCM = changedFilesInfo => {
  134. const {repos} = changedFilesInfo; // no SCM (git/hg/...) is found in any of the roots.
  135. const noSCM = Object.values(repos).every(scm => scm.size === 0);
  136. return !noSCM;
  137. };
  138. class SearchSource {
  139. constructor(context) {
  140. _defineProperty(this, '_context', void 0);
  141. _defineProperty(this, '_dependencyResolver', void 0);
  142. _defineProperty(this, '_testPathCases', []);
  143. const {config} = context;
  144. this._context = context;
  145. this._dependencyResolver = null;
  146. const rootPattern = new RegExp(
  147. config.roots
  148. .map(dir => (0, _jestRegexUtil().escapePathForRegex)(dir + path().sep))
  149. .join('|')
  150. );
  151. this._testPathCases.push({
  152. isMatch: path => rootPattern.test(path),
  153. stat: 'roots'
  154. });
  155. if (config.testMatch.length) {
  156. this._testPathCases.push({
  157. isMatch: (0, _jestUtil().globsToMatcher)(config.testMatch),
  158. stat: 'testMatch'
  159. });
  160. }
  161. if (config.testPathIgnorePatterns.length) {
  162. const testIgnorePatternsRegex = new RegExp(
  163. config.testPathIgnorePatterns.join('|')
  164. );
  165. this._testPathCases.push({
  166. isMatch: path => !testIgnorePatternsRegex.test(path),
  167. stat: 'testPathIgnorePatterns'
  168. });
  169. }
  170. if (config.testRegex.length) {
  171. this._testPathCases.push({
  172. isMatch: regexToMatcher(config.testRegex),
  173. stat: 'testRegex'
  174. });
  175. }
  176. }
  177. _getOrBuildDependencyResolver() {
  178. if (!this._dependencyResolver) {
  179. this._dependencyResolver = new (_jestResolveDependencies().default)(
  180. this._context.resolver,
  181. this._context.hasteFS,
  182. (0, _jestSnapshot().buildSnapshotResolver)(this._context.config)
  183. );
  184. }
  185. return this._dependencyResolver;
  186. }
  187. _filterTestPathsWithStats(allPaths, testPathPattern) {
  188. const data = {
  189. stats: {
  190. roots: 0,
  191. testMatch: 0,
  192. testPathIgnorePatterns: 0,
  193. testRegex: 0
  194. },
  195. tests: [],
  196. total: allPaths.length
  197. };
  198. const testCases = Array.from(this._testPathCases); // clone
  199. if (testPathPattern) {
  200. const regex = (0, _jestUtil().testPathPatternToRegExp)(testPathPattern);
  201. testCases.push({
  202. isMatch: path => regex.test(path),
  203. stat: 'testPathPattern'
  204. });
  205. data.stats.testPathPattern = 0;
  206. }
  207. data.tests = allPaths.filter(test => {
  208. let filterResult = true;
  209. for (const {isMatch, stat} of testCases) {
  210. if (isMatch(test.path)) {
  211. data.stats[stat]++;
  212. } else {
  213. filterResult = false;
  214. }
  215. }
  216. return filterResult;
  217. });
  218. return data;
  219. }
  220. _getAllTestPaths(testPathPattern) {
  221. return this._filterTestPathsWithStats(
  222. toTests(this._context, this._context.hasteFS.getAllFiles()),
  223. testPathPattern
  224. );
  225. }
  226. isTestFilePath(path) {
  227. return this._testPathCases.every(testCase => testCase.isMatch(path));
  228. }
  229. findMatchingTests(testPathPattern) {
  230. return this._getAllTestPaths(testPathPattern);
  231. }
  232. findRelatedTests(allPaths, collectCoverage) {
  233. const dependencyResolver = this._getOrBuildDependencyResolver();
  234. if (!collectCoverage) {
  235. return {
  236. tests: toTests(
  237. this._context,
  238. dependencyResolver.resolveInverse(
  239. allPaths,
  240. this.isTestFilePath.bind(this),
  241. {
  242. skipNodeResolution: this._context.config.skipNodeResolution
  243. }
  244. )
  245. )
  246. };
  247. }
  248. const testModulesMap = dependencyResolver.resolveInverseModuleMap(
  249. allPaths,
  250. this.isTestFilePath.bind(this),
  251. {
  252. skipNodeResolution: this._context.config.skipNodeResolution
  253. }
  254. );
  255. const allPathsAbsolute = Array.from(allPaths).map(p => path().resolve(p));
  256. const collectCoverageFrom = new Set();
  257. testModulesMap.forEach(testModule => {
  258. if (!testModule.dependencies) {
  259. return;
  260. }
  261. testModule.dependencies.forEach(p => {
  262. if (!allPathsAbsolute.includes(p)) {
  263. return;
  264. }
  265. const filename = (0, _jestConfig().replaceRootDirInPath)(
  266. this._context.config.rootDir,
  267. p
  268. );
  269. collectCoverageFrom.add(
  270. path().isAbsolute(filename)
  271. ? path().relative(this._context.config.rootDir, filename)
  272. : filename
  273. );
  274. });
  275. });
  276. return {
  277. collectCoverageFrom,
  278. tests: toTests(
  279. this._context,
  280. testModulesMap.map(testModule => testModule.file)
  281. )
  282. };
  283. }
  284. findTestsByPaths(paths) {
  285. return {
  286. tests: toTests(
  287. this._context,
  288. paths
  289. .map(p => path().resolve(this._context.config.cwd, p))
  290. .filter(this.isTestFilePath.bind(this))
  291. )
  292. };
  293. }
  294. findRelatedTestsFromPattern(paths, collectCoverage) {
  295. if (Array.isArray(paths) && paths.length) {
  296. const resolvedPaths = paths.map(p =>
  297. path().resolve(this._context.config.cwd, p)
  298. );
  299. return this.findRelatedTests(new Set(resolvedPaths), collectCoverage);
  300. }
  301. return {
  302. tests: []
  303. };
  304. }
  305. findTestRelatedToChangedFiles(changedFilesInfo, collectCoverage) {
  306. if (!hasSCM(changedFilesInfo)) {
  307. return {
  308. noSCM: true,
  309. tests: []
  310. };
  311. }
  312. const {changedFiles} = changedFilesInfo;
  313. return this.findRelatedTests(changedFiles, collectCoverage);
  314. }
  315. _getTestPaths(globalConfig, changedFiles) {
  316. if (globalConfig.onlyChanged) {
  317. if (!changedFiles) {
  318. throw new Error('Changed files must be set when running with -o.');
  319. }
  320. return this.findTestRelatedToChangedFiles(
  321. changedFiles,
  322. globalConfig.collectCoverage
  323. );
  324. }
  325. let paths = globalConfig.nonFlagArgs;
  326. if (globalConfig.findRelatedTests && 'win32' === os().platform()) {
  327. const allFiles = this._context.hasteFS.getAllFiles();
  328. const options = {
  329. nocase: true,
  330. windows: false
  331. };
  332. paths = paths
  333. .map(p => {
  334. const relativePath = path()
  335. .resolve(this._context.config.cwd, p)
  336. .replace(/\\/g, '\\\\');
  337. const match = (0, _micromatch().default)(
  338. allFiles,
  339. relativePath,
  340. options
  341. );
  342. return match[0];
  343. })
  344. .filter(Boolean);
  345. }
  346. if (globalConfig.runTestsByPath && paths && paths.length) {
  347. return this.findTestsByPaths(paths);
  348. } else if (globalConfig.findRelatedTests && paths && paths.length) {
  349. return this.findRelatedTestsFromPattern(
  350. paths,
  351. globalConfig.collectCoverage
  352. );
  353. } else if (globalConfig.testPathPattern != null) {
  354. return this.findMatchingTests(globalConfig.testPathPattern);
  355. } else {
  356. return {
  357. tests: []
  358. };
  359. }
  360. }
  361. async getTestPaths(globalConfig, changedFiles, filter) {
  362. const searchResult = this._getTestPaths(globalConfig, changedFiles);
  363. const filterPath = globalConfig.filter;
  364. if (filter) {
  365. const tests = searchResult.tests;
  366. const filterResult = await filter(tests.map(test => test.path));
  367. if (!Array.isArray(filterResult.filtered)) {
  368. throw new Error(
  369. `Filter ${filterPath} did not return a valid test list`
  370. );
  371. }
  372. const filteredSet = new Set(
  373. filterResult.filtered.map(result => result.test)
  374. );
  375. return {
  376. ...searchResult,
  377. tests: tests.filter(test => filteredSet.has(test.path))
  378. };
  379. }
  380. return searchResult;
  381. }
  382. findRelatedSourcesFromTestsInChangedFiles(changedFilesInfo) {
  383. if (!hasSCM(changedFilesInfo)) {
  384. return [];
  385. }
  386. const {changedFiles} = changedFilesInfo;
  387. const dependencyResolver = this._getOrBuildDependencyResolver();
  388. const relatedSourcesSet = new Set();
  389. changedFiles.forEach(filePath => {
  390. if (this.isTestFilePath(filePath)) {
  391. const sourcePaths = dependencyResolver.resolve(filePath, {
  392. skipNodeResolution: this._context.config.skipNodeResolution
  393. });
  394. sourcePaths.forEach(sourcePath => relatedSourcesSet.add(sourcePath));
  395. }
  396. });
  397. return Array.from(relatedSourcesSet);
  398. }
  399. }
  400. exports.default = SearchSource;