@jackwener/opencli 1.7.16 → 1.7.17
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 +8 -9
- package/README.zh-CN.md +8 -8
- package/cli-manifest.json +97 -271
- package/clis/chatgpt/ask.js +1 -1
- package/clis/chatgpt/commands.test.js +2 -2
- package/clis/chatgpt/detail.js +1 -1
- package/clis/chatgpt/history.js +1 -1
- package/clis/chatgpt/image.js +38 -4
- package/clis/chatgpt/image.test.js +68 -1
- package/clis/chatgpt/new.js +1 -1
- package/clis/chatgpt/read.js +1 -1
- package/clis/chatgpt/send.js +1 -1
- package/clis/chatgpt/status.js +1 -1
- package/clis/chatgpt/utils.js +208 -16
- package/clis/chatgpt/utils.test.js +131 -2
- package/clis/claude/ask.js +1 -1
- package/clis/claude/detail.js +1 -1
- package/clis/claude/history.js +1 -1
- package/clis/claude/new.js +1 -1
- package/clis/claude/read.js +1 -1
- package/clis/claude/send.js +1 -1
- package/clis/claude/status.js +1 -1
- package/clis/deepseek/ask.js +1 -1
- package/clis/deepseek/detail.js +1 -1
- package/clis/deepseek/history.js +1 -1
- package/clis/deepseek/new.js +1 -1
- package/clis/deepseek/read.js +1 -1
- package/clis/deepseek/send.js +1 -1
- package/clis/deepseek/status.js +1 -1
- package/clis/doubao/ask.js +1 -1
- package/clis/doubao/detail.js +1 -1
- package/clis/doubao/history.js +1 -1
- package/clis/doubao/meeting-summary.js +1 -1
- package/clis/doubao/meeting-transcript.js +1 -1
- package/clis/doubao/new.js +1 -1
- package/clis/doubao/read.js +1 -1
- package/clis/doubao/send.js +1 -1
- package/clis/doubao/status.js +1 -1
- package/clis/gemini/ask.js +1 -1
- package/clis/gemini/deep-research-result.js +1 -1
- package/clis/gemini/deep-research.js +1 -1
- package/clis/gemini/image.js +1 -1
- package/clis/gemini/new.js +1 -1
- package/clis/grok/ask.js +1 -1
- package/clis/grok/detail.js +1 -1
- package/clis/grok/history.js +1 -1
- package/clis/grok/image.js +1 -1
- package/clis/grok/new.js +1 -1
- package/clis/grok/read.js +1 -1
- package/clis/grok/send.js +1 -1
- package/clis/grok/status.js +1 -1
- package/clis/notebooklm/current.js +1 -1
- package/clis/notebooklm/get.js +1 -1
- package/clis/notebooklm/history.js +1 -1
- package/clis/notebooklm/note-list.js +1 -1
- package/clis/notebooklm/notes-get.js +1 -1
- package/clis/notebooklm/open.js +2 -2
- package/clis/notebooklm/open.test.js +1 -1
- package/clis/notebooklm/source-fulltext.js +1 -1
- package/clis/notebooklm/source-get.js +1 -1
- package/clis/notebooklm/source-guide.js +1 -1
- package/clis/notebooklm/source-list.js +1 -1
- package/clis/notebooklm/summary.js +1 -1
- package/clis/qwen/ask.js +1 -1
- package/clis/qwen/detail.js +1 -1
- package/clis/qwen/history.js +1 -1
- package/clis/qwen/image.js +1 -1
- package/clis/qwen/new.js +1 -1
- package/clis/qwen/read.js +1 -1
- package/clis/qwen/send.js +1 -1
- package/clis/qwen/status.js +1 -1
- package/clis/reddit/comment.js +1 -1
- package/clis/reddit/frontpage.js +1 -1
- package/clis/reddit/popular.js +1 -1
- package/clis/reddit/read.js +1 -1
- package/clis/reddit/read.test.js +2 -2
- package/clis/reddit/save.js +1 -1
- package/clis/reddit/saved.js +1 -1
- package/clis/reddit/search.js +1 -1
- package/clis/reddit/subreddit.js +1 -1
- package/clis/reddit/subscribe.js +1 -1
- package/clis/reddit/upvote.js +1 -1
- package/clis/reddit/upvoted.js +1 -1
- package/clis/reddit/user-comments.js +1 -1
- package/clis/reddit/user-posts.js +1 -1
- package/clis/reddit/user.js +1 -1
- package/clis/twitter/article.js +1 -1
- package/clis/twitter/bookmark-folder.js +1 -1
- package/clis/twitter/bookmark-folders.js +1 -1
- package/clis/twitter/bookmarks.js +1 -1
- package/clis/twitter/download.js +1 -1
- package/clis/twitter/followers.js +1 -1
- package/clis/twitter/following.js +1 -1
- package/clis/twitter/likes.js +1 -1
- package/clis/twitter/list-tweets.js +1 -1
- package/clis/twitter/lists.js +1 -1
- package/clis/twitter/notifications.js +1 -1
- package/clis/twitter/profile.js +1 -1
- package/clis/twitter/search.js +1 -1
- package/clis/twitter/thread.js +1 -1
- package/clis/twitter/timeline.js +1 -1
- package/clis/twitter/trending.js +1 -1
- package/clis/twitter/tweets.js +1 -1
- package/clis/yuanbao/ask.js +1 -1
- package/clis/yuanbao/detail.js +1 -1
- package/clis/yuanbao/history.js +1 -1
- package/clis/yuanbao/new.js +1 -1
- package/clis/yuanbao/read.js +1 -1
- package/clis/yuanbao/send.js +1 -1
- package/clis/yuanbao/status.js +1 -1
- package/dist/src/browser/bridge.d.ts +3 -1
- package/dist/src/browser/bridge.js +3 -1
- package/dist/src/browser/cdp.d.ts +3 -1
- package/dist/src/browser/daemon-client.d.ts +7 -14
- package/dist/src/browser/daemon-client.js +2 -6
- package/dist/src/browser/network-cache.d.ts +5 -5
- package/dist/src/browser/network-cache.js +8 -8
- package/dist/src/browser/network-cache.test.js +4 -4
- package/dist/src/browser/page.d.ts +8 -7
- package/dist/src/browser/page.js +23 -16
- package/dist/src/browser/page.test.js +60 -30
- package/dist/src/build-manifest.js +1 -1
- package/dist/src/cli.js +60 -162
- package/dist/src/cli.test.js +178 -197
- package/dist/src/commanderAdapter.js +2 -0
- package/dist/src/discovery.js +1 -1
- package/dist/src/doctor.d.ts +0 -4
- package/dist/src/doctor.js +8 -72
- package/dist/src/doctor.test.js +26 -97
- package/dist/src/execution.d.ts +1 -0
- package/dist/src/execution.js +20 -21
- package/dist/src/execution.test.js +27 -31
- package/dist/src/help.js +7 -1
- package/dist/src/main.js +0 -19
- package/dist/src/manifest-types.d.ts +2 -4
- package/dist/src/observation/artifact.js +1 -1
- package/dist/src/observation/artifact.test.js +3 -3
- package/dist/src/observation/events.d.ts +1 -1
- package/dist/src/observation/manager.js +1 -1
- package/dist/src/observation/manager.test.js +3 -3
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry.d.ts +3 -12
- package/dist/src/registry.js +6 -10
- package/dist/src/runtime.d.ts +7 -2
- package/dist/src/runtime.js +3 -1
- package/dist/src/serialization.d.ts +1 -1
- package/dist/src/serialization.js +1 -1
- package/dist/src/types.d.ts +0 -15
- package/package.json +1 -1
package/dist/src/cli.test.js
CHANGED
|
@@ -343,7 +343,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
343
343
|
const program = createProgram('', '');
|
|
344
344
|
const browser = program.commands.find(cmd => cmd.name() === 'browser');
|
|
345
345
|
expect(browser).toBeTruthy();
|
|
346
|
-
process.argv = ['node', 'opencli', 'browser', '--help', '-f', 'yaml'];
|
|
346
|
+
process.argv = ['node', 'opencli', 'browser', '--session', 'test', '--help', '-f', 'yaml'];
|
|
347
347
|
const data = yaml.load(browser.helpInformation());
|
|
348
348
|
expect(data.namespace).toBe('browser');
|
|
349
349
|
expect(data.command).toBe('opencli browser');
|
|
@@ -351,8 +351,8 @@ describe('createProgram root help descriptions', () => {
|
|
|
351
351
|
expect(data.command_count).toBeGreaterThan(20);
|
|
352
352
|
expect(data.namespace_options).toEqual(expect.arrayContaining([
|
|
353
353
|
expect.objectContaining({
|
|
354
|
-
name: '
|
|
355
|
-
flags: '--
|
|
354
|
+
name: 'session',
|
|
355
|
+
flags: '--session <name>',
|
|
356
356
|
takes_value: 'required',
|
|
357
357
|
}),
|
|
358
358
|
expect.objectContaining({
|
|
@@ -360,11 +360,6 @@ describe('createProgram root help descriptions', () => {
|
|
|
360
360
|
flags: '--window <mode>',
|
|
361
361
|
takes_value: 'required',
|
|
362
362
|
}),
|
|
363
|
-
expect.objectContaining({
|
|
364
|
-
name: 'keepTab',
|
|
365
|
-
flags: '--keep-tab <bool>',
|
|
366
|
-
takes_value: 'required',
|
|
367
|
-
}),
|
|
368
363
|
]));
|
|
369
364
|
expect(data.global_options).toEqual(expect.arrayContaining([
|
|
370
365
|
expect.objectContaining({
|
|
@@ -411,7 +406,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
411
406
|
const browser = program.commands.find(cmd => cmd.name() === 'browser');
|
|
412
407
|
const tab = browser.commands.find(cmd => cmd.name() === 'tab');
|
|
413
408
|
expect(tab).toBeTruthy();
|
|
414
|
-
process.argv = ['node', 'opencli', 'browser', 'tab', '--help', '-f', 'yaml'];
|
|
409
|
+
process.argv = ['node', 'opencli', 'browser', '--session', 'test', 'tab', '--help', '-f', 'yaml'];
|
|
415
410
|
const data = yaml.load(tab.helpInformation());
|
|
416
411
|
expect(data).toMatchObject({
|
|
417
412
|
namespace: 'browser',
|
|
@@ -431,7 +426,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
431
426
|
usage: 'opencli browser tab close [targetId] [options]',
|
|
432
427
|
positionals: [{ name: 'targetId', help: 'Target tab/page identity returned by "browser open", "browser tab new", or "browser tab list"' }],
|
|
433
428
|
});
|
|
434
|
-
expect(data.namespace_options.map((option) => option.name)).toEqual(['
|
|
429
|
+
expect(data.namespace_options.map((option) => option.name)).toEqual(['session', 'window']);
|
|
435
430
|
expect(data.structured_help).toMatchObject({
|
|
436
431
|
usage: 'opencli browser tab --help -f yaml',
|
|
437
432
|
});
|
|
@@ -447,7 +442,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
447
442
|
const browser = program.commands.find(cmd => cmd.name() === 'browser');
|
|
448
443
|
const click = browser.commands.find(cmd => cmd.name() === 'click');
|
|
449
444
|
expect(click).toBeTruthy();
|
|
450
|
-
process.argv = ['node', 'opencli', 'browser', 'click', '--help', '-f', 'yaml'];
|
|
445
|
+
process.argv = ['node', 'opencli', 'browser', '--session', 'test', 'click', '--help', '-f', 'yaml'];
|
|
451
446
|
const data = yaml.load(click.helpInformation());
|
|
452
447
|
expect(data).toMatchObject({
|
|
453
448
|
namespace: 'browser',
|
|
@@ -460,7 +455,7 @@ describe('createProgram root help descriptions', () => {
|
|
|
460
455
|
},
|
|
461
456
|
});
|
|
462
457
|
expect(data.command_options.map((option) => option.name)).toEqual(['role', 'name', 'label', 'text', 'testid', 'nth', 'tab']);
|
|
463
|
-
expect(data.namespace_options.map((option) => option.name)).toEqual(['
|
|
458
|
+
expect(data.namespace_options.map((option) => option.name)).toEqual(['session', 'window']);
|
|
464
459
|
expect(data.global_options.map((option) => option.name)).toContain('profile');
|
|
465
460
|
}
|
|
466
461
|
finally {
|
|
@@ -664,7 +659,7 @@ describe('browser verify', () => {
|
|
|
664
659
|
fs.mkdirSync(adapterDir, { recursive: true });
|
|
665
660
|
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
666
661
|
const program = createProgram('', '');
|
|
667
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--no-fixture', '--trace', 'retain-on-failure']);
|
|
662
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'verify', 'hn/top', '--no-fixture', '--trace', 'retain-on-failure']);
|
|
668
663
|
expect(mockExecFileSync).toHaveBeenCalledTimes(1);
|
|
669
664
|
const [, execArgs] = mockExecFileSync.mock.calls[0];
|
|
670
665
|
expect(execArgs.slice(-6)).toEqual(['hn', 'top', '--trace', 'retain-on-failure', '--format', 'json']);
|
|
@@ -692,7 +687,7 @@ describe('browser verify', () => {
|
|
|
692
687
|
fs.mkdirSync(adapterDir, { recursive: true });
|
|
693
688
|
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
694
689
|
const program = createProgram('', '');
|
|
695
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--no-fixture', '--seed-args', 'opencli-verify']);
|
|
690
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'verify', 'hn/top', '--no-fixture', '--seed-args', 'opencli-verify']);
|
|
696
691
|
expect(mockExecFileSync).toHaveBeenCalledTimes(1);
|
|
697
692
|
const [, execArgs] = mockExecFileSync.mock.calls[0];
|
|
698
693
|
expect(execArgs.slice(-5)).toEqual(['hn', 'top', 'opencli-verify', '--format', 'json']);
|
|
@@ -721,7 +716,7 @@ describe('browser verify', () => {
|
|
|
721
716
|
fs.mkdirSync(adapterDir, { recursive: true });
|
|
722
717
|
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
723
718
|
const program = createProgram('', '');
|
|
724
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--write-fixture', '--seed-args', 'opencli-verify']);
|
|
719
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'verify', 'hn/top', '--write-fixture', '--seed-args', 'opencli-verify']);
|
|
725
720
|
const fixtureFile = path.join(fakeHome, '.opencli', 'sites', 'hn', 'verify', 'top.json');
|
|
726
721
|
const fixture = JSON.parse(fs.readFileSync(fixtureFile, 'utf-8'));
|
|
727
722
|
expect(fixture.args).toEqual(['opencli-verify']);
|
|
@@ -753,7 +748,7 @@ describe('browser verify', () => {
|
|
|
753
748
|
fs.mkdirSync(adapterDir, { recursive: true });
|
|
754
749
|
fs.writeFileSync(path.join(adapterDir, 'top.js'), 'export default {};\n', 'utf-8');
|
|
755
750
|
const program = createProgram('', '');
|
|
756
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'verify', 'hn/top', '--no-fixture']);
|
|
751
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'verify', 'hn/top', '--no-fixture']);
|
|
757
752
|
expect(process.exitCode).toBe(1);
|
|
758
753
|
const output = consoleLogSpy.mock.calls.map((args) => args.join(' ')).join('\n');
|
|
759
754
|
expect(output).toContain('Adapter output violates row shape conventions');
|
|
@@ -827,8 +822,8 @@ describe('profile list', () => {
|
|
|
827
822
|
describe('browser tab targeting commands', () => {
|
|
828
823
|
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
829
824
|
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
830
|
-
function getBrowserStateFile(cacheDir) {
|
|
831
|
-
return path.join(cacheDir, 'browser-state',
|
|
825
|
+
function getBrowserStateFile(cacheDir, session = 'test') {
|
|
826
|
+
return path.join(cacheDir, 'browser-state', `${session}.json`);
|
|
832
827
|
}
|
|
833
828
|
beforeEach(() => {
|
|
834
829
|
process.exitCode = undefined;
|
|
@@ -838,9 +833,8 @@ describe('browser tab targeting commands', () => {
|
|
|
838
833
|
mockBrowserConnect.mockClear();
|
|
839
834
|
mockBrowserClose.mockReset().mockResolvedValue(undefined);
|
|
840
835
|
delete process.env.OPENCLI_WINDOW;
|
|
841
|
-
delete process.env.OPENCLI_KEEP_TAB;
|
|
842
836
|
mockBindTab.mockReset().mockResolvedValue({
|
|
843
|
-
|
|
837
|
+
session: 'test',
|
|
844
838
|
page: 'tab-2',
|
|
845
839
|
url: 'https://user.example/inbox',
|
|
846
840
|
title: 'Inbox',
|
|
@@ -879,6 +873,7 @@ describe('browser tab targeting commands', () => {
|
|
|
879
873
|
state: 'complete',
|
|
880
874
|
elapsedMs: 10,
|
|
881
875
|
}),
|
|
876
|
+
session: 'test',
|
|
882
877
|
};
|
|
883
878
|
});
|
|
884
879
|
function lastJsonLog() {
|
|
@@ -890,55 +885,37 @@ describe('browser tab targeting commands', () => {
|
|
|
890
885
|
throw new Error(`Expected string arg to console.log, got ${typeof last}`);
|
|
891
886
|
return JSON.parse(last);
|
|
892
887
|
}
|
|
893
|
-
it('binds the current Chrome tab into a
|
|
888
|
+
it('binds the current Chrome tab into a browser session', async () => {
|
|
894
889
|
const program = createProgram('', '');
|
|
895
|
-
await program.parseAsync(['node', 'opencli', 'browser', '
|
|
896
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30,
|
|
897
|
-
expect(mockBindTab).toHaveBeenCalledWith('
|
|
898
|
-
matchDomain: 'user.example',
|
|
899
|
-
matchPathPrefix: '/inbox',
|
|
900
|
-
});
|
|
890
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'bind']);
|
|
891
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30, session: 'test', surface: 'browser' });
|
|
892
|
+
expect(mockBindTab).toHaveBeenCalledWith('test', {});
|
|
901
893
|
const out = lastJsonLog();
|
|
902
|
-
expect(out.
|
|
894
|
+
expect(out.session).toBe('test');
|
|
903
895
|
expect(out.url).toBe('https://user.example/inbox');
|
|
904
896
|
});
|
|
905
|
-
it('
|
|
897
|
+
it('requires an explicit session for browser commands', async () => {
|
|
906
898
|
const program = createProgram('', '');
|
|
907
|
-
await program.parseAsync(['node', 'opencli', 'browser', '
|
|
899
|
+
await program.parseAsync(['node', 'opencli', 'browser', 'state']);
|
|
908
900
|
expect(mockBrowserConnect).not.toHaveBeenCalled();
|
|
909
|
-
expect(
|
|
910
|
-
const out = lastJsonLog();
|
|
911
|
-
expect(out.error.code).toBe('invalid_bind_workspace');
|
|
901
|
+
expect(stderrSpy.mock.calls.flat().join('')).toContain('--session <name> is required');
|
|
912
902
|
expect(process.exitCode).toBeDefined();
|
|
913
903
|
});
|
|
914
|
-
it('runs browser commands against an explicit
|
|
904
|
+
it('runs browser commands against an explicit session', async () => {
|
|
915
905
|
const program = createProgram('', '');
|
|
916
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--
|
|
917
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30,
|
|
906
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'state']);
|
|
907
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30, session: 'test', surface: 'browser', windowMode: 'foreground' });
|
|
918
908
|
expect(browserState.page?.snapshot).toHaveBeenCalled();
|
|
919
909
|
});
|
|
920
910
|
it('passes browser --window through Commander options without relying on env pre-processing', async () => {
|
|
921
911
|
const program = createProgram('', '');
|
|
922
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--window', 'background', 'state']);
|
|
923
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30,
|
|
912
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', '--window', 'background', 'state']);
|
|
913
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30, session: 'test', surface: 'browser', windowMode: 'background' });
|
|
924
914
|
expect(browserState.page?.snapshot).toHaveBeenCalled();
|
|
925
915
|
});
|
|
926
|
-
it('releases non-bound browser tab leases when --keep-tab=false', async () => {
|
|
927
|
-
const program = createProgram('', '');
|
|
928
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--keep-tab', 'false', 'state']);
|
|
929
|
-
expect(browserState.page?.snapshot).toHaveBeenCalled();
|
|
930
|
-
expect(browserState.page?.closeWindow).toHaveBeenCalled();
|
|
931
|
-
});
|
|
932
|
-
it('does not auto-release explicit bound workspaces when --keep-tab=false', async () => {
|
|
933
|
-
const program = createProgram('', '');
|
|
934
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--workspace', 'bound:default', '--keep-tab', 'false', 'state']);
|
|
935
|
-
expect(browserState.page?.snapshot).toHaveBeenCalled();
|
|
936
|
-
expect(browserState.page?.closeWindow).not.toHaveBeenCalled();
|
|
937
|
-
expect(stderrSpy.mock.calls.flat().join('')).toContain('--window/--keep-tab ignored for bound:* workspaces');
|
|
938
|
-
});
|
|
939
916
|
it('passes the opt-in AX source to browser state', async () => {
|
|
940
917
|
const program = createProgram('', '');
|
|
941
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'state', '--source', 'ax']);
|
|
918
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'state', '--source', 'ax']);
|
|
942
919
|
expect(browserState.page?.snapshot).toHaveBeenCalledWith({ viewportExpand: 2000, source: 'ax' });
|
|
943
920
|
});
|
|
944
921
|
it('prints DOM vs AX snapshot metrics without changing default state output', async () => {
|
|
@@ -952,7 +929,7 @@ describe('browser tab targeting commands', () => {
|
|
|
952
929
|
}),
|
|
953
930
|
};
|
|
954
931
|
const program = createProgram('', '');
|
|
955
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'state', '--compare-sources']);
|
|
932
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'state', '--compare-sources']);
|
|
956
933
|
expect(browserState.page?.snapshot).toHaveBeenCalledWith({ viewportExpand: 2000, source: 'dom' });
|
|
957
934
|
expect(browserState.page?.snapshot).toHaveBeenCalledWith({ viewportExpand: 2000, source: 'ax' });
|
|
958
935
|
const out = lastJsonLog();
|
|
@@ -970,7 +947,7 @@ describe('browser tab targeting commands', () => {
|
|
|
970
947
|
}),
|
|
971
948
|
};
|
|
972
949
|
const program = createProgram('', '');
|
|
973
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'state', '--compare-sources']);
|
|
950
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'state', '--compare-sources']);
|
|
974
951
|
const out = lastJsonLog();
|
|
975
952
|
expect(out.sources.dom).toMatchObject({ ok: true, refs: 1 });
|
|
976
953
|
expect(out.sources.ax).toMatchObject({
|
|
@@ -980,7 +957,7 @@ describe('browser tab targeting commands', () => {
|
|
|
980
957
|
});
|
|
981
958
|
it('rejects unknown browser state sources before touching the page', async () => {
|
|
982
959
|
const program = createProgram('', '');
|
|
983
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'state', '--source', 'magic']);
|
|
960
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'state', '--source', 'magic']);
|
|
984
961
|
expect(browserState.page?.snapshot).not.toHaveBeenCalled();
|
|
985
962
|
const out = lastJsonLog();
|
|
986
963
|
expect(out.error.code).toBe('invalid_source');
|
|
@@ -988,7 +965,7 @@ describe('browser tab targeting commands', () => {
|
|
|
988
965
|
});
|
|
989
966
|
it('captures annotated screenshots through the visual ref overlay path', async () => {
|
|
990
967
|
const program = createProgram('', '');
|
|
991
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'screenshot', '--annotate']);
|
|
968
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'screenshot', '--annotate']);
|
|
992
969
|
expect(browserState.page?.annotatedScreenshot).toHaveBeenCalledWith({
|
|
993
970
|
fullPage: false,
|
|
994
971
|
annotate: true,
|
|
@@ -999,38 +976,36 @@ describe('browser tab targeting commands', () => {
|
|
|
999
976
|
expect(browserState.page?.screenshot).not.toHaveBeenCalled();
|
|
1000
977
|
expect(consoleLogSpy).toHaveBeenLastCalledWith('annotated-base64-shot');
|
|
1001
978
|
});
|
|
1002
|
-
it('
|
|
979
|
+
it('allows history navigation in a bound session', async () => {
|
|
1003
980
|
browserState.page = {
|
|
1004
981
|
...browserState.page,
|
|
1005
|
-
workspace: 'bound:default',
|
|
1006
982
|
evaluate: vi.fn(),
|
|
1007
983
|
wait: vi.fn(),
|
|
984
|
+
session: 'test',
|
|
1008
985
|
};
|
|
1009
986
|
const program = createProgram('', '');
|
|
1010
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--
|
|
1011
|
-
expect(browserState.page?.evaluate).
|
|
1012
|
-
const out = lastJsonLog();
|
|
1013
|
-
expect(out.error.code).toBe('bound_navigation_blocked');
|
|
987
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'back']);
|
|
988
|
+
expect(browserState.page?.evaluate).toHaveBeenCalledWith('history.back()');
|
|
1014
989
|
});
|
|
1015
|
-
it('unbinds a
|
|
990
|
+
it('unbinds a session through the daemon close-window command', async () => {
|
|
1016
991
|
const program = createProgram('', '');
|
|
1017
|
-
await program.parseAsync(['node', 'opencli', 'browser', '
|
|
1018
|
-
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30,
|
|
1019
|
-
expect(mockSendCommand).toHaveBeenCalledWith('close-window', {
|
|
992
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'unbind']);
|
|
993
|
+
expect(mockBrowserConnect).toHaveBeenCalledWith({ timeout: 30, session: 'test', surface: 'browser' });
|
|
994
|
+
expect(mockSendCommand).toHaveBeenCalledWith('close-window', { session: 'test', surface: 'browser' });
|
|
1020
995
|
const out = lastJsonLog();
|
|
1021
|
-
expect(out).toEqual({ unbound: true,
|
|
996
|
+
expect(out).toEqual({ unbound: true, session: 'test' });
|
|
1022
997
|
});
|
|
1023
998
|
it('does not print false success when unbind fails', async () => {
|
|
1024
|
-
mockSendCommand.mockRejectedValueOnce(new BrowserCommandError('
|
|
999
|
+
mockSendCommand.mockRejectedValueOnce(new BrowserCommandError('Session "test" is not attached to a tab.', 'bound_session_missing', 'Run bind again, then retry the browser command.'));
|
|
1025
1000
|
const program = createProgram('', '');
|
|
1026
|
-
await program.parseAsync(['node', 'opencli', 'browser', '
|
|
1001
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'unbind']);
|
|
1027
1002
|
const out = lastJsonLog();
|
|
1028
1003
|
expect(out.error.code).toBe('bound_session_missing');
|
|
1029
1004
|
expect(process.exitCode).toBeDefined();
|
|
1030
1005
|
});
|
|
1031
1006
|
it('accepts JavaScript dialogs through the browser dialog command', async () => {
|
|
1032
1007
|
const program = createProgram('', '');
|
|
1033
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'dialog', 'accept', '--text', 'ok']);
|
|
1008
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'dialog', 'accept', '--text', 'ok']);
|
|
1034
1009
|
expect(browserState.page?.handleJavaScriptDialog).toHaveBeenCalledWith(true, 'ok');
|
|
1035
1010
|
const out = lastJsonLog();
|
|
1036
1011
|
expect(out).toEqual({ handled: true, action: 'accept', text: 'ok' });
|
|
@@ -1041,7 +1016,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1041
1016
|
evaluate: vi.fn().mockRejectedValue(new Error('JavaScript dialog showing')),
|
|
1042
1017
|
};
|
|
1043
1018
|
const program = createProgram('', '');
|
|
1044
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
1019
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', 'document.title']);
|
|
1045
1020
|
const out = lastJsonLog();
|
|
1046
1021
|
expect(out.error.code).toBe('javascript_dialog_open');
|
|
1047
1022
|
expect(out.error.hint).toContain('browser dialog accept');
|
|
@@ -1049,7 +1024,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1049
1024
|
});
|
|
1050
1025
|
it('binds browser commands to an explicit target tab via --tab', async () => {
|
|
1051
1026
|
const program = createProgram('', '');
|
|
1052
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', '--tab', 'tab-2', 'document.title']);
|
|
1027
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', '--tab', 'tab-2', 'document.title']);
|
|
1053
1028
|
expect(browserState.page?.setActivePage).toHaveBeenCalledWith('tab-2');
|
|
1054
1029
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith('document.title');
|
|
1055
1030
|
});
|
|
@@ -1061,7 +1036,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1061
1036
|
evaluate: vi.fn(),
|
|
1062
1037
|
};
|
|
1063
1038
|
const program = createProgram('', '');
|
|
1064
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', '--tab', 'tab-stale', 'document.title']);
|
|
1039
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', '--tab', 'tab-stale', 'document.title']);
|
|
1065
1040
|
expect(process.exitCode).toBeDefined();
|
|
1066
1041
|
expect(browserState.page?.setActivePage).not.toHaveBeenCalled();
|
|
1067
1042
|
expect(browserState.page?.evaluate).not.toHaveBeenCalled();
|
|
@@ -1069,50 +1044,50 @@ describe('browser tab targeting commands', () => {
|
|
|
1069
1044
|
});
|
|
1070
1045
|
it('lists tabs with target IDs via browser tab list', async () => {
|
|
1071
1046
|
const program = createProgram('', '');
|
|
1072
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'list']);
|
|
1047
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'list']);
|
|
1073
1048
|
expect(browserState.page?.tabs).toHaveBeenCalledTimes(1);
|
|
1074
1049
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"page": "tab-1"');
|
|
1075
1050
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"page": "tab-2"');
|
|
1076
1051
|
});
|
|
1077
1052
|
it('creates a new tab and prints its target ID', async () => {
|
|
1078
1053
|
const program = createProgram('', '');
|
|
1079
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'new', 'https://three.example']);
|
|
1054
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'new', 'https://three.example']);
|
|
1080
1055
|
expect(browserState.page?.newTab).toHaveBeenCalledWith('https://three.example');
|
|
1081
1056
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"page": "tab-3"');
|
|
1082
1057
|
});
|
|
1083
1058
|
it('prints the resolved target ID when browser open creates or navigates a tab', async () => {
|
|
1084
1059
|
const program = createProgram('', '');
|
|
1085
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'open', 'https://example.com']);
|
|
1060
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'open', 'https://example.com']);
|
|
1086
1061
|
expect(browserState.page?.goto).toHaveBeenCalledWith('https://example.com');
|
|
1087
1062
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"url": "https://one.example"');
|
|
1088
1063
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"page": "tab-1"');
|
|
1089
1064
|
});
|
|
1090
1065
|
it('lists cross-origin frames via browser frames', async () => {
|
|
1091
1066
|
const program = createProgram('', '');
|
|
1092
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'frames']);
|
|
1067
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'frames']);
|
|
1093
1068
|
expect(browserState.page?.frames).toHaveBeenCalledTimes(1);
|
|
1094
1069
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"frameId": "frame-1"');
|
|
1095
1070
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"url": "https://x.example/embed"');
|
|
1096
1071
|
});
|
|
1097
1072
|
it('routes browser eval --frame through frame-targeted evaluation', async () => {
|
|
1098
1073
|
const program = createProgram('', '');
|
|
1099
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', '--frame', '0', 'document.title']);
|
|
1074
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', '--frame', '0', 'document.title']);
|
|
1100
1075
|
expect(browserState.page?.evaluateInFrame).toHaveBeenCalledWith('document.title', 0);
|
|
1101
1076
|
expect(browserState.page?.evaluate).not.toHaveBeenCalled();
|
|
1102
1077
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('inside frame');
|
|
1103
1078
|
});
|
|
1104
1079
|
it('does not promote a newly created tab to the persisted default target', async () => {
|
|
1105
1080
|
const program = createProgram('', '');
|
|
1106
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'new', 'https://three.example']);
|
|
1107
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
1081
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'new', 'https://three.example']);
|
|
1082
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', 'document.title']);
|
|
1108
1083
|
expect(browserState.page?.newTab).toHaveBeenCalledWith('https://three.example');
|
|
1109
1084
|
expect(browserState.page?.setActivePage).not.toHaveBeenCalled();
|
|
1110
1085
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith('document.title');
|
|
1111
1086
|
});
|
|
1112
1087
|
it('persists an explicitly selected tab as the default target for later untargeted commands', async () => {
|
|
1113
1088
|
const program = createProgram('', '');
|
|
1114
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'select', 'tab-2']);
|
|
1115
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
1089
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'select', 'tab-2']);
|
|
1090
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', 'document.title']);
|
|
1116
1091
|
expect(browserState.page?.selectTab).toHaveBeenCalledWith('tab-2');
|
|
1117
1092
|
expect(browserState.page?.setActivePage).toHaveBeenCalledWith('tab-2');
|
|
1118
1093
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith('document.title');
|
|
@@ -1121,7 +1096,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1121
1096
|
it('clears a saved default target when it is no longer present in the current session', async () => {
|
|
1122
1097
|
const cacheDir = String(process.env.OPENCLI_CACHE_DIR);
|
|
1123
1098
|
const program = createProgram('', '');
|
|
1124
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'select', 'tab-2']);
|
|
1099
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'select', 'tab-2']);
|
|
1125
1100
|
expect(fs.existsSync(getBrowserStateFile(cacheDir))).toBe(true);
|
|
1126
1101
|
browserState.page = {
|
|
1127
1102
|
setActivePage: vi.fn(),
|
|
@@ -1130,35 +1105,36 @@ describe('browser tab targeting commands', () => {
|
|
|
1130
1105
|
evaluate: vi.fn().mockResolvedValue({ ok: true }),
|
|
1131
1106
|
readNetworkCapture: vi.fn().mockResolvedValue([]),
|
|
1132
1107
|
};
|
|
1133
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
1108
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', 'document.title']);
|
|
1134
1109
|
expect(browserState.page?.setActivePage).not.toHaveBeenCalled();
|
|
1135
1110
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith('document.title');
|
|
1136
1111
|
expect(fs.existsSync(getBrowserStateFile(cacheDir))).toBe(false);
|
|
1137
1112
|
});
|
|
1138
1113
|
it('clears the persisted default target when that tab is closed', async () => {
|
|
1139
1114
|
const program = createProgram('', '');
|
|
1140
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'select', 'tab-2']);
|
|
1141
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'close', 'tab-2']);
|
|
1115
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'select', 'tab-2']);
|
|
1116
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'close', 'tab-2']);
|
|
1142
1117
|
vi.mocked(browserState.page?.setActivePage).mockClear();
|
|
1143
1118
|
vi.mocked(browserState.page?.evaluate).mockClear();
|
|
1144
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'eval', 'document.title']);
|
|
1119
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'eval', 'document.title']);
|
|
1145
1120
|
expect(browserState.page?.closeTab).toHaveBeenCalledWith('tab-2');
|
|
1146
1121
|
expect(browserState.page?.setActivePage).not.toHaveBeenCalled();
|
|
1147
1122
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith('document.title');
|
|
1148
1123
|
});
|
|
1149
1124
|
it('closes a tab by target ID', async () => {
|
|
1150
1125
|
const program = createProgram('', '');
|
|
1151
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'close', 'tab-2']);
|
|
1126
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'close', 'tab-2']);
|
|
1152
1127
|
expect(browserState.page?.closeTab).toHaveBeenCalledWith('tab-2');
|
|
1153
1128
|
expect(consoleLogSpy.mock.calls.flat().join('\n')).toContain('"closed": "tab-2"');
|
|
1154
1129
|
});
|
|
1155
1130
|
it('rejects closing a stale tab target ID that is no longer in the current session', async () => {
|
|
1156
1131
|
browserState.page = {
|
|
1132
|
+
session: 'test',
|
|
1157
1133
|
tabs: vi.fn().mockResolvedValue([]),
|
|
1158
1134
|
closeTab: vi.fn(),
|
|
1159
1135
|
};
|
|
1160
1136
|
const program = createProgram('', '');
|
|
1161
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'tab', 'close', 'tab-stale']);
|
|
1137
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'tab', 'close', 'tab-stale']);
|
|
1162
1138
|
expect(process.exitCode).toBeDefined();
|
|
1163
1139
|
expect(browserState.page?.closeTab).not.toHaveBeenCalled();
|
|
1164
1140
|
expect(stderrSpy.mock.calls.flat().join('\n')).toContain('Target tab tab-stale is not part of the current browser session');
|
|
@@ -1205,7 +1181,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1205
1181
|
]),
|
|
1206
1182
|
};
|
|
1207
1183
|
const program = createProgram('', '');
|
|
1208
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'analyze', 'https://target.example/']);
|
|
1184
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'analyze', 'https://target.example/']);
|
|
1209
1185
|
const out = lastJsonLog();
|
|
1210
1186
|
expect(browserState.page?.readNetworkCapture).toHaveBeenCalledTimes(2);
|
|
1211
1187
|
expect(out.anti_bot.vendor).toBe('cloudflare');
|
|
@@ -1266,7 +1242,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1266
1242
|
readNetworkCapture: vi.fn().mockResolvedValue([]),
|
|
1267
1243
|
};
|
|
1268
1244
|
const program = createProgram('', '');
|
|
1269
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'analyze', 'https://target.example/']);
|
|
1245
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'analyze', 'https://target.example/']);
|
|
1270
1246
|
const out = lastJsonLog();
|
|
1271
1247
|
expect(browserState.page?.readNetworkCapture).toHaveBeenCalledTimes(2);
|
|
1272
1248
|
expect(bufferReads).toBe(2);
|
|
@@ -1305,7 +1281,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1305
1281
|
]),
|
|
1306
1282
|
};
|
|
1307
1283
|
const program = createProgram('', '');
|
|
1308
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'wait', 'xhr', '/api/target', '--timeout', '900']);
|
|
1284
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'wait', 'xhr', '/api/target', '--timeout', '900']);
|
|
1309
1285
|
const out = lastJsonLog();
|
|
1310
1286
|
expect(browserState.page?.startNetworkCapture).toHaveBeenCalledTimes(1);
|
|
1311
1287
|
expect(browserState.page?.evaluate).toHaveBeenCalledWith(expect.stringContaining('window.__opencli_net'));
|
|
@@ -1353,7 +1329,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1353
1329
|
readNetworkCapture: vi.fn().mockResolvedValue([]),
|
|
1354
1330
|
};
|
|
1355
1331
|
const program = createProgram('', '');
|
|
1356
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'wait', 'xhr', '/api/target', '--timeout', '900']);
|
|
1332
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'wait', 'xhr', '/api/target', '--timeout', '900']);
|
|
1357
1333
|
const out = lastJsonLog();
|
|
1358
1334
|
expect(browserState.page?.startNetworkCapture).toHaveBeenCalledTimes(1);
|
|
1359
1335
|
expect(browserState.page?.readNetworkCapture).toHaveBeenCalledTimes(2);
|
|
@@ -1377,7 +1353,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1377
1353
|
tabs: vi.fn().mockResolvedValue([{ index: 0, page: 'tab-1', url: 'https://target.example', title: 'Target', active: true }]),
|
|
1378
1354
|
};
|
|
1379
1355
|
const program = createProgram('', '');
|
|
1380
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'wait', 'download', 'receipt', '--timeout', '900']);
|
|
1356
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'wait', 'download', 'receipt', '--timeout', '900']);
|
|
1381
1357
|
expect(browserState.page?.waitForDownload).toHaveBeenCalledWith('receipt', 900);
|
|
1382
1358
|
expect(lastJsonLog()).toEqual({
|
|
1383
1359
|
downloaded: true,
|
|
@@ -1403,7 +1379,7 @@ describe('browser tab targeting commands', () => {
|
|
|
1403
1379
|
tabs: vi.fn().mockResolvedValue([{ index: 0, page: 'tab-1', url: 'https://target.example', title: 'Target', active: true }]),
|
|
1404
1380
|
};
|
|
1405
1381
|
const program = createProgram('', '');
|
|
1406
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'wait', 'download', 'receipt', '--timeout', '900']);
|
|
1382
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'wait', 'download', 'receipt', '--timeout', '900']);
|
|
1407
1383
|
const out = lastJsonLog();
|
|
1408
1384
|
expect(out.error.code).toBe('download_not_seen');
|
|
1409
1385
|
expect(out.download.elapsedMs).toBe(900);
|
|
@@ -1413,10 +1389,10 @@ describe('browser tab targeting commands', () => {
|
|
|
1413
1389
|
describe('browser network command', () => {
|
|
1414
1390
|
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1415
1391
|
function getNetworkCachePath(cacheDir) {
|
|
1416
|
-
return path.join(cacheDir, 'browser-network', '
|
|
1392
|
+
return path.join(cacheDir, 'browser-network', 'test.json');
|
|
1417
1393
|
}
|
|
1418
|
-
function
|
|
1419
|
-
return path.join(cacheDir, 'browser-network', '
|
|
1394
|
+
function getCustomNetworkCachePath(cacheDir) {
|
|
1395
|
+
return path.join(cacheDir, 'browser-network', 'custom.json');
|
|
1420
1396
|
}
|
|
1421
1397
|
function lastJsonLog() {
|
|
1422
1398
|
const calls = consoleLogSpy.mock.calls;
|
|
@@ -1434,6 +1410,7 @@ describe('browser network command', () => {
|
|
|
1434
1410
|
mockBrowserConnect.mockClear();
|
|
1435
1411
|
mockBrowserClose.mockReset().mockResolvedValue(undefined);
|
|
1436
1412
|
browserState.page = {
|
|
1413
|
+
session: 'test',
|
|
1437
1414
|
setActivePage: vi.fn(),
|
|
1438
1415
|
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
1439
1416
|
tabs: vi.fn().mockResolvedValue([{ page: 'tab-1', active: true }]),
|
|
@@ -1460,7 +1437,7 @@ describe('browser network command', () => {
|
|
|
1460
1437
|
it('emits JSON with shape previews and persists the capture to disk', async () => {
|
|
1461
1438
|
const cacheDir = String(process.env.OPENCLI_CACHE_DIR);
|
|
1462
1439
|
const program = createProgram('', '');
|
|
1463
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1440
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1464
1441
|
const out = lastJsonLog();
|
|
1465
1442
|
expect(out.count).toBe(1);
|
|
1466
1443
|
expect(out.filtered_out).toBe(1);
|
|
@@ -1469,22 +1446,22 @@ describe('browser network command', () => {
|
|
|
1469
1446
|
expect(out.entries[0]).not.toHaveProperty('body');
|
|
1470
1447
|
expect(fs.existsSync(getNetworkCachePath(cacheDir))).toBe(true);
|
|
1471
1448
|
});
|
|
1472
|
-
it('uses the selected browser
|
|
1449
|
+
it('uses the selected browser session for network cache scope', async () => {
|
|
1473
1450
|
const cacheDir = String(process.env.OPENCLI_CACHE_DIR);
|
|
1474
1451
|
browserState.page = {
|
|
1475
1452
|
...browserState.page,
|
|
1476
|
-
|
|
1453
|
+
session: 'custom',
|
|
1477
1454
|
};
|
|
1478
1455
|
const program = createProgram('', '');
|
|
1479
|
-
await program.parseAsync(['node', 'opencli', 'browser', '--
|
|
1456
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'custom', 'network']);
|
|
1480
1457
|
const out = lastJsonLog();
|
|
1481
|
-
expect(out.
|
|
1482
|
-
expect(fs.existsSync(
|
|
1458
|
+
expect(out.session).toBe('custom');
|
|
1459
|
+
expect(fs.existsSync(getCustomNetworkCachePath(cacheDir))).toBe(true);
|
|
1483
1460
|
expect(fs.existsSync(getNetworkCachePath(cacheDir))).toBe(false);
|
|
1484
1461
|
});
|
|
1485
1462
|
it('--all includes static resources that the default filter drops', async () => {
|
|
1486
1463
|
const program = createProgram('', '');
|
|
1487
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--all']);
|
|
1464
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--all']);
|
|
1488
1465
|
const out = lastJsonLog();
|
|
1489
1466
|
expect(out.count).toBe(2);
|
|
1490
1467
|
expect(out.entries.map((e) => e.key)).toContain('UserTweets');
|
|
@@ -1519,7 +1496,7 @@ describe('browser network command', () => {
|
|
|
1519
1496
|
},
|
|
1520
1497
|
]);
|
|
1521
1498
|
const program = createProgram('', '');
|
|
1522
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--since', '120s', '--failed']);
|
|
1499
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--since', '120s', '--failed']);
|
|
1523
1500
|
const out = lastJsonLog();
|
|
1524
1501
|
expect(out.count).toBe(1);
|
|
1525
1502
|
expect(out.entries[0].url).toBe('https://api.example.com/new-fail');
|
|
@@ -1543,7 +1520,7 @@ describe('browser network command', () => {
|
|
|
1543
1520
|
},
|
|
1544
1521
|
]);
|
|
1545
1522
|
const program = createProgram('', '');
|
|
1546
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1523
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1547
1524
|
const out = lastJsonLog();
|
|
1548
1525
|
expect(out.count).toBe(1);
|
|
1549
1526
|
expect(out.filtered_out).toBe(1);
|
|
@@ -1553,16 +1530,16 @@ describe('browser network command', () => {
|
|
|
1553
1530
|
});
|
|
1554
1531
|
it('--raw emits full bodies inline for every entry', async () => {
|
|
1555
1532
|
const program = createProgram('', '');
|
|
1556
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--raw']);
|
|
1533
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--raw']);
|
|
1557
1534
|
const out = lastJsonLog();
|
|
1558
1535
|
expect(out.entries[0].body).toEqual({ data: { user: { rest_id: '42' } } });
|
|
1559
1536
|
expect(out.entries[0].timestamp).toMatch(/T/);
|
|
1560
1537
|
});
|
|
1561
1538
|
it('--detail <key> returns the full body for the requested entry', async () => {
|
|
1562
1539
|
const program = createProgram('', '');
|
|
1563
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1540
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1564
1541
|
consoleLogSpy.mockClear();
|
|
1565
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--detail', 'UserTweets']);
|
|
1542
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--detail', 'UserTweets']);
|
|
1566
1543
|
const out = lastJsonLog();
|
|
1567
1544
|
expect(out.key).toBe('UserTweets');
|
|
1568
1545
|
expect(out.body).toEqual({ data: { user: { rest_id: '42' } } });
|
|
@@ -1571,9 +1548,9 @@ describe('browser network command', () => {
|
|
|
1571
1548
|
});
|
|
1572
1549
|
it('--detail reports key_not_found with the list of available keys', async () => {
|
|
1573
1550
|
const program = createProgram('', '');
|
|
1574
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1551
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1575
1552
|
consoleLogSpy.mockClear();
|
|
1576
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--detail', 'NopeOp']);
|
|
1553
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--detail', 'NopeOp']);
|
|
1577
1554
|
const out = lastJsonLog();
|
|
1578
1555
|
expect(out.error.code).toBe('key_not_found');
|
|
1579
1556
|
expect(out.error.available_keys).toContain('UserTweets');
|
|
@@ -1581,7 +1558,7 @@ describe('browser network command', () => {
|
|
|
1581
1558
|
});
|
|
1582
1559
|
it('--detail reports cache_missing when no capture has been persisted yet', async () => {
|
|
1583
1560
|
const program = createProgram('', '');
|
|
1584
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--detail', 'UserTweets']);
|
|
1561
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--detail', 'UserTweets']);
|
|
1585
1562
|
const out = lastJsonLog();
|
|
1586
1563
|
expect(out.error.code).toBe('cache_missing');
|
|
1587
1564
|
expect(process.exitCode).toBeDefined();
|
|
@@ -1589,7 +1566,7 @@ describe('browser network command', () => {
|
|
|
1589
1566
|
it('emits capture_failed when readNetworkCapture throws', async () => {
|
|
1590
1567
|
browserState.page.readNetworkCapture = vi.fn().mockRejectedValue(new Error('CDP disconnected'));
|
|
1591
1568
|
const program = createProgram('', '');
|
|
1592
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1569
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1593
1570
|
const out = lastJsonLog();
|
|
1594
1571
|
expect(out.error.code).toBe('capture_failed');
|
|
1595
1572
|
expect(out.error.message).toContain('CDP disconnected');
|
|
@@ -1602,7 +1579,7 @@ describe('browser network command', () => {
|
|
|
1602
1579
|
const clashDir = path.join(cacheDir, 'browser-network');
|
|
1603
1580
|
fs.writeFileSync(clashDir, 'not-a-directory');
|
|
1604
1581
|
const program = createProgram('', '');
|
|
1605
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1582
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1606
1583
|
const out = lastJsonLog();
|
|
1607
1584
|
expect(out.cache_warning).toMatch(/Could not persist capture cache/);
|
|
1608
1585
|
expect(out.count).toBe(1);
|
|
@@ -1627,7 +1604,7 @@ describe('browser network command', () => {
|
|
|
1627
1604
|
});
|
|
1628
1605
|
it('narrows entries to those whose shape has ALL named fields', async () => {
|
|
1629
1606
|
const program = createProgram('', '');
|
|
1630
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'author,text,likes']);
|
|
1607
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'author,text,likes']);
|
|
1631
1608
|
const out = lastJsonLog();
|
|
1632
1609
|
expect(out.count).toBe(1);
|
|
1633
1610
|
expect(out.filter).toEqual(['author', 'text', 'likes']);
|
|
@@ -1636,14 +1613,14 @@ describe('browser network command', () => {
|
|
|
1636
1613
|
});
|
|
1637
1614
|
it('matches container segments too, not just leaf names (any-segment rule)', async () => {
|
|
1638
1615
|
const program = createProgram('', '');
|
|
1639
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'data,items']);
|
|
1616
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'data,items']);
|
|
1640
1617
|
const out = lastJsonLog();
|
|
1641
1618
|
expect(out.count).toBe(1);
|
|
1642
1619
|
expect(out.entries[0].key).toBe('UserTweets');
|
|
1643
1620
|
});
|
|
1644
1621
|
it('drops entries that are missing any required field (AND semantics)', async () => {
|
|
1645
1622
|
const program = createProgram('', '');
|
|
1646
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'author,followers']);
|
|
1623
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'author,followers']);
|
|
1647
1624
|
const out = lastJsonLog();
|
|
1648
1625
|
expect(out.count).toBe(0);
|
|
1649
1626
|
expect(out.entries).toEqual([]);
|
|
@@ -1652,7 +1629,7 @@ describe('browser network command', () => {
|
|
|
1652
1629
|
});
|
|
1653
1630
|
it('returns empty entries (not an error) when nothing matches', async () => {
|
|
1654
1631
|
const program = createProgram('', '');
|
|
1655
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'nonexistent_field']);
|
|
1632
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'nonexistent_field']);
|
|
1656
1633
|
const out = lastJsonLog();
|
|
1657
1634
|
expect(out.count).toBe(0);
|
|
1658
1635
|
expect(out.entries).toEqual([]);
|
|
@@ -1661,43 +1638,43 @@ describe('browser network command', () => {
|
|
|
1661
1638
|
});
|
|
1662
1639
|
it('is case-sensitive so agents do not conflate `Id` with `id`', async () => {
|
|
1663
1640
|
const program = createProgram('', '');
|
|
1664
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'Data']);
|
|
1641
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'Data']);
|
|
1665
1642
|
const out = lastJsonLog();
|
|
1666
1643
|
expect(out.count).toBe(0);
|
|
1667
1644
|
});
|
|
1668
1645
|
it('persists the full (unfiltered) capture so --detail lookups still find filtered-out keys', async () => {
|
|
1669
1646
|
const program = createProgram('', '');
|
|
1670
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'author,text,likes']);
|
|
1647
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'author,text,likes']);
|
|
1671
1648
|
consoleLogSpy.mockClear();
|
|
1672
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--detail', 'UserProfile']);
|
|
1649
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--detail', 'UserProfile']);
|
|
1673
1650
|
const out = lastJsonLog();
|
|
1674
1651
|
expect(out.key).toBe('UserProfile');
|
|
1675
1652
|
expect(out.body).toEqual({ data: { user: { id: 'u1', followers: 10 } } });
|
|
1676
1653
|
});
|
|
1677
1654
|
it('composes with --raw: entries keep full bodies, filter still narrows', async () => {
|
|
1678
1655
|
const program = createProgram('', '');
|
|
1679
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'author', '--raw']);
|
|
1656
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'author', '--raw']);
|
|
1680
1657
|
const out = lastJsonLog();
|
|
1681
1658
|
expect(out.count).toBe(1);
|
|
1682
1659
|
expect(out.entries[0].body).toEqual({ data: { items: [{ author: 'a', text: 't', likes: 1 }] } });
|
|
1683
1660
|
});
|
|
1684
1661
|
it('reports invalid_filter for empty value', async () => {
|
|
1685
1662
|
const program = createProgram('', '');
|
|
1686
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', '']);
|
|
1663
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', '']);
|
|
1687
1664
|
const out = lastJsonLog();
|
|
1688
1665
|
expect(out.error.code).toBe('invalid_filter');
|
|
1689
1666
|
expect(process.exitCode).toBeDefined();
|
|
1690
1667
|
});
|
|
1691
1668
|
it('reports invalid_filter for commas-only value', async () => {
|
|
1692
1669
|
const program = createProgram('', '');
|
|
1693
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', ',,,']);
|
|
1670
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', ',,,']);
|
|
1694
1671
|
const out = lastJsonLog();
|
|
1695
1672
|
expect(out.error.code).toBe('invalid_filter');
|
|
1696
1673
|
expect(process.exitCode).toBeDefined();
|
|
1697
1674
|
});
|
|
1698
1675
|
it('rejects --filter combined with --detail as invalid_args', async () => {
|
|
1699
1676
|
const program = createProgram('', '');
|
|
1700
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--filter', 'author', '--detail', 'UserTweets']);
|
|
1677
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--filter', 'author', '--detail', 'UserTweets']);
|
|
1701
1678
|
const out = lastJsonLog();
|
|
1702
1679
|
expect(out.error.code).toBe('invalid_args');
|
|
1703
1680
|
expect(out.error.message).toContain('--filter');
|
|
@@ -1719,7 +1696,7 @@ describe('browser network command', () => {
|
|
|
1719
1696
|
},
|
|
1720
1697
|
]);
|
|
1721
1698
|
const program = createProgram('', '');
|
|
1722
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1699
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1723
1700
|
const out = lastJsonLog();
|
|
1724
1701
|
expect(out.body_truncated_count).toBe(1);
|
|
1725
1702
|
expect(out.entries[0].body_truncated).toBe(true);
|
|
@@ -1738,9 +1715,9 @@ describe('browser network command', () => {
|
|
|
1738
1715
|
},
|
|
1739
1716
|
]);
|
|
1740
1717
|
const program = createProgram('', '');
|
|
1741
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1718
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1742
1719
|
consoleLogSpy.mockClear();
|
|
1743
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--detail', 'GET api.example.com/huge']);
|
|
1720
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--detail', 'GET api.example.com/huge']);
|
|
1744
1721
|
const out = lastJsonLog();
|
|
1745
1722
|
expect(out.body_truncated).toBe(true);
|
|
1746
1723
|
expect(out.body_full_size).toBe(50_000_000);
|
|
@@ -1758,10 +1735,10 @@ describe('browser network command', () => {
|
|
|
1758
1735
|
},
|
|
1759
1736
|
]);
|
|
1760
1737
|
const program = createProgram('', '');
|
|
1761
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1738
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1762
1739
|
consoleLogSpy.mockClear();
|
|
1763
1740
|
await program.parseAsync([
|
|
1764
|
-
'node', 'opencli', 'browser', 'network',
|
|
1741
|
+
'node', 'opencli', 'browser', '--session', 'test', 'network',
|
|
1765
1742
|
'--detail', 'GET api.example.com/plain',
|
|
1766
1743
|
'--max-body', '100',
|
|
1767
1744
|
]);
|
|
@@ -1783,10 +1760,10 @@ describe('browser network command', () => {
|
|
|
1783
1760
|
},
|
|
1784
1761
|
]);
|
|
1785
1762
|
const program = createProgram('', '');
|
|
1786
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1763
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1787
1764
|
consoleLogSpy.mockClear();
|
|
1788
1765
|
await program.parseAsync([
|
|
1789
|
-
'node', 'opencli', 'browser', 'network',
|
|
1766
|
+
'node', 'opencli', 'browser', '--session', 'test', 'network',
|
|
1790
1767
|
'--detail', 'GET api.example.com/json',
|
|
1791
1768
|
'--max-body', '10',
|
|
1792
1769
|
]);
|
|
@@ -1807,10 +1784,10 @@ describe('browser network command', () => {
|
|
|
1807
1784
|
},
|
|
1808
1785
|
]);
|
|
1809
1786
|
const program = createProgram('', '');
|
|
1810
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network']);
|
|
1787
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network']);
|
|
1811
1788
|
consoleLogSpy.mockClear();
|
|
1812
1789
|
await program.parseAsync([
|
|
1813
|
-
'node', 'opencli', 'browser', 'network',
|
|
1790
|
+
'node', 'opencli', 'browser', '--session', 'test', 'network',
|
|
1814
1791
|
'--detail', 'GET api.example.com/x',
|
|
1815
1792
|
'--max-body', 'abc',
|
|
1816
1793
|
]);
|
|
@@ -1830,7 +1807,7 @@ describe('browser network command', () => {
|
|
|
1830
1807
|
},
|
|
1831
1808
|
]);
|
|
1832
1809
|
const program = createProgram('', '');
|
|
1833
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'network', '--raw']);
|
|
1810
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'network', '--raw']);
|
|
1834
1811
|
const out = lastJsonLog();
|
|
1835
1812
|
expect(out.entries).toHaveLength(1);
|
|
1836
1813
|
const entry = out.entries[0];
|
|
@@ -1851,6 +1828,7 @@ describe('browser console command', () => {
|
|
|
1851
1828
|
mockBrowserClose.mockReset().mockResolvedValue(undefined);
|
|
1852
1829
|
const now = Date.now();
|
|
1853
1830
|
browserState.page = {
|
|
1831
|
+
session: 'test',
|
|
1854
1832
|
setActivePage: vi.fn(),
|
|
1855
1833
|
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
1856
1834
|
tabs: vi.fn().mockResolvedValue([{ page: 'tab-1', active: true }]),
|
|
@@ -1872,7 +1850,7 @@ describe('browser console command', () => {
|
|
|
1872
1850
|
}
|
|
1873
1851
|
it('filters console messages by level and time window', async () => {
|
|
1874
1852
|
const program = createProgram('', '');
|
|
1875
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'console', '--level', 'error', '--since', '120s']);
|
|
1853
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'console', '--level', 'error', '--since', '120s']);
|
|
1876
1854
|
const out = lastJsonLog();
|
|
1877
1855
|
expect(out.count).toBe(1);
|
|
1878
1856
|
expect(out.messages[0]).toMatchObject({ type: 'error', text: 'boom' });
|
|
@@ -1909,14 +1887,14 @@ describe('browser get html command', () => {
|
|
|
1909
1887
|
const big = '<div>' + 'x'.repeat(100_000) + '</div>';
|
|
1910
1888
|
browserState.page.evaluate.mockResolvedValueOnce({ kind: 'ok', html: big });
|
|
1911
1889
|
const program = createProgram('', '');
|
|
1912
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html']);
|
|
1890
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html']);
|
|
1913
1891
|
expect(lastLogArg()).toBe(big);
|
|
1914
1892
|
});
|
|
1915
1893
|
it('caps output with --max and prepends a visible truncation marker', async () => {
|
|
1916
1894
|
const big = '<div>' + 'x'.repeat(500) + '</div>';
|
|
1917
1895
|
browserState.page.evaluate.mockResolvedValueOnce({ kind: 'ok', html: big });
|
|
1918
1896
|
const program = createProgram('', '');
|
|
1919
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--max', '100']);
|
|
1897
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--max', '100']);
|
|
1920
1898
|
const out = String(lastLogArg());
|
|
1921
1899
|
expect(out.startsWith('<!-- opencli: truncated 100 of')).toBe(true);
|
|
1922
1900
|
expect(out.length).toBeGreaterThan(100);
|
|
@@ -1924,21 +1902,21 @@ describe('browser get html command', () => {
|
|
|
1924
1902
|
});
|
|
1925
1903
|
it('rejects negative --max with invalid_max error', async () => {
|
|
1926
1904
|
const program = createProgram('', '');
|
|
1927
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--max', '-1']);
|
|
1905
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--max', '-1']);
|
|
1928
1906
|
expect(lastJsonLog().error.code).toBe('invalid_max');
|
|
1929
1907
|
expect(process.exitCode).toBeDefined();
|
|
1930
1908
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
1931
1909
|
});
|
|
1932
1910
|
it('rejects fractional --max with invalid_max error', async () => {
|
|
1933
1911
|
const program = createProgram('', '');
|
|
1934
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--max', '1.5']);
|
|
1912
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--max', '1.5']);
|
|
1935
1913
|
expect(lastJsonLog().error.code).toBe('invalid_max');
|
|
1936
1914
|
expect(process.exitCode).toBeDefined();
|
|
1937
1915
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
1938
1916
|
});
|
|
1939
1917
|
it('rejects non-numeric --max (e.g. "10abc") with invalid_max error', async () => {
|
|
1940
1918
|
const program = createProgram('', '');
|
|
1941
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--max', '10abc']);
|
|
1919
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--max', '10abc']);
|
|
1942
1920
|
expect(lastJsonLog().error.code).toBe('invalid_max');
|
|
1943
1921
|
expect(process.exitCode).toBeDefined();
|
|
1944
1922
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
@@ -1950,7 +1928,7 @@ describe('browser get html command', () => {
|
|
|
1950
1928
|
tree: { tag: 'div', attrs: { class: 'hero' }, text: 'Hi', children: [] },
|
|
1951
1929
|
});
|
|
1952
1930
|
const program = createProgram('', '');
|
|
1953
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--selector', '.hero', '--as', 'json']);
|
|
1931
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--selector', '.hero', '--as', 'json']);
|
|
1954
1932
|
const out = lastJsonLog();
|
|
1955
1933
|
expect(out.matched).toBe(1);
|
|
1956
1934
|
expect(out.tree.tag).toBe('div');
|
|
@@ -1959,14 +1937,14 @@ describe('browser get html command', () => {
|
|
|
1959
1937
|
it('--as json emits selector_not_found when matched is 0', async () => {
|
|
1960
1938
|
browserState.page.evaluate.mockResolvedValueOnce({ selector: '.missing', matched: 0, tree: null });
|
|
1961
1939
|
const program = createProgram('', '');
|
|
1962
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--selector', '.missing', '--as', 'json']);
|
|
1940
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--selector', '.missing', '--as', 'json']);
|
|
1963
1941
|
expect(lastJsonLog().error.code).toBe('selector_not_found');
|
|
1964
1942
|
expect(process.exitCode).toBeDefined();
|
|
1965
1943
|
});
|
|
1966
1944
|
it('raw mode emits selector_not_found when the selector matches nothing', async () => {
|
|
1967
1945
|
browserState.page.evaluate.mockResolvedValueOnce({ kind: 'ok', html: null });
|
|
1968
1946
|
const program = createProgram('', '');
|
|
1969
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--selector', '.missing']);
|
|
1947
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--selector', '.missing']);
|
|
1970
1948
|
expect(lastJsonLog().error.code).toBe('selector_not_found');
|
|
1971
1949
|
expect(process.exitCode).toBeDefined();
|
|
1972
1950
|
});
|
|
@@ -1976,7 +1954,7 @@ describe('browser get html command', () => {
|
|
|
1976
1954
|
reason: "'##$@@' is not a valid selector",
|
|
1977
1955
|
});
|
|
1978
1956
|
const program = createProgram('', '');
|
|
1979
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--selector', '##$@@']);
|
|
1957
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--selector', '##$@@']);
|
|
1980
1958
|
const err = lastJsonLog().error;
|
|
1981
1959
|
expect(err.code).toBe('invalid_selector');
|
|
1982
1960
|
expect(err.message).toContain('##$@@');
|
|
@@ -1990,7 +1968,7 @@ describe('browser get html command', () => {
|
|
|
1990
1968
|
reason: "'##$@@' is not a valid selector",
|
|
1991
1969
|
});
|
|
1992
1970
|
const program = createProgram('', '');
|
|
1993
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--selector', '##$@@', '--as', 'json']);
|
|
1971
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--selector', '##$@@', '--as', 'json']);
|
|
1994
1972
|
const err = lastJsonLog().error;
|
|
1995
1973
|
expect(err.code).toBe('invalid_selector');
|
|
1996
1974
|
expect(err.message).toContain('##$@@');
|
|
@@ -1998,7 +1976,7 @@ describe('browser get html command', () => {
|
|
|
1998
1976
|
});
|
|
1999
1977
|
it('rejects unknown --as format with invalid_format error', async () => {
|
|
2000
1978
|
const program = createProgram('', '');
|
|
2001
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'html', '--as', 'yaml']);
|
|
1979
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'html', '--as', 'yaml']);
|
|
2002
1980
|
expect(lastJsonLog().error.code).toBe('invalid_format');
|
|
2003
1981
|
expect(process.exitCode).toBeDefined();
|
|
2004
1982
|
});
|
|
@@ -2031,6 +2009,7 @@ function installSelectorFirstTestHarness(label, pageOverrides) {
|
|
|
2031
2009
|
setActivePage: vi.fn(),
|
|
2032
2010
|
getActivePage: vi.fn().mockReturnValue('tab-1'),
|
|
2033
2011
|
tabs: vi.fn().mockResolvedValue([{ page: 'tab-1', active: true }]),
|
|
2012
|
+
session: 'test',
|
|
2034
2013
|
...pageOverrides(),
|
|
2035
2014
|
};
|
|
2036
2015
|
});
|
|
@@ -2052,7 +2031,7 @@ describe('browser find command', () => {
|
|
|
2052
2031
|
],
|
|
2053
2032
|
});
|
|
2054
2033
|
const program = createProgram('', '');
|
|
2055
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--css', '.btn']);
|
|
2034
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--css', '.btn']);
|
|
2056
2035
|
const out = lastJsonLog();
|
|
2057
2036
|
expect(out.matches_n).toBe(2);
|
|
2058
2037
|
expect(out.entries).toHaveLength(2);
|
|
@@ -2068,7 +2047,7 @@ describe('browser find command', () => {
|
|
|
2068
2047
|
],
|
|
2069
2048
|
});
|
|
2070
2049
|
const program = createProgram('', '');
|
|
2071
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--role', 'button', '--name', 'Save']);
|
|
2050
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--role', 'button', '--name', 'Save']);
|
|
2072
2051
|
const js = browserState.page.evaluate.mock.calls[0][0];
|
|
2073
2052
|
expect(js).toContain('CRITERIA');
|
|
2074
2053
|
expect(js).toContain('function accessibleName');
|
|
@@ -2082,7 +2061,7 @@ describe('browser find command', () => {
|
|
|
2082
2061
|
it('forwards --limit / --text-max into the generated JS', async () => {
|
|
2083
2062
|
browserState.page.evaluate.mockResolvedValueOnce({ matches_n: 0, entries: [] });
|
|
2084
2063
|
const program = createProgram('', '');
|
|
2085
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--css', '.btn', '--limit', '3', '--text-max', '20']);
|
|
2064
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--css', '.btn', '--limit', '3', '--text-max', '20']);
|
|
2086
2065
|
const js = browserState.page.evaluate.mock.calls[0][0];
|
|
2087
2066
|
expect(js).toContain('LIMIT = 3');
|
|
2088
2067
|
expect(js).toContain('TEXT_MAX = 20');
|
|
@@ -2092,7 +2071,7 @@ describe('browser find command', () => {
|
|
|
2092
2071
|
error: { code: 'invalid_selector', message: 'Invalid CSS selector: ">>>"', hint: 'Check the selector syntax.' },
|
|
2093
2072
|
});
|
|
2094
2073
|
const program = createProgram('', '');
|
|
2095
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--css', '>>>']);
|
|
2074
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--css', '>>>']);
|
|
2096
2075
|
expect(lastJsonLog().error.code).toBe('invalid_selector');
|
|
2097
2076
|
expect(process.exitCode).toBeDefined();
|
|
2098
2077
|
});
|
|
@@ -2101,20 +2080,20 @@ describe('browser find command', () => {
|
|
|
2101
2080
|
error: { code: 'selector_not_found', message: 'CSS selector ".missing" matched 0 elements', hint: 'Use browser state to inspect the page.' },
|
|
2102
2081
|
});
|
|
2103
2082
|
const program = createProgram('', '');
|
|
2104
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--css', '.missing']);
|
|
2083
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--css', '.missing']);
|
|
2105
2084
|
expect(lastJsonLog().error.code).toBe('selector_not_found');
|
|
2106
2085
|
expect(process.exitCode).toBeDefined();
|
|
2107
2086
|
});
|
|
2108
2087
|
it('rejects missing --css with usage_error (no evaluate call)', async () => {
|
|
2109
2088
|
const program = createProgram('', '');
|
|
2110
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find']);
|
|
2089
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find']);
|
|
2111
2090
|
expect(lastJsonLog().error.code).toBe('usage_error');
|
|
2112
2091
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
2113
2092
|
expect(process.exitCode).toBeDefined();
|
|
2114
2093
|
});
|
|
2115
2094
|
it('rejects malformed --limit with usage_error (no evaluate call)', async () => {
|
|
2116
2095
|
const program = createProgram('', '');
|
|
2117
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'find', '--css', '.btn', '--limit', 'abc']);
|
|
2096
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'find', '--css', '.btn', '--limit', 'abc']);
|
|
2118
2097
|
expect(lastJsonLog().error.code).toBe('usage_error');
|
|
2119
2098
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
2120
2099
|
expect(process.exitCode).toBeDefined();
|
|
@@ -2131,7 +2110,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2131
2110
|
// 2nd call: getTextResolvedJs -> the element's text
|
|
2132
2111
|
evalMock.mockResolvedValueOnce('Hello world');
|
|
2133
2112
|
const program = createProgram('', '');
|
|
2134
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '7']);
|
|
2113
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '7']);
|
|
2135
2114
|
expect(lastJsonLog()).toEqual({ value: 'Hello world', matches_n: 1, match_level: 'exact' });
|
|
2136
2115
|
});
|
|
2137
2116
|
it('resolves a semantic locator to a ref before get text', async () => {
|
|
@@ -2145,7 +2124,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2145
2124
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' });
|
|
2146
2125
|
evalMock.mockResolvedValueOnce('Save');
|
|
2147
2126
|
const program = createProgram('', '');
|
|
2148
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '--role', 'button', '--name', 'Save']);
|
|
2127
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '--role', 'button', '--name', 'Save']);
|
|
2149
2128
|
expect(evalMock.mock.calls[0][0]).toContain('function accessibleName');
|
|
2150
2129
|
expect(evalMock.mock.calls[1][0]).toContain('const ref = "12"');
|
|
2151
2130
|
expect(lastJsonLog()).toEqual({ value: 'Save', matches_n: 1, match_level: 'exact' });
|
|
@@ -2163,7 +2142,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2163
2142
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' });
|
|
2164
2143
|
evalMock.mockResolvedValueOnce('Save');
|
|
2165
2144
|
const program = createProgram('', '');
|
|
2166
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '--role', 'button', '--name', 'Save']);
|
|
2145
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '--role', 'button', '--name', 'Save']);
|
|
2167
2146
|
expect(evalMock.mock.calls[0][0]).toContain('const LIMIT = 6');
|
|
2168
2147
|
expect(evalMock.mock.calls[1][0]).toContain('const ref = "12"');
|
|
2169
2148
|
expect(lastJsonLog()).toEqual({ value: 'Save', matches_n: 1, match_level: 'exact', total_matches: 3 });
|
|
@@ -2173,7 +2152,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2173
2152
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 3, match_level: 'exact' });
|
|
2174
2153
|
evalMock.mockResolvedValueOnce('first');
|
|
2175
2154
|
const program = createProgram('', '');
|
|
2176
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '.btn']);
|
|
2155
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '.btn']);
|
|
2177
2156
|
expect(lastJsonLog()).toEqual({ value: 'first', matches_n: 3, match_level: 'exact' });
|
|
2178
2157
|
});
|
|
2179
2158
|
it('parses the attributes payload back into a real object', async () => {
|
|
@@ -2182,7 +2161,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2182
2161
|
// getAttributesResolvedJs returns a JSON-encoded string — the CLI must parse it
|
|
2183
2162
|
evalMock.mockResolvedValueOnce(JSON.stringify({ id: 'nav', class: 'hero' }));
|
|
2184
2163
|
const program = createProgram('', '');
|
|
2185
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'attributes', '#nav']);
|
|
2164
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'attributes', '#nav']);
|
|
2186
2165
|
const out = lastJsonLog();
|
|
2187
2166
|
expect(out.matches_n).toBe(1);
|
|
2188
2167
|
expect(out.match_level).toBe('exact');
|
|
@@ -2196,7 +2175,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2196
2175
|
hint: 'Try a less specific selector.',
|
|
2197
2176
|
});
|
|
2198
2177
|
const program = createProgram('', '');
|
|
2199
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '.missing']);
|
|
2178
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '.missing']);
|
|
2200
2179
|
expect(lastJsonLog().error.code).toBe('selector_not_found');
|
|
2201
2180
|
expect(process.exitCode).toBeDefined();
|
|
2202
2181
|
});
|
|
@@ -2205,7 +2184,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2205
2184
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 4, match_level: 'exact' });
|
|
2206
2185
|
evalMock.mockResolvedValueOnce('second');
|
|
2207
2186
|
const program = createProgram('', '');
|
|
2208
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'value', '.btn', '--nth', '1']);
|
|
2187
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'value', '.btn', '--nth', '1']);
|
|
2209
2188
|
const resolveJs = evalMock.mock.calls[0][0];
|
|
2210
2189
|
// resolveTargetJs embeds nth as a raw number literal; look for the binding
|
|
2211
2190
|
expect(resolveJs).toContain('const nth = 1');
|
|
@@ -2213,7 +2192,7 @@ describe('browser get text/value/attributes commands', () => {
|
|
|
2213
2192
|
});
|
|
2214
2193
|
it('rejects malformed --nth with usage_error before touching the page', async () => {
|
|
2215
2194
|
const program = createProgram('', '');
|
|
2216
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'get', 'text', '.btn', '--nth', 'abc']);
|
|
2195
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'get', 'text', '.btn', '--nth', 'abc']);
|
|
2217
2196
|
expect(lastJsonLog().error.code).toBe('usage_error');
|
|
2218
2197
|
expect(browserState.page.evaluate).not.toHaveBeenCalled();
|
|
2219
2198
|
expect(process.exitCode).toBeDefined();
|
|
@@ -2261,7 +2240,7 @@ describe('browser click/type commands', () => {
|
|
|
2261
2240
|
it('emits {clicked, target, matches_n, match_level} on success', async () => {
|
|
2262
2241
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2263
2242
|
const program = createProgram('', '');
|
|
2264
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '#save']);
|
|
2243
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '#save']);
|
|
2265
2244
|
expect(browserState.page.click).toHaveBeenCalledWith('#save', {});
|
|
2266
2245
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '#save', matches_n: 1, match_level: 'exact' });
|
|
2267
2246
|
});
|
|
@@ -2274,7 +2253,7 @@ describe('browser click/type commands', () => {
|
|
|
2274
2253
|
});
|
|
2275
2254
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2276
2255
|
const program = createProgram('', '');
|
|
2277
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '--role', 'button', '--name', 'Submit']);
|
|
2256
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '--role', 'button', '--name', 'Submit']);
|
|
2278
2257
|
expect(browserState.page.click).toHaveBeenCalledWith('23', {});
|
|
2279
2258
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '23', matches_n: 1, match_level: 'exact' });
|
|
2280
2259
|
});
|
|
@@ -2287,7 +2266,7 @@ describe('browser click/type commands', () => {
|
|
|
2287
2266
|
],
|
|
2288
2267
|
});
|
|
2289
2268
|
const program = createProgram('', '');
|
|
2290
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '--role', 'button', '--name', 'Save']);
|
|
2269
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '--role', 'button', '--name', 'Save']);
|
|
2291
2270
|
const err = lastJsonLog().error;
|
|
2292
2271
|
expect(err.code).toBe('semantic_ambiguous');
|
|
2293
2272
|
expect(err.matches_n).toBe(2);
|
|
@@ -2302,7 +2281,7 @@ describe('browser click/type commands', () => {
|
|
|
2302
2281
|
],
|
|
2303
2282
|
});
|
|
2304
2283
|
const program = createProgram('', '');
|
|
2305
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'hover', '--role', 'button', '--name', 'Settings']);
|
|
2284
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'hover', '--role', 'button', '--name', 'Settings']);
|
|
2306
2285
|
expect(browserState.page.hover).toHaveBeenCalledWith('31', {});
|
|
2307
2286
|
expect(lastJsonLog()).toEqual({ hovered: true, target: '31', matches_n: 1, match_level: 'exact' });
|
|
2308
2287
|
});
|
|
@@ -2315,7 +2294,7 @@ describe('browser click/type commands', () => {
|
|
|
2315
2294
|
});
|
|
2316
2295
|
browserState.page.setChecked.mockResolvedValueOnce({ checked: true, changed: false, matches_n: 1, match_level: 'exact', kind: 'checkbox' });
|
|
2317
2296
|
const program = createProgram('', '');
|
|
2318
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'check', '--role', 'checkbox', '--name', 'Accept']);
|
|
2297
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'check', '--role', 'checkbox', '--name', 'Accept']);
|
|
2319
2298
|
expect(browserState.page.setChecked).toHaveBeenCalledWith('32', true, {});
|
|
2320
2299
|
expect(lastJsonLog()).toEqual({ checked: true, changed: false, target: '32', matches_n: 1, match_level: 'exact', kind: 'checkbox' });
|
|
2321
2300
|
});
|
|
@@ -2339,7 +2318,7 @@ describe('browser click/type commands', () => {
|
|
|
2339
2318
|
multiple: false,
|
|
2340
2319
|
});
|
|
2341
2320
|
const program = createProgram('', '');
|
|
2342
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'upload', '--role', 'button', '--name', 'Upload receipt', file]);
|
|
2321
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'upload', '--role', 'button', '--name', 'Upload receipt', file]);
|
|
2343
2322
|
expect(browserState.page.uploadFiles).toHaveBeenCalledWith('33', [file], {});
|
|
2344
2323
|
expect(lastJsonLog()).toMatchObject({ uploaded: true, target: '33', files: 1 });
|
|
2345
2324
|
});
|
|
@@ -2355,7 +2334,7 @@ describe('browser click/type commands', () => {
|
|
|
2355
2334
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2356
2335
|
browserState.page.typeText.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2357
2336
|
const program = createProgram('', '');
|
|
2358
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'type', '--label', 'Email', 'me@example.com']);
|
|
2337
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'type', '--label', 'Email', 'me@example.com']);
|
|
2359
2338
|
expect(browserState.page.click).toHaveBeenCalledWith('34', {});
|
|
2360
2339
|
expect(browserState.page.typeText).toHaveBeenCalledWith('34', 'me@example.com', {});
|
|
2361
2340
|
expect(lastJsonLog()).toMatchObject({ typed: true, target: '34', text: 'me@example.com' });
|
|
@@ -2377,7 +2356,7 @@ describe('browser click/type commands', () => {
|
|
|
2377
2356
|
match_level: 'exact',
|
|
2378
2357
|
});
|
|
2379
2358
|
const program = createProgram('', '');
|
|
2380
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '--label', 'Email', 'me@example.com']);
|
|
2359
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'fill', '--label', 'Email', 'me@example.com']);
|
|
2381
2360
|
expect(browserState.page.fillText).toHaveBeenCalledWith('35', 'me@example.com', {});
|
|
2382
2361
|
expect(lastJsonLog()).toMatchObject({ filled: true, verified: true, target: '35', text: 'me@example.com' });
|
|
2383
2362
|
});
|
|
@@ -2409,6 +2388,8 @@ describe('browser click/type commands', () => {
|
|
|
2409
2388
|
'node',
|
|
2410
2389
|
'opencli',
|
|
2411
2390
|
'browser',
|
|
2391
|
+
'--session',
|
|
2392
|
+
'test',
|
|
2412
2393
|
'drag',
|
|
2413
2394
|
'--from-role',
|
|
2414
2395
|
'button',
|
|
@@ -2425,13 +2406,13 @@ describe('browser click/type commands', () => {
|
|
|
2425
2406
|
it('surfaces match_level=stable when resolver falls back to fingerprint match', async () => {
|
|
2426
2407
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 1, match_level: 'stable' });
|
|
2427
2408
|
const program = createProgram('', '');
|
|
2428
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '7']);
|
|
2409
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '7']);
|
|
2429
2410
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '7', matches_n: 1, match_level: 'stable' });
|
|
2430
2411
|
});
|
|
2431
2412
|
it('forwards --nth as ResolveOptions.nth to page.click', async () => {
|
|
2432
2413
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 3, match_level: 'exact' });
|
|
2433
2414
|
const program = createProgram('', '');
|
|
2434
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '.btn', '--nth', '2']);
|
|
2415
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '.btn', '--nth', '2']);
|
|
2435
2416
|
expect(browserState.page.click).toHaveBeenCalledWith('.btn', { nth: 2 });
|
|
2436
2417
|
expect(lastJsonLog()).toEqual({ clicked: true, target: '.btn', matches_n: 3, match_level: 'exact' });
|
|
2437
2418
|
});
|
|
@@ -2443,7 +2424,7 @@ describe('browser click/type commands', () => {
|
|
|
2443
2424
|
matches_n: 3,
|
|
2444
2425
|
}));
|
|
2445
2426
|
const program = createProgram('', '');
|
|
2446
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '.btn']);
|
|
2427
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '.btn']);
|
|
2447
2428
|
const err = lastJsonLog().error;
|
|
2448
2429
|
expect(err.code).toBe('selector_ambiguous');
|
|
2449
2430
|
expect(err.matches_n).toBe(3);
|
|
@@ -2457,13 +2438,13 @@ describe('browser click/type commands', () => {
|
|
|
2457
2438
|
matches_n: 3,
|
|
2458
2439
|
}));
|
|
2459
2440
|
const program = createProgram('', '');
|
|
2460
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '.btn', '--nth', '99']);
|
|
2441
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '.btn', '--nth', '99']);
|
|
2461
2442
|
expect(lastJsonLog().error.code).toBe('selector_nth_out_of_range');
|
|
2462
2443
|
expect(process.exitCode).toBeDefined();
|
|
2463
2444
|
});
|
|
2464
2445
|
it('rejects malformed --nth on click with usage_error before touching the page', async () => {
|
|
2465
2446
|
const program = createProgram('', '');
|
|
2466
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'click', '.btn', '--nth', 'abc']);
|
|
2447
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'click', '.btn', '--nth', 'abc']);
|
|
2467
2448
|
expect(lastJsonLog().error.code).toBe('usage_error');
|
|
2468
2449
|
expect(browserState.page.click).not.toHaveBeenCalled();
|
|
2469
2450
|
expect(process.exitCode).toBeDefined();
|
|
@@ -2471,21 +2452,21 @@ describe('browser click/type commands', () => {
|
|
|
2471
2452
|
it('hover: delegates to page.hover and emits a structured envelope', async () => {
|
|
2472
2453
|
browserState.page.hover.mockResolvedValueOnce({ matches_n: 2, match_level: 'exact' });
|
|
2473
2454
|
const program = createProgram('', '');
|
|
2474
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'hover', '.menu', '--nth', '1']);
|
|
2455
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'hover', '.menu', '--nth', '1']);
|
|
2475
2456
|
expect(browserState.page.hover).toHaveBeenCalledWith('.menu', { nth: 1 });
|
|
2476
2457
|
expect(lastJsonLog()).toEqual({ hovered: true, target: '.menu', matches_n: 2, match_level: 'exact' });
|
|
2477
2458
|
});
|
|
2478
2459
|
it('focus: delegates to page.focus and reports whether the element took focus', async () => {
|
|
2479
2460
|
browserState.page.focus.mockResolvedValueOnce({ focused: true, matches_n: 1, match_level: 'stable' });
|
|
2480
2461
|
const program = createProgram('', '');
|
|
2481
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'focus', '7']);
|
|
2462
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'focus', '7']);
|
|
2482
2463
|
expect(browserState.page.focus).toHaveBeenCalledWith('7', {});
|
|
2483
2464
|
expect(lastJsonLog()).toEqual({ focused: true, target: '7', matches_n: 1, match_level: 'stable' });
|
|
2484
2465
|
});
|
|
2485
2466
|
it('dblclick: delegates to page.dblClick and emits a structured envelope', async () => {
|
|
2486
2467
|
browserState.page.dblClick.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2487
2468
|
const program = createProgram('', '');
|
|
2488
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'dblclick', '#row']);
|
|
2469
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'dblclick', '#row']);
|
|
2489
2470
|
expect(browserState.page.dblClick).toHaveBeenCalledWith('#row', {});
|
|
2490
2471
|
expect(lastJsonLog()).toEqual({ dblclicked: true, target: '#row', matches_n: 1, match_level: 'exact' });
|
|
2491
2472
|
});
|
|
@@ -2498,7 +2479,7 @@ describe('browser click/type commands', () => {
|
|
|
2498
2479
|
kind: 'checkbox',
|
|
2499
2480
|
});
|
|
2500
2481
|
const program = createProgram('', '');
|
|
2501
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'check', '.todo', '--nth', '1']);
|
|
2482
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'check', '.todo', '--nth', '1']);
|
|
2502
2483
|
expect(browserState.page.setChecked).toHaveBeenCalledWith('.todo', true, { nth: 1 });
|
|
2503
2484
|
expect(lastJsonLog()).toEqual({ checked: true, changed: true, target: '.todo', matches_n: 2, match_level: 'exact', kind: 'checkbox' });
|
|
2504
2485
|
});
|
|
@@ -2511,7 +2492,7 @@ describe('browser click/type commands', () => {
|
|
|
2511
2492
|
kind: 'checkbox',
|
|
2512
2493
|
});
|
|
2513
2494
|
const program = createProgram('', '');
|
|
2514
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'uncheck', '#subscribe']);
|
|
2495
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'uncheck', '#subscribe']);
|
|
2515
2496
|
expect(browserState.page.setChecked).toHaveBeenCalledWith('#subscribe', false, {});
|
|
2516
2497
|
expect(lastJsonLog()).toEqual({ checked: false, changed: false, target: '#subscribe', matches_n: 1, match_level: 'stable', kind: 'checkbox' });
|
|
2517
2498
|
});
|
|
@@ -2529,7 +2510,7 @@ describe('browser click/type commands', () => {
|
|
|
2529
2510
|
multiple: false,
|
|
2530
2511
|
});
|
|
2531
2512
|
const program = createProgram('', '');
|
|
2532
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'upload', '#file', file]);
|
|
2513
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'upload', '#file', file]);
|
|
2533
2514
|
expect(browserState.page.uploadFiles).toHaveBeenCalledWith('#file', [file], {});
|
|
2534
2515
|
expect(lastJsonLog()).toEqual({
|
|
2535
2516
|
uploaded: true,
|
|
@@ -2543,7 +2524,7 @@ describe('browser click/type commands', () => {
|
|
|
2543
2524
|
});
|
|
2544
2525
|
it('upload: rejects missing files before touching the page', async () => {
|
|
2545
2526
|
const program = createProgram('', '');
|
|
2546
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'upload', '#file', '/tmp/opencli-missing-file']);
|
|
2527
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'upload', '#file', '/tmp/opencli-missing-file']);
|
|
2547
2528
|
expect(lastJsonLog().error.code).toBe('file_not_found');
|
|
2548
2529
|
expect(browserState.page.uploadFiles).not.toHaveBeenCalled();
|
|
2549
2530
|
expect(process.exitCode).toBeDefined();
|
|
@@ -2559,7 +2540,7 @@ describe('browser click/type commands', () => {
|
|
|
2559
2540
|
target_match_level: 'stable',
|
|
2560
2541
|
});
|
|
2561
2542
|
const program = createProgram('', '');
|
|
2562
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'drag', '.card', '.lane', '--from-nth', '2', '--to-nth', '1']);
|
|
2543
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'drag', '.card', '.lane', '--from-nth', '2', '--to-nth', '1']);
|
|
2563
2544
|
expect(browserState.page.drag).toHaveBeenCalledWith('.card', '.lane', { from: { nth: 2 }, to: { nth: 1 } });
|
|
2564
2545
|
expect(lastJsonLog()).toEqual({
|
|
2565
2546
|
dragged: true,
|
|
@@ -2573,7 +2554,7 @@ describe('browser click/type commands', () => {
|
|
|
2573
2554
|
});
|
|
2574
2555
|
it('drag: rejects malformed --from-nth before touching the page', async () => {
|
|
2575
2556
|
const program = createProgram('', '');
|
|
2576
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'drag', '.card', '.lane', '--from-nth', 'abc']);
|
|
2557
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'drag', '.card', '.lane', '--from-nth', 'abc']);
|
|
2577
2558
|
expect(lastJsonLog().error.code).toBe('usage_error');
|
|
2578
2559
|
expect(browserState.page.drag).not.toHaveBeenCalled();
|
|
2579
2560
|
expect(process.exitCode).toBeDefined();
|
|
@@ -2583,7 +2564,7 @@ describe('browser click/type commands', () => {
|
|
|
2583
2564
|
browserState.page.typeText.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2584
2565
|
browserState.page.evaluate.mockResolvedValueOnce(false); // isAutocomplete
|
|
2585
2566
|
const program = createProgram('', '');
|
|
2586
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'type', '#q', 'hello']);
|
|
2567
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'type', '#q', 'hello']);
|
|
2587
2568
|
expect(browserState.page.click).toHaveBeenCalledWith('#q', {});
|
|
2588
2569
|
expect(browserState.page.wait).toHaveBeenCalledWith(0.3);
|
|
2589
2570
|
expect(browserState.page.typeText).toHaveBeenCalledWith('#q', 'hello', {});
|
|
@@ -2596,7 +2577,7 @@ describe('browser click/type commands', () => {
|
|
|
2596
2577
|
browserState.page.typeText.mockResolvedValueOnce({ matches_n: 1, match_level: 'exact' });
|
|
2597
2578
|
browserState.page.evaluate.mockResolvedValueOnce(true);
|
|
2598
2579
|
const program = createProgram('', '');
|
|
2599
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'type', '#q', 'hi']);
|
|
2580
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'type', '#q', 'hi']);
|
|
2600
2581
|
const waitCalls = browserState.page.wait.mock.calls;
|
|
2601
2582
|
expect(waitCalls).toContainEqual([0.3]);
|
|
2602
2583
|
expect(waitCalls).toContainEqual([0.4]);
|
|
@@ -2608,7 +2589,7 @@ describe('browser click/type commands', () => {
|
|
|
2608
2589
|
browserState.page.typeText.mockResolvedValueOnce({ matches_n: 1, match_level: 'reidentified' });
|
|
2609
2590
|
browserState.page.evaluate.mockResolvedValueOnce(false);
|
|
2610
2591
|
const program = createProgram('', '');
|
|
2611
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'type', '9', 'hi']);
|
|
2592
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'type', '9', 'hi']);
|
|
2612
2593
|
// The typeText call is the authoritative match_level source for the `type` envelope.
|
|
2613
2594
|
expect(lastJsonLog().match_level).toBe('reidentified');
|
|
2614
2595
|
});
|
|
@@ -2616,7 +2597,7 @@ describe('browser click/type commands', () => {
|
|
|
2616
2597
|
browserState.page.click.mockResolvedValueOnce({ matches_n: 5, match_level: 'exact' });
|
|
2617
2598
|
browserState.page.typeText.mockResolvedValueOnce({ matches_n: 5, match_level: 'exact' });
|
|
2618
2599
|
const program = createProgram('', '');
|
|
2619
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'type', '.field', 'x', '--nth', '3']);
|
|
2600
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'type', '.field', 'x', '--nth', '3']);
|
|
2620
2601
|
expect(browserState.page.click).toHaveBeenCalledWith('.field', { nth: 3 });
|
|
2621
2602
|
expect(browserState.page.typeText).toHaveBeenCalledWith('.field', 'x', { nth: 3 });
|
|
2622
2603
|
});
|
|
@@ -2632,7 +2613,7 @@ describe('browser click/type commands', () => {
|
|
|
2632
2613
|
mode: 'textarea',
|
|
2633
2614
|
});
|
|
2634
2615
|
const program = createProgram('', '');
|
|
2635
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'line1\\n/ / raw']);
|
|
2616
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'fill', '#msg', 'line1\\n/ / raw']);
|
|
2636
2617
|
expect(browserState.page.fillText).toHaveBeenCalledWith('#msg', 'line1\\n/ / raw', {});
|
|
2637
2618
|
expect(lastJsonLog()).toEqual({
|
|
2638
2619
|
filled: true,
|
|
@@ -2658,7 +2639,7 @@ describe('browser click/type commands', () => {
|
|
|
2658
2639
|
match_level: 'exact',
|
|
2659
2640
|
});
|
|
2660
2641
|
const program = createProgram('', '');
|
|
2661
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'expected']);
|
|
2642
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'fill', '#msg', 'expected']);
|
|
2662
2643
|
expect(lastJsonLog()).toEqual({
|
|
2663
2644
|
filled: true,
|
|
2664
2645
|
verified: false,
|
|
@@ -2673,7 +2654,7 @@ describe('browser click/type commands', () => {
|
|
|
2673
2654
|
});
|
|
2674
2655
|
it('fill: forwards --nth to page.fillText', async () => {
|
|
2675
2656
|
const program = createProgram('', '');
|
|
2676
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'fill', '.field', 'x', '--nth', '2']);
|
|
2657
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'fill', '.field', 'x', '--nth', '2']);
|
|
2677
2658
|
expect(browserState.page.fillText).toHaveBeenCalledWith('.field', 'x', { nth: 2 });
|
|
2678
2659
|
});
|
|
2679
2660
|
});
|
|
@@ -2686,7 +2667,7 @@ describe('browser select command', () => {
|
|
|
2686
2667
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' });
|
|
2687
2668
|
evalMock.mockResolvedValueOnce({ selected: 'US' });
|
|
2688
2669
|
const program = createProgram('', '');
|
|
2689
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'select', '#country', 'US']);
|
|
2670
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'select', '#country', 'US']);
|
|
2690
2671
|
expect(lastJsonLog()).toEqual({ selected: 'US', target: '#country', matches_n: 1, match_level: 'exact' });
|
|
2691
2672
|
});
|
|
2692
2673
|
it('maps "Not a <select>" to a not_a_select error envelope', async () => {
|
|
@@ -2694,7 +2675,7 @@ describe('browser select command', () => {
|
|
|
2694
2675
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' });
|
|
2695
2676
|
evalMock.mockResolvedValueOnce({ error: 'Not a <select>' });
|
|
2696
2677
|
const program = createProgram('', '');
|
|
2697
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'select', '#not-select', 'US']);
|
|
2678
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'select', '#not-select', 'US']);
|
|
2698
2679
|
const err = lastJsonLog().error;
|
|
2699
2680
|
expect(err.code).toBe('not_a_select');
|
|
2700
2681
|
expect(err.matches_n).toBe(1);
|
|
@@ -2705,7 +2686,7 @@ describe('browser select command', () => {
|
|
|
2705
2686
|
evalMock.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' });
|
|
2706
2687
|
evalMock.mockResolvedValueOnce({ error: 'Option "XX" not found', available: ['US', 'CA'] });
|
|
2707
2688
|
const program = createProgram('', '');
|
|
2708
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'select', '#country', 'XX']);
|
|
2689
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'select', '#country', 'XX']);
|
|
2709
2690
|
const err = lastJsonLog().error;
|
|
2710
2691
|
expect(err.code).toBe('option_not_found');
|
|
2711
2692
|
expect(err.available).toEqual(['US', 'CA']);
|
|
@@ -2723,7 +2704,7 @@ describe('browser select command', () => {
|
|
|
2723
2704
|
.mockResolvedValueOnce({ ok: true, matches_n: 1, match_level: 'exact' })
|
|
2724
2705
|
.mockResolvedValueOnce({ selected: 'Uruguay' });
|
|
2725
2706
|
const program = createProgram('', '');
|
|
2726
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'select', '--label', 'Country', 'Uruguay']);
|
|
2707
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'select', '--label', 'Country', 'Uruguay']);
|
|
2727
2708
|
expect(lastJsonLog()).toEqual({ selected: 'Uruguay', target: '36', matches_n: 1, match_level: 'exact' });
|
|
2728
2709
|
});
|
|
2729
2710
|
it('surfaces selector_ambiguous from the resolver before calling selectResolvedJs', async () => {
|
|
@@ -2735,7 +2716,7 @@ describe('browser select command', () => {
|
|
|
2735
2716
|
matches_n: 2,
|
|
2736
2717
|
});
|
|
2737
2718
|
const program = createProgram('', '');
|
|
2738
|
-
await program.parseAsync(['node', 'opencli', 'browser', 'select', '.dropdown', 'US']);
|
|
2719
|
+
await program.parseAsync(['node', 'opencli', 'browser', '--session', 'test', 'select', '.dropdown', 'US']);
|
|
2739
2720
|
expect(lastJsonLog().error.code).toBe('selector_ambiguous');
|
|
2740
2721
|
// The select payload JS must not fire when resolution fails
|
|
2741
2722
|
expect(browserState.page.evaluate.mock.calls).toHaveLength(1);
|