mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Add --video and --autostart (#271849)
This enables: * Playwright video recording * Starting the Code OSS on server start (for clients like Copilot CLI that don't react to tool list changes)
This commit is contained in:
committed by
GitHub
parent
e5243d2a17
commit
af1cbea727
@@ -24,6 +24,7 @@ export interface LaunchOptions {
|
||||
readonly logger: Logger;
|
||||
logsPath: string;
|
||||
crashesPath: string;
|
||||
readonly videosPath?: string;
|
||||
verbose?: boolean;
|
||||
useInMemorySecretStorage?: boolean;
|
||||
readonly extraArgs?: string[];
|
||||
|
||||
@@ -105,7 +105,13 @@ async function launchBrowser(options: LaunchOptions, endpoint: string) {
|
||||
|
||||
browser.on('disconnected', () => logger.log(`Playwright: browser disconnected`));
|
||||
|
||||
const context = await measureAndLog(() => browser.newContext(), 'browser.newContext', logger);
|
||||
const context = await measureAndLog(
|
||||
() => browser.newContext({
|
||||
recordVideo: options.videosPath ? { dir: options.videosPath } : undefined
|
||||
}),
|
||||
'browser.newContext',
|
||||
logger
|
||||
);
|
||||
|
||||
if (tracing) {
|
||||
try {
|
||||
|
||||
@@ -33,6 +33,7 @@ async function launchElectron(configuration: IElectronConfiguration, options: La
|
||||
const electron = await measureAndLog(() => playwrightImpl._electron.launch({
|
||||
executablePath: configuration.electronPath,
|
||||
args: configuration.args,
|
||||
recordVideo: options.videosPath ? { dir: options.videosPath } : undefined,
|
||||
env: configuration.env as { [key: string]: string },
|
||||
timeout: 0
|
||||
}), 'playwright-electron#launch', logger);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
SCRIPT_DIR="$(dirname -- "$( readlink -f -- "$0"; )")"
|
||||
# Go to mcp server project root
|
||||
cd "$SCRIPT_DIR/.."
|
||||
|
||||
# Start mcp
|
||||
npm run start-stdio -- --video --autostart
|
||||
@@ -10,71 +10,12 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as vscodetest from '@vscode/test-electron';
|
||||
import { createApp, retry } from './utils';
|
||||
import * as minimist from 'minimist';
|
||||
import { opts } from './options';
|
||||
|
||||
const rootPath = path.join(__dirname, '..', '..', '..');
|
||||
|
||||
const [, , ...args] = process.argv;
|
||||
const opts = minimist(args, {
|
||||
string: [
|
||||
'browser',
|
||||
'build',
|
||||
'stable-build',
|
||||
'wait-time',
|
||||
'test-repo',
|
||||
'electronArgs'
|
||||
],
|
||||
boolean: [
|
||||
'verbose',
|
||||
'remote',
|
||||
'web',
|
||||
'headless',
|
||||
'tracing'
|
||||
],
|
||||
default: {
|
||||
verbose: false
|
||||
}
|
||||
}) as {
|
||||
verbose?: boolean;
|
||||
remote?: boolean;
|
||||
headless?: boolean;
|
||||
web?: boolean;
|
||||
tracing?: boolean;
|
||||
build?: string;
|
||||
'stable-build'?: string;
|
||||
browser?: 'chromium' | 'webkit' | 'firefox' | 'chromium-msedge' | 'chromium-chrome' | undefined;
|
||||
electronArgs?: string;
|
||||
};
|
||||
|
||||
const logsRootPath = (() => {
|
||||
const logsParentPath = path.join(rootPath, '.build', 'logs');
|
||||
|
||||
let logsName: string;
|
||||
if (opts.web) {
|
||||
logsName = 'smoke-tests-browser';
|
||||
} else if (opts.remote) {
|
||||
logsName = 'smoke-tests-remote';
|
||||
} else {
|
||||
logsName = 'smoke-tests-electron';
|
||||
}
|
||||
|
||||
return path.join(logsParentPath, logsName);
|
||||
})();
|
||||
|
||||
const crashesRootPath = (() => {
|
||||
const crashesParentPath = path.join(rootPath, '.build', 'crashes');
|
||||
|
||||
let crashesName: string;
|
||||
if (opts.web) {
|
||||
crashesName = 'smoke-tests-browser';
|
||||
} else if (opts.remote) {
|
||||
crashesName = 'smoke-tests-remote';
|
||||
} else {
|
||||
crashesName = 'smoke-tests-electron';
|
||||
}
|
||||
|
||||
return path.join(crashesParentPath, crashesName);
|
||||
})();
|
||||
const logsRootPath = path.join(rootPath, '.build', 'vscode-playwright-mcp', 'logs');
|
||||
const crashesRootPath = path.join(rootPath, '.build', 'vscode-playwright-mcp', 'crashes');
|
||||
const videoRootPath = path.join(rootPath, '.build', 'vscode-playwright-mcp', 'videos');
|
||||
|
||||
const logger = createLogger();
|
||||
|
||||
@@ -291,7 +232,7 @@ async function setup(): Promise<void> {
|
||||
logger.log('Smoketest setup done!\n');
|
||||
}
|
||||
|
||||
export async function getApplication() {
|
||||
export async function getApplication({ recordVideo }: { recordVideo?: boolean } = {}) {
|
||||
const testCodePath = getDevElectronPath();
|
||||
const electronPath = testCodePath;
|
||||
if (!fs.existsSync(electronPath || '')) {
|
||||
@@ -315,6 +256,7 @@ export async function getApplication() {
|
||||
logger,
|
||||
logsPath: path.join(logsRootPath, 'suite_unknown'),
|
||||
crashesPath: path.join(crashesRootPath, 'suite_unknown'),
|
||||
videosPath: (recordVideo || opts.video) ? videoRootPath : undefined,
|
||||
verbose: opts.verbose,
|
||||
remote: opts.remote,
|
||||
web: opts.web,
|
||||
@@ -350,12 +292,12 @@ export class ApplicationService {
|
||||
return this._application;
|
||||
}
|
||||
|
||||
async getOrCreateApplication(): Promise<Application> {
|
||||
async getOrCreateApplication({ recordVideo }: { recordVideo?: boolean } = {}): Promise<Application> {
|
||||
if (this._closing) {
|
||||
await this._closing;
|
||||
}
|
||||
if (!this._application) {
|
||||
this._application = await getApplication();
|
||||
this._application = await getApplication({ recordVideo });
|
||||
this._application.code.driver.currentPage.on('close', () => {
|
||||
this._closing = (async () => {
|
||||
if (this._application) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { ApplicationService } from './application';
|
||||
import { applyAllTools } from './automationTools/index.js';
|
||||
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export async function getServer(appService: ApplicationService): Promise<Server> {
|
||||
const server = new McpServer({
|
||||
@@ -18,9 +19,11 @@ export async function getServer(appService: ApplicationService): Promise<Server>
|
||||
server.tool(
|
||||
'vscode_automation_start',
|
||||
'Start VS Code Build',
|
||||
{},
|
||||
async () => {
|
||||
const app = await appService.getOrCreateApplication();
|
||||
{
|
||||
recordVideo: z.boolean().optional()
|
||||
},
|
||||
async ({ recordVideo }) => {
|
||||
const app = await appService.getOrCreateApplication({ recordVideo });
|
||||
return {
|
||||
content: [{
|
||||
type: 'text' as const,
|
||||
|
||||
@@ -32,20 +32,20 @@ export function applyCoreTools(server: McpServer, appService: ApplicationService
|
||||
// }
|
||||
// );
|
||||
|
||||
// I don't think Playwright needs this
|
||||
// server.tool(
|
||||
// 'vscode_automation_stop',
|
||||
// 'Stop the VS Code application',
|
||||
// async () => {
|
||||
// await app.stop();
|
||||
// return {
|
||||
// content: [{
|
||||
// type: 'text' as const,
|
||||
// text: 'VS Code stopped successfully'
|
||||
// }]
|
||||
// };
|
||||
// }
|
||||
// );
|
||||
tools.push(server.tool(
|
||||
'vscode_automation_stop',
|
||||
'Stop the VS Code application',
|
||||
async () => {
|
||||
const app = await appService.getOrCreateApplication();
|
||||
await app.stop();
|
||||
return {
|
||||
content: [{
|
||||
type: 'text' as const,
|
||||
text: 'VS Code stopped successfully'
|
||||
}]
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
// This doesn't seem particularly useful
|
||||
// server.tool(
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ApplicationService } from './application';
|
||||
import { createInMemoryTransportPair } from './inMemoryTransport';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { Application } from '../../automation';
|
||||
import { opts } from './options';
|
||||
|
||||
interface SubServerConfig {
|
||||
subServer: Client;
|
||||
@@ -81,6 +82,9 @@ export async function getServer(): Promise<Server> {
|
||||
}
|
||||
});
|
||||
|
||||
if (opts.autostart) {
|
||||
await appService.getOrCreateApplication();
|
||||
}
|
||||
return multiplexServer.server;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as minimist from 'minimist';
|
||||
|
||||
const [, , ...args] = process.argv;
|
||||
export const opts = minimist(args, {
|
||||
string: [
|
||||
'browser',
|
||||
'build',
|
||||
'stable-build',
|
||||
'wait-time',
|
||||
'test-repo',
|
||||
'electronArgs'
|
||||
],
|
||||
boolean: [
|
||||
'verbose',
|
||||
'remote',
|
||||
'web',
|
||||
'headless',
|
||||
'tracing',
|
||||
'video',
|
||||
'autostart'
|
||||
],
|
||||
default: {
|
||||
verbose: false
|
||||
}
|
||||
}) as {
|
||||
verbose?: boolean;
|
||||
remote?: boolean;
|
||||
headless?: boolean;
|
||||
web?: boolean;
|
||||
tracing?: boolean;
|
||||
build?: string;
|
||||
'stable-build'?: string;
|
||||
browser?: 'chromium' | 'webkit' | 'firefox' | 'chromium-msedge' | 'chromium-chrome' | undefined;
|
||||
electronArgs?: string;
|
||||
video?: boolean;
|
||||
autostart?: boolean;
|
||||
};
|
||||
Reference in New Issue
Block a user