ScuttleBot

scuttlebot / tests / e2e / node_modules / playwright / lib / mcp / test / testContext.js
Source Blame History 285 lines
f7eb47b… lmata 1 "use strict";
f7eb47b… lmata 2 var __create = Object.create;
f7eb47b… lmata 3 var __defProp = Object.defineProperty;
f7eb47b… lmata 4 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
f7eb47b… lmata 5 var __getOwnPropNames = Object.getOwnPropertyNames;
f7eb47b… lmata 6 var __getProtoOf = Object.getPrototypeOf;
f7eb47b… lmata 7 var __hasOwnProp = Object.prototype.hasOwnProperty;
f7eb47b… lmata 8 var __export = (target, all) => {
f7eb47b… lmata 9 for (var name in all)
f7eb47b… lmata 10 __defProp(target, name, { get: all[name], enumerable: true });
f7eb47b… lmata 11 };
f7eb47b… lmata 12 var __copyProps = (to, from, except, desc) => {
f7eb47b… lmata 13 if (from && typeof from === "object" || typeof from === "function") {
f7eb47b… lmata 14 for (let key of __getOwnPropNames(from))
f7eb47b… lmata 15 if (!__hasOwnProp.call(to, key) && key !== except)
f7eb47b… lmata 16 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
f7eb47b… lmata 17 }
f7eb47b… lmata 18 return to;
f7eb47b… lmata 19 };
f7eb47b… lmata 20 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
f7eb47b… lmata 21 // If the importer is in node compatibility mode or this is not an ESM
f7eb47b… lmata 22 // file that has been converted to a CommonJS file using a Babel-
f7eb47b… lmata 23 // compatible transform (i.e. "__esModule" has not been set), then set
f7eb47b… lmata 24 // "default" to the CommonJS "module.exports" for node compatibility.
f7eb47b… lmata 25 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
f7eb47b… lmata 26 mod
f7eb47b… lmata 27 ));
f7eb47b… lmata 28 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
f7eb47b… lmata 29 var testContext_exports = {};
f7eb47b… lmata 30 __export(testContext_exports, {
f7eb47b… lmata 31 GeneratorJournal: () => GeneratorJournal,
f7eb47b… lmata 32 TestContext: () => TestContext,
f7eb47b… lmata 33 createScreen: () => createScreen
f7eb47b… lmata 34 });
f7eb47b… lmata 35 module.exports = __toCommonJS(testContext_exports);
f7eb47b… lmata 36 var import_fs = __toESM(require("fs"));
f7eb47b… lmata 37 var import_os = __toESM(require("os"));
f7eb47b… lmata 38 var import_path = __toESM(require("path"));
f7eb47b… lmata 39 var import_utils = require("playwright-core/lib/utils");
f7eb47b… lmata 40 var import_base = require("../../reporters/base");
f7eb47b… lmata 41 var import_list = __toESM(require("../../reporters/list"));
f7eb47b… lmata 42 var import_streams = require("./streams");
f7eb47b… lmata 43 var import_util = require("../../util");
f7eb47b… lmata 44 var import_testRunner = require("../../runner/testRunner");
f7eb47b… lmata 45 var import_seed = require("./seed");
f7eb47b… lmata 46 var import_exports = require("../sdk/exports");
f7eb47b… lmata 47 var import_configLoader = require("../../common/configLoader");
f7eb47b… lmata 48 var import_response = require("../browser/response");
f7eb47b… lmata 49 var import_log = require("../log");
f7eb47b… lmata 50 class GeneratorJournal {
f7eb47b… lmata 51 constructor(rootPath, plan, seed) {
f7eb47b… lmata 52 this._rootPath = rootPath;
f7eb47b… lmata 53 this._plan = plan;
f7eb47b… lmata 54 this._seed = seed;
f7eb47b… lmata 55 this._steps = [];
f7eb47b… lmata 56 }
f7eb47b… lmata 57 logStep(title, code) {
f7eb47b… lmata 58 if (title)
f7eb47b… lmata 59 this._steps.push({ title, code });
f7eb47b… lmata 60 }
f7eb47b… lmata 61 journal() {
f7eb47b… lmata 62 const result = [];
f7eb47b… lmata 63 result.push(`# Plan`);
f7eb47b… lmata 64 result.push(this._plan);
f7eb47b… lmata 65 result.push(`# Seed file: ${(0, import_utils.toPosixPath)(import_path.default.relative(this._rootPath, this._seed.file))}`);
f7eb47b… lmata 66 result.push("```ts");
f7eb47b… lmata 67 result.push(this._seed.content);
f7eb47b… lmata 68 result.push("```");
f7eb47b… lmata 69 result.push(`# Steps`);
f7eb47b… lmata 70 result.push(this._steps.map((step) => `### ${step.title}
f7eb47b… lmata 71 \`\`\`ts
f7eb47b… lmata 72 ${step.code}
f7eb47b… lmata 73 \`\`\``).join("\n\n"));
f7eb47b… lmata 74 result.push(bestPracticesMarkdown);
f7eb47b… lmata 75 return result.join("\n\n");
f7eb47b… lmata 76 }
f7eb47b… lmata 77 }
f7eb47b… lmata 78 class TestContext {
f7eb47b… lmata 79 constructor(clientInfo, configPath, options) {
f7eb47b… lmata 80 this._clientInfo = clientInfo;
f7eb47b… lmata 81 const rootPath = (0, import_exports.firstRootPath)(clientInfo);
f7eb47b… lmata 82 this._configLocation = (0, import_configLoader.resolveConfigLocation)(configPath || rootPath);
f7eb47b… lmata 83 this.rootPath = rootPath || this._configLocation.configDir;
f7eb47b… lmata 84 if (options?.headless !== void 0)
f7eb47b… lmata 85 this.computedHeaded = !options.headless;
f7eb47b… lmata 86 else
f7eb47b… lmata 87 this.computedHeaded = !process.env.CI && !(import_os.default.platform() === "linux" && !process.env.DISPLAY);
f7eb47b… lmata 88 }
f7eb47b… lmata 89 existingTestRunner() {
f7eb47b… lmata 90 return this._testRunnerAndScreen?.testRunner;
f7eb47b… lmata 91 }
f7eb47b… lmata 92 async _cleanupTestRunner() {
f7eb47b… lmata 93 if (!this._testRunnerAndScreen)
f7eb47b… lmata 94 return;
f7eb47b… lmata 95 await this._testRunnerAndScreen.testRunner.stopTests();
f7eb47b… lmata 96 this._testRunnerAndScreen.claimStdio();
f7eb47b… lmata 97 try {
f7eb47b… lmata 98 await this._testRunnerAndScreen.testRunner.runGlobalTeardown();
f7eb47b… lmata 99 } finally {
f7eb47b… lmata 100 this._testRunnerAndScreen.releaseStdio();
f7eb47b… lmata 101 this._testRunnerAndScreen = void 0;
f7eb47b… lmata 102 }
f7eb47b… lmata 103 }
f7eb47b… lmata 104 async createTestRunner() {
f7eb47b… lmata 105 await this._cleanupTestRunner();
f7eb47b… lmata 106 const testRunner = new import_testRunner.TestRunner(this._configLocation, {});
f7eb47b… lmata 107 await testRunner.initialize({});
f7eb47b… lmata 108 const testPaused = new import_utils.ManualPromise();
f7eb47b… lmata 109 const testRunnerAndScreen = {
f7eb47b… lmata 110 ...createScreen(),
f7eb47b… lmata 111 testRunner,
f7eb47b… lmata 112 waitForTestPaused: () => testPaused
f7eb47b… lmata 113 };
f7eb47b… lmata 114 this._testRunnerAndScreen = testRunnerAndScreen;
f7eb47b… lmata 115 testRunner.on(import_testRunner.TestRunnerEvent.TestPaused, (params) => {
f7eb47b… lmata 116 testRunnerAndScreen.sendMessageToPausedTest = params.sendMessage;
f7eb47b… lmata 117 testPaused.resolve();
f7eb47b… lmata 118 });
f7eb47b… lmata 119 return testRunnerAndScreen;
f7eb47b… lmata 120 }
f7eb47b… lmata 121 async getOrCreateSeedFile(seedFile, projectName) {
f7eb47b… lmata 122 const configDir = this._configLocation.configDir;
f7eb47b… lmata 123 const { testRunner } = await this.createTestRunner();
f7eb47b… lmata 124 const config = await testRunner.loadConfig();
f7eb47b… lmata 125 const project = (0, import_seed.seedProject)(config, projectName);
f7eb47b… lmata 126 if (!seedFile) {
f7eb47b… lmata 127 seedFile = await (0, import_seed.ensureSeedFile)(project);
f7eb47b… lmata 128 } else {
f7eb47b… lmata 129 const candidateFiles = [];
f7eb47b… lmata 130 const testDir = project.project.testDir;
f7eb47b… lmata 131 candidateFiles.push(import_path.default.resolve(testDir, seedFile));
f7eb47b… lmata 132 candidateFiles.push(import_path.default.resolve(configDir, seedFile));
f7eb47b… lmata 133 candidateFiles.push(import_path.default.resolve(this.rootPath, seedFile));
f7eb47b… lmata 134 let resolvedSeedFile;
f7eb47b… lmata 135 for (const candidateFile of candidateFiles) {
f7eb47b… lmata 136 if (await (0, import_util.fileExistsAsync)(candidateFile)) {
f7eb47b… lmata 137 resolvedSeedFile = candidateFile;
f7eb47b… lmata 138 break;
f7eb47b… lmata 139 }
f7eb47b… lmata 140 }
f7eb47b… lmata 141 if (!resolvedSeedFile)
f7eb47b… lmata 142 throw new Error("seed test not found.");
f7eb47b… lmata 143 seedFile = resolvedSeedFile;
f7eb47b… lmata 144 }
f7eb47b… lmata 145 const seedFileContent = await import_fs.default.promises.readFile(seedFile, "utf8");
f7eb47b… lmata 146 return {
f7eb47b… lmata 147 file: seedFile,
f7eb47b… lmata 148 content: seedFileContent,
f7eb47b… lmata 149 projectName: project.project.name
f7eb47b… lmata 150 };
f7eb47b… lmata 151 }
f7eb47b… lmata 152 async runSeedTest(seedFile, projectName) {
f7eb47b… lmata 153 const result = await this.runTestsWithGlobalSetupAndPossiblePause({
f7eb47b… lmata 154 headed: this.computedHeaded,
f7eb47b… lmata 155 locations: ["/" + (0, import_utils.escapeRegExp)(seedFile) + "/"],
f7eb47b… lmata 156 projects: [projectName],
f7eb47b… lmata 157 timeout: 0,
f7eb47b… lmata 158 workers: 1,
f7eb47b… lmata 159 pauseAtEnd: true,
f7eb47b… lmata 160 disableConfigReporters: true,
f7eb47b… lmata 161 failOnLoadErrors: true
f7eb47b… lmata 162 });
f7eb47b… lmata 163 if (result.status === "passed")
f7eb47b… lmata 164 result.output += "\nError: seed test not found.";
f7eb47b… lmata 165 else if (result.status !== "paused")
f7eb47b… lmata 166 result.output += "\nError while running the seed test.";
f7eb47b… lmata 167 return result;
f7eb47b… lmata 168 }
f7eb47b… lmata 169 async runTestsWithGlobalSetupAndPossiblePause(params) {
f7eb47b… lmata 170 const configDir = this._configLocation.configDir;
f7eb47b… lmata 171 const testRunnerAndScreen = await this.createTestRunner();
f7eb47b… lmata 172 const { testRunner, screen, claimStdio, releaseStdio } = testRunnerAndScreen;
f7eb47b… lmata 173 claimStdio();
f7eb47b… lmata 174 try {
f7eb47b… lmata 175 const setupReporter = new MCPListReporter({ configDir, screen, includeTestId: true });
f7eb47b… lmata 176 const { status: status2 } = await testRunner.runGlobalSetup([setupReporter]);
f7eb47b… lmata 177 if (status2 !== "passed")
f7eb47b… lmata 178 return { output: testRunnerAndScreen.output.join("\n"), status: status2 };
f7eb47b… lmata 179 } finally {
f7eb47b… lmata 180 releaseStdio();
f7eb47b… lmata 181 }
f7eb47b… lmata 182 let status = "passed";
f7eb47b… lmata 183 const cleanup = async () => {
f7eb47b… lmata 184 claimStdio();
f7eb47b… lmata 185 try {
f7eb47b… lmata 186 const result = await testRunner.runGlobalTeardown();
f7eb47b… lmata 187 if (status === "passed")
f7eb47b… lmata 188 status = result.status;
f7eb47b… lmata 189 } finally {
f7eb47b… lmata 190 releaseStdio();
f7eb47b… lmata 191 }
f7eb47b… lmata 192 };
f7eb47b… lmata 193 try {
f7eb47b… lmata 194 const reporter = new MCPListReporter({ configDir, screen, includeTestId: true });
f7eb47b… lmata 195 status = await Promise.race([
f7eb47b… lmata 196 testRunner.runTests(reporter, params).then((result) => result.status),
f7eb47b… lmata 197 testRunnerAndScreen.waitForTestPaused().then(() => "paused")
f7eb47b… lmata 198 ]);
f7eb47b… lmata 199 if (status === "paused") {
f7eb47b… lmata 200 const response = await testRunnerAndScreen.sendMessageToPausedTest({ request: { initialize: { clientInfo: this._clientInfo } } });
f7eb47b… lmata 201 if (response.error)
f7eb47b… lmata 202 throw new Error(response.error.message);
f7eb47b… lmata 203 testRunnerAndScreen.output.push(response.response.initialize.pausedMessage);
f7eb47b… lmata 204 return { output: testRunnerAndScreen.output.join("\n"), status };
f7eb47b… lmata 205 }
f7eb47b… lmata 206 } catch (e) {
f7eb47b… lmata 207 status = "failed";
f7eb47b… lmata 208 testRunnerAndScreen.output.push(String(e));
f7eb47b… lmata 209 await cleanup();
f7eb47b… lmata 210 return { output: testRunnerAndScreen.output.join("\n"), status };
f7eb47b… lmata 211 }
f7eb47b… lmata 212 await cleanup();
f7eb47b… lmata 213 return { output: testRunnerAndScreen.output.join("\n"), status };
f7eb47b… lmata 214 }
f7eb47b… lmata 215 async close() {
f7eb47b… lmata 216 await this._cleanupTestRunner().catch(import_log.logUnhandledError);
f7eb47b… lmata 217 }
f7eb47b… lmata 218 async sendMessageToPausedTest(request) {
f7eb47b… lmata 219 const sendMessage = this._testRunnerAndScreen?.sendMessageToPausedTest;
f7eb47b… lmata 220 if (!sendMessage)
f7eb47b… lmata 221 throw new Error("Must setup test before interacting with the page");
f7eb47b… lmata 222 const result = await sendMessage({ request });
f7eb47b… lmata 223 if (result.error)
f7eb47b… lmata 224 throw new Error(result.error.message);
f7eb47b… lmata 225 if (typeof request?.callTool?.arguments?.["intent"] === "string") {
f7eb47b… lmata 226 const response = (0, import_response.parseResponse)(result.response.callTool);
f7eb47b… lmata 227 if (response && !response.isError && response.code)
f7eb47b… lmata 228 this.generatorJournal?.logStep(request.callTool.arguments["intent"], response.code);
f7eb47b… lmata 229 }
f7eb47b… lmata 230 return result.response;
f7eb47b… lmata 231 }
f7eb47b… lmata 232 }
f7eb47b… lmata 233 function createScreen() {
f7eb47b… lmata 234 const output = [];
f7eb47b… lmata 235 const stdout = new import_streams.StringWriteStream(output, "stdout");
f7eb47b… lmata 236 const stderr = new import_streams.StringWriteStream(output, "stderr");
f7eb47b… lmata 237 const screen = {
f7eb47b… lmata 238 ...import_base.terminalScreen,
f7eb47b… lmata 239 isTTY: false,
f7eb47b… lmata 240 colors: import_utils.noColors,
f7eb47b… lmata 241 stdout,
f7eb47b… lmata 242 stderr
f7eb47b… lmata 243 };
f7eb47b… lmata 244 const originalStdoutWrite = process.stdout.write;
f7eb47b… lmata 245 const originalStderrWrite = process.stderr.write;
f7eb47b… lmata 246 const claimStdio = () => {
f7eb47b… lmata 247 process.stdout.write = (chunk) => {
f7eb47b… lmata 248 stdout.write(chunk);
f7eb47b… lmata 249 return true;
f7eb47b… lmata 250 };
f7eb47b… lmata 251 process.stderr.write = (chunk) => {
f7eb47b… lmata 252 stderr.write(chunk);
f7eb47b… lmata 253 return true;
f7eb47b… lmata 254 };
f7eb47b… lmata 255 };
f7eb47b… lmata 256 const releaseStdio = () => {
f7eb47b… lmata 257 process.stdout.write = originalStdoutWrite;
f7eb47b… lmata 258 process.stderr.write = originalStderrWrite;
f7eb47b… lmata 259 };
f7eb47b… lmata 260 return { screen, claimStdio, releaseStdio, output };
f7eb47b… lmata 261 }
f7eb47b… lmata 262 const bestPracticesMarkdown = `
f7eb47b… lmata 263 # Best practices
f7eb47b… lmata 264 - Do not improvise, do not add directives that were not asked for
f7eb47b… lmata 265 - Use clear, descriptive assertions to validate the expected behavior
f7eb47b… lmata 266 - Use reliable locators from this log
f7eb47b… lmata 267 - Use local variables for locators that are used multiple times
f7eb47b… lmata 268 - Use Playwright waiting assertions and best practices from this log
f7eb47b… lmata 269 - NEVER! use page.waitForLoadState()
f7eb47b… lmata 270 - NEVER! use page.waitForNavigation()
f7eb47b… lmata 271 - NEVER! use page.waitForTimeout()
f7eb47b… lmata 272 - NEVER! use page.evaluate()
f7eb47b… lmata 273 `;
f7eb47b… lmata 274 class MCPListReporter extends import_list.default {
f7eb47b… lmata 275 async onTestPaused() {
f7eb47b… lmata 276 await new Promise(() => {
f7eb47b… lmata 277 });
f7eb47b… lmata 278 }
f7eb47b… lmata 279 }
f7eb47b… lmata 280 // Annotate the CommonJS export names for ESM import in node:
f7eb47b… lmata 281 0 && (module.exports = {
f7eb47b… lmata 282 GeneratorJournal,
f7eb47b… lmata 283 TestContext,
f7eb47b… lmata 284 createScreen
f7eb47b… lmata 285 });

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button