browsecraft 0.1.0 → 0.1.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 +390 -0
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Browsecraft
|
|
2
|
+
|
|
3
|
+
Browser automation that just works.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { Browser } from 'browsecraft';
|
|
7
|
+
|
|
8
|
+
const browser = await Browser.launch();
|
|
9
|
+
const page = await browser.newPage();
|
|
10
|
+
|
|
11
|
+
await page.goto('https://www.saucedemo.com');
|
|
12
|
+
await page.fill('Username', 'standard_user');
|
|
13
|
+
await page.fill('Password', 'secret_sauce');
|
|
14
|
+
await page.click('Login');
|
|
15
|
+
|
|
16
|
+
await page.waitForURL('inventory');
|
|
17
|
+
console.log(await page.title()); // "Swag Labs"
|
|
18
|
+
|
|
19
|
+
await browser.close();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
No CSS selectors. No XPath. Just tell it what you see on the page.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install browsecraft
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Requires Node.js 20+ and Chrome, Edge, or Firefox installed on your machine.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
Create a file called `test.mjs`:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { Browser } from 'browsecraft';
|
|
38
|
+
|
|
39
|
+
const browser = await Browser.launch();
|
|
40
|
+
const page = await browser.newPage();
|
|
41
|
+
|
|
42
|
+
await page.goto('https://example.com');
|
|
43
|
+
console.log(await page.title()); // "Example Domain"
|
|
44
|
+
|
|
45
|
+
const screenshot = await page.screenshot();
|
|
46
|
+
const { writeFileSync } = await import('node:fs');
|
|
47
|
+
writeFileSync('screenshot.png', screenshot);
|
|
48
|
+
|
|
49
|
+
await browser.close();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Run it:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
node test.mjs
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That's it. No config files, no test runner setup, no boilerplate.
|
|
59
|
+
|
|
60
|
+
## API
|
|
61
|
+
|
|
62
|
+
### Launch a browser
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { Browser } from 'browsecraft';
|
|
66
|
+
|
|
67
|
+
const browser = await Browser.launch({
|
|
68
|
+
browser: 'chrome', // 'chrome' | 'firefox' | 'edge' (default: 'chrome')
|
|
69
|
+
headless: true, // default: true
|
|
70
|
+
maximized: false, // maximize the window (headed mode only)
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const page = await browser.newPage();
|
|
74
|
+
// ... do things ...
|
|
75
|
+
await browser.close();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Navigate
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
await page.goto('https://example.com');
|
|
82
|
+
|
|
83
|
+
const url = await page.url();
|
|
84
|
+
const title = await page.title();
|
|
85
|
+
const html = await page.content();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Click
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// By visible text — Browsecraft finds the element for you
|
|
92
|
+
await page.click('Submit');
|
|
93
|
+
await page.click('Add to cart');
|
|
94
|
+
|
|
95
|
+
// By CSS selector — when you need precision
|
|
96
|
+
await page.click({ selector: '[data-test="login-button"]' });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Fill in fields
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
// By label or placeholder text
|
|
103
|
+
await page.fill('Username', 'standard_user');
|
|
104
|
+
await page.fill('Password', 'secret_sauce');
|
|
105
|
+
|
|
106
|
+
// By CSS selector
|
|
107
|
+
await page.fill({ selector: '#email' }, 'user@example.com');
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Wait for things
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
await page.waitForURL('dashboard'); // URL contains "dashboard"
|
|
114
|
+
await page.waitForURL(/checkout/); // URL matches regex
|
|
115
|
+
await page.waitForSelector({ selector: '.loaded' }); // element appears
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Find elements
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const el = await page.get({ selector: '.product-card' });
|
|
122
|
+
const btn = await page.getByText('Add to Cart');
|
|
123
|
+
|
|
124
|
+
await el.textContent();
|
|
125
|
+
await el.isVisible();
|
|
126
|
+
await el.getAttribute('href');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Read text from the page
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const text = await page.innerText({ selector: '.message' });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Run JavaScript in the browser
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const count = await page.evaluate('document.querySelectorAll(".item").length');
|
|
139
|
+
const data = await page.evaluate('({ name: "test", items: [1, 2, 3] })');
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Screenshots
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
const buffer = await page.screenshot(); // returns PNG Buffer
|
|
146
|
+
|
|
147
|
+
import { writeFileSync } from 'node:fs';
|
|
148
|
+
writeFileSync('screenshot.png', buffer);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Cookies
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
await page.getCookies();
|
|
155
|
+
await page.clearCookies();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Multiple tabs
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const page1 = await browser.newPage();
|
|
162
|
+
const page2 = await browser.newPage();
|
|
163
|
+
|
|
164
|
+
browser.openPages; // [page1, page2]
|
|
165
|
+
browser.isConnected; // true
|
|
166
|
+
|
|
167
|
+
await page1.close();
|
|
168
|
+
await page2.close();
|
|
169
|
+
await browser.close();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## BDD Testing
|
|
173
|
+
|
|
174
|
+
Browsecraft has a built-in BDD framework. No Cucumber, no third-party dependencies — everything is custom-built.
|
|
175
|
+
|
|
176
|
+
There are two ways to write BDD tests:
|
|
177
|
+
|
|
178
|
+
### Option 1: Gherkin `.feature` files
|
|
179
|
+
|
|
180
|
+
Write scenarios in plain English, then wire them to code with step definitions.
|
|
181
|
+
|
|
182
|
+
**`features/login.feature`**
|
|
183
|
+
|
|
184
|
+
```gherkin
|
|
185
|
+
Feature: User Login
|
|
186
|
+
|
|
187
|
+
Scenario: Successful login
|
|
188
|
+
Given I am on the login page
|
|
189
|
+
When I fill "Username" with "standard_user"
|
|
190
|
+
And I fill "Password" with "secret_sauce"
|
|
191
|
+
And I click "Login"
|
|
192
|
+
Then I should see "Products"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**`test.mjs`**
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
import { readFileSync } from 'node:fs';
|
|
199
|
+
import { Browser } from 'browsecraft';
|
|
200
|
+
import {
|
|
201
|
+
parseGherkin, Given, When, Then,
|
|
202
|
+
BddExecutor, globalRegistry,
|
|
203
|
+
} from 'browsecraft-bdd';
|
|
204
|
+
|
|
205
|
+
// --- Step definitions ---
|
|
206
|
+
// Each Given/When/Then matches a line in the .feature file.
|
|
207
|
+
// {string} captures a quoted argument.
|
|
208
|
+
|
|
209
|
+
Given('I am on the login page', async (world) => {
|
|
210
|
+
await world.page.goto('https://www.saucedemo.com');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
When('I fill {string} with {string}', async (world, field, value) => {
|
|
214
|
+
await world.page.fill(field, value);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
When('I click {string}', async (world, text) => {
|
|
218
|
+
await world.page.click(text);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
Then('I should see {string}', async (world, text) => {
|
|
222
|
+
const content = await world.page.content();
|
|
223
|
+
if (!content.includes(text)) throw new Error(`Expected "${text}" on page`);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// --- Run ---
|
|
227
|
+
|
|
228
|
+
const browser = await Browser.launch();
|
|
229
|
+
const doc = parseGherkin(readFileSync('features/login.feature', 'utf-8'));
|
|
230
|
+
|
|
231
|
+
const executor = new BddExecutor({
|
|
232
|
+
registry: globalRegistry,
|
|
233
|
+
worldFactory: async () => ({
|
|
234
|
+
page: await browser.newPage(),
|
|
235
|
+
browser,
|
|
236
|
+
ctx: {},
|
|
237
|
+
attach: () => {},
|
|
238
|
+
log: console.log,
|
|
239
|
+
}),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const result = await executor.runDocument(doc);
|
|
243
|
+
console.log(`${result.summary.scenarios.passed}/${result.summary.scenarios.total} passed`);
|
|
244
|
+
await browser.close();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Option 2: TypeScript-native BDD
|
|
248
|
+
|
|
249
|
+
Same structured BDD output, but no `.feature` files — write everything in code.
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
import { Browser } from 'browsecraft';
|
|
253
|
+
import { feature, scenario, given, when, then, runFeatures } from 'browsecraft-bdd';
|
|
254
|
+
|
|
255
|
+
feature('User Login', () => {
|
|
256
|
+
scenario('Successful login', ({ page }) => {
|
|
257
|
+
given('I am on the login page', () =>
|
|
258
|
+
page.goto('https://www.saucedemo.com'));
|
|
259
|
+
|
|
260
|
+
when('I enter credentials and log in', async () => {
|
|
261
|
+
await page.fill('Username', 'standard_user');
|
|
262
|
+
await page.fill('Password', 'secret_sauce');
|
|
263
|
+
await page.click('Login');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
then('I should see the products page', () =>
|
|
267
|
+
page.waitForURL('inventory'));
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const browser = await Browser.launch();
|
|
272
|
+
const result = await runFeatures({
|
|
273
|
+
worldFactory: async () => ({
|
|
274
|
+
page: await browser.newPage(),
|
|
275
|
+
browser,
|
|
276
|
+
ctx: {},
|
|
277
|
+
attach: () => {},
|
|
278
|
+
log: console.log,
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
console.log(`${result.summary.scenarios.passed}/${result.summary.scenarios.total} passed`);
|
|
282
|
+
await browser.close();
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Tags
|
|
286
|
+
|
|
287
|
+
Filter which scenarios run using tag expressions:
|
|
288
|
+
|
|
289
|
+
```gherkin
|
|
290
|
+
@smoke
|
|
291
|
+
Scenario: Quick test
|
|
292
|
+
...
|
|
293
|
+
|
|
294
|
+
@slow @integration
|
|
295
|
+
Scenario: Full flow
|
|
296
|
+
...
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
const executor = new BddExecutor({
|
|
301
|
+
tagFilter: '@smoke and not @slow',
|
|
302
|
+
// ...
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Supports `and`, `or`, `not`, and parentheses.
|
|
307
|
+
|
|
308
|
+
### Hooks
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
import { Before, After, BeforeAll, AfterAll } from 'browsecraft-bdd';
|
|
312
|
+
|
|
313
|
+
BeforeAll(async () => { /* one-time setup */ });
|
|
314
|
+
AfterAll(async () => { /* one-time teardown */ });
|
|
315
|
+
Before(async (ctx) => { /* before each scenario */ });
|
|
316
|
+
After(async (ctx) => { /* after each scenario */ });
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Gherkin parser features
|
|
320
|
+
|
|
321
|
+
The built-in parser handles the full Gherkin spec:
|
|
322
|
+
|
|
323
|
+
- Scenario Outlines with Examples tables
|
|
324
|
+
- Background steps
|
|
325
|
+
- Data Tables and Doc Strings
|
|
326
|
+
- Tags on features and scenarios
|
|
327
|
+
- Rules
|
|
328
|
+
- Comments
|
|
329
|
+
- [Multiple languages](https://cucumber.io/docs/gherkin/languages/) (English, Spanish, French, German, Japanese, and more)
|
|
330
|
+
|
|
331
|
+
## AI Features (Optional)
|
|
332
|
+
|
|
333
|
+
AI features use the [GitHub Models API](https://github.com/marketplace/models) — free with any GitHub account. Set a PAT with the `models` scope:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
export GITHUB_TOKEN=ghp_...
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Everything works without AI. These features enhance the experience when available.
|
|
340
|
+
|
|
341
|
+
| Feature | What it does |
|
|
342
|
+
| --- | --- |
|
|
343
|
+
| Self-healing selectors | When a CSS selector breaks, suggests a replacement using page context |
|
|
344
|
+
| Test generation | Generates test code from a natural-language description |
|
|
345
|
+
| Visual regression | Compares screenshots pixel-by-pixel, with optional AI semantic analysis |
|
|
346
|
+
| Auto-step generation | Writes BDD step definitions from `.feature` files automatically |
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
import { healSelector, generateTest, compareScreenshots } from 'browsecraft-ai';
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Examples
|
|
353
|
+
|
|
354
|
+
The [`examples/`](examples/) directory has complete, runnable projects you can copy as a starting point:
|
|
355
|
+
|
|
356
|
+
| Example | What it shows |
|
|
357
|
+
| --- | --- |
|
|
358
|
+
| [`getting-started/`](examples/getting-started/) | Imperative tests — login, cart, checkout, screenshots |
|
|
359
|
+
| [`bdd-gherkin/`](examples/bdd-gherkin/) | `.feature` files with step definitions (9 scenarios) |
|
|
360
|
+
| [`bdd-typescript/`](examples/bdd-typescript/) | TypeScript-native BDD with `feature()`/`scenario()` (6 scenarios) |
|
|
361
|
+
|
|
362
|
+
All examples test against the [Sauce Labs Demo App](https://www.saucedemo.com) (public, no account needed):
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
cd examples/getting-started
|
|
366
|
+
npm install
|
|
367
|
+
node test.mjs # headless
|
|
368
|
+
node test.mjs --headed # watch it run
|
|
369
|
+
node test.mjs --maximized # full screen
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Architecture
|
|
373
|
+
|
|
374
|
+
Five npm packages, one monorepo:
|
|
375
|
+
|
|
376
|
+
| Package | Role |
|
|
377
|
+
| --- | --- |
|
|
378
|
+
| `browsecraft` | Main package. Page API, Browser, config, CLI. |
|
|
379
|
+
| `browsecraft-bdd` | Gherkin parser, step registry, executor, hooks, tags, TS-native BDD. |
|
|
380
|
+
| `browsecraft-bidi` | WebDriver BiDi protocol client and browser launcher. |
|
|
381
|
+
| `browsecraft-runner` | Test file discovery, execution, reporter types. |
|
|
382
|
+
| `browsecraft-ai` | Self-healing selectors, test generation, visual diff. |
|
|
383
|
+
|
|
384
|
+
Most users only need `browsecraft`. Add `browsecraft-bdd` for BDD, `browsecraft-ai` for AI features.
|
|
385
|
+
|
|
386
|
+
Built on the [W3C WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) protocol — controls real, unpatched browser binaries. No special builds, no browser extensions.
|
|
387
|
+
|
|
388
|
+
## License
|
|
389
|
+
|
|
390
|
+
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browsecraft",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "AI-native browser automation framework. Craft browser tests that just work.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"browsecraft-
|
|
29
|
-
"browsecraft-
|
|
30
|
-
"browsecraft-
|
|
28
|
+
"browsecraft-bidi": "0.1.1",
|
|
29
|
+
"browsecraft-bdd": "0.1.1",
|
|
30
|
+
"browsecraft-runner": "0.1.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.10.0",
|