Implement comprehensive frontend integration testing with Playwright
- Add Playwright E2E testing framework with cross-browser support (Chrome, Firefox) - Create authentication helpers for CalDAV server integration - Implement calendar interaction helpers with event creation, drag-and-drop, and view switching - Add comprehensive drag-and-drop test suite with event cleanup - Configure CI/CD integration with Gitea Actions for headless testing - Support both local development and CI environments with proper dependency management - Include video recording, screenshots, and HTML reporting for test debugging - Handle Firefox-specific timing and interaction challenges with force clicks and timeouts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										402
									
								
								frontend/e2e/node_modules/playwright/lib/runner/testRunner.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								frontend/e2e/node_modules/playwright/lib/runner/testRunner.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,402 @@ | ||||
| "use strict"; | ||||
| var __create = Object.create; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __getProtoOf = Object.getPrototypeOf; | ||||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||||
| var __export = (target, all) => { | ||||
|   for (var name in all) | ||||
|     __defProp(target, name, { get: all[name], enumerable: true }); | ||||
| }; | ||||
| var __copyProps = (to, from, except, desc) => { | ||||
|   if (from && typeof from === "object" || typeof from === "function") { | ||||
|     for (let key of __getOwnPropNames(from)) | ||||
|       if (!__hasOwnProp.call(to, key) && key !== except) | ||||
|         __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||||
|   } | ||||
|   return to; | ||||
| }; | ||||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||||
|   // If the importer is in node compatibility mode or this is not an ESM | ||||
|   // file that has been converted to a CommonJS file using a Babel- | ||||
|   // compatible transform (i.e. "__esModule" has not been set), then set | ||||
|   // "default" to the CommonJS "module.exports" for node compatibility. | ||||
|   isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||||
|   mod | ||||
| )); | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var testRunner_exports = {}; | ||||
| __export(testRunner_exports, { | ||||
|   TestRunner: () => TestRunner, | ||||
|   TestRunnerEvent: () => TestRunnerEvent, | ||||
|   runAllTestsWithConfig: () => runAllTestsWithConfig | ||||
| }); | ||||
| module.exports = __toCommonJS(testRunner_exports); | ||||
| var import_events = __toESM(require("events")); | ||||
| var import_fs = __toESM(require("fs")); | ||||
| var import_path = __toESM(require("path")); | ||||
| var import_server = require("playwright-core/lib/server"); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_configLoader = require("../common/configLoader"); | ||||
| var import_fsWatcher = require("../fsWatcher"); | ||||
| var import_teleReceiver = require("../isomorphic/teleReceiver"); | ||||
| var import_gitCommitInfoPlugin = require("../plugins/gitCommitInfoPlugin"); | ||||
| var import_webServerPlugin = require("../plugins/webServerPlugin"); | ||||
| var import_base = require("../reporters/base"); | ||||
| var import_internalReporter = require("../reporters/internalReporter"); | ||||
| var import_compilationCache = require("../transform/compilationCache"); | ||||
| var import_util = require("../util"); | ||||
| var import_reporters = require("./reporters"); | ||||
| var import_tasks = require("./tasks"); | ||||
| var import_lastRun = require("./lastRun"); | ||||
| const TestRunnerEvent = { | ||||
|   TestFilesChanged: "testFilesChanged", | ||||
|   RecoverFromStepError: "recoverFromStepError" | ||||
| }; | ||||
| class TestRunner extends import_events.default { | ||||
|   constructor(configLocation, configCLIOverrides) { | ||||
|     super(); | ||||
|     this._watchedProjectDirs = /* @__PURE__ */ new Set(); | ||||
|     this._ignoredProjectOutputs = /* @__PURE__ */ new Set(); | ||||
|     this._watchedTestDependencies = /* @__PURE__ */ new Set(); | ||||
|     this._queue = Promise.resolve(); | ||||
|     this._watchTestDirs = false; | ||||
|     this._populateDependenciesOnList = false; | ||||
|     this._recoverFromStepErrors = false; | ||||
|     this._resumeAfterStepErrors = /* @__PURE__ */ new Map(); | ||||
|     this.configLocation = configLocation; | ||||
|     this._configCLIOverrides = configCLIOverrides; | ||||
|     this._watcher = new import_fsWatcher.Watcher((events) => { | ||||
|       const collector = /* @__PURE__ */ new Set(); | ||||
|       events.forEach((f) => (0, import_compilationCache.collectAffectedTestFiles)(f.file, collector)); | ||||
|       this.emit(TestRunnerEvent.TestFilesChanged, [...collector]); | ||||
|     }); | ||||
|   } | ||||
|   async initialize(params) { | ||||
|     this._watchTestDirs = !!params.watchTestDirs; | ||||
|     this._populateDependenciesOnList = !!params.populateDependenciesOnList; | ||||
|     this._recoverFromStepErrors = !!params.recoverFromStepErrors; | ||||
|   } | ||||
|   resizeTerminal(params) { | ||||
|     process.stdout.columns = params.cols; | ||||
|     process.stdout.rows = params.rows; | ||||
|     process.stderr.columns = params.cols; | ||||
|     process.stderr.rows = params.rows; | ||||
|   } | ||||
|   hasSomeBrowsers() { | ||||
|     for (const browserName of ["chromium", "webkit", "firefox"]) { | ||||
|       try { | ||||
|         import_server.registry.findExecutable(browserName).executablePathOrDie("javascript"); | ||||
|         return true; | ||||
|       } catch { | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|   async installBrowsers() { | ||||
|     const executables = import_server.registry.defaultExecutables(); | ||||
|     await import_server.registry.install(executables, false); | ||||
|   } | ||||
|   async runGlobalSetup(userReporters) { | ||||
|     await this.runGlobalTeardown(); | ||||
|     const reporter = new import_internalReporter.InternalReporter(userReporters); | ||||
|     const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [ | ||||
|       ...(0, import_tasks.createGlobalSetupTasks)(config) | ||||
|     ]); | ||||
|     if (status !== "passed") | ||||
|       await cleanup(); | ||||
|     else | ||||
|       this._globalSetup = { cleanup }; | ||||
|     return { status }; | ||||
|   } | ||||
|   async runGlobalTeardown() { | ||||
|     const globalSetup = this._globalSetup; | ||||
|     const status = await globalSetup?.cleanup(); | ||||
|     this._globalSetup = void 0; | ||||
|     return { status }; | ||||
|   } | ||||
|   async startDevServer(userReporter, mode) { | ||||
|     await this.stopDevServer(); | ||||
|     const reporter = new import_internalReporter.InternalReporter([userReporter]); | ||||
|     const config = await this._loadConfigOrReportError(reporter); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [ | ||||
|       ...(0, import_tasks.createPluginSetupTasks)(config), | ||||
|       (0, import_tasks.createLoadTask)(mode, { failOnLoadErrors: true, filterOnly: false }), | ||||
|       (0, import_tasks.createStartDevServerTask)() | ||||
|     ]); | ||||
|     if (status !== "passed") | ||||
|       await cleanup(); | ||||
|     else | ||||
|       this._devServer = { cleanup }; | ||||
|     return { status }; | ||||
|   } | ||||
|   async stopDevServer() { | ||||
|     const devServer = this._devServer; | ||||
|     const status = await devServer?.cleanup(); | ||||
|     this._devServer = void 0; | ||||
|     return { status }; | ||||
|   } | ||||
|   async clearCache(userReporter) { | ||||
|     const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter] : []); | ||||
|     const config = await this._loadConfigOrReportError(reporter); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ | ||||
|       ...(0, import_tasks.createPluginSetupTasks)(config), | ||||
|       (0, import_tasks.createClearCacheTask)(config) | ||||
|     ]); | ||||
|     return { status }; | ||||
|   } | ||||
|   async listFiles(userReporter, projects) { | ||||
|     const reporter = new import_internalReporter.InternalReporter([userReporter]); | ||||
|     const config = await this._loadConfigOrReportError(reporter); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     config.cliProjectFilter = projects?.length ? projects : void 0; | ||||
|     const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ | ||||
|       (0, import_tasks.createListFilesTask)(), | ||||
|       (0, import_tasks.createReportBeginTask)() | ||||
|     ]); | ||||
|     return { status }; | ||||
|   } | ||||
|   async listTests(userReporter, params) { | ||||
|     let result; | ||||
|     this._queue = this._queue.then(async () => { | ||||
|       const { config, status } = await this._innerListTests(userReporter, params); | ||||
|       if (config) | ||||
|         await this._updateWatchedDirs(config); | ||||
|       result = { status }; | ||||
|     }).catch(printInternalError); | ||||
|     await this._queue; | ||||
|     return result; | ||||
|   } | ||||
|   async _innerListTests(userReporter, params) { | ||||
|     const overrides = { | ||||
|       ...this._configCLIOverrides, | ||||
|       repeatEach: 1, | ||||
|       retries: 0 | ||||
|     }; | ||||
|     const reporter = new import_internalReporter.InternalReporter([userReporter]); | ||||
|     const config = await this._loadConfigOrReportError(reporter, overrides); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     config.cliArgs = params.locations || []; | ||||
|     config.cliGrep = params.grep; | ||||
|     config.cliGrepInvert = params.grepInvert; | ||||
|     config.cliProjectFilter = params.projects?.length ? params.projects : void 0; | ||||
|     config.cliListOnly = true; | ||||
|     const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ | ||||
|       (0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: false, filterOnly: false, populateDependencies: this._populateDependenciesOnList }), | ||||
|       (0, import_tasks.createReportBeginTask)() | ||||
|     ]); | ||||
|     return { config, status }; | ||||
|   } | ||||
|   async _updateWatchedDirs(config) { | ||||
|     this._watchedProjectDirs = /* @__PURE__ */ new Set(); | ||||
|     this._ignoredProjectOutputs = /* @__PURE__ */ new Set(); | ||||
|     for (const p of config.projects) { | ||||
|       this._watchedProjectDirs.add(p.project.testDir); | ||||
|       this._ignoredProjectOutputs.add(p.project.outputDir); | ||||
|     } | ||||
|     const result = await resolveCtDirs(config); | ||||
|     if (result) { | ||||
|       this._watchedProjectDirs.add(result.templateDir); | ||||
|       this._ignoredProjectOutputs.add(result.outDir); | ||||
|     } | ||||
|     if (this._watchTestDirs) | ||||
|       await this._updateWatcher(false); | ||||
|   } | ||||
|   async _updateWatcher(reportPending) { | ||||
|     await this._watcher.update([...this._watchedProjectDirs, ...this._watchedTestDependencies], [...this._ignoredProjectOutputs], reportPending); | ||||
|   } | ||||
|   async runTests(userReporter, params) { | ||||
|     let result = { status: "passed" }; | ||||
|     this._queue = this._queue.then(async () => { | ||||
|       result = await this._innerRunTests(userReporter, params).catch((e) => { | ||||
|         printInternalError(e); | ||||
|         return { status: "failed" }; | ||||
|       }); | ||||
|     }); | ||||
|     await this._queue; | ||||
|     return result; | ||||
|   } | ||||
|   async _innerRunTests(userReporter, params) { | ||||
|     await this.stopTests(); | ||||
|     const overrides = { | ||||
|       ...this._configCLIOverrides, | ||||
|       repeatEach: 1, | ||||
|       retries: 0, | ||||
|       preserveOutputDir: true, | ||||
|       reporter: params.reporters ? params.reporters.map((r) => [r]) : void 0, | ||||
|       use: { | ||||
|         ...this._configCLIOverrides.use, | ||||
|         ...params.trace === "on" ? { trace: { mode: "on", sources: false, _live: true } } : {}, | ||||
|         ...params.trace === "off" ? { trace: "off" } : {}, | ||||
|         ...params.video === "on" || params.video === "off" ? { video: params.video } : {}, | ||||
|         ...params.headed !== void 0 ? { headless: !params.headed } : {}, | ||||
|         _optionContextReuseMode: params.reuseContext ? "when-possible" : void 0, | ||||
|         _optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : void 0 | ||||
|       }, | ||||
|       ...params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {}, | ||||
|       ...params.updateSourceMethod ? { updateSourceMethod: params.updateSourceMethod } : {}, | ||||
|       ...params.workers ? { workers: params.workers } : {} | ||||
|     }; | ||||
|     if (params.trace === "on") | ||||
|       process.env.PW_LIVE_TRACE_STACKS = "1"; | ||||
|     else | ||||
|       process.env.PW_LIVE_TRACE_STACKS = void 0; | ||||
|     const config = await this._loadConfigOrReportError(new import_internalReporter.InternalReporter([userReporter]), overrides); | ||||
|     if (!config) | ||||
|       return { status: "failed" }; | ||||
|     config.cliListOnly = false; | ||||
|     config.cliPassWithNoTests = true; | ||||
|     config.cliArgs = params.locations || []; | ||||
|     config.cliGrep = params.grep; | ||||
|     config.cliGrepInvert = params.grepInvert; | ||||
|     config.cliProjectFilter = params.projects?.length ? params.projects : void 0; | ||||
|     config.preOnlyTestFilters = []; | ||||
|     if (params.testIds) { | ||||
|       const testIdSet = new Set(params.testIds); | ||||
|       config.preOnlyTestFilters.push((test) => testIdSet.has(test.id)); | ||||
|     } | ||||
|     const configReporters = await (0, import_reporters.createReporters)(config, "test", true); | ||||
|     const reporter = new import_internalReporter.InternalReporter([...configReporters, userReporter]); | ||||
|     const stop = new import_utils.ManualPromise(); | ||||
|     const tasks = [ | ||||
|       (0, import_tasks.createApplyRebaselinesTask)(), | ||||
|       (0, import_tasks.createLoadTask)("out-of-process", { filterOnly: true, failOnLoadErrors: false, doNotRunDepsOutsideProjectFilter: true }), | ||||
|       ...(0, import_tasks.createRunTestsTasks)(config) | ||||
|     ]; | ||||
|     const testRun = new import_tasks.TestRun(config, reporter); | ||||
|     testRun.failureTracker.setRecoverFromStepErrorHandler(this._recoverFromStepError.bind(this)); | ||||
|     const run = (0, import_tasks.runTasks)(testRun, tasks, 0, stop).then(async (status) => { | ||||
|       this._testRun = void 0; | ||||
|       return status; | ||||
|     }); | ||||
|     this._testRun = { run, stop }; | ||||
|     return { status: await run }; | ||||
|   } | ||||
|   async _recoverFromStepError(stepId, error) { | ||||
|     if (!this._recoverFromStepErrors) | ||||
|       return { stepId, status: "failed" }; | ||||
|     const recoveryPromise = new import_utils.ManualPromise(); | ||||
|     this._resumeAfterStepErrors.set(stepId, recoveryPromise); | ||||
|     if (!error?.message || !error?.location) | ||||
|       return { stepId, status: "failed" }; | ||||
|     this.emit(TestRunnerEvent.RecoverFromStepError, stepId, error.message, error.location); | ||||
|     const recoveredResult = await recoveryPromise; | ||||
|     if (recoveredResult.stepId !== stepId) | ||||
|       return { stepId, status: "failed" }; | ||||
|     return recoveredResult; | ||||
|   } | ||||
|   async resumeAfterStepError(params) { | ||||
|     const recoveryPromise = this._resumeAfterStepErrors.get(params.stepId); | ||||
|     if (recoveryPromise) | ||||
|       recoveryPromise.resolve(params); | ||||
|   } | ||||
|   async watch(fileNames) { | ||||
|     this._watchedTestDependencies = /* @__PURE__ */ new Set(); | ||||
|     for (const fileName of fileNames) { | ||||
|       this._watchedTestDependencies.add(fileName); | ||||
|       (0, import_compilationCache.dependenciesForTestFile)(fileName).forEach((file) => this._watchedTestDependencies.add(file)); | ||||
|     } | ||||
|     await this._updateWatcher(true); | ||||
|   } | ||||
|   async findRelatedTestFiles(files, userReporter) { | ||||
|     const errorReporter = (0, import_reporters.createErrorCollectingReporter)(import_base.internalScreen); | ||||
|     const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter, errorReporter] : [errorReporter]); | ||||
|     const config = await this._loadConfigOrReportError(reporter); | ||||
|     if (!config) | ||||
|       return { errors: errorReporter.errors(), testFiles: [] }; | ||||
|     const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ | ||||
|       ...(0, import_tasks.createPluginSetupTasks)(config), | ||||
|       (0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: true, filterOnly: false, populateDependencies: true }) | ||||
|     ]); | ||||
|     if (status !== "passed") | ||||
|       return { errors: errorReporter.errors(), testFiles: [] }; | ||||
|     return { testFiles: (0, import_compilationCache.affectedTestFiles)(files) }; | ||||
|   } | ||||
|   async stopTests() { | ||||
|     this._testRun?.stop?.resolve(); | ||||
|     await this._testRun?.run; | ||||
|     this._resumeAfterStepErrors.clear(); | ||||
|   } | ||||
|   async closeGracefully() { | ||||
|     (0, import_utils.gracefullyProcessExitDoNotHang)(0); | ||||
|   } | ||||
|   async _loadConfig(overrides) { | ||||
|     try { | ||||
|       const config = await (0, import_configLoader.loadConfig)(this.configLocation, overrides); | ||||
|       if (!this._plugins) { | ||||
|         (0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p })); | ||||
|         (0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config); | ||||
|         this._plugins = config.plugins || []; | ||||
|       } else { | ||||
|         config.plugins.splice(0, config.plugins.length, ...this._plugins); | ||||
|       } | ||||
|       return { config }; | ||||
|     } catch (e) { | ||||
|       return { config: null, error: (0, import_util.serializeError)(e) }; | ||||
|     } | ||||
|   } | ||||
|   async _loadConfigOrReportError(reporter, overrides) { | ||||
|     const { config, error } = await this._loadConfig(overrides); | ||||
|     if (config) | ||||
|       return config; | ||||
|     reporter.onConfigure(import_teleReceiver.baseFullConfig); | ||||
|     reporter.onError(error); | ||||
|     await reporter.onEnd({ status: "failed" }); | ||||
|     await reporter.onExit(); | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| function printInternalError(e) { | ||||
|   console.error("Internal error:", e); | ||||
| } | ||||
| async function resolveCtDirs(config) { | ||||
|   const use = config.config.projects[0].use; | ||||
|   const relativeTemplateDir = use.ctTemplateDir || "playwright"; | ||||
|   const templateDir = await import_fs.default.promises.realpath(import_path.default.normalize(import_path.default.join(config.configDir, relativeTemplateDir))).catch(() => void 0); | ||||
|   if (!templateDir) | ||||
|     return null; | ||||
|   const outDir = use.ctCacheDir ? import_path.default.resolve(config.configDir, use.ctCacheDir) : import_path.default.resolve(templateDir, ".cache"); | ||||
|   return { | ||||
|     outDir, | ||||
|     templateDir | ||||
|   }; | ||||
| } | ||||
| async function runAllTestsWithConfig(config) { | ||||
|   const listOnly = config.cliListOnly; | ||||
|   (0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config); | ||||
|   (0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p })); | ||||
|   const reporters = await (0, import_reporters.createReporters)(config, listOnly ? "list" : "test", false); | ||||
|   const lastRun = new import_lastRun.LastRunReporter(config); | ||||
|   if (config.cliLastFailed) | ||||
|     await lastRun.filterLastFailed(); | ||||
|   const reporter = new import_internalReporter.InternalReporter([...reporters, lastRun]); | ||||
|   const tasks = listOnly ? [ | ||||
|     (0, import_tasks.createLoadTask)("in-process", { failOnLoadErrors: true, filterOnly: false }), | ||||
|     (0, import_tasks.createReportBeginTask)() | ||||
|   ] : [ | ||||
|     (0, import_tasks.createApplyRebaselinesTask)(), | ||||
|     ...(0, import_tasks.createGlobalSetupTasks)(config), | ||||
|     (0, import_tasks.createLoadTask)("in-process", { filterOnly: true, failOnLoadErrors: true }), | ||||
|     ...(0, import_tasks.createRunTestsTasks)(config) | ||||
|   ]; | ||||
|   const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), tasks, config.config.globalTimeout); | ||||
|   await new Promise((resolve) => process.stdout.write("", () => resolve())); | ||||
|   await new Promise((resolve) => process.stderr.write("", () => resolve())); | ||||
|   return status; | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   TestRunner, | ||||
|   TestRunnerEvent, | ||||
|   runAllTestsWithConfig | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone