@autotests/playwright-impact 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +108 -76
- package/package.json +11 -2
- package/tests/analyze-impacted-specs.integration.test.js +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Kiselev
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,117 +1,149 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Playwright Spec Impact
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
If you use Playwright + POM and your CI runs are slow, this library selects only specs affected by your changes.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
It solves a common problem: broad reruns after small UI/POM changes. Instead of running everything, it computes the smallest reliable spec set to execute.
|
|
5
|
+
It reads changed files, finds impacted specs, and helps you run only what matters.
|
|
8
6
|
|
|
9
7
|
## Install
|
|
10
8
|
|
|
11
9
|
```bash
|
|
12
10
|
// npm
|
|
13
|
-
npm i @autotests/
|
|
11
|
+
npm i @autotests/playwright-impact
|
|
14
12
|
|
|
15
13
|
// pnpm
|
|
16
|
-
pnpm add @autotests/
|
|
14
|
+
pnpm add @autotests/playwright-impact
|
|
17
15
|
```
|
|
18
16
|
|
|
19
|
-
##
|
|
17
|
+
## Quick Start (Copy & Run)
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
- POM-heavy repositories often change in narrow areas but still trigger wide test execution.
|
|
23
|
-
- Running all specs for each PR slows CI, delays triage, and increases infrastructure cost.
|
|
24
|
-
- `test-impact-core` changes this by selecting only specs impacted by changed files and changed method behavior.
|
|
19
|
+
### Minimal working code
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
Create `impact.js` in your repo root:
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
```js
|
|
24
|
+
const { analyzeImpactedSpecs } = require('@autotests/playwright-impact');
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
const result = analyzeImpactedSpecs({
|
|
27
|
+
repoRoot: process.cwd(),
|
|
28
|
+
profile: {
|
|
29
|
+
testsRootRelative: 'tests',
|
|
30
|
+
changedSpecPrefix: 'tests/',
|
|
31
|
+
isRelevantPomPath: (filePath) =>
|
|
32
|
+
(filePath.startsWith('src/pages/') || filePath.startsWith('src/utils/')) &&
|
|
33
|
+
(filePath.endsWith('.ts') || filePath.endsWith('.tsx')),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
if (!result.hasAnythingToRun) {
|
|
38
|
+
console.log('No impacted specs found');
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
for (const spec of result.selectedSpecsRelative) {
|
|
43
|
+
console.log(spec);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
Save as `impact.js`
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
2. Compute impacted specs.
|
|
43
|
-
3. Exit early when `hasAnythingToRun` is false.
|
|
44
|
-
4. Run only `selectedSpecsRelative` in Playwright.
|
|
49
|
+
Run: `node impact.js`
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
Use output paths in your Playwright CLI
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
### What you need to change
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
2. Mark directly changed spec files.
|
|
52
|
-
3. Detect semantic impact in changed POM/utility methods.
|
|
53
|
-
4. Map impacted methods to fixture usage in specs.
|
|
54
|
-
5. Apply selection policy and return final spec set.
|
|
55
|
+
You only need to adjust:
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
- `testsRootRelative`: folder where your Playwright specs live.
|
|
58
|
+
- `changedSpecPrefix`: prefix used to detect directly changed specs.
|
|
59
|
+
- `isRelevantPomPath`: rule for which source files are treated as POM/utility inputs.
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
- A change in one shared POM/utility file often leads teams to run a large suite or the full project.
|
|
61
|
+
### Example output
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
- The same change results in a narrowed list of impacted specs only.
|
|
63
|
-
- Directly changed specs are still always included.
|
|
64
|
-
- CI runs faster, and failures are easier to link to the actual code change.
|
|
63
|
+
Example output:
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
```text
|
|
66
|
+
tests/auth/login.spec.ts
|
|
67
|
+
tests/cart/cart.spec.ts
|
|
68
|
+
```
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
If nothing is impacted:
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- `profile.changedSpecPrefix`: prefix for direct changed spec detection.
|
|
74
|
-
- `profile.isRelevantPomPath(filePath)`: function that returns true for relevant POM/utility files.
|
|
72
|
+
```text
|
|
73
|
+
No impacted specs found
|
|
74
|
+
```
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
## Minimal CI Script
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Use this when your branch is compared to `origin/main`.
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `baseRef`: git ref for comparison (example: `origin/main`).
|
|
83
|
-
- `includeUntrackedSpecs` (default `true`): include untracked `*.spec.ts`/`*.spec.tsx` as direct changed specs.
|
|
84
|
-
- `includeWorkingTreeWithBase` (default `true`): when `baseRef` is set, union committed diff (`base...HEAD`) with current working tree diff (`HEAD`).
|
|
85
|
-
- `fileExtensions` (default `['.ts', '.tsx']`): file extensions to analyze.
|
|
86
|
-
- `selectionBias` (default `'fail-open'`): uncertain call-site behavior.
|
|
80
|
+
```js
|
|
81
|
+
const { analyzeImpactedSpecs } = require('@autotests/playwright-impact');
|
|
87
82
|
|
|
88
|
-
|
|
83
|
+
const result = analyzeImpactedSpecs({
|
|
84
|
+
repoRoot: process.cwd(),
|
|
85
|
+
baseRef: 'origin/main',
|
|
86
|
+
profile: {
|
|
87
|
+
testsRootRelative: 'tests',
|
|
88
|
+
changedSpecPrefix: 'tests/',
|
|
89
|
+
isRelevantPomPath: (filePath) =>
|
|
90
|
+
(filePath.startsWith('src/pages/') || filePath.startsWith('src/utils/')) &&
|
|
91
|
+
(filePath.endsWith('.ts') || filePath.endsWith('.tsx')),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
if (!result.hasAnythingToRun) {
|
|
96
|
+
console.log('No impacted specs found');
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
console.log(result.selectedSpecsRelative.join(' '));
|
|
101
|
+
```
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
- `coverageStats.uncertainCallSites`: count of uncertain call sites encountered.
|
|
98
|
-
- `coverageStats.statusFallbackHits`: count of git status fallbacks (`C/T/U/unknown`).
|
|
99
|
-
- `changedEntriesBySource`: diagnostics for diff sources (`base...HEAD`, working tree, untracked).
|
|
103
|
+
## Typical CI Usage
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
1. Compare current branch with `origin/main`.
|
|
106
|
+
2. Compute impacted specs.
|
|
107
|
+
3. Exit with `0` if nothing should run.
|
|
108
|
+
4. Pass `selectedSpecsRelative` to your Playwright runner.
|
|
109
|
+
|
|
110
|
+
## How It Works (High Level)
|
|
111
|
+
|
|
112
|
+
1. Read changed files from Git.
|
|
113
|
+
2. Include directly changed specs.
|
|
114
|
+
3. Detect impacted specs from changed POM/utility code.
|
|
115
|
+
4. Return a final spec list.
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
- `matched-precise`: spec has precise impacted fixture method usage.
|
|
105
|
-
- `matched-uncertain-fail-open`: uncertain match retained by fail-open policy.
|
|
106
|
-
- `retained-no-bindings`: spec kept because no fixture bindings were found in callback params.
|
|
117
|
+
## Advanced Config
|
|
107
118
|
|
|
108
|
-
|
|
119
|
+
Required:
|
|
120
|
+
|
|
121
|
+
- `repoRoot`
|
|
122
|
+
- `profile.testsRootRelative`
|
|
123
|
+
- `profile.changedSpecPrefix`
|
|
124
|
+
- `profile.isRelevantPomPath(filePath)`
|
|
125
|
+
|
|
126
|
+
Optional:
|
|
127
|
+
|
|
128
|
+
- `analysisRootsRelative`
|
|
129
|
+
- `fixturesTypesRelative`
|
|
130
|
+
- `baseRef`
|
|
131
|
+
- `includeUntrackedSpecs`
|
|
132
|
+
- `includeWorkingTreeWithBase`
|
|
133
|
+
- `fileExtensions`
|
|
134
|
+
- `selectionBias`
|
|
135
|
+
|
|
136
|
+
## Advanced Diagnostics
|
|
109
137
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
138
|
+
- `warnings`
|
|
139
|
+
- `selectionReasons`
|
|
140
|
+
- `coverageStats.uncertainCallSites`
|
|
141
|
+
- `coverageStats.statusFallbackHits`
|
|
142
|
+
- `changedEntriesBySource`
|
|
112
143
|
|
|
113
|
-
##
|
|
144
|
+
## Reason Codes
|
|
114
145
|
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
146
|
+
- `direct-changed-spec`
|
|
147
|
+
- `matched-precise`
|
|
148
|
+
- `matched-uncertain-fail-open`
|
|
149
|
+
- `retained-no-bindings`
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autotests/playwright-impact",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Core impacted-test selection library for changed POM/method analysis",
|
|
5
5
|
"license": "ISC",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/akiselevaristek/playwright-impact.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/akiselevaristek/playwright-impact#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/akiselevaristek/playwright-impact/issues"
|
|
13
|
+
},
|
|
6
14
|
"main": "src/index.js",
|
|
7
15
|
"exports": {
|
|
8
16
|
".": "./src/index.js"
|
|
@@ -11,7 +19,8 @@
|
|
|
11
19
|
"files": [
|
|
12
20
|
"src",
|
|
13
21
|
"tests",
|
|
14
|
-
"README.md"
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
15
24
|
],
|
|
16
25
|
"publishConfig": {
|
|
17
26
|
"access": "public"
|
|
@@ -44,6 +44,34 @@ const createBaseRepo = () => {
|
|
|
44
44
|
return dir;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
test('README minimal profile style works with tests/ root and reports changed spec', () => {
|
|
48
|
+
const dir = createTempDir();
|
|
49
|
+
initGitRepo(dir);
|
|
50
|
+
|
|
51
|
+
writeFile(dir, 'src/pages/LoginPage.ts', 'export class LoginPage { open(){ return 1; } }\n');
|
|
52
|
+
writeFile(dir, 'src/utils/session.ts', 'export const getSession = () => "ok";\n');
|
|
53
|
+
writeFile(dir, 'src/fixtures/types.ts', 'type T = { loginPage: Pages.LoginPage };\n');
|
|
54
|
+
writeFile(dir, 'tests/auth/login.spec.ts', 'test("login", async ({ loginPage }) => { await loginPage.open(); });\n');
|
|
55
|
+
commitAll(dir, 'base');
|
|
56
|
+
|
|
57
|
+
const profile = {
|
|
58
|
+
testsRootRelative: 'tests',
|
|
59
|
+
changedSpecPrefix: 'tests/',
|
|
60
|
+
isRelevantPomPath: (filePath) =>
|
|
61
|
+
(filePath.startsWith('src/pages/') || filePath.startsWith('src/utils/')) &&
|
|
62
|
+
(filePath.endsWith('.ts') || filePath.endsWith('.tsx')),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const cleanResult = analyzeImpactedSpecs({ repoRoot: dir, profile });
|
|
66
|
+
assert.equal(cleanResult.hasAnythingToRun, false);
|
|
67
|
+
|
|
68
|
+
writeFile(dir, 'tests/auth/login.spec.ts', 'test("login", async ({ loginPage }) => { await loginPage.open(); await loginPage.open(); });\n');
|
|
69
|
+
|
|
70
|
+
const changedResult = analyzeImpactedSpecs({ repoRoot: dir, profile });
|
|
71
|
+
assert.equal(changedResult.hasAnythingToRun, true);
|
|
72
|
+
assert.equal(changedResult.selectedSpecsRelative.includes('tests/auth/login.spec.ts'), true);
|
|
73
|
+
});
|
|
74
|
+
|
|
47
75
|
test('analyzeImpactedSpecs returns no work when nothing changed', () => {
|
|
48
76
|
const dir = createBaseRepo();
|
|
49
77
|
const result = analyzeImpactedSpecs({ repoRoot: dir, profile: genericProfile, includeUntrackedSpecs: true });
|