debugController.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  4. var __getOwnPropNames = Object.getOwnPropertyNames;
  5. var __hasOwnProp = Object.prototype.hasOwnProperty;
  6. var __export = (target, all) => {
  7. for (var name in all)
  8. __defProp(target, name, { get: all[name], enumerable: true });
  9. };
  10. var __copyProps = (to, from, except, desc) => {
  11. if (from && typeof from === "object" || typeof from === "function") {
  12. for (let key of __getOwnPropNames(from))
  13. if (!__hasOwnProp.call(to, key) && key !== except)
  14. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  15. }
  16. return to;
  17. };
  18. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  19. var debugController_exports = {};
  20. __export(debugController_exports, {
  21. DebugController: () => DebugController
  22. });
  23. module.exports = __toCommonJS(debugController_exports);
  24. var import_instrumentation = require("./instrumentation");
  25. var import_processLauncher = require("./utils/processLauncher");
  26. var import_recorder = require("./recorder");
  27. var import_utils = require("../utils");
  28. var import_ariaSnapshot = require("../utils/isomorphic/ariaSnapshot");
  29. var import_utilsBundle = require("../utilsBundle");
  30. var import_locatorParser = require("../utils/isomorphic/locatorParser");
  31. var import_language = require("./codegen/language");
  32. var import_recorderUtils = require("./recorder/recorderUtils");
  33. var import_javascript = require("./codegen/javascript");
  34. class DebugController extends import_instrumentation.SdkObject {
  35. constructor(playwright) {
  36. super({ attribution: { isInternalPlaywright: true }, instrumentation: (0, import_instrumentation.createInstrumentation)() }, void 0, "DebugController");
  37. this._sdkLanguage = "javascript";
  38. this._generateAutoExpect = false;
  39. this._playwright = playwright;
  40. }
  41. static {
  42. this.Events = {
  43. StateChanged: "stateChanged",
  44. InspectRequested: "inspectRequested",
  45. SourceChanged: "sourceChanged",
  46. Paused: "paused",
  47. SetModeRequested: "setModeRequested"
  48. };
  49. }
  50. initialize(codegenId, sdkLanguage) {
  51. this._sdkLanguage = sdkLanguage;
  52. }
  53. dispose() {
  54. this.setReportStateChanged(false);
  55. }
  56. setReportStateChanged(enabled) {
  57. if (enabled && !this._trackHierarchyListener) {
  58. this._trackHierarchyListener = {
  59. onPageOpen: () => this._emitSnapshot(false),
  60. onPageClose: () => this._emitSnapshot(false)
  61. };
  62. this._playwright.instrumentation.addListener(this._trackHierarchyListener, null);
  63. this._emitSnapshot(true);
  64. } else if (!enabled && this._trackHierarchyListener) {
  65. this._playwright.instrumentation.removeListener(this._trackHierarchyListener);
  66. this._trackHierarchyListener = void 0;
  67. }
  68. }
  69. async setRecorderMode(progress, params) {
  70. await progress.race(this._closeBrowsersWithoutPages());
  71. this._generateAutoExpect = !!params.generateAutoExpect;
  72. if (params.mode === "none") {
  73. for (const recorder of await progress.race(this._allRecorders())) {
  74. recorder.hideHighlightedSelector();
  75. recorder.setMode("none");
  76. }
  77. return;
  78. }
  79. if (!this._playwright.allBrowsers().length)
  80. await this._playwright.chromium.launch(progress, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS });
  81. const pages = this._playwright.allPages();
  82. if (!pages.length) {
  83. const [browser] = this._playwright.allBrowsers();
  84. const context = await browser.newContextForReuse(progress, {});
  85. await context.newPage(progress);
  86. }
  87. if (params.testIdAttributeName) {
  88. for (const page of this._playwright.allPages())
  89. page.browserContext.selectors().setTestIdAttributeName(params.testIdAttributeName);
  90. }
  91. for (const recorder of await progress.race(this._allRecorders())) {
  92. recorder.hideHighlightedSelector();
  93. recorder.setMode(params.mode);
  94. }
  95. }
  96. async highlight(progress, params) {
  97. if (params.selector)
  98. (0, import_locatorParser.unsafeLocatorOrSelectorAsSelector)(this._sdkLanguage, params.selector, "data-testid");
  99. const ariaTemplate = params.ariaTemplate ? (0, import_ariaSnapshot.parseAriaSnapshotUnsafe)(import_utilsBundle.yaml, params.ariaTemplate) : void 0;
  100. for (const recorder of await progress.race(this._allRecorders())) {
  101. if (ariaTemplate)
  102. recorder.setHighlightedAriaTemplate(ariaTemplate);
  103. else if (params.selector)
  104. recorder.setHighlightedSelector(params.selector);
  105. }
  106. }
  107. async hideHighlight(progress) {
  108. for (const recorder of await progress.race(this._allRecorders()))
  109. recorder.hideHighlightedSelector();
  110. await Promise.all(this._playwright.allPages().map((p) => p.hideHighlight().catch(() => {
  111. })));
  112. }
  113. async resume(progress) {
  114. for (const recorder of await progress.race(this._allRecorders()))
  115. recorder.resume();
  116. }
  117. kill() {
  118. (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0);
  119. }
  120. _emitSnapshot(initial) {
  121. const pageCount = this._playwright.allPages().length;
  122. if (initial && !pageCount)
  123. return;
  124. this.emit(DebugController.Events.StateChanged, { pageCount });
  125. }
  126. async _allRecorders() {
  127. const contexts = /* @__PURE__ */ new Set();
  128. for (const page of this._playwright.allPages())
  129. contexts.add(page.browserContext);
  130. const recorders = await Promise.all([...contexts].map((c) => import_recorder.Recorder.forContext(c, { omitCallTracking: true })));
  131. const nonNullRecorders = recorders.filter(Boolean);
  132. for (const recorder of recorders)
  133. wireListeners(recorder, this);
  134. return nonNullRecorders;
  135. }
  136. async _closeBrowsersWithoutPages() {
  137. for (const browser of this._playwright.allBrowsers()) {
  138. for (const context of browser.contexts()) {
  139. if (!context.pages().length)
  140. await context.close({ reason: "Browser collected" });
  141. }
  142. if (!browser.contexts())
  143. await browser.close({ reason: "Browser collected" });
  144. }
  145. }
  146. }
  147. const wiredSymbol = Symbol("wired");
  148. function wireListeners(recorder, debugController) {
  149. if (recorder[wiredSymbol])
  150. return;
  151. recorder[wiredSymbol] = true;
  152. const actions = [];
  153. const languageGenerator = new import_javascript.JavaScriptLanguageGenerator(
  154. /* isPlaywrightTest */
  155. true
  156. );
  157. const actionsChanged = () => {
  158. const aa = (0, import_recorderUtils.collapseActions)(actions);
  159. const { header, footer, text, actionTexts } = (0, import_language.generateCode)(aa, languageGenerator, {
  160. browserName: "chromium",
  161. launchOptions: {},
  162. contextOptions: {},
  163. generateAutoExpect: debugController._generateAutoExpect
  164. });
  165. debugController.emit(DebugController.Events.SourceChanged, { text, header, footer, actions: actionTexts });
  166. };
  167. recorder.on(import_recorder.RecorderEvent.ElementPicked, (elementInfo) => {
  168. const locator = (0, import_utils.asLocator)(debugController._sdkLanguage, elementInfo.selector);
  169. debugController.emit(DebugController.Events.InspectRequested, { selector: elementInfo.selector, locator, ariaSnapshot: elementInfo.ariaSnapshot });
  170. });
  171. recorder.on(import_recorder.RecorderEvent.PausedStateChanged, (paused) => {
  172. debugController.emit(DebugController.Events.Paused, { paused });
  173. });
  174. recorder.on(import_recorder.RecorderEvent.ModeChanged, (mode) => {
  175. debugController.emit(DebugController.Events.SetModeRequested, { mode });
  176. });
  177. recorder.on(import_recorder.RecorderEvent.ActionAdded, (action) => {
  178. actions.push(action);
  179. actionsChanged();
  180. });
  181. recorder.on(import_recorder.RecorderEvent.SignalAdded, (signal) => {
  182. const lastAction = actions.findLast((a) => a.frame.pageGuid === signal.frame.pageGuid);
  183. if (lastAction)
  184. lastAction.action.signals.push(signal.signal);
  185. actionsChanged();
  186. });
  187. }
  188. // Annotate the CommonJS export names for ESM import in node:
  189. 0 && (module.exports = {
  190. DebugController
  191. });