tamash-playwright 0.5.0 → 0.6.1

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/README.md CHANGED
@@ -1,189 +1,207 @@
1
- # tamash-playwright
2
-
3
- `tamash-playwright` is a plug and play self-healing solution for any Playwright test framework. All you need to do is install the package, update your AI API key details, and import `test` from `tamash-playwright`.
4
-
5
- That's it. No code changes required if you're following standard Playwright best practices.
6
-
7
- ### Why you need this
8
-
9
- Websites change often. A button gets renamed or moved, and your test can't find it anymore — even though the app still works fine for real users. Normally, that just means a broken test.
10
-
11
- `tamash-playwright` fixes this automatically. When a test can't find an element, it asks an AI model to find it on the current page and tries again. If it succeeds, your test keeps going. If not, it fails normally, just like before.
12
-
13
- Here are the detailed steps to use this package.
14
-
15
- ## Step 1: Install it
16
-
17
- ```sh
18
- npm install tamash-playwright
19
- ```
20
-
21
- You also need Playwright's own test package, if you don't already have it:
22
-
23
- ```sh
24
- npm install -D @playwright/test
25
- ```
26
-
27
- ## Step 2: Connect an AI model
28
-
29
- `tamash-playwright` needs an AI model to decide where a broken element actually went. Pick one of Ollama, OpenAI, Anthropic (Claude), or Google Gemini, and give it an API key.
30
-
31
- Create a file named `.env` in your project folder:
32
-
33
- ```sh
34
- # Master on/off switch. Leave this as true, or remove the line entirely.
35
- HEALER_ENABLED=true
36
-
37
- # Pick one: ollama | openai | anthropic | gemini
38
- HEALER_PROVIDER=ollama
39
-
40
- # Optional, off by default — see "Action recovery" below.
41
- # HEALER_ACTION_RECOVERY_ENABLED=true
42
-
43
- # --- Ollama Cloud (https://ollama.com) ---
44
- OLLAMA_MODEL=gpt-oss:120b
45
- OLLAMA_API_KEY=
46
-
47
- # --- OpenAI ---
48
- # OPENAI_MODEL=gpt-4.1-mini
49
- # OPENAI_API_KEY=
50
-
51
- # --- Anthropic (Claude) ---
52
- # ANTHROPIC_MODEL=claude-haiku-4-5
53
- # ANTHROPIC_API_KEY=
54
-
55
- # --- Google Gemini ---
56
- # GEMINI_MODEL=
57
- # GEMINI_API_KEY=
58
- ```
59
-
60
- Just fill in the API key and model for whichever one you want to use, and leave the rest as-is (or delete them).
61
-
62
- ### Getting a free Ollama key (fastest way to get started)
63
-
64
- Ollama Cloud is a quick, free way to get an API key without signing up for OpenAI/Anthropic/Gemini billing.
65
-
66
- 1. Go to [ollama.com](https://ollama.com/) and create an account.
67
- 2. Once signed in, go to [ollama.com/settings/keys](https://ollama.com/settings/keys).
68
- 3. Create a new API key and copy it.
69
- 4. Paste it into your `.env` file:
70
-
71
- ```sh
72
- HEALER_ENABLED=true
73
- HEALER_PROVIDER=ollama
74
- OLLAMA_MODEL=gpt-oss:120b
75
- OLLAMA_API_KEY=paste_your_key_here
76
- ```
77
-
78
- That's all you need — no other variables required.
79
-
80
- ## Step 3: Check your setup
81
-
82
- Run the built-in doctor command to confirm everything's wired up correctly before you rely on it:
83
-
84
- ```sh
85
- npx tamash-playwright doctor
86
- ```
87
-
88
- It checks:
89
-
90
- 1. **AI connectivity** confirms `HEALER_ENABLED`/`HEALER_PROVIDER` are set correctly and actually calls your configured provider to make sure the API key and model work.
91
- 2. **Action recovery status** — whether `HEALER_ACTION_RECOVERY_ENABLED` is on (see below).
92
- 3. **Vision capability** — whether your configured model is expected to support the screenshot-based fallback (see below), based on its name.
93
- 4. **Missing `.describe()` labels** — scans your test files (`tests/` by default, or pass `--dir <path>`) for locators that don't have a `.describe('...')` label, and flags the ones most worth fixing (raw CSS/XPath selectors first).
94
- 5. **Locators written directly in test files** — flags any locator defined inline in a test rather than inside a Page Object class, which is a Playwright best practice regardless of self-healing: it keeps tests readable and means a UI change only needs a fix in one place.
95
-
96
- If it finds issues, the fastest fix is to open the project in an AI coding assistant (Claude Code, Cursor, GitHub Copilot, etc.) and ask it to address what it flagged — add `.describe()` calls, or extract locators into Page Object classes. You can also add a standing rule to that assistant's instructions/skill file (e.g. `CLAUDE.md`, `.cursor/rules`, `.github/copilot-instructions.md`) so it follows both practices automatically on any new test code going forward.
97
-
98
- ## Step 4: Use it in your tests
99
-
100
- Change one line at the top of your test file — everything else about how you write tests stays exactly the same:
101
-
102
- ```ts
103
- // Before
104
- import { test, expect } from '@playwright/test';
105
-
106
- // After
107
- import { test, expect } from 'tamash-playwright';
108
- ```
109
-
110
- That's it. Write your tests as normal:
111
-
112
- ```ts
113
- import { test, expect } from 'tamash-playwright';
114
-
115
- test('logs in', async ({ page }) => {
116
- await page.goto('/');
117
- const txtUserName = page.locator('input[name="username"]').describe('User Name Textbox');
118
- await txtUserName.fill('testadmin');
119
-
120
- const txtPassword = page.locator('input[placeholder="Password"]').describe('Password Textbox');
121
- await txtPassword.fill('secret');
122
-
123
- const btnLogin = page.locator('button[type="submit"]').describe('Login Button');
124
- await btnLogin.click();
125
-
126
- await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
127
- });
128
- ```
129
-
130
- ### A quick tip for better results
131
-
132
- If you're using plain CSS selectors (like `page.locator('input[name="username"]')`) rather than Playwright's more descriptive locators (`getByRole`, `getByPlaceholder`, etc.), it helps to add a short, human-readable label so the healer knows what it's actually looking for. Chain `.describe('...')` right onto the locator:
133
-
134
- ```ts
135
- test('login test using CSS Selectors', async ({ page }) => {
136
- await page.goto('https://example.com/auth/login');
137
-
138
- const txtUserName = page.locator('input[name="username"]').describe('User Name Textbox');
139
- await txtUserName.fill('testadmin');
140
-
141
- const txtPassword = page.locator('input[placeholder="Password"]').describe('Password Textbox');
142
- await txtPassword.fill('secret');
143
-
144
- const btnLogin = page.locator('button[type="submit"]').describe('Login Button');
145
- await btnLogin.click();
146
-
147
- await expect(page.locator('h6')).toHaveText('Dashboard');
148
- });
149
- ```
150
-
151
- This step is optional, but recommended — without it, the healer has to guess purely from a broken CSS selector, which gives it a lot less to work with.
152
-
153
- ## What else it heals no extra setup needed
154
-
155
- Beyond a single broken `click`/`fill`/`getByRole` on the main page, all of this works automatically once you've done Steps 1–2:
156
-
157
- - **Popups and extra tabs.** A page opened via `context.newPage()`, `window.open`, or a `target="_blank"` link is just as healing-aware as your main `page` — no manual wrapping needed.
158
- - **Elements inside `<iframe>`s.** `page.frameLocator('#my-iframe')` and anything chained off it heals the same way, scoped correctly to the iframe's own document.
159
- - **Most of the Playwright API surface**, not just clicks and fills — `check`, `selectOption`, `dragTo`, `dispatchEvent`, read methods like `textContent`/`getAttribute`/`isChecked`, `screenshot`, and more. Methods that can't be safely healed by guessing a replacement element (`dragTo`, `drop`) are still reported honestly on failure, they're just never silently retried with a different element.
160
-
161
- ## When text alone isn't enough: vision fallback
162
-
163
- Sometimes an element has nothing useful to match on by text — an icon-only button with no label, or several visually distinct elements that all look identical in the accessibility tree. If your configured model supports image input (e.g. `gpt-4o`, `claude-haiku-4-5`, `gemini-2.0-flash`), `tamash-playwright` automatically falls back to a screenshot-based search after the normal text-based attempt fails — no separate setup, it just uses the same provider and API key from Step 2. Run `npx tamash-playwright doctor` to check whether your configured model is expected to support this.
164
-
165
- ## Action recovery (optional)
166
-
167
- Occasionally a locator heals correctly — the AI found the right element — but the *action* on it still fails, e.g. it's covered by an overlay or needs scrolling into view first. Set `HEALER_ACTION_RECOVERY_ENABLED=true` to let the AI pick a recovery tactic from a fixed, safe set (scroll into view, retry bypassing actionability checks, wait briefly and retry, or dispatch the DOM event directly) before giving up. It's off by default since it's a second, more speculative layer of intervention beyond selector healing — the AI only ever picks from that fixed menu, it never decides how to interact with the page on its own.
168
-
169
- ## Checking what actually happened
170
-
171
- Every healing attempt — whether it succeeded or not shows up in Playwright's own HTML report (`npx playwright show-report`), no separate report to check:
172
-
173
- - An annotation on the test summarizing what happened, e.g. `Recovered using ollama:gpt-oss:120b (role:button:Submit)`.
174
- - A JSON attachment with the full detail: which provider was used, whether the vision or action-recovery fallback was involved, the AI's suggested selector, token cost, and — if it didn't heal — which stage it stopped at (e.g. `ai_declined`, `replay_failed`).
175
- - Exactly where in your own code the locator was created — a test file or a Page Object class, whichever it really is so you know which line to go fix even if you never look at the healing report again.
176
-
177
- The same detail is also printed to the console as it happens, one line per attempt:
178
-
179
- ```
180
- [self-healer] src/pages/loginpage.ts:11 — locator.fill "Username Textbox" -> HEALED [provider=ollama:gpt-oss:120b, vision=no, actionRecovery=no, suggested="role:textbox:Username", 620 tokens (489 input + 131 output)] — locator.fill: Timeout 8000ms exceeded.
181
- ```
182
-
183
- ## License
184
-
185
- Free to use, including commercially. The source code may not be copied, modified, redistributed, or resold without prior written permission. See the LICENSE file included in this package for the full terms.
186
-
187
- ## Support
188
-
189
- For questions or concerns, contact us at support@vibetestq.com.
1
+ # tamash-playwright
2
+
3
+ `tamash-playwright` is a plug and play self-healing solution for any Playwright test framework. All you need to do is install the package, update your AI API key details, and import `test` from `tamash-playwright`.
4
+
5
+ That's it. No code changes required if you're following standard Playwright best practices.
6
+
7
+ ### Why you need this
8
+
9
+ Websites change often. A button gets renamed or moved, and your test can't find it anymore — even though the app still works fine for real users. Normally, that just means a broken test.
10
+
11
+ `tamash-playwright` fixes this automatically. When a test can't find an element, it asks an AI model to find it on the current page and tries again. If it succeeds, your test keeps going. If not, it fails normally, just like before.
12
+
13
+ **Want to see it working before you set it up yourself?** Clone the sample repo [github.com/qtpsudhakarproducts/tamash-playwright-typescript-playwright](https://github.com/qtpsudhakarproducts/tamash-playwright-typescript-playwright) a full worked example with both a plain-locator test and a Page Object Model test, an intentionally broken selector, and step-by-step setup instructions.
14
+
15
+ Here are the detailed steps to use this package.
16
+
17
+ ## Step 1: Install it
18
+
19
+ ```sh
20
+ npm install tamash-playwright
21
+ ```
22
+
23
+ You also need Playwright's own test package, if you don't already have it:
24
+
25
+ ```sh
26
+ npm install -D @playwright/test
27
+ ```
28
+
29
+ ## Step 2: Connect an AI model
30
+
31
+ `tamash-playwright` needs an AI model to decide where a broken element actually went. Pick one of Ollama, OpenAI, Anthropic (Claude), or Google Gemini, and give it an API key.
32
+
33
+ Create a file named `.env` in your project folder:
34
+
35
+ ```sh
36
+ # Master on/off switch. Leave this as true, or remove the line entirely.
37
+ HEALER_ENABLED=true
38
+
39
+ # Pick one: ollama | openai | anthropic | gemini
40
+ HEALER_PROVIDER=ollama
41
+
42
+ # Optional, off by default — see "Action recovery" below.
43
+ # HEALER_ACTION_RECOVERY_ENABLED=true
44
+
45
+ # --- Ollama Cloud (https://ollama.com) ---
46
+ OLLAMA_MODEL=gpt-oss:120b
47
+ OLLAMA_API_KEY=
48
+
49
+ # --- OpenAI ---
50
+ # OPENAI_MODEL=gpt-4.1-mini
51
+ # OPENAI_API_KEY=
52
+
53
+ # --- Anthropic (Claude) ---
54
+ # ANTHROPIC_MODEL=claude-haiku-4-5
55
+ # ANTHROPIC_API_KEY=
56
+
57
+ # --- Google Gemini ---
58
+ # GEMINI_MODEL=
59
+ # GEMINI_API_KEY=
60
+ ```
61
+
62
+ Just fill in the API key and model for whichever one you want to use, and leave the rest as-is (or delete them).
63
+
64
+ ### Getting a free Ollama key (fastest way to get started)
65
+
66
+ Ollama Cloud is a quick, free way to get an API key without signing up for OpenAI/Anthropic/Gemini billing.
67
+
68
+ 1. Go to [ollama.com](https://ollama.com/) and create an account.
69
+ 2. Once signed in, go to [ollama.com/settings/keys](https://ollama.com/settings/keys).
70
+ 3. Create a new API key and copy it.
71
+ 4. Paste it into your `.env` file:
72
+
73
+ ```sh
74
+ HEALER_ENABLED=true
75
+ HEALER_PROVIDER=ollama
76
+ OLLAMA_MODEL=gpt-oss:120b
77
+ OLLAMA_API_KEY=paste_your_key_here
78
+ ```
79
+
80
+ That's all you need no other variables required.
81
+
82
+ ### Important: set `actionTimeout` in your `playwright.config.ts`
83
+
84
+ By default, Playwright lets a broken locator retry silently for your *entire* test timeout before it ever throws an error — which means self-healing never gets a turn at all, since it only kicks in once an action actually fails. Set `actionTimeout` to something well below your test timeout so a broken locator fails fast, leaving real time for healing to run:
85
+
86
+ ```ts
87
+ export default defineConfig({
88
+ timeout: 60000, // your overall test timeout
89
+ use: {
90
+ actionTimeout: 8000, // must be comfortably less than the test timeout above
91
+ },
92
+ });
93
+ ```
94
+
95
+ Without this, healing attempts will show `stage=no_snapshot` in the console and never recover anything — not because healing failed, but because it never had time to run before the whole test was torn down.
96
+
97
+ ## Step 3: Check your setup
98
+
99
+ Run the built-in doctor command to confirm everything's wired up correctly before you rely on it:
100
+
101
+ ```sh
102
+ npx tamash-playwright doctor
103
+ ```
104
+
105
+ It checks:
106
+
107
+ 1. **AI connectivity** confirms `HEALER_ENABLED`/`HEALER_PROVIDER` are set correctly and actually calls your configured provider to make sure the API key and model work.
108
+ 2. **`actionTimeout` configuration** — checks your `playwright.config.ts` for an `actionTimeout` set well below your test `timeout` (see above); flags it if missing or too close to the test timeout, since that silently starves self-healing of any time to run.
109
+ 3. **Action recovery status** — whether `HEALER_ACTION_RECOVERY_ENABLED` is on (see below).
110
+ 4. **Vision capability** — whether your configured model is expected to support the screenshot-based fallback (see below), based on its name.
111
+ 5. **Missing `.describe()` labels** — scans your test files (`tests/` by default, or pass `--dir <path>`) for locators that don't have a `.describe('...')` label, and flags the ones most worth fixing (raw CSS/XPath selectors first).
112
+ 6. **Locators written directly in test files** — flags any locator defined inline in a test rather than inside a Page Object class, which is a Playwright best practice regardless of self-healing: it keeps tests readable and means a UI change only needs a fix in one place.
113
+
114
+ If it finds issues, the fastest fix is to open the project in an AI coding assistant (Claude Code, Cursor, GitHub Copilot, etc.) and ask it to address what it flagged — add `.describe()` calls, or extract locators into Page Object classes. You can also add a standing rule to that assistant's instructions/skill file (e.g. `CLAUDE.md`, `.cursor/rules`, `.github/copilot-instructions.md`) so it follows both practices automatically on any new test code going forward.
115
+
116
+ ## Step 4: Use it in your tests
117
+
118
+ Change one line at the top of your test file — everything else about how you write tests stays exactly the same:
119
+
120
+ ```ts
121
+ // Before
122
+ import { test, expect } from '@playwright/test';
123
+
124
+ // After
125
+ import { test, expect } from 'tamash-playwright';
126
+ ```
127
+
128
+ That's it. Write your tests as normal:
129
+
130
+ ```ts
131
+ import { test, expect } from 'tamash-playwright';
132
+
133
+ test('logs in', async ({ page }) => {
134
+ await page.goto('/');
135
+ const txtUserName = page.locator('input[name="username"]').describe('User Name Textbox');
136
+ await txtUserName.fill('testadmin');
137
+
138
+ const txtPassword = page.locator('input[placeholder="Password"]').describe('Password Textbox');
139
+ await txtPassword.fill('secret');
140
+
141
+ const btnLogin = page.locator('button[type="submit"]').describe('Login Button');
142
+ await btnLogin.click();
143
+
144
+ await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
145
+ });
146
+ ```
147
+
148
+ ### A quick tip for better results
149
+
150
+ If you're using plain CSS selectors (like `page.locator('input[name="username"]')`) rather than Playwright's more descriptive locators (`getByRole`, `getByPlaceholder`, etc.), it helps to add a short, human-readable label so the healer knows what it's actually looking for. Chain `.describe('...')` right onto the locator:
151
+
152
+ ```ts
153
+ test('login test using CSS Selectors', async ({ page }) => {
154
+ await page.goto('https://example.com/auth/login');
155
+
156
+ const txtUserName = page.locator('input[name="username"]').describe('User Name Textbox');
157
+ await txtUserName.fill('testadmin');
158
+
159
+ const txtPassword = page.locator('input[placeholder="Password"]').describe('Password Textbox');
160
+ await txtPassword.fill('secret');
161
+
162
+ const btnLogin = page.locator('button[type="submit"]').describe('Login Button');
163
+ await btnLogin.click();
164
+
165
+ await expect(page.locator('h6')).toHaveText('Dashboard');
166
+ });
167
+ ```
168
+
169
+ This step is optional, but recommended — without it, the healer has to guess purely from a broken CSS selector, which gives it a lot less to work with.
170
+
171
+ ## What else it heals — no extra setup needed
172
+
173
+ Beyond a single broken `click`/`fill`/`getByRole` on the main page, all of this works automatically once you've done Steps 1–2:
174
+
175
+ - **Popups and extra tabs.** A page opened via `context.newPage()`, `window.open`, or a `target="_blank"` link is just as healing-aware as your main `page` no manual wrapping needed.
176
+ - **Elements inside `<iframe>`s.** `page.frameLocator('#my-iframe')` and anything chained off it heals the same way, scoped correctly to the iframe's own document.
177
+ - **Most of the Playwright API surface**, not just clicks and fills — `check`, `selectOption`, `dragTo`, `dispatchEvent`, read methods like `textContent`/`getAttribute`/`isChecked`, `screenshot`, and more. Methods that can't be safely healed by guessing a replacement element (`dragTo`, `drop`) are still reported honestly on failure, they're just never silently retried with a different element.
178
+
179
+ ## When text alone isn't enough: vision fallback
180
+
181
+ Sometimes an element has nothing useful to match on by text — an icon-only button with no label, or several visually distinct elements that all look identical in the accessibility tree. If your configured model supports image input (e.g. `gpt-4o`, `claude-haiku-4-5`, `gemini-2.0-flash`), `tamash-playwright` automatically falls back to a screenshot-based search after the normal text-based attempt fails — no separate setup, it just uses the same provider and API key from Step 2. Run `npx tamash-playwright doctor` to check whether your configured model is expected to support this.
182
+
183
+ ## Action recovery (optional)
184
+
185
+ Occasionally a locator heals correctly — the AI found the right element — but the *action* on it still fails, e.g. it's covered by an overlay or needs scrolling into view first. Set `HEALER_ACTION_RECOVERY_ENABLED=true` to let the AI pick a recovery tactic from a fixed, safe set (scroll into view, retry bypassing actionability checks, wait briefly and retry, or dispatch the DOM event directly) before giving up. It's off by default since it's a second, more speculative layer of intervention beyond selector healing — the AI only ever picks from that fixed menu, it never decides how to interact with the page on its own.
186
+
187
+ ## Checking what actually happened
188
+
189
+ Every healing attempt — whether it succeeded or not — shows up in Playwright's own HTML report (`npx playwright show-report`), no separate report to check:
190
+
191
+ - An annotation on the test summarizing what happened, e.g. `Recovered using ollama:gpt-oss:120b (role:button:Submit)`.
192
+ - A JSON attachment with the full detail: which provider was used, whether the vision or action-recovery fallback was involved, the AI's suggested selector, token cost, and — if it didn't heal — which stage it stopped at (e.g. `ai_declined`, `replay_failed`).
193
+ - Exactly where in your own code the locator was created — a test file or a Page Object class, whichever it really is — so you know which line to go fix even if you never look at the healing report again.
194
+
195
+ The same detail is also printed to the console as it happens, one line per attempt:
196
+
197
+ ```
198
+ [self-healer] src/pages/loginpage.ts:11 — locator.fill "Username Textbox" -> HEALED [provider=ollama:gpt-oss:120b, vision=no, actionRecovery=no, suggested="role:textbox:Username", 620 tokens (489 input + 131 output)] — locator.fill: Timeout 8000ms exceeded.
199
+ ```
200
+
201
+ ## License
202
+
203
+ Free to use, including commercially. The source code may not be copied, modified, redistributed, or resold without prior written permission. See the LICENSE file included in this package for the full terms.
204
+
205
+ ## Support
206
+
207
+ For questions or concerns, contact us at support@vibetestq.com.
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAyHA,wBAAsB,SAAS,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAclE"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAqJA,wBAAsB,SAAS,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBlE"}
@@ -9,6 +9,7 @@ const dotenv_1 = __importDefault(require("dotenv"));
9
9
  const providers_1 = require("../healer/providers");
10
10
  const healer_1 = require("../healer");
11
11
  const scanDescribe_1 = require("./scanDescribe");
12
+ const scanActionTimeout_1 = require("./scanActionTimeout");
12
13
  dotenv_1.default.config({ path: path_1.default.resolve(process.cwd(), '.env') });
13
14
  const CONNECTIVITY_TIMEOUT_MS = 15000;
14
15
  const CONNECTIVITY_SNAPSHOT = '- generic "tamash-playwright doctor check":\n - button "OK"';
@@ -106,6 +107,31 @@ function checkPageObjectUsage(occurrences) {
106
107
  console.log(' page.locator(...) calls). Tests then call page-object methods, not raw locators. This keeps');
107
108
  console.log(' tests readable and means a UI change only needs a fix in one place.');
108
109
  }
110
+ function checkActionTimeoutSetup() {
111
+ const result = (0, scanActionTimeout_1.checkActionTimeout)();
112
+ switch (result.status) {
113
+ case 'no_config':
114
+ console.log('[INFO] Could not find a playwright.config.(ts|js|mjs|cjs) in the current directory to check.');
115
+ console.log(' Make sure `actionTimeout` (inside `use: {...}`) is set well below your test `timeout` —');
116
+ console.log(' without it, a broken locator can retry silently for the entire test timeout, leaving no');
117
+ console.log(' time for self-healing to run at all.');
118
+ return;
119
+ case 'not_set':
120
+ console.log(`[WARN] No \`actionTimeout\` found in ${result.configFile}.`);
121
+ console.log(' Without it, Playwright lets a broken locator retry for your whole test timeout before');
122
+ console.log(' ever throwing — self-healing never gets a turn. Add this to your `use: {...}` block:');
123
+ console.log(' actionTimeout: 8000, // comfortably less than your test timeout');
124
+ return;
125
+ case 'too_close':
126
+ console.log(`[WARN] ${result.configFile} sets actionTimeout: ${result.actionTimeout} >= timeout: ${result.testTimeout}.`);
127
+ console.log(' actionTimeout must be well below the overall test timeout, or self-healing still won\'t');
128
+ console.log(' have any time left to run once an action fails.');
129
+ return;
130
+ case 'ok':
131
+ console.log(`[OK] ${result.configFile} sets actionTimeout: ${result.actionTimeout}ms — self-healing has room to run.`);
132
+ return;
133
+ }
134
+ }
109
135
  function parseTestDir(args) {
110
136
  const flagIndex = args.indexOf('--dir');
111
137
  const value = flagIndex !== -1 ? args[flagIndex + 1] : undefined;
@@ -115,6 +141,8 @@ async function runDoctor(args = []) {
115
141
  console.log('tamash-playwright doctor\n');
116
142
  await checkProviderConnectivity();
117
143
  console.log('');
144
+ checkActionTimeoutSetup();
145
+ console.log('');
118
146
  const testDir = parseTestDir(args);
119
147
  const relativeDir = path_1.default.relative(process.cwd(), testDir) || testDir;
120
148
  console.log(`Scanning ${relativeDir} for locators...`);
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export type ActionTimeoutCheck = {
2
+ status: 'no_config';
3
+ } | {
4
+ status: 'not_set';
5
+ configFile: string;
6
+ } | {
7
+ status: 'ok';
8
+ configFile: string;
9
+ actionTimeout: number;
10
+ } | {
11
+ status: 'too_close';
12
+ configFile: string;
13
+ actionTimeout: number;
14
+ testTimeout: number;
15
+ };
16
+ export declare function checkActionTimeout(cwd?: string): ActionTimeoutCheck;
17
+ //# sourceMappingURL=scanActionTimeout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanActionTimeout.d.ts","sourceRoot":"","sources":["../../src/cli/scanActionTimeout.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AA6E5F,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAsB,GAAG,kBAAkB,CAoBlF"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkActionTimeout = checkActionTimeout;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const CONFIG_FILENAMES = ['playwright.config.ts', 'playwright.config.js', 'playwright.config.mjs', 'playwright.config.cjs'];
10
+ function findConfigFile(cwd) {
11
+ for (const name of CONFIG_FILENAMES) {
12
+ const full = path_1.default.join(cwd, name);
13
+ if (fs_1.default.existsSync(full))
14
+ return full;
15
+ }
16
+ return undefined;
17
+ }
18
+ // `\b` can't match inside "actionTimeout" right before "Timeout" (no non-word char between "n"
19
+ // and "T"), and the key itself is matched case-sensitively, so this can't accidentally pick up
20
+ // `actionTimeout: N` when scanning for the plain `timeout: N` key.
21
+ function extractNumber(content, key) {
22
+ const match = content.match(new RegExp(`\\b${key}\\s*:\\s*(\\d+)`));
23
+ return match ? Number(match[1]) : undefined;
24
+ }
25
+ // Strips `//` and `/* */` comments before extractNumber ever sees the text — otherwise a
26
+ // commented-out `// actionTimeout: 8000,` reads as active, which is worse than not checking at
27
+ // all (a confident false "OK" on the exact footgun this check exists to catch). Has to respect
28
+ // string boundaries so a `//` inside a legitimate value (e.g. `baseURL: 'https://...'`) is never
29
+ // mistaken for a comment start — same character-scanning approach as findFactoryCallOnLine in
30
+ // applyHeals.ts, not a real parser, just comment/string-aware instead of comment-blind.
31
+ function stripComments(content) {
32
+ let result = '';
33
+ let i = 0;
34
+ let inString = null;
35
+ while (i < content.length) {
36
+ const ch = content[i];
37
+ if (inString) {
38
+ result += ch;
39
+ if (ch === '\\') {
40
+ result += content[i + 1] ?? '';
41
+ i += 2;
42
+ continue;
43
+ }
44
+ if (ch === inString) {
45
+ inString = null;
46
+ }
47
+ i++;
48
+ continue;
49
+ }
50
+ if (ch === '"' || ch === "'" || ch === '`') {
51
+ inString = ch;
52
+ result += ch;
53
+ i++;
54
+ continue;
55
+ }
56
+ if (ch === '/' && content[i + 1] === '/') {
57
+ while (i < content.length && content[i] !== '\n')
58
+ i++;
59
+ continue;
60
+ }
61
+ if (ch === '/' && content[i + 1] === '*') {
62
+ i += 2;
63
+ while (i < content.length && !(content[i] === '*' && content[i + 1] === '/'))
64
+ i++;
65
+ i += 2;
66
+ continue;
67
+ }
68
+ result += ch;
69
+ i++;
70
+ }
71
+ return result;
72
+ }
73
+ // A plain regex scan over the config file's text, not a real parser — same "good enough for a
74
+ // human to review" philosophy as scanDescribe.ts. Exists to catch the single most common
75
+ // self-healing footgun: without an `actionTimeout` well below the test `timeout`, a broken locator
76
+ // can retry silently for the entire test timeout before ever throwing, leaving self-healing with
77
+ // no time left to run at all (see the `no_snapshot` failure stage in healer/index.ts).
78
+ function checkActionTimeout(cwd = process.cwd()) {
79
+ const configFile = findConfigFile(cwd);
80
+ if (!configFile) {
81
+ return { status: 'no_config' };
82
+ }
83
+ const content = stripComments(fs_1.default.readFileSync(configFile, 'utf-8'));
84
+ const relativeFile = path_1.default.relative(cwd, configFile);
85
+ const actionTimeout = extractNumber(content, 'actionTimeout');
86
+ if (actionTimeout === undefined) {
87
+ return { status: 'not_set', configFile: relativeFile };
88
+ }
89
+ const testTimeout = extractNumber(content, 'timeout');
90
+ if (testTimeout !== undefined && actionTimeout >= testTimeout) {
91
+ return { status: 'too_close', configFile: relativeFile, actionTimeout, testTimeout };
92
+ }
93
+ return { status: 'ok', configFile: relativeFile, actionTimeout };
94
+ }
95
+ //# sourceMappingURL=scanActionTimeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanActionTimeout.js","sourceRoot":"","sources":["../../src/cli/scanActionTimeout.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "tamash-playwright",
3
- "version": "0.5.0",
4
- "description": "Plug and Play Self-healing for Playwright and automatically recovers broken selectors using an AI model (Ollama, OpenAI, Anthropic, or Gemini).",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "bin": {
8
- "tamash-playwright": "dist/cli/index.js"
9
- },
10
- "files": [
11
- "dist",
12
- "README.md",
13
- ".env.example"
14
- ],
15
- "scripts": {
16
- "build": "tsc -p tsconfig.json",
17
- "prepublishOnly": "npm run build"
18
- },
19
- "keywords": [
20
- "playwright",
21
- "self-healing",
22
- "test-automation",
23
- "llm",
24
- "ollama",
25
- "openai",
26
- "anthropic",
27
- "gemini"
28
- ],
29
- "author": "QtpSudhakar / VibeTestQ",
30
- "license": "SEE LICENSE IN LICENSE",
31
- "peerDependencies": {
32
- "@playwright/test": ">=1.40.0"
33
- },
34
- "dependencies": {
35
- "@anthropic-ai/sdk": "^0.115.0",
36
- "dotenv": "^16.0.1"
37
- },
38
- "devDependencies": {
39
- "@playwright/test": "^1.62.1",
40
- "@types/node": "^26.1.2",
41
- "typescript": "^7.0.2"
42
- }
43
- }
1
+ {
2
+ "name": "tamash-playwright",
3
+ "version": "0.6.1",
4
+ "description": "Plug and Play Self-healing for Playwright and automatically recovers broken selectors using an AI model (Ollama, OpenAI, Anthropic, or Gemini).",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "tamash-playwright": "dist/cli/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ ".env.example"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.json",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "playwright",
21
+ "self-healing",
22
+ "test-automation",
23
+ "llm",
24
+ "ollama",
25
+ "openai",
26
+ "anthropic",
27
+ "gemini"
28
+ ],
29
+ "author": "QtpSudhakar / VibeTestQ",
30
+ "license": "SEE LICENSE IN LICENSE",
31
+ "peerDependencies": {
32
+ "@playwright/test": ">=1.40.0"
33
+ },
34
+ "dependencies": {
35
+ "@anthropic-ai/sdk": "^0.115.0",
36
+ "dotenv": "^16.0.1"
37
+ },
38
+ "devDependencies": {
39
+ "@playwright/test": "^1.62.1",
40
+ "@types/node": "^26.1.2",
41
+ "typescript": "^7.0.2"
42
+ }
43
+ }