Files
AI/참고/playwright-main/tests/mcp/cli-session.spec.ts
2026-05-12 19:40:31 +09:00

353 lines
15 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 fs from 'fs';
import path from 'path';
import { test, expect, daemonFolder } from './cli-fixtures';
import { killProcessGroup } from '../config/commonFixtures';
import playwright from '../../packages/playwright-core';
test('list', async ({ cli, server }) => {
const { output: emptyOutput } = await cli('list');
expect(emptyOutput).toContain('(no browsers)');
await cli('open', server.HELLO_WORLD);
const { output: listOutput } = await cli('list');
expect(listOutput).toContain('### Browsers');
expect(listOutput).toContain('- default:');
});
test('close', async ({ cli, server }) => {
await cli('open', server.HELLO_WORLD);
const { output } = await cli('close');
expect(output).toContain(`Browser 'default' closed`);
const { output: listOutput } = await cli('list');
expect(listOutput).toContain('(no browsers)');
});
test('close named session', async ({ cli, server }) => {
await cli('-s', 'mysession', 'open', server.HELLO_WORLD);
const { output } = await cli('-s', 'mysession', 'close');
expect(output).toContain(`Browser 'mysession' closed`);
});
test('close non-running session', async ({ cli }) => {
const { output } = await cli('-s', 'nonexistent', 'close');
expect(output).toContain(`Browser 'nonexistent' is not open.`);
});
test('close-all', async ({ cli, server }) => {
await cli('-s', 'session1', 'open', server.HELLO_WORLD);
await cli('-s', 'session2', 'open', server.HELLO_WORLD);
const { output: listBefore } = await cli('list');
expect(listBefore).toContain('session1');
expect(listBefore).toContain('session2');
await cli('close-all');
const { output: listAfter } = await cli('list');
expect(listAfter).not.toContain('session1');
});
test('delete-data', async ({ cli, server, mcpBrowserNormalized }) => {
await cli('open', server.HELLO_WORLD, '--persistent');
const dataDir = path.resolve(await daemonFolder(), 'ud-default-' + mcpBrowserNormalized);
expect(fs.existsSync(dataDir)).toBe(true);
const { output } = await cli('delete-data');
expect(output).toContain(`Deleted user data for browser 'default'.`);
expect(fs.existsSync(dataDir)).toBe(false);
});
test('delete-data named session', async ({ cli, server, mcpBrowserNormalized }) => {
await cli('-s', 'mysession', 'open', server.HELLO_WORLD, '--persistent');
const dataDir = path.resolve(await daemonFolder(), 'ud-mysession-' + mcpBrowserNormalized);
expect(fs.existsSync(dataDir)).toBe(true);
const { output } = await cli('-s', 'mysession', 'delete-data');
expect(output).toContain(`Deleted user data for browser 'mysession'.`);
expect(fs.existsSync(dataDir)).toBe(false);
});
test('delete-data non-existent session', async ({ cli }) => {
const { output } = await cli('-s', 'nonexistent', 'delete-data');
expect(output).toContain(`No user data found for browser 'nonexistent'.`);
});
test('session stops when browser exits', async ({ cli, server }) => {
await cli('open', server.HELLO_WORLD);
const { output: listBefore } = await cli('list');
expect(listBefore).toContain('default');
// Close the browser - this will cause the daemon to exit so the command may fail
await cli('run-code', '() => page.context().browser().close()').catch(() => {});
await cli('close');
const { output: listAfter } = await cli('list');
expect(listAfter).toContain('(no browsers)');
});
test('session reopen with different config', async ({ cli, server }, testInfo) => {
const config = { browser: { contextOptions: { viewport: { width: 700, height: 500 } } } };
const configPath = testInfo.outputPath('config.json');
await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2));
{
await cli('open', server.HELLO_WORLD, '--config=' + configPath);
const { output } = await cli('eval', 'window.innerWidth + "x" + window.innerHeight');
expect(output).toContain('700x500');
}
{
// Reopen without config should use default viewport
await cli('open', server.HELLO_WORLD);
const { output } = await cli('eval', 'window.innerWidth + "x" + window.innerHeight');
expect(output).toContain('1280x720');
}
});
test('workspace isolation - sessions in different workspaces are isolated', async ({ cli, server }, testInfo) => {
// Create two separate workspaces with their own daemon dirs
const workspace1 = testInfo.outputPath('workspace1');
const workspace2 = testInfo.outputPath('workspace2');
await fs.promises.mkdir(workspace1, { recursive: true });
await fs.promises.mkdir(workspace2, { recursive: true });
await cli('install', { cwd: workspace1 });
await cli('install', { cwd: workspace2 });
expect(fs.existsSync(path.join(workspace1, '.playwright'))).toBe(true);
expect(fs.existsSync(path.join(workspace2, '.playwright'))).toBe(true);
// Open sessions in both workspaces
await cli('open', server.HELLO_WORLD, { cwd: workspace1 });
await cli('open', server.HELLO_WORLD, { cwd: workspace2 });
const { output: list1 } = await cli('list', { cwd: workspace1 });
expect(list1).toContain('default');
const { output: list2 } = await cli('list', { cwd: workspace2 });
expect(list2).toContain(' default');
await cli('close', { cwd: workspace1 });
const { output: list1After } = await cli('list', { cwd: workspace1 });
expect(list1After).not.toContain('### Browsers');
const { output: list2After } = await cli('list', { cwd: workspace2 });
expect(list2After).toContain('default');
await cli('close', { cwd: workspace2 });
});
test('list --all lists sessions from all workspaces', async ({ cli, server }, testInfo) => {
// Create two separate workspaces with their own daemon dirs
const workspace1 = testInfo.outputPath('workspace1');
const workspace2 = testInfo.outputPath('workspace2');
const workspace3 = testInfo.outputPath('workspace3');
await fs.promises.mkdir(workspace1, { recursive: true });
await fs.promises.mkdir(workspace2, { recursive: true });
await fs.promises.mkdir(workspace3, { recursive: true });
await cli('install', { cwd: workspace1 });
await cli('install', { cwd: workspace2 });
await cli('install', { cwd: workspace3 });
await cli('-s', 'session1', 'open', server.HELLO_WORLD, { cwd: workspace1 });
await cli('-s', 'session2', 'open', server.HELLO_WORLD, { cwd: workspace2 });
const session3 = await cli('-s', 'session3', 'open', server.HELLO_WORLD, { cwd: workspace3 });
// List all sessions from workspace1
const { output: allList } = await cli('list', '--all', { cwd: workspace1 });
// Should include both workspace folders and sessions
expect(allList).toContain('/:');
expect(allList).toContain('session1');
expect(allList).toContain('..' + path.sep + 'workspace2:');
expect(allList).toContain('session2');
expect(allList).toContain('..' + path.sep + 'workspace3:');
expect(allList).toContain('session3');
const rootDir = test.info().outputPath('daemon');
const dirs = await fs.promises.readdir(rootDir);
const getSessionFiles = async () => (await Promise.all(dirs.map(dir => fs.promises.readdir(path.join(rootDir, dir))))).flat();
const sessionFilesBefore = await getSessionFiles();
expect(sessionFilesBefore).toContain('session1.session');
expect(sessionFilesBefore).toContain('session2.session');
expect(sessionFilesBefore).toContain('session3.session');
await cli('-s', 'session1', 'close', { cwd: workspace1 });
const { output: listTwo } = await cli('list', '--all', { cwd: workspace2 });
expect(listTwo).not.toContain('workspace1');
expect(listTwo).not.toContain('session1');
expect(listTwo).toContain('/:');
expect(listTwo).toContain('session2');
expect(listTwo).toContain('workspace3');
expect(listTwo).toContain('session3');
const sessionFilesAfterClose = await getSessionFiles();
expect(sessionFilesAfterClose).not.toContain('session1.session');
expect(sessionFilesAfterClose).toContain('session2.session');
expect(sessionFilesAfterClose).toContain('session3.session');
killProcessGroup(session3.daemonPid);
const { output: listOne } = await cli('list', '--all', { cwd: workspace2 });
expect(listOne).not.toContain('workspace1');
expect(listOne).not.toContain('session1');
expect(listOne).toContain('/:');
expect(listOne).toContain('session2');
expect(listOne).not.toContain('workspace3');
expect(listOne).not.toContain('session3');
const sessionFilesAfterList = await getSessionFiles();
expect(sessionFilesAfterList).not.toContain('session1.session');
expect(sessionFilesAfterList).toContain('session2.session');
expect(sessionFilesAfterList).not.toContain('session3.session');
});
test('newer client with older daemon is compatible', async ({ cli, server }) => {
await cli('open', server.HELLO_WORLD);
const { exitCode } = await cli('eval', '1+1', { env: { PLAYWRIGHT_CLI_VERSION_FOR_TEST: '9.9.9' } });
expect(exitCode).toBe(0);
});
test('older client with newer daemon fails with version mismatch error', async ({ cli, server }) => {
await cli('open', server.HELLO_WORLD);
const { output, error, exitCode } = await cli('eval', '1+1', { env: { PLAYWRIGHT_CLI_VERSION_FOR_TEST: '0.0.1' } });
expect(exitCode).not.toBe(0);
const fullOutput = output + error;
expect(fullOutput).toContain('Client is v0.0.1');
expect(fullOutput).toContain('playwright-cli open');
expect(fullOutput).toContain('to restart the browser session');
});
test('older client with newer daemon - named session includes session name in error', async ({ cli, server }) => {
await cli('-s', 'mysession', 'open', server.HELLO_WORLD);
const { output, error, exitCode } = await cli('-s', 'mysession', 'eval', '1+1', { env: { PLAYWRIGHT_CLI_VERSION_FOR_TEST: '0.0.1' } });
expect(exitCode).not.toBe(0);
const fullOutput = output + error;
expect(fullOutput).toContain('Client is v0.0.1');
expect(fullOutput).toContain(`session 'mysession'`);
expect(fullOutput).toContain('playwright-cli -s=mysession open');
});
test('older client with newer daemon - list shows incompatible warning', async ({ cli, server }) => {
await cli('open', server.HELLO_WORLD);
const { output } = await cli('list', { env: { PLAYWRIGHT_CLI_VERSION_FOR_TEST: '0.0.1' } });
expect(output).toContain('### Browsers');
expect(output).toContain('- default:');
expect(output).toContain('[incompatible please re-open]');
});
test.describe('browser server', () => {
test.beforeEach(async ({ mcpBrowser }, testInfo) => {
test.skip(!['chrome', 'chromium', 'webkit', 'firefox'].includes(mcpBrowser));
process.env.PLAYWRIGHT_SERVER_REGISTRY = testInfo.outputPath('registry');
});
test('list browser servers', async ({ cli, mcpBrowser }) => {
const browserName = mcpBrowser.replace('chrome', 'chromium');
await using browser = await playwright[browserName].launch({ headless: true });
await browser.bind('foobar', { workspaceDir: 'workspace1' });
const { output } = await cli('list', '--all');
expect(output).toBe(`### Browser servers available for attach
workspace1:
- browser "foobar":
- browser: ${/* FIX browser._options */ mcpBrowser.replace('chrome', 'chromium')}
- version: ${version}
- data-dir: <in-memory>
- run \`playwright-cli attach "foobar"\` to attach`);
});
test('attach to browser server', async ({ cli, mcpBrowser }) => {
const browserName = mcpBrowser.replace('chrome', 'chromium');
await using browser = await playwright[browserName].launch({ headless: true });
await browser.bind('foobar', { workspaceDir: 'workspace1' });
const page = await browser.newPage();
await page.setContent('<title>My Page</title>');
const { output: openOutput } = await cli('attach', 'foobar');
expect(openOutput).toContain('### Session `foobar` created, attached to `foobar`.');
expect(openOutput).toContain('Run commands with: playwright-cli --s=foobar <command>');
const { output: listOutput } = await cli('list', '--all');
expect(listOutput).toBe(`### Browsers
/:
- foobar:
- status: open
- browser-type: ${mcpBrowser.replace('chrome', 'chromium')} (attached)
### Browser servers available for attach
workspace1:
- browser "foobar":
- browser: ${mcpBrowser.replace('chrome', 'chromium')}
- version: ${version}
- data-dir: <in-memory>
- run \`playwright-cli attach "foobar"\` to attach`);
});
test('fail to attach to browser server without contexts', async ({ cli, mcpBrowser }) => {
const browserName = mcpBrowser.replace('chrome', 'chromium');
await using browser = await playwright[browserName].launch({ headless: true });
await browser.bind('foobar', { workspaceDir: 'workspace1' });
const { error } = await cli('attach', 'foobar');
expect(error).toContain('Error: unable to connect to a browser that does not have any contexts');
});
test('attach with session alias', async ({ cli, mcpBrowser }) => {
const browserName = mcpBrowser.replace('chrome', 'chromium');
await using browser = await playwright[browserName].launch({ headless: true });
await browser.bind('foobar', { workspaceDir: 'workspace1' });
const page = await browser.newPage();
await page.setContent('<title>Alias Page</title>');
const { output: openOutput } = await cli('attach', 'foobar', '--session=mybrowser');
expect(openOutput).toContain('### Session `mybrowser` created, attached to `foobar`.');
expect(openOutput).toContain('Run commands with: playwright-cli --s=mybrowser <command>');
await cli('-s', 'mybrowser', 'close');
});
test('detach from browser server', async ({ cli, mcpBrowser }) => {
const browserName = mcpBrowser.replace('chrome', 'chromium');
await using browser = await playwright[browserName].launch({ headless: true });
await browser.newPage();
await browser.bind('foobar', { workspaceDir: 'workspace1' });
const { output: openOutput } = await cli('attach', 'foobar');
expect(openOutput).toContain('Session `foobar` created, attached to `foobar`');
await cli('-s', 'foobar', 'close');
const { output: listOutput } = await cli('list', '--all');
expect(listOutput).toBe(`### Browser servers available for attach
workspace1:
- browser \"foobar\":
- browser: ${/* FIX browser._options */ mcpBrowser.replace('chrome', 'chromium')}
- version: ${version}
- data-dir: <in-memory>
- run \`playwright-cli attach \"foobar\"\` to attach`);
});
});
const version = 'v' + require('../../packages/playwright-core/package.json').version;