browser-commander 0.10.0 → 0.12.0
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/CHANGELOG.md +16 -0
- package/README.md +58 -0
- package/examples/pdf-generation.js +7 -4
- package/package.json +1 -1
- package/scripts/run-tests.mjs +19 -1
- package/src/README.md +13 -0
- package/src/bindings.js +29 -1
- package/src/browser/launcher.js +17 -0
- package/src/browser/navigation.js +58 -0
- package/src/browser/storage-state.js +133 -0
- package/src/core/engine-adapter.js +33 -0
- package/src/core/navigation-manager.js +57 -24
- package/src/elements/content.js +55 -0
- package/src/exports.js +4 -0
- package/tests/e2e/playwright.e2e.test.js +19 -0
- package/tests/e2e/puppeteer.e2e.test.js +19 -0
- package/tests/helpers/mocks.js +10 -0
- package/tests/unit/bindings.test.js +68 -0
- package/tests/unit/browser/navigation.test.js +61 -0
- package/tests/unit/browser/storage-state.test.js +148 -0
- package/tests/unit/core/navigation-manager.test.js +62 -0
- package/tests/unit/elements/content.test.js +102 -0
- package/tests/unit/scripts/ci-workflow-policy.test.js +73 -0
- package/tests/unit/scripts/npm-release-helpers.test.js +20 -7
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert';
|
|
3
3
|
import {
|
|
4
|
+
content,
|
|
5
|
+
innerText,
|
|
4
6
|
textContent,
|
|
5
7
|
inputValue,
|
|
6
8
|
getAttribute,
|
|
@@ -14,6 +16,106 @@ import {
|
|
|
14
16
|
} from '../../helpers/mocks.js';
|
|
15
17
|
|
|
16
18
|
describe('content', () => {
|
|
19
|
+
describe('content', () => {
|
|
20
|
+
for (const [engine, createPage] of [
|
|
21
|
+
['playwright', createMockPlaywrightPage],
|
|
22
|
+
['puppeteer', createMockPuppeteerPage],
|
|
23
|
+
]) {
|
|
24
|
+
it(`should return the full page HTML for ${engine}`, async () => {
|
|
25
|
+
const page = createPage();
|
|
26
|
+
page.content = async () =>
|
|
27
|
+
'<!doctype html><html><body>Hello</body></html>';
|
|
28
|
+
|
|
29
|
+
const html = await content({ page, engine });
|
|
30
|
+
|
|
31
|
+
assert.strictEqual(
|
|
32
|
+
html,
|
|
33
|
+
'<!doctype html><html><body>Hello</body></html>'
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
it('should return null on navigation errors', async () => {
|
|
39
|
+
const page = createMockPlaywrightPage();
|
|
40
|
+
page.content = async () => {
|
|
41
|
+
throw new Error('Execution context was destroyed');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
assert.strictEqual(await content({ page, engine: 'playwright' }), null);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('innerText', () => {
|
|
49
|
+
it('should return rendered text for Playwright', async () => {
|
|
50
|
+
const page = createMockPlaywrightPage({
|
|
51
|
+
elements: { main: { innerText: 'Rendered text', count: 1 } },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const text = await innerText({
|
|
55
|
+
page,
|
|
56
|
+
engine: 'playwright',
|
|
57
|
+
selector: 'main',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
assert.strictEqual(text, 'Rendered text');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return rendered text for Puppeteer', async () => {
|
|
64
|
+
const page = createMockPuppeteerPage({
|
|
65
|
+
elements: { main: { innerText: 'Rendered text', count: 1 } },
|
|
66
|
+
});
|
|
67
|
+
page.evaluate = async () => 'Rendered text';
|
|
68
|
+
|
|
69
|
+
const text = await innerText({
|
|
70
|
+
page,
|
|
71
|
+
engine: 'puppeteer',
|
|
72
|
+
selector: 'main',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
assert.strictEqual(text, 'Rendered text');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should default to the document body', async () => {
|
|
79
|
+
const page = createMockPlaywrightPage({
|
|
80
|
+
elements: { body: { innerText: 'Page text', count: 1 } },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
assert.strictEqual(
|
|
84
|
+
await innerText({ page, engine: 'playwright' }),
|
|
85
|
+
'Page text'
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return null when the element is not found', async () => {
|
|
90
|
+
const page = createMockPuppeteerPage({
|
|
91
|
+
elements: { main: { count: 0 } },
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
assert.strictEqual(
|
|
95
|
+
await innerText({ page, engine: 'puppeteer', selector: 'main' }),
|
|
96
|
+
null
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return null on navigation errors', async () => {
|
|
101
|
+
const page = createMockPlaywrightPage();
|
|
102
|
+
page.locator = () => ({
|
|
103
|
+
count: async () => 1,
|
|
104
|
+
first() {
|
|
105
|
+
return this;
|
|
106
|
+
},
|
|
107
|
+
innerText: async () => {
|
|
108
|
+
throw new Error('Execution context was destroyed');
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
assert.strictEqual(
|
|
113
|
+
await innerText({ page, engine: 'playwright', selector: 'main' }),
|
|
114
|
+
null
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
17
119
|
describe('textContent', () => {
|
|
18
120
|
it('should throw when selector is not provided', async () => {
|
|
19
121
|
const page = createMockPlaywrightPage();
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { describe, it } from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { checkWorkflow } from '../../../../scripts/check-ci-workflows.mjs';
|
|
8
|
+
|
|
9
|
+
function withWorkflow(content, callback) {
|
|
10
|
+
const directory = mkdtempSync(join(tmpdir(), 'browser-commander-ci-'));
|
|
11
|
+
const filePath = join(directory, 'workflow.yml');
|
|
12
|
+
writeFileSync(filePath, content);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return callback(filePath);
|
|
16
|
+
} finally {
|
|
17
|
+
rmSync(directory, { force: true, recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('CI workflow policy', () => {
|
|
22
|
+
it('accepts bounded job-scoped checks', () => {
|
|
23
|
+
const workflow = `name: Test
|
|
24
|
+
on: push
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
env:
|
|
28
|
+
GIT_CONFIG_KEY_0: init.defaultBranch
|
|
29
|
+
jobs:
|
|
30
|
+
test:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
timeout-minutes: 5
|
|
33
|
+
concurrency:
|
|
34
|
+
group: \${{ github.workflow }}-\${{ github.ref }}-test
|
|
35
|
+
cancel-in-progress: true
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v6
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
withWorkflow(workflow, (filePath) => {
|
|
41
|
+
assert.equal(checkWorkflow(filePath), 0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('rejects workflow-wide cancellation and unbounded jobs', () => {
|
|
46
|
+
const workflow = `name: Test
|
|
47
|
+
on: push
|
|
48
|
+
concurrency:
|
|
49
|
+
group: test
|
|
50
|
+
cancel-in-progress: true
|
|
51
|
+
jobs:
|
|
52
|
+
test:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v6
|
|
56
|
+
- run: npm install
|
|
57
|
+
- run: echo "\${{ github.head_ref }}"
|
|
58
|
+
- uses: codecov/codecov-action@v6
|
|
59
|
+
with:
|
|
60
|
+
file: coverage.xml
|
|
61
|
+
`;
|
|
62
|
+
const originalError = console.error;
|
|
63
|
+
console.error = () => {};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
withWorkflow(workflow, (filePath) => {
|
|
67
|
+
assert.equal(checkWorkflow(filePath), 7);
|
|
68
|
+
});
|
|
69
|
+
} finally {
|
|
70
|
+
console.error = originalError;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -127,12 +127,25 @@ describe('test runner helper', () => {
|
|
|
127
127
|
reporter: 'spec',
|
|
128
128
|
targets: ['tests/unit'],
|
|
129
129
|
});
|
|
130
|
-
assert.deepEqual(
|
|
131
|
-
'
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
assert.deepEqual(
|
|
131
|
+
buildNodeTestArgs(options, ['tests/unit/a.test.js'], '24.18.0'),
|
|
132
|
+
[
|
|
133
|
+
'--test',
|
|
134
|
+
'--test-isolation=none',
|
|
135
|
+
'--experimental-test-coverage',
|
|
136
|
+
'--test-reporter',
|
|
137
|
+
'spec',
|
|
138
|
+
'tests/unit/a.test.js',
|
|
139
|
+
]
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('keeps process isolation on Node versions without isolation controls', () => {
|
|
144
|
+
const options = parseTestRunnerArgs(['tests/unit']);
|
|
145
|
+
|
|
146
|
+
assert.deepEqual(
|
|
147
|
+
buildNodeTestArgs(options, ['tests/unit/a.test.js'], '20.20.2'),
|
|
148
|
+
['--test', 'tests/unit/a.test.js']
|
|
149
|
+
);
|
|
137
150
|
});
|
|
138
151
|
});
|