Repository: jestjs/jest (TypeScript)

Issue:
Jest25: --findRelatedTests not working in hidden directory on windows
## 🐛 Bug Report

Since v25, `--findRelatedTests` no longer works on Windows when running in a hidden directory (a directory starting with a `.`).

## To Reproduce

A really small reproduction repo can be found here: https://github.com/nicojs/jest-find-related-test-bug

On windows:

```
git clone git@github.com:nicojs/jest-find-related-test-bug.git
cd jest-find-related-test-bug/.hidden
npm i
npx jest --findRelatedTests src/sum.js
```

## Expected behavior

```
 PASS  src/sum.test.js
  √ adds 1 + 2 to equal 3 (2ms)
  √ sub 1 - 0 to equal 1

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.381s
Ran all test suites related to files matching /src\\sum.js/i.
```

But was:

```
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\z\github\nicojs\jest-find-related-test-bug\.hidden
  4 files checked.
  testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 1 match
  testPathIgnorePatterns: \\node_modules\\ - 4 matches
  testRegex:  - 0 matches
Pattern: src\\sum.js - 0 matches
```

This behavior broke with jest@25. I.e. this works:

```
npm i jest@24
npx jest --findRelatedTests src/sum.js
```

Moving the code to a non-hidden directory also works:

```
cd ..
mv .hidden hidden
cd hidden
npx jest --findRelatedTests src/sum.js
```

Or Linux also seems to work (tested with Windows Subsystem for Linux).

## Link to repl or repo (highly encouraged)

https://github.com/nicojs/jest-find-related-test-bug

## envinfo

```
 System:
    OS: Windows 10 10.0.18363
    CPU: (8) x64 Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz
  Binaries:
    Node: 12.16.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.12.3 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 6.13.6 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    jest: ^25.2.3 => 25.2.3
```



Hints:

Patch:
diff --git a/packages/jest-core/src/SearchSource.ts b/packages/jest-core/src/SearchSource.ts
--- a/packages/jest-core/src/SearchSource.ts
+++ b/packages/jest-core/src/SearchSource.ts
@@ -288,18 +288,7 @@ export default class SearchSource {
     let paths = globalConfig.nonFlagArgs;
 
     if (globalConfig.findRelatedTests && 'win32' === os.platform()) {
-      const allFiles = this._context.hasteFS.getAllFiles();
-      const options = {nocase: true, windows: false};
-
-      paths = paths
-        .map(p => {
-          const relativePath = path
-            .resolve(this._context.config.cwd, p)
-            .replace(/\\/g, '\\\\');
-          const match = micromatch(allFiles, relativePath, options);
-          return match[0];
-        })
-        .filter(Boolean);
+      paths = this.filterPathsWin32(paths);
     }
 
     if (globalConfig.runTestsByPath && paths && paths.length) {
@@ -316,6 +305,32 @@ export default class SearchSource {
     }
   }
 
+  public filterPathsWin32(paths: Array<string>): Array<string> {
+    const allFiles = this._context.hasteFS.getAllFiles();
+    const options = {nocase: true, windows: false};
+
+    function normalizePosix(filePath: string) {
+      return filePath.replace(/\\/g, '/');
+    }
+
+    paths = paths
+      .map(p => {
+        // micromatch works with forward slashes: https://github.com/micromatch/micromatch#backslashes
+        const normalizedPath = normalizePosix(
+          path.resolve(this._context.config.cwd, p),
+        );
+        const match = micromatch(
+          allFiles.map(normalizePosix),
+          normalizedPath,
+          options,
+        );
+        return match[0];
+      })
+      .filter(Boolean)
+      .map(p => path.resolve(p));
+    return paths;
+  }
+
   async getTestPaths(
     globalConfig: Config.GlobalConfig,
     changedFiles: ChangedFiles | undefined,


Test patch:
diff --git a/packages/jest-core/src/__tests__/SearchSource.test.ts b/packages/jest-core/src/__tests__/SearchSource.test.ts
--- a/packages/jest-core/src/__tests__/SearchSource.test.ts
+++ b/packages/jest-core/src/__tests__/SearchSource.test.ts
@@ -406,6 +406,81 @@ describe('SearchSource', () => {
     });
   });
 
+  describe('filterPathsWin32', () => {
+    beforeEach(async () => {
+      const config = (
+        await normalize(
+          {
+            name,
+            rootDir: '.',
+            roots: [],
+          },
+          {} as Config.Argv,
+        )
+      ).options;
+      const context = await Runtime.createContext(config, {
+        maxWorkers,
+        watchman: false,
+      });
+
+      searchSource = new SearchSource(context);
+      context.hasteFS.getAllFiles = () => [
+        path.resolve('packages/lib/my-lib.ts'),
+        path.resolve('packages/@core/my-app.ts'),
+        path.resolve('packages/+cli/my-cli.ts'),
+        path.resolve('packages/.hidden/my-app-hidden.ts'),
+        path.resolve('packages/programs (x86)/my-program.ts'),
+      ];
+    });
+
+    it('should allow a simple match', async () => {
+      const result = searchSource.filterPathsWin32(['packages/lib/my-lib.ts']);
+      expect(result).toEqual([path.resolve('packages/lib/my-lib.ts')]);
+    });
+    it('should allow to match a file inside a hidden directory', async () => {
+      const result = searchSource.filterPathsWin32([
+        'packages/.hidden/my-app-hidden.ts',
+      ]);
+      expect(result).toEqual([
+        path.resolve('packages/.hidden/my-app-hidden.ts'),
+      ]);
+    });
+    it('should allow to match a file inside a directory prefixed with a "@"', async () => {
+      const result = searchSource.filterPathsWin32([
+        'packages/@core/my-app.ts',
+      ]);
+      expect(result).toEqual([path.resolve('packages/@core/my-app.ts')]);
+    });
+    it('should allow to match a file inside a directory prefixed with a "+"', async () => {
+      const result = searchSource.filterPathsWin32(['packages/+cli/my-cli.ts']);
+      expect(result).toEqual([path.resolve('packages/+cli/my-cli.ts')]);
+    });
+    it('should allow an @(pattern)', () => {
+      const result = searchSource.filterPathsWin32([
+        'packages/@(@core)/my-app.ts',
+      ]);
+      expect(result).toEqual([path.resolve('packages/@core/my-app.ts')]);
+    });
+    it('should allow a +(pattern)', () => {
+      const result = searchSource.filterPathsWin32([
+        'packages/+(@core)/my-app.ts',
+      ]);
+      expect(result).toEqual([path.resolve('packages/@core/my-app.ts')]);
+    });
+    it('should allow for (pattern) in file path', () => {
+      const result = searchSource.filterPathsWin32([
+        'packages/programs (x86)/my-program.ts',
+      ]);
+      expect(result).toEqual([
+        path.resolve('packages/programs (x86)/my-program.ts'),
+      ]);
+    });
+    it('should allow no results found', () => {
+      const result = searchSource.filterPathsWin32(['not/exists']);
+      expect(result).toHaveLength(0);
+    });
+  });
+
   describe('findRelatedTests', () => {
     const rootDir = path.join(
       __dirname,