@empiricalrun/playwright-utils 0.61.3 → 0.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/generated/api-worker-openapi.d.ts +178 -0
  3. package/dist/generated/api-worker-openapi.d.ts.map +1 -1
  4. package/dist/test/expect/visual.d.ts.map +1 -1
  5. package/dist/test/expect/visual.js +0 -7
  6. package/dist/test/index.d.ts.map +1 -1
  7. package/dist/test/index.js +10 -8
  8. package/dist/test/scripts/index.d.ts.map +1 -1
  9. package/dist/test/scripts/index.js +1 -6
  10. package/dist/test/scripts/pw-locator-patch/highlight/expect.js +1 -1
  11. package/dist/test/scripts/pw-locator-patch/highlight/hover.d.ts.map +1 -1
  12. package/dist/test/scripts/pw-locator-patch/highlight/hover.js +0 -1
  13. package/dist/test/scripts/pw-locator-patch/highlight/inner-text.js +1 -1
  14. package/dist/test/scripts/pw-locator-patch/highlight/input-value.js +1 -1
  15. package/dist/test/scripts/pw-locator-patch/highlight/is-checked.js +1 -1
  16. package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.js +1 -1
  17. package/dist/test/scripts/pw-locator-patch/highlight/is-editable.js +1 -1
  18. package/dist/test/scripts/pw-locator-patch/highlight/text-content.js +1 -1
  19. package/dist/test/scripts/pw-locator-patch/utils/index.d.ts +1 -2
  20. package/dist/test/scripts/pw-locator-patch/utils/index.d.ts.map +1 -1
  21. package/dist/test/scripts/pw-locator-patch/utils/index.js +3 -25
  22. package/dist/test/types.d.ts +1 -0
  23. package/dist/test/types.d.ts.map +1 -1
  24. package/docs/fixtures.md +10 -8
  25. package/package.json +1 -1
  26. package/tsconfig.tsbuildinfo +1 -1
  27. package/dist/test/scripts/mouse-pointer.d.ts +0 -5
  28. package/dist/test/scripts/mouse-pointer.d.ts.map +0 -1
  29. package/dist/test/scripts/mouse-pointer.js +0 -125
@@ -1 +1 @@
1
- {"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAWhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA0E7C,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CA2FxB"}
1
+ {"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAOhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAsE7C,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CAwFxB"}
@@ -11,7 +11,6 @@ const os_1 = __importDefault(require("os"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const api_worker_client_1 = require("../../api-worker-client");
13
13
  const telemetry_1 = require("../../telemetry");
14
- const mouse_pointer_1 = require("../scripts/mouse-pointer");
15
14
  function getCallerLine() {
16
15
  const stack = new Error().stack;
17
16
  if (!stack) {
@@ -75,16 +74,10 @@ issues or page loading bugs.
75
74
  You are to respond to your visual evaluation with a Pass or Fail, and provide
76
75
  a reason for your evaluation.
77
76
  `;
78
- function isLocator(target) {
79
- return "page" in target && typeof target.page === "function";
80
- }
81
77
  async function toLookRight(target, pageDescription) {
82
- const page = isLocator(target) ? target.page() : target;
83
- await (0, mouse_pointer_1.removeMousePointerHighlighter)(page);
84
78
  const screenshot = await target.screenshot();
85
79
  attachScreenshot(screenshot, pageDescription);
86
80
  const base64Image = screenshot.toString("base64");
87
- await (0, mouse_pointer_1.addHighlighterScriptsToPage)(page);
88
81
  const model = "gpt-4o";
89
82
  // Same api-worker proxy the overlay-dismissal agent uses, so a test run needs
90
83
  // no OpenAI key of its own; undefined falls back to the SDK's env defaults.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,KAAK,mBAAmB,EAAwB,MAAM,SAAS,CAAC;AACzE,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC7C,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SACnE;KACF;CACF;AAED,QAAA,MAAM,YAAY,mBAA6B,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,WACX,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,YACQ,mBAAmB,uHAqH7B,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,KAAK,mBAAmB,EAAwB,MAAM,SAAS,CAAC;AACzE,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC7C,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SACnE;KACF;CACF;AAED,QAAA,MAAM,YAAY,mBAA6B,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,WACX,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,YACQ,mBAAmB,uHAwH7B,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
@@ -27,13 +27,18 @@ const defaultOptions = {
27
27
  };
28
28
  const baseTestFixture = function (testFn, options = defaultOptions) {
29
29
  const coverageEnabled = options.collectCoverage ?? false;
30
+ // Playwright draws the cursor, target box and action title into the video.
31
+ // 100ms per action keeps them visible without the default 500ms slowdown.
32
+ const showActions = options.mousePointerHighlighter === false ? undefined : { duration: 100 };
33
+ const recordVideoOptions = (dir) => ({
34
+ dir,
35
+ size: { width: 1280, height: 720 },
36
+ showActions,
37
+ });
30
38
  const extendedTestFn = testFn.extend({
31
39
  context: async ({ browser }, use, testInfo) => {
32
40
  const context = await browser.newContext({
33
- recordVideo: {
34
- dir: testInfo.outputDir,
35
- size: { width: 1280, height: 720 },
36
- },
41
+ recordVideo: recordVideoOptions(testInfo.outputDir),
37
42
  });
38
43
  const pages = [];
39
44
  context.on("page", (page) => pages.push(page));
@@ -67,10 +72,7 @@ const baseTestFixture = function (testFn, options = defaultOptions) {
67
72
  async function createContext(contextOptions = {}) {
68
73
  const pageContext = await browser.newContext({
69
74
  ...contextOptions,
70
- recordVideo: {
71
- dir: testInfo.outputDir,
72
- size: { width: 1280, height: 720 },
73
- },
75
+ recordVideo: recordVideoOptions(testInfo.outputDir),
74
76
  });
75
77
  const pages = [];
76
78
  pageContext.on("page", (page) => pages.push(page));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK3C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AASjC,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAAgC,QAsB1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI3C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAQjC,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAAgC,QAmB1C"}
@@ -5,9 +5,7 @@ const logger_1 = require("../../logger");
5
5
  const agent_capabilities_1 = require("./agent-capabilities");
6
6
  const locator_highlights_1 = require("./locator-highlights");
7
7
  const locator_vision_1 = require("./locator-vision");
8
- const mouse_pointer_1 = require("./mouse-pointer");
9
8
  const defaultOptions = {
10
- mousePointerHighlighter: true,
11
9
  locatorHighlighter: true,
12
10
  visionMethods: true,
13
11
  agentCapabilities: true,
@@ -18,9 +16,6 @@ function injectLocatorHighlightScripts(page, testFn, options = defaultOptions) {
18
16
  ...options,
19
17
  };
20
18
  try {
21
- if (opts.mousePointerHighlighter) {
22
- (0, mouse_pointer_1.addMousePointerHighlighter)(page);
23
- }
24
19
  if (opts.locatorHighlighter) {
25
20
  (0, locator_highlights_1.addLocatorHighlights)(page, testFn);
26
21
  }
@@ -32,6 +27,6 @@ function injectLocatorHighlightScripts(page, testFn, options = defaultOptions) {
32
27
  }
33
28
  }
34
29
  catch (e) {
35
- logger_1.logger.debug("Failed to add mouse pointer highlighter", e);
30
+ logger_1.logger.debug("Failed to inject locator highlight scripts", e);
36
31
  }
37
32
  }
@@ -15,7 +15,7 @@ function patchExpect(LocatorClass) {
15
15
  LocatorClass.prototype._expect = async function (expression, options) {
16
16
  const result = await originalExpect.apply(this, [expression, options]);
17
17
  if (result.matches) {
18
- await (0, utils_1.scrollAndHighlight)(this);
18
+ await (0, utils_1.scrollIntoView)(this);
19
19
  }
20
20
  return result;
21
21
  };
@@ -1 +1 @@
1
- {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/highlight/hover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC,wBAAgB,UAAU,CACxB,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,EAC/C,MAAM,EAAE,MAAM,QAoDf"}
1
+ {"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/highlight/hover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC,wBAAgB,UAAU,CACxB,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,EAC/C,MAAM,EAAE,MAAM,QAmDf"}
@@ -21,7 +21,6 @@ function patchHover(LocatorClass, testFn) {
21
21
  overlayAttemptsRemaining--;
22
22
  try {
23
23
  let result = await originalHover.apply(this, [options]);
24
- await (0, utils_2.highlight)(this);
25
24
  // If hover is successful, we can save overlay dismissal code to cache and return
26
25
  await overlayDismiss.saveToCache();
27
26
  return result;
@@ -22,7 +22,7 @@ function patchInnerText(LocatorClass, testFn) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- await (0, utils_1.scrollAndHighlight)(this);
25
+ await (0, utils_1.scrollIntoView)(this);
26
26
  }, { box: true });
27
27
  return result;
28
28
  };
@@ -22,7 +22,7 @@ function patchInputValue(LocatorClass, testFn) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- await (0, utils_1.scrollAndHighlight)(this);
25
+ await (0, utils_1.scrollIntoView)(this);
26
26
  }, { box: true });
27
27
  return result;
28
28
  };
@@ -22,7 +22,7 @@ function patchIsChecked(LocatorClass, testFn) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- await (0, utils_1.scrollAndHighlight)(this);
25
+ await (0, utils_1.scrollIntoView)(this);
26
26
  }, { box: true });
27
27
  return result;
28
28
  };
@@ -22,7 +22,7 @@ function patchIsDisabled(LocatorClass, testFn) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- await (0, utils_1.scrollAndHighlight)(this);
25
+ await (0, utils_1.scrollIntoView)(this);
26
26
  }, { box: true });
27
27
  return result;
28
28
  };
@@ -21,7 +21,7 @@ function patchIsEditable(LocatorClass, testFn) {
21
21
  }
22
22
  throw error;
23
23
  }
24
- await (0, utils_1.scrollAndHighlight)(this);
24
+ await (0, utils_1.scrollIntoView)(this);
25
25
  }, { box: true });
26
26
  return result;
27
27
  };
@@ -22,7 +22,7 @@ function patchTextContent(LocatorClass, testFn) {
22
22
  }
23
23
  throw error;
24
24
  }
25
- await (0, utils_1.scrollAndHighlight)(this);
25
+ await (0, utils_1.scrollIntoView)(this);
26
26
  }, { box: true });
27
27
  return result;
28
28
  };
@@ -1,5 +1,4 @@
1
1
  import { Locator } from "playwright-core";
2
2
  export declare function description(locator: Locator): string;
3
- export declare function scrollAndHighlight(locator: Locator): Promise<void>;
4
- export declare function highlight(locator: Locator): Promise<void>;
3
+ export declare function scrollIntoView(locator: Locator): Promise<void>;
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK1C,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,UA0B3C;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,iBAsBxD;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,iBAiB/C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK1C,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,UA0B3C;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,iBAgBpD"}
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.description = description;
4
- exports.scrollAndHighlight = scrollAndHighlight;
5
- exports.highlight = highlight;
4
+ exports.scrollIntoView = scrollIntoView;
6
5
  const logger_1 = require("../../../../logger");
7
6
  const constants_1 = require("../../../constants");
8
7
  function description(locator) {
@@ -26,7 +25,7 @@ function description(locator) {
26
25
  return fallbackDescription;
27
26
  }
28
27
  }
29
- async function scrollAndHighlight(locator) {
28
+ async function scrollIntoView(locator) {
30
29
  try {
31
30
  await locator.evaluate((node) => {
32
31
  if (node.scrollIntoViewIfNeeded) {
@@ -35,30 +34,9 @@ async function scrollAndHighlight(locator) {
35
34
  else {
36
35
  node.scrollIntoView();
37
36
  }
38
- node.classList.add("empirical-element-grab-highlight");
39
- setTimeout(() => {
40
- if (node && node.isConnected && node.classList) {
41
- node.classList.remove("empirical-element-grab-highlight");
42
- }
43
- }, 2000);
44
37
  }, undefined, { timeout: constants_1.DEFAULT_SCRIPT_EXECUTION_TIMEOUT });
45
38
  }
46
39
  catch {
47
- logger_1.logger.debug("Failed to scroll and highlight for locator method");
48
- }
49
- }
50
- async function highlight(locator) {
51
- try {
52
- await locator.evaluate((node) => {
53
- node.classList.add("empirical-element-grab-highlight");
54
- setTimeout(() => {
55
- if (node && node.isConnected && node.classList) {
56
- node.classList.remove("empirical-element-grab-highlight");
57
- }
58
- }, 2000);
59
- }, undefined, { timeout: constants_1.DEFAULT_SCRIPT_EXECUTION_TIMEOUT });
60
- }
61
- catch {
62
- logger_1.logger.debug("Failed to add highlight for locator method");
40
+ logger_1.logger.debug("Failed to scroll into view for locator method");
63
41
  }
64
42
  }
@@ -1,4 +1,5 @@
1
1
  export type HighlighterOpts = {
2
+ /** Show Playwright's action annotations (cursor, target box, title) in videos */
2
3
  mousePointerHighlighter?: boolean;
3
4
  locatorHighlighter?: boolean;
4
5
  visionMethods?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/test/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/test/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,iFAAiF;IACjF,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC"}
package/docs/fixtures.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Fixtures
2
2
 
3
3
  The playwright-utils package provides fixtures that wrap around Playwright's built-in
4
- `page`, `context` fixtures to provide a mouse highlighter (which makes it easier to
5
- see actions taken in a video).
4
+ `page`, `context` fixtures to record videos with Playwright's action annotations
5
+ (cursor, target box and action title), which make it easier to see actions taken in a video.
6
6
 
7
7
  To use this, you can use the `baseTestFixture` and `extendExpect` imports
8
8
  in your fixtures file.
@@ -17,8 +17,8 @@ export const expect = extendExpect(baseExpect);
17
17
 
18
18
  ## Highlighters and agentic behaviors
19
19
 
20
- `baseTestFixture` patches the playwright page object to add the following methods to add
21
- mouse highlights and locator highlights.
20
+ `baseTestFixture` patches the playwright page object to add locator helpers.
21
+ Pass `mousePointerHighlighter: false` to turn off the action annotations in videos.
22
22
 
23
23
  These methods also enable agentic behaviors like dismissing overlays.
24
24
 
@@ -49,7 +49,7 @@ a fresh, new browser context, and a page inside it.
49
49
 
50
50
  There are two benefits of using this to create contexts or pages:
51
51
  1. Videos get recorded and attached to the test report
52
- 2. Mouse highlights are available
52
+ 2. Action annotations are shown in the video
53
53
 
54
54
  ```ts
55
55
  import { test, expect } from "./fixtures";
@@ -61,12 +61,14 @@ test("Example test", async ({ page: builtInPage, customContextPageProvider }) =>
61
61
  });
62
62
  ```
63
63
 
64
- ### Inject mouse highlighter manually
64
+ ### Inject locator scripts manually
65
65
 
66
- In some cases, you want to inject mouse highlights to a page. This is useful if the test
66
+ In some cases, you want to patch a page you created yourself. This is useful if the test
67
67
  requires `launchPersistentContext`, which means the browser context is created manually.
68
68
 
69
- With `injectLocatorHighlightScripts`, you can still get mouse highlights in any page.
69
+ With `injectLocatorHighlightScripts`, you still get the locator helpers in any page. For
70
+ action annotations in the video, pass `recordVideo: { dir, showActions: { duration: 100 } }`
71
+ when you create the context.
70
72
 
71
73
  ```ts
72
74
  import { injectLocatorHighlightScripts, baseTestFixture } from "@empiricalrun/playwright-utils/test";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.61.3",
3
+ "version": "0.62.0",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -1 +1 @@
1
- {"version":"7.0.0-dev.20260623.1","root":["./src/api-worker-client.ts","./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/generated/api-worker-openapi.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/overlay-tests/visual.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/parity-utils.ts","./src/reporter/reporter-state.ts","./src/reporter/test-case-sync-reporter.ts","./src/reporter/test-case-sync.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"]}
1
+ {"version":"7.0.0-dev.20260623.1","root":["./src/api-worker-client.ts","./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/generated/api-worker-openapi.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/overlay-tests/visual.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/parity-utils.ts","./src/reporter/reporter-state.ts","./src/reporter/test-case-sync-reporter.ts","./src/reporter/test-case-sync.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"]}
@@ -1,5 +0,0 @@
1
- import type { Page } from "@playwright/test";
2
- export declare function addHighlighterScriptsToPage(page: Page): Promise<void>;
3
- export declare function removeMousePointerHighlighter(page: Page): Promise<void>;
4
- export declare function addMousePointerHighlighter(page: Page): void;
5
- //# sourceMappingURL=mouse-pointer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mouse-pointer.d.ts","sourceRoot":"","sources":["../../../src/test/scripts/mouse-pointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAmD7C,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,IAAI,iBAqD3D;AAED,wBAAsB,6BAA6B,CAAC,IAAI,EAAE,IAAI,iBA6B7D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,QAUpD"}
@@ -1,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addHighlighterScriptsToPage = addHighlighterScriptsToPage;
4
- exports.removeMousePointerHighlighter = removeMousePointerHighlighter;
5
- exports.addMousePointerHighlighter = addMousePointerHighlighter;
6
- const logger_1 = require("../../logger");
7
- const STYLES_FOR_MOUSE_POINTER = `
8
- .empirical-mouse-pointer {
9
- pointer-events: none;
10
- position: absolute;
11
- top: 0;
12
- z-index: 10000;
13
- left: 0;
14
- width: 20px;
15
- height: 20px;
16
- background: rgba(0,0,0,.4);
17
- border: 1px dashed #FF000D;
18
- border-radius: 10px;
19
- margin: -10px 0 0 -10px;
20
- padding: 0;
21
- transition: background .2s, border-radius .2s, border-color .2s;
22
- }
23
- .empirical-mouse-pointer.button-1 {
24
- transition: none;
25
- background: rgba(0,0,0,0.9);
26
- }
27
- .empirical-mouse-pointer.button-2 {
28
- transition: none;
29
- border-color: rgba(0,0,255,0.9);
30
- }
31
- .empirical-mouse-pointer.button-3 {
32
- transition: none;
33
- border-radius: 4px;
34
- }
35
- .empirical-mouse-pointer.button-4 {
36
- transition: none;
37
- border-color: rgba(255,0,0,0.9);
38
- }
39
- .empirical-mouse-pointer.button-5 {
40
- transition: none;
41
- border-color: rgba(0,255,0,0.9);
42
- }
43
- .empirical-element-grab-highlight {
44
- outline: 2px dashed cyan !important;
45
- }
46
- *:focus {
47
- outline: 2px dashed #FF000D !important;
48
- }
49
- *:focus-visible {
50
- outline: 2px dashed #FF000D !important;
51
- }
52
- `;
53
- async function addHighlighterScriptsToPage(page) {
54
- try {
55
- await page.evaluate((stylesString) => {
56
- const mouseBox = document.createElement("div");
57
- mouseBox.classList.add("empirical-mouse-pointer");
58
- const styleElement = document.createElement("style");
59
- styleElement.textContent = stylesString;
60
- document.head.appendChild(styleElement);
61
- document.body.appendChild(mouseBox);
62
- document.addEventListener("mousemove", (event) => {
63
- mouseBox.style.left = `${event.pageX}px`;
64
- mouseBox.style.top = `${event.pageY}px`;
65
- updateButtons(event.buttons);
66
- }, true);
67
- document.addEventListener("mousedown", (event) => {
68
- updateButtons(event.buttons);
69
- mouseBox.classList.add(`button-${event.button + 1}`);
70
- }, true);
71
- document.addEventListener("mouseup", (event) => {
72
- updateButtons(event.buttons);
73
- mouseBox.classList.remove(`button-${event.button + 1}`);
74
- }, true);
75
- // @ts-ignore
76
- function updateButtons(buttons) {
77
- for (let i = 0; i < 5; i++) {
78
- mouseBox.classList.toggle(`button-${i + 1}`, (buttons / Math.pow(2, i)) % 2 >= 1);
79
- }
80
- }
81
- }, STYLES_FOR_MOUSE_POINTER);
82
- }
83
- catch (e) {
84
- logger_1.logger.debug(`Error adding mouse pointer highlighter`, e);
85
- }
86
- }
87
- async function removeMousePointerHighlighter(page) {
88
- try {
89
- await page.evaluate(() => {
90
- // check if any element has the class empirical-element-grab-highlight
91
- // if so, remove the class
92
- const hasHighlight = document.querySelectorAll(".empirical-element-grab-highlight");
93
- if (hasHighlight) {
94
- hasHighlight.forEach((highlight) => {
95
- highlight.classList.remove("empirical-element-grab-highlight");
96
- });
97
- }
98
- // find div.empirical-mouse-pointer and remove it
99
- const mousePointer = document.querySelector(".empirical-mouse-pointer");
100
- if (mousePointer) {
101
- mousePointer.remove();
102
- }
103
- // find style element with text content of STYLE_FOR_MOUSE_POINTER
104
- const styleElements = document.querySelectorAll("style");
105
- styleElements.forEach((styleElement) => {
106
- if (styleElement.textContent?.includes("empirical-mouse-pointer")) {
107
- styleElement.remove();
108
- }
109
- });
110
- });
111
- }
112
- catch (e) {
113
- logger_1.logger.debug(`Error removing mouse pointer highlighter`, e);
114
- }
115
- }
116
- function addMousePointerHighlighter(page) {
117
- page.on("load", async () => {
118
- await addHighlighterScriptsToPage(page);
119
- });
120
- page.context().on("page", async (newPage) => {
121
- newPage?.on("load", async () => {
122
- await addHighlighterScriptsToPage(newPage);
123
- });
124
- });
125
- }