224 lines
8.8 KiB
TypeScript
224 lines
8.8 KiB
TypeScript
|
|
/**
|
||
|
|
* Copyright (c) Microsoft Corporation.
|
||
|
|
*
|
||
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
* you may not use this file except in compliance with the License.
|
||
|
|
* You may obtain a copy of the License at
|
||
|
|
*
|
||
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
*
|
||
|
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
* See the License for the specific language governing permissions and
|
||
|
|
* limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { test, expect } from './cli-fixtures';
|
||
|
|
|
||
|
|
const SNAPSHOT_FILE = expect.stringMatching(/^\.playwright-cli[\\/]page-[\dTZ:.-]+\.yml$/);
|
||
|
|
|
||
|
|
test('output is pretty-printed', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', 'list');
|
||
|
|
expect(output).toBe(`{
|
||
|
|
"browsers": []
|
||
|
|
}`);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('version command returns JSON', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', '--version');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed).toEqual({ version: expect.any(String) });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('unknown command returns JSON error with exit 1', async ({ cli }) => {
|
||
|
|
const { output, exitCode } = await cli('--json', 'nope');
|
||
|
|
expect(JSON.parse(output)).toEqual({ isError: true, error: 'Unknown command: nope' });
|
||
|
|
expect(exitCode).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('unknown flag returns JSON error with exit 1', async ({ cli }) => {
|
||
|
|
const { output, exitCode } = await cli('--json', 'list', '--bogus');
|
||
|
|
expect(JSON.parse(output)).toEqual({ isError: true, error: 'Unknown option: --bogus' });
|
||
|
|
expect(exitCode).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('running tool command without open browser returns JSON error', async ({ cli }) => {
|
||
|
|
const { output, exitCode } = await cli('--json', 'goto', 'about:blank');
|
||
|
|
expect(JSON.parse(output)).toEqual({
|
||
|
|
isError: true,
|
||
|
|
error: `The browser 'default' is not open, please run open first`,
|
||
|
|
});
|
||
|
|
expect(exitCode).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('list returns empty browsers before open', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', 'list');
|
||
|
|
expect(JSON.parse(output)).toEqual({ browsers: [] });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('close returns not-open status when nothing is running', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', 'close');
|
||
|
|
expect(JSON.parse(output)).toEqual({ session: 'default', status: 'not-open' });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('delete-data returns not-deleted when there is no session', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', 'delete-data');
|
||
|
|
expect(JSON.parse(output)).toEqual({ session: 'default', deleted: false });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('kill-all returns zero killed when filter matches nothing', async ({ cli }) => {
|
||
|
|
const { output } = await cli('--json', 'kill-all', { env: { PWTEST_KILL_ALL_PID_FILTER_FOR_TEST: '0' } });
|
||
|
|
expect(JSON.parse(output)).toEqual({ killed: 0, pids: [] });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('open returns envelope with session, pid and snapshot file', async ({ cli, server }) => {
|
||
|
|
const { output } = await cli('--json', 'open', server.HELLO_WORLD);
|
||
|
|
expect(JSON.parse(output)).toEqual({
|
||
|
|
session: 'default',
|
||
|
|
pid: expect.any(Number),
|
||
|
|
result: { snapshot: { file: SNAPSHOT_FILE } },
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('list after open returns one browser entry', async ({ cli, server, mcpBrowserNormalized }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'list');
|
||
|
|
expect(JSON.parse(output)).toEqual({
|
||
|
|
browsers: [{
|
||
|
|
name: 'default',
|
||
|
|
workspace: expect.any(String),
|
||
|
|
status: 'open',
|
||
|
|
browserType: mcpBrowserNormalized,
|
||
|
|
userDataDir: null,
|
||
|
|
headed: false,
|
||
|
|
persistent: false,
|
||
|
|
attached: false,
|
||
|
|
compatible: true,
|
||
|
|
version: expect.any(String),
|
||
|
|
}],
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test('goto returns snapshot file envelope', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'goto', server.HELLO_WORLD);
|
||
|
|
expect(JSON.parse(output)).toEqual({ snapshot: { file: SNAPSHOT_FILE } });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('eval returns result string', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'eval', '() => 1 + 2');
|
||
|
|
expect(JSON.parse(output)).toEqual({ result: '3' });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('eval with error surfaces isError JSON', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'eval', '() => { throw new Error("boom"); }');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.isError).toBe(true);
|
||
|
|
expect(parsed.error).toContain('boom');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tab-new creates a new tab and returns tab list', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'tab-new');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.result.split('\n')).toEqual([
|
||
|
|
`- 0: [Title](${server.HELLO_WORLD})`,
|
||
|
|
'- 1: (current) [](about:blank)',
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tab-list lists all tabs', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
await cli('tab-new');
|
||
|
|
const { output } = await cli('--json', 'tab-list');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.result.split('\n')).toEqual([
|
||
|
|
`- 0: [Title](${server.HELLO_WORLD})`,
|
||
|
|
'- 1: (current) [](about:blank)',
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tab-close closes a tab and returns remaining tabs', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
await cli('tab-new');
|
||
|
|
const { output } = await cli('--json', 'tab-close');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.result.split('\n')).toEqual([
|
||
|
|
`- 0: (current) [Title](${server.HELLO_WORLD})`,
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('snapshot returns inline snapshot yaml', async ({ cli, server }) => {
|
||
|
|
server.setContent('/', '<h1>Hi</h1>', 'text/html');
|
||
|
|
await cli('open', server.PREFIX);
|
||
|
|
const { output } = await cli('--json', 'snapshot');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(typeof parsed.snapshot).toBe('string');
|
||
|
|
expect(parsed.snapshot).toContain('heading "Hi"');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tool error on bad navigation returns JSON error', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'goto', 'https://not-a-real-domain.example.invalid');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.isError).toBe(true);
|
||
|
|
expect(typeof parsed.error).toBe('string');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('close after open returns closed status', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'close');
|
||
|
|
expect(JSON.parse(output)).toEqual({ session: 'default', status: 'closed' });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('close-all after open returns closed sessions', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.HELLO_WORLD);
|
||
|
|
const { output } = await cli('--json', 'close-all');
|
||
|
|
expect(JSON.parse(output)).toEqual({ closed: ['default'] });
|
||
|
|
});
|
||
|
|
|
||
|
|
test('requests returns numbered list as JSON result', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.PREFIX);
|
||
|
|
await cli('eval', '() => fetch("/hello-world")');
|
||
|
|
const { output } = await cli('--json', 'requests');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(typeof parsed.result).toBe('string');
|
||
|
|
expect(parsed.result).toMatch(new RegExp(String.raw`^\d+\. \[GET\] [^ ]+/hello-world => \[200\] OK$`, 'm'));
|
||
|
|
});
|
||
|
|
|
||
|
|
test('request and per-part commands return JSON result', async ({ cli, server }) => {
|
||
|
|
server.setContent('/', `
|
||
|
|
<button onclick="fetch('/api', { method: 'POST', headers: { 'X-Custom-Header': 'test-value' }, body: JSON.stringify({ key: 'value' }) })">Click me</button>
|
||
|
|
`, 'text/html');
|
||
|
|
server.setRoute('/api', (_req, res) => {
|
||
|
|
res.setHeader('X-Custom-Response', 'response-value');
|
||
|
|
res.setHeader('Content-Type', 'application/json');
|
||
|
|
res.end(JSON.stringify({ name: 'John Doe' }));
|
||
|
|
});
|
||
|
|
await cli('open', server.PREFIX);
|
||
|
|
await cli('click', 'e2');
|
||
|
|
|
||
|
|
const { output: list } = await cli('requests');
|
||
|
|
const num = list.match(/^(\d+)\. \[POST\] [^ ]+\/api =>/m)![1];
|
||
|
|
|
||
|
|
expect(JSON.parse((await cli('--json', 'request-headers', num)).output).result).toContain('x-custom-header: test-value');
|
||
|
|
expect(JSON.parse((await cli('--json', 'request-body', num)).output)).toEqual({ result: '{"key":"value"}' });
|
||
|
|
expect(JSON.parse((await cli('--json', 'response-headers', num)).output).result).toContain('x-custom-response: response-value');
|
||
|
|
expect(JSON.parse((await cli('--json', 'response-body', num)).output)).toEqual({ result: '{"name":"John Doe"}' });
|
||
|
|
|
||
|
|
const detail = JSON.parse((await cli('--json', 'request', num)).output);
|
||
|
|
expect(detail.result).toContain(`#${num} [POST] ${server.PREFIX}/api`);
|
||
|
|
expect(detail.result).toContain(`Run \`response-body ${num}\` to read the response body.`);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('request with out-of-range index returns JSON error', async ({ cli, server }) => {
|
||
|
|
await cli('open', server.PREFIX);
|
||
|
|
const { output } = await cli('--json', 'request', '999');
|
||
|
|
const parsed = JSON.parse(output);
|
||
|
|
expect(parsed.isError).toBe(true);
|
||
|
|
expect(parsed.error).toContain('Request #999 not found');
|
||
|
|
});
|