Skip to content

Commit 349cbda

Browse files
authored
Merge pull request #137 from joomcode/fix/duplicate-test-file-path
fix: show global error on duplicate test file paths
2 parents db52305 + 5d5d635 commit 349cbda

29 files changed

Lines changed: 345 additions & 165 deletions

src/README.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,20 @@ Modules in the dependency graph should only import the modules above them:
4040
33. `utils/promise`
4141
34. `utils/resourceUsage`
4242
35. `utils/fs`
43-
36. `utils/getGlobalErrorHandler`
44-
37. `utils/tests`
45-
38. `utils/end`
46-
39. `utils/pack`
47-
40. `useContext`
48-
41. `context`
49-
42. `utils/step`
50-
43. `utils/apiStatistics`
51-
44. `utils/selectors`
52-
45. `selectors`
53-
46. `utils/log`
54-
47. `step`
55-
48. `utils/waitForEvents`
56-
49. `utils/expect`
57-
50. `expect`
58-
51. ...
43+
36. `utils/completedTestRuns`
44+
37. `utils/getGlobalErrorHandler`
45+
38. `utils/tests`
46+
39. `utils/end`
47+
40. `utils/pack`
48+
41. `useContext`
49+
42. `context`
50+
43. `utils/step`
51+
44. `utils/apiStatistics`
52+
45. `utils/selectors`
53+
46. `selectors`
54+
47. `utils/log`
55+
48. `step`
56+
49. `utils/waitForEvents`
57+
50. `utils/expect`
58+
51. `expect`
59+
52. ...

src/constants/internal.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export {
5252
API_STATISTICS_PATH,
5353
AUTOTESTS_DIRECTORY_PATH,
5454
COMPILED_USERLAND_CONFIG_DIRECTORY,
55+
COMPLETED_TEST_RUNS_PATH,
5556
CONFIG_PATH,
5657
DOT_ENV_PATH,
5758
EVENTS_DIRECTORY_PATH,
@@ -61,6 +62,7 @@ export {
6162
INSTALLED_E2ED_DIRECTORY_PATH,
6263
INTERNAL_DIRECTORY_NAME,
6364
INTERNAL_REPORTS_DIRECTORY_PATH,
65+
NOT_INCLUDED_IN_PACK_TESTS_PATH,
6466
REPORTS_DIRECTORY_PATH,
6567
SCREENSHOTS_DIRECTORY_PATH,
6668
START_INFO_PATH,

src/constants/paths.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ export const COMPILED_USERLAND_CONFIG_DIRECTORY = join(
9999
'config',
100100
) as DirectoryPathFromRoot;
101101

102+
/**
103+
* Relative (from root) path to file with already completed test runs.
104+
* @internal
105+
*/
106+
export const COMPLETED_TEST_RUNS_PATH = join(
107+
TMP_DIRECTORY_PATH,
108+
'completedTestRuns.txt',
109+
) as FilePathFromRoot;
110+
102111
/**
103112
* Relative (from root) path to `config` file,
104113
* that plays the role of the internal Playwright config.
@@ -137,6 +146,17 @@ export const GLOBAL_WARNINGS_PATH = join(
137146
'globalWarnings.txt',
138147
) as FilePathFromRoot;
139148

149+
/**
150+
* Relative (from root) path to text file with list of not included in pack tests.
151+
* For each not included in pack test in this file, a relative path
152+
* to the file of this test is saved in a separate line.
153+
* @internal
154+
*/
155+
export const NOT_INCLUDED_IN_PACK_TESTS_PATH = join(
156+
TMP_DIRECTORY_PATH,
157+
'notIncludedInPackTests.txt',
158+
) as FilePathFromRoot;
159+
140160
/**
141161
* Relative (from root) path to directory with tests screenshots.
142162
* @internal

src/types/internal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export type {
164164
TestStaticOptions,
165165
} from './testRun';
166166
/** @internal */
167-
export type {FullTestRun, RunTest, Test, TestUnit} from './testRun';
167+
export type {CompletedTestRun, FullTestRun, RunTest, Test, TestUnit} from './testRun';
168168
export type {MergeTuples, TupleRest} from './tuples';
169169
export type {
170170
CloneWithoutUndefinedProperties,

src/types/testRun.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import type {TestFilePath} from './paths';
1111
import type {StringForLogs} from './string';
1212
import type {TestMetaPlaceholder} from './userland';
1313

14+
/**
15+
* Completed test run object used by internal runtime mechanics.
16+
* @internal
17+
*/
18+
export type CompletedTestRun<TestMeta = TestMetaPlaceholder> = Readonly<{
19+
status: TestRunStatus | 'started';
20+
}> &
21+
TestStaticOptions<TestMeta>;
22+
1423
/**
1524
* Full test run object result of userland hooks (like mainParams and runHash).
1625
* @internal
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {FAILED_TEST_RUN_STATUSES, TestRunStatus} from '../../constants/internal';
2+
3+
import type {CompletedTestRun} from '../../types/internal';
4+
5+
/**
6+
* Returns `true`, if test run was successful, and `false` otherwise.
7+
* @internal
8+
*/
9+
export const getIsSuccessfulTestRun = ({status}: CompletedTestRun): boolean =>
10+
status !== 'started' &&
11+
status !== TestRunStatus.Broken &&
12+
!FAILED_TEST_RUN_STATUSES.includes(status);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {writeGlobalError} from '../fs';
2+
3+
import {getIsTestOptionsDifferent} from './getIsTestOptionsDifferent';
4+
5+
import type {CompletedTestRun, TestStaticOptions} from '../../types/internal';
6+
7+
/**
8+
* Returns `true`, if new test file path is uniq in completed test runs,
9+
* otherwise writes global error.
10+
* @internal
11+
*/
12+
export const getIsTestFilePathUniq = async (
13+
testStaticOptions: TestStaticOptions,
14+
completedTestRuns: readonly CompletedTestRun[],
15+
): Promise<boolean> => {
16+
const {filePath, name, options} = testStaticOptions;
17+
18+
const testRunsWithFilePath = completedTestRuns.filter((run) => run.filePath === filePath);
19+
20+
for (const completedTestRun of testRunsWithFilePath) {
21+
if (
22+
name !== completedTestRun.name ||
23+
getIsTestOptionsDifferent(options, completedTestRun.options)
24+
) {
25+
await writeGlobalError(
26+
`The file "${filePath}" contains two different tests: "${completedTestRun.name}" (${JSON.stringify(completedTestRun.options)}) and "${name}" (${JSON.stringify(options)})`,
27+
);
28+
29+
return false;
30+
}
31+
}
32+
33+
return true;
34+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {writeGlobalError} from '../fs';
2+
3+
import {getIsTestOptionsDifferent} from './getIsTestOptionsDifferent';
4+
5+
import type {CompletedTestRun, TestStaticOptions} from '../../types/internal';
6+
7+
/**
8+
* Returns `true`, if new test file has uniq name in completed test runs,
9+
* otherwise writes global error.
10+
* @internal
11+
*/
12+
export const getIsTestNameUniq = async (
13+
testStaticOptions: TestStaticOptions,
14+
completedTestRuns: readonly CompletedTestRun[],
15+
): Promise<boolean> => {
16+
const {filePath, name, options} = testStaticOptions;
17+
18+
const testRunsWithName = completedTestRuns.filter((run) => run.name === name);
19+
20+
for (const completedTestRun of testRunsWithName) {
21+
if (
22+
filePath !== completedTestRun.filePath ||
23+
getIsTestOptionsDifferent(options, completedTestRun.options)
24+
) {
25+
await writeGlobalError(
26+
`There are two different tests with the same name "${name}": "${completedTestRun.filePath}" (${JSON.stringify(completedTestRun.options)}) and "${filePath}" (${JSON.stringify(options)})`,
27+
);
28+
29+
return false;
30+
}
31+
}
32+
33+
return true;
34+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type {TestOptions} from '../../types/internal';
2+
3+
/**
4+
* Returns `true`, if test options different, and `false` otherwise.
5+
* @internal
6+
*/
7+
export const getIsTestOptionsDifferent = (
8+
firstOptions: TestOptions,
9+
secondOptions: TestOptions,
10+
): boolean => {
11+
const firstMeta = {...firstOptions.meta, skipReason: undefined};
12+
const secondMeta = {...secondOptions.meta, skipReason: undefined};
13+
14+
return (
15+
JSON.stringify({...firstOptions, meta: firstMeta}) !==
16+
JSON.stringify({...secondOptions, meta: secondMeta})
17+
);
18+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {readCompletedTestRuns} from '../fs';
2+
3+
import {getIsSuccessfulTestRun} from './getIsSuccessfulTestRun';
4+
5+
import type {TestFilePath} from '../../types/internal';
6+
7+
/**
8+
* Get array of test file paths of successful tests.
9+
* @internal
10+
*/
11+
export const getSuccessfulTestFilePaths = async (): Promise<readonly TestFilePath[]> => {
12+
const completedTestRuns = await readCompletedTestRuns();
13+
14+
const successfulTestFilePaths: TestFilePath[] = [];
15+
16+
for (const completedTestRun of completedTestRuns) {
17+
if (getIsSuccessfulTestRun(completedTestRun)) {
18+
successfulTestFilePaths.push(completedTestRun.filePath);
19+
}
20+
}
21+
22+
return successfulTestFilePaths;
23+
};

0 commit comments

Comments
 (0)