@wordpress/e2e-test-utils-playwright 1.46.0 → 1.48.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 (295) hide show
  1. package/build-types/admin/index.d.ts +3 -0
  2. package/build-types/admin/index.d.ts.map +1 -1
  3. package/build-types/admin/visit-site-editor.d.ts.map +1 -1
  4. package/build-types/admin/wait-for-site-editor.d.ts +12 -0
  5. package/build-types/admin/wait-for-site-editor.d.ts.map +1 -0
  6. package/build-types/metrics/index.d.ts +22 -5
  7. package/build-types/metrics/index.d.ts.map +1 -1
  8. package/build-types/request-utils/index.d.ts +3 -1
  9. package/build-types/request-utils/index.d.ts.map +1 -1
  10. package/build-types/request-utils/preferences.d.ts +11 -0
  11. package/build-types/request-utils/preferences.d.ts.map +1 -1
  12. package/package.json +4 -5
  13. package/src/admin/create-new-post.ts +47 -0
  14. package/src/admin/edit-post.ts +24 -0
  15. package/{build/admin/get-page-error.js → src/admin/get-page-error.ts} +12 -9
  16. package/src/admin/index.ts +52 -0
  17. package/src/admin/visit-admin-page.ts +43 -0
  18. package/src/admin/visit-site-editor.ts +62 -0
  19. package/src/admin/wait-for-site-editor.ts +52 -0
  20. package/src/config.ts +12 -0
  21. package/src/editor/click-block-options-menu-item.ts +18 -0
  22. package/src/editor/click-block-toolbar-button.ts +21 -0
  23. package/src/editor/get-blocks.ts +75 -0
  24. package/src/editor/get-edited-post-content.ts +19 -0
  25. package/src/editor/index.ts +95 -0
  26. package/src/editor/insert-block.ts +54 -0
  27. package/src/editor/open-document-settings-sidebar.ts +31 -0
  28. package/src/editor/preview.ts +32 -0
  29. package/src/editor/publish-post.ts +51 -0
  30. package/src/editor/save-draft.ts +20 -0
  31. package/src/editor/select-blocks.ts +48 -0
  32. package/src/editor/set-content.ts +24 -0
  33. package/src/editor/set-is-fixed-toolbar.ts +20 -0
  34. package/src/editor/set-preferences.ts +37 -0
  35. package/src/editor/show-block-toolbar.ts +20 -0
  36. package/src/editor/site-editor.ts +55 -0
  37. package/src/editor/switch-editor-tool.ts +34 -0
  38. package/src/editor/switch-to-legacy-canvas.ts +20 -0
  39. package/src/editor/transform-block-to.ts +34 -0
  40. package/src/index.ts +8 -0
  41. package/src/lighthouse/index.ts +75 -0
  42. package/src/metrics/index.ts +441 -0
  43. package/src/page-utils/drag-files.ts +173 -0
  44. package/src/page-utils/emulate-network-conditions.ts +101 -0
  45. package/src/page-utils/index.ts +54 -0
  46. package/src/page-utils/is-current-url.ts +20 -0
  47. package/src/page-utils/keycodes.ts +48 -0
  48. package/src/page-utils/press-keys.ts +195 -0
  49. package/{build → src}/page-utils/set-browser-viewport.js +14 -12
  50. package/src/request-utils/blocks.ts +58 -0
  51. package/src/request-utils/comments.ts +80 -0
  52. package/src/request-utils/gutenberg-experiments.ts +62 -0
  53. package/src/request-utils/index.ts +222 -0
  54. package/src/request-utils/login.ts +34 -0
  55. package/src/request-utils/media.ts +102 -0
  56. package/src/request-utils/menus.ts +145 -0
  57. package/src/request-utils/pages.ts +75 -0
  58. package/src/request-utils/patterns.ts +31 -0
  59. package/src/request-utils/plugins.ts +103 -0
  60. package/src/request-utils/posts.ts +69 -0
  61. package/src/request-utils/preferences.ts +63 -0
  62. package/{build/request-utils/records.js → src/request-utils/records.ts} +15 -10
  63. package/src/request-utils/rest.ts +222 -0
  64. package/src/request-utils/site-settings.ts +58 -0
  65. package/src/request-utils/templates.ts +83 -0
  66. package/src/request-utils/themes.ts +110 -0
  67. package/src/request-utils/users.ts +125 -0
  68. package/src/request-utils/widgets.js +68 -0
  69. package/src/test.ts +204 -0
  70. package/src/types.ts +13 -0
  71. package/build/admin/create-new-post.cjs +0 -51
  72. package/build/admin/create-new-post.cjs.map +0 -7
  73. package/build/admin/create-new-post.js +0 -31
  74. package/build/admin/create-new-post.js.map +0 -1
  75. package/build/admin/edit-post.cjs +0 -40
  76. package/build/admin/edit-post.cjs.map +0 -7
  77. package/build/admin/edit-post.js +0 -20
  78. package/build/admin/edit-post.js.map +0 -1
  79. package/build/admin/get-page-error.cjs +0 -36
  80. package/build/admin/get-page-error.cjs.map +0 -7
  81. package/build/admin/get-page-error.js.map +0 -1
  82. package/build/admin/index.cjs +0 -59
  83. package/build/admin/index.cjs.map +0 -7
  84. package/build/admin/index.js +0 -37
  85. package/build/admin/index.js.map +0 -1
  86. package/build/admin/visit-admin-page.cjs +0 -47
  87. package/build/admin/visit-admin-page.cjs.map +0 -7
  88. package/build/admin/visit-admin-page.js +0 -32
  89. package/build/admin/visit-admin-page.js.map +0 -1
  90. package/build/admin/visit-site-editor.cjs +0 -90
  91. package/build/admin/visit-site-editor.cjs.map +0 -7
  92. package/build/admin/visit-site-editor.js +0 -73
  93. package/build/admin/visit-site-editor.js.map +0 -1
  94. package/build/config.cjs +0 -45
  95. package/build/config.cjs.map +0 -7
  96. package/build/config.js +0 -13
  97. package/build/config.js.map +0 -1
  98. package/build/editor/click-block-options-menu-item.cjs +0 -34
  99. package/build/editor/click-block-options-menu-item.cjs.map +0 -7
  100. package/build/editor/click-block-options-menu-item.js +0 -17
  101. package/build/editor/click-block-options-menu-item.js.map +0 -1
  102. package/build/editor/click-block-toolbar-button.cjs +0 -38
  103. package/build/editor/click-block-toolbar-button.cjs.map +0 -7
  104. package/build/editor/click-block-toolbar-button.js +0 -16
  105. package/build/editor/click-block-toolbar-button.js.map +0 -1
  106. package/build/editor/get-blocks.cjs +0 -64
  107. package/build/editor/get-blocks.cjs.map +0 -7
  108. package/build/editor/get-blocks.js +0 -45
  109. package/build/editor/get-blocks.js.map +0 -1
  110. package/build/editor/get-edited-post-content.cjs +0 -36
  111. package/build/editor/get-edited-post-content.cjs.map +0 -7
  112. package/build/editor/get-edited-post-content.js +0 -15
  113. package/build/editor/get-edited-post-content.js.map +0 -1
  114. package/build/editor/index.cjs +0 -97
  115. package/build/editor/index.cjs.map +0 -7
  116. package/build/editor/index.js +0 -75
  117. package/build/editor/index.js.map +0 -1
  118. package/build/editor/insert-block.cjs +0 -55
  119. package/build/editor/insert-block.cjs.map +0 -7
  120. package/build/editor/insert-block.js +0 -23
  121. package/build/editor/insert-block.js.map +0 -1
  122. package/build/editor/open-document-settings-sidebar.cjs +0 -42
  123. package/build/editor/open-document-settings-sidebar.cjs.map +0 -7
  124. package/build/editor/open-document-settings-sidebar.js +0 -27
  125. package/build/editor/open-document-settings-sidebar.js.map +0 -1
  126. package/build/editor/preview.cjs +0 -42
  127. package/build/editor/preview.cjs.map +0 -7
  128. package/build/editor/preview.js +0 -21
  129. package/build/editor/preview.js.map +0 -1
  130. package/build/editor/publish-post.cjs +0 -47
  131. package/build/editor/publish-post.cjs.map +0 -7
  132. package/build/editor/publish-post.js +0 -45
  133. package/build/editor/publish-post.js.map +0 -1
  134. package/build/editor/save-draft.cjs +0 -34
  135. package/build/editor/save-draft.cjs.map +0 -7
  136. package/build/editor/save-draft.js +0 -18
  137. package/build/editor/save-draft.js.map +0 -1
  138. package/build/editor/select-blocks.cjs +0 -51
  139. package/build/editor/select-blocks.cjs.map +0 -7
  140. package/build/editor/select-blocks.js +0 -28
  141. package/build/editor/select-blocks.js.map +0 -1
  142. package/build/editor/set-content.cjs +0 -39
  143. package/build/editor/set-content.cjs.map +0 -7
  144. package/build/editor/set-content.js +0 -17
  145. package/build/editor/set-content.js.map +0 -1
  146. package/build/editor/set-is-fixed-toolbar.cjs +0 -36
  147. package/build/editor/set-is-fixed-toolbar.cjs.map +0 -7
  148. package/build/editor/set-is-fixed-toolbar.js +0 -18
  149. package/build/editor/set-is-fixed-toolbar.js.map +0 -1
  150. package/build/editor/set-preferences.cjs +0 -43
  151. package/build/editor/set-preferences.cjs.map +0 -7
  152. package/build/editor/set-preferences.js +0 -21
  153. package/build/editor/set-preferences.js.map +0 -1
  154. package/build/editor/show-block-toolbar.cjs +0 -35
  155. package/build/editor/show-block-toolbar.cjs.map +0 -7
  156. package/build/editor/show-block-toolbar.js +0 -19
  157. package/build/editor/show-block-toolbar.js.map +0 -1
  158. package/build/editor/site-editor.cjs +0 -51
  159. package/build/editor/site-editor.cjs.map +0 -7
  160. package/build/editor/site-editor.js +0 -44
  161. package/build/editor/site-editor.js.map +0 -1
  162. package/build/editor/switch-editor-tool.cjs +0 -47
  163. package/build/editor/switch-editor-tool.cjs.map +0 -7
  164. package/build/editor/switch-editor-tool.js +0 -33
  165. package/build/editor/switch-editor-tool.js.map +0 -1
  166. package/build/editor/switch-to-legacy-canvas.cjs +0 -39
  167. package/build/editor/switch-to-legacy-canvas.cjs.map +0 -7
  168. package/build/editor/switch-to-legacy-canvas.js +0 -18
  169. package/build/editor/switch-to-legacy-canvas.js.map +0 -1
  170. package/build/editor/transform-block-to.cjs +0 -46
  171. package/build/editor/transform-block-to.cjs.map +0 -7
  172. package/build/editor/transform-block-to.js +0 -24
  173. package/build/editor/transform-block-to.js.map +0 -1
  174. package/build/index.cjs +0 -51
  175. package/build/index.cjs.map +0 -7
  176. package/build/index.js +0 -19
  177. package/build/index.js.map +0 -1
  178. package/build/lighthouse/index.cjs +0 -96
  179. package/build/lighthouse/index.cjs.map +0 -7
  180. package/build/lighthouse/index.js +0 -61
  181. package/build/lighthouse/index.js.map +0 -1
  182. package/build/metrics/index.cjs +0 -309
  183. package/build/metrics/index.cjs.map +0 -7
  184. package/build/metrics/index.js +0 -258
  185. package/build/metrics/index.js.map +0 -1
  186. package/build/page-utils/drag-files.cjs +0 -143
  187. package/build/page-utils/drag-files.cjs.map +0 -7
  188. package/build/page-utils/drag-files.js +0 -119
  189. package/build/page-utils/drag-files.js.map +0 -1
  190. package/build/page-utils/emulate-network-conditions.cjs +0 -99
  191. package/build/page-utils/emulate-network-conditions.cjs.map +0 -7
  192. package/build/page-utils/emulate-network-conditions.js +0 -71
  193. package/build/page-utils/emulate-network-conditions.js.map +0 -1
  194. package/build/page-utils/index.cjs +0 -59
  195. package/build/page-utils/index.cjs.map +0 -7
  196. package/build/page-utils/index.js +0 -37
  197. package/build/page-utils/index.js.map +0 -1
  198. package/build/page-utils/is-current-url.cjs +0 -36
  199. package/build/page-utils/is-current-url.cjs.map +0 -7
  200. package/build/page-utils/is-current-url.js +0 -21
  201. package/build/page-utils/is-current-url.js.map +0 -1
  202. package/build/page-utils/keycodes.cjs +0 -55
  203. package/build/page-utils/keycodes.cjs.map +0 -7
  204. package/build/page-utils/keycodes.js +0 -41
  205. package/build/page-utils/keycodes.js.map +0 -1
  206. package/build/page-utils/press-keys.cjs +0 -165
  207. package/build/page-utils/press-keys.cjs.map +0 -7
  208. package/build/page-utils/press-keys.js +0 -141
  209. package/build/page-utils/press-keys.js.map +0 -1
  210. package/build/page-utils/set-browser-viewport.cjs +0 -39
  211. package/build/page-utils/set-browser-viewport.cjs.map +0 -7
  212. package/build/page-utils/set-browser-viewport.js.map +0 -1
  213. package/build/request-utils/blocks.cjs +0 -51
  214. package/build/request-utils/blocks.cjs.map +0 -7
  215. package/build/request-utils/blocks.js +0 -40
  216. package/build/request-utils/blocks.js.map +0 -1
  217. package/build/request-utils/comments.cjs +0 -66
  218. package/build/request-utils/comments.cjs.map +0 -7
  219. package/build/request-utils/comments.js +0 -52
  220. package/build/request-utils/comments.js.map +0 -1
  221. package/build/request-utils/gutenberg-experiments.cjs +0 -56
  222. package/build/request-utils/gutenberg-experiments.cjs.map +0 -7
  223. package/build/request-utils/gutenberg-experiments.js +0 -47
  224. package/build/request-utils/gutenberg-experiments.js.map +0 -1
  225. package/build/request-utils/index.cjs +0 -199
  226. package/build/request-utils/index.cjs.map +0 -7
  227. package/build/request-utils/index.js +0 -194
  228. package/build/request-utils/index.js.map +0 -1
  229. package/build/request-utils/login.cjs +0 -48
  230. package/build/request-utils/login.cjs.map +0 -7
  231. package/build/request-utils/login.js +0 -21
  232. package/build/request-utils/login.js.map +0 -1
  233. package/build/request-utils/media.cjs +0 -83
  234. package/build/request-utils/media.cjs.map +0 -7
  235. package/build/request-utils/media.js +0 -106
  236. package/build/request-utils/media.js.map +0 -1
  237. package/build/request-utils/menus.cjs +0 -128
  238. package/build/request-utils/menus.cjs.map +0 -7
  239. package/build/request-utils/menus.js +0 -116
  240. package/build/request-utils/menus.js.map +0 -1
  241. package/build/request-utils/pages.cjs +0 -66
  242. package/build/request-utils/pages.cjs.map +0 -7
  243. package/build/request-utils/pages.js +0 -53
  244. package/build/request-utils/pages.js.map +0 -1
  245. package/build/request-utils/patterns.cjs +0 -44
  246. package/build/request-utils/patterns.cjs.map +0 -7
  247. package/build/request-utils/patterns.js +0 -27
  248. package/build/request-utils/patterns.js.map +0 -1
  249. package/build/request-utils/plugins.cjs +0 -81
  250. package/build/request-utils/plugins.cjs.map +0 -7
  251. package/build/request-utils/plugins.js +0 -85
  252. package/build/request-utils/plugins.js.map +0 -1
  253. package/build/request-utils/posts.cjs +0 -56
  254. package/build/request-utils/posts.cjs.map +0 -7
  255. package/build/request-utils/posts.js +0 -42
  256. package/build/request-utils/posts.js.map +0 -1
  257. package/build/request-utils/preferences.cjs +0 -41
  258. package/build/request-utils/preferences.cjs.map +0 -7
  259. package/build/request-utils/preferences.js +0 -20
  260. package/build/request-utils/preferences.js.map +0 -1
  261. package/build/request-utils/records.cjs +0 -37
  262. package/build/request-utils/records.cjs.map +0 -7
  263. package/build/request-utils/records.js.map +0 -1
  264. package/build/request-utils/rest.cjs +0 -174
  265. package/build/request-utils/rest.cjs.map +0 -7
  266. package/build/request-utils/rest.js +0 -181
  267. package/build/request-utils/rest.js.map +0 -1
  268. package/build/request-utils/site-settings.cjs +0 -45
  269. package/build/request-utils/site-settings.cjs.map +0 -7
  270. package/build/request-utils/site-settings.js +0 -33
  271. package/build/request-utils/site-settings.js.map +0 -1
  272. package/build/request-utils/templates.cjs +0 -68
  273. package/build/request-utils/templates.cjs.map +0 -7
  274. package/build/request-utils/templates.js +0 -54
  275. package/build/request-utils/templates.js.map +0 -1
  276. package/build/request-utils/themes.cjs +0 -92
  277. package/build/request-utils/themes.cjs.map +0 -7
  278. package/build/request-utils/themes.js +0 -69
  279. package/build/request-utils/themes.js.map +0 -1
  280. package/build/request-utils/users.cjs +0 -83
  281. package/build/request-utils/users.cjs.map +0 -7
  282. package/build/request-utils/users.js +0 -81
  283. package/build/request-utils/users.js.map +0 -1
  284. package/build/request-utils/widgets.cjs +0 -79
  285. package/build/request-utils/widgets.cjs.map +0 -7
  286. package/build/request-utils/widgets.js +0 -61
  287. package/build/request-utils/widgets.js.map +0 -1
  288. package/build/test.cjs +0 -136
  289. package/build/test.cjs.map +0 -7
  290. package/build/test.js +0 -189
  291. package/build/test.js.map +0 -1
  292. package/build/types.cjs +0 -19
  293. package/build/types.cjs.map +0 -7
  294. package/build/types.js +0 -3
  295. package/build/types.js.map +0 -1
@@ -1,309 +0,0 @@
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
-
20
- // packages/e2e-test-utils-playwright/src/metrics/index.ts
21
- var metrics_exports = {};
22
- __export(metrics_exports, {
23
- Metrics: () => Metrics
24
- });
25
- module.exports = __toCommonJS(metrics_exports);
26
- var import_path = require("path");
27
- var Metrics = class {
28
- browser;
29
- page;
30
- trace;
31
- webVitals = {};
32
- constructor({ page }) {
33
- this.page = page;
34
- this.browser = page.context().browser();
35
- this.trace = { traceEvents: [] };
36
- }
37
- /**
38
- * Returns durations from the Server-Timing header.
39
- *
40
- * @param fields Optional fields to filter.
41
- */
42
- async getServerTiming(fields = []) {
43
- return this.page.evaluate(
44
- (f) => performance.getEntriesByType(
45
- "navigation"
46
- )[0].serverTiming.reduce(
47
- (acc, entry) => {
48
- if (f.length === 0 || f.includes(entry.name)) {
49
- acc[entry.name] = entry.duration;
50
- }
51
- return acc;
52
- },
53
- {}
54
- ),
55
- fields
56
- );
57
- }
58
- /**
59
- * Returns time to first byte (TTFB) using the Navigation Timing API.
60
- *
61
- * @see https://web.dev/ttfb/#measure-ttfb-in-javascript
62
- *
63
- * @return TTFB value.
64
- */
65
- async getTimeToFirstByte() {
66
- return await this.page.evaluate(() => {
67
- const { responseStart, startTime } = performance.getEntriesByType(
68
- "navigation"
69
- )[0];
70
- return responseStart - startTime;
71
- });
72
- }
73
- /**
74
- * Returns the Largest Contentful Paint (LCP) value using the dedicated API.
75
- *
76
- * @see https://w3c.github.io/largest-contentful-paint/
77
- * @see https://web.dev/lcp/#measure-lcp-in-javascript
78
- *
79
- * @return LCP value.
80
- */
81
- async getLargestContentfulPaint() {
82
- return await this.page.evaluate(
83
- () => new Promise((resolve) => {
84
- new PerformanceObserver((entryList) => {
85
- const entries = entryList.getEntries();
86
- const largestPaintEntry = entries.at(-1);
87
- resolve(largestPaintEntry?.startTime || 0);
88
- }).observe({
89
- type: "largest-contentful-paint",
90
- buffered: true
91
- });
92
- })
93
- );
94
- }
95
- /**
96
- * Returns the Cumulative Layout Shift (CLS) value using the dedicated API.
97
- *
98
- * @see https://github.com/WICG/layout-instability
99
- * @see https://web.dev/cls/#measure-layout-shifts-in-javascript
100
- *
101
- * @return CLS value.
102
- */
103
- async getCumulativeLayoutShift() {
104
- return await this.page.evaluate(
105
- () => new Promise((resolve) => {
106
- let CLS = 0;
107
- new PerformanceObserver((l) => {
108
- const entries = l.getEntries();
109
- entries.forEach((entry) => {
110
- if (!entry.hadRecentInput) {
111
- CLS += entry.value;
112
- }
113
- });
114
- resolve(CLS);
115
- }).observe({
116
- type: "layout-shift",
117
- buffered: true
118
- });
119
- })
120
- );
121
- }
122
- /**
123
- * Returns the loading durations using the Navigation Timing API. All the
124
- * durations exclude the server response time.
125
- *
126
- * @return Object with loading metrics durations.
127
- */
128
- async getLoadingDurations() {
129
- return await this.page.evaluate(() => {
130
- const [
131
- {
132
- requestStart,
133
- responseStart,
134
- responseEnd,
135
- domContentLoadedEventEnd,
136
- loadEventEnd
137
- }
138
- ] = performance.getEntriesByType(
139
- "navigation"
140
- );
141
- const paintTimings = performance.getEntriesByType(
142
- "paint"
143
- );
144
- const firstPaintStartTime = paintTimings.find(
145
- ({ name }) => name === "first-paint"
146
- ).startTime;
147
- const firstContentfulPaintStartTime = paintTimings.find(
148
- ({ name }) => name === "first-contentful-paint"
149
- ).startTime;
150
- return {
151
- // Server side metric.
152
- serverResponse: responseStart - requestStart,
153
- // For client side metrics, consider the end of the response (the
154
- // browser receives the HTML) as the start time (0).
155
- firstPaint: firstPaintStartTime - responseEnd,
156
- domContentLoaded: domContentLoadedEventEnd - responseEnd,
157
- loaded: loadEventEnd - responseEnd,
158
- firstContentfulPaint: firstContentfulPaintStartTime - responseEnd,
159
- timeSinceResponseEnd: performance.now() - responseEnd
160
- };
161
- });
162
- }
163
- /**
164
- * Starts Chromium tracing with predefined options for performance testing.
165
- *
166
- * @param options Options to pass to `browser.startTracing()`.
167
- */
168
- async startTracing(options = {}) {
169
- return await this.browser.startTracing(this.page, {
170
- screenshots: false,
171
- categories: ["devtools.timeline"],
172
- ...options
173
- });
174
- }
175
- /**
176
- * Stops Chromium tracing and saves the trace.
177
- */
178
- async stopTracing() {
179
- const traceBuffer = await this.browser.stopTracing();
180
- const traceJSON = JSON.parse(traceBuffer.toString());
181
- this.trace = traceJSON;
182
- }
183
- /**
184
- * @return Durations of all traced `keydown`, `keypress`, and `keyup`
185
- * events.
186
- */
187
- getTypingEventDurations() {
188
- return [
189
- this.getEventDurations("keydown"),
190
- this.getEventDurations("keypress"),
191
- this.getEventDurations("keyup")
192
- ];
193
- }
194
- /**
195
- * @return Durations of all traced `focus` and `focusin` events.
196
- */
197
- getSelectionEventDurations() {
198
- return [
199
- this.getEventDurations("focus"),
200
- this.getEventDurations("focusin")
201
- ];
202
- }
203
- /**
204
- * @return Durations of all traced `click` events.
205
- */
206
- getClickEventDurations() {
207
- return [this.getEventDurations("click")];
208
- }
209
- /**
210
- * @return Durations of all traced `mouseover` and `mouseout` events.
211
- */
212
- getHoverEventDurations() {
213
- return [
214
- this.getEventDurations("mouseover"),
215
- this.getEventDurations("mouseout")
216
- ];
217
- }
218
- /**
219
- * @param eventType Type of event to filter.
220
- * @return Durations of all events of a given type.
221
- */
222
- getEventDurations(eventType) {
223
- if (this.trace.traceEvents.length === 0) {
224
- throw new Error(
225
- "No trace events found. Did you forget to call stopTracing()?"
226
- );
227
- }
228
- return this.trace.traceEvents.filter(
229
- (item) => item.cat === "devtools.timeline" && item.name === "EventDispatch" && item?.args?.data?.type === eventType && !!item.dur
230
- ).map((item) => item.dur ? item.dur / 1e3 : 0);
231
- }
232
- /**
233
- * Initializes the web-vitals library upon next page navigation.
234
- *
235
- * Defaults to automatically triggering the navigation,
236
- * but it can also be done manually.
237
- *
238
- * @example
239
- * ```js
240
- * await metrics.initWebVitals();
241
- * console.log( await metrics.getWebVitals() );
242
- * ```
243
- *
244
- * @example
245
- * ```js
246
- * await metrics.initWebVitals( false );
247
- * await page.goto( '/some-other-page' );
248
- * console.log( await metrics.getWebVitals() );
249
- * ```
250
- *
251
- * @param reload Whether to force navigation by reloading the current page.
252
- */
253
- async initWebVitals(reload = true) {
254
- await this.page.addInitScript({
255
- path: (0, import_path.join)(
256
- __dirname,
257
- "../../../../node_modules/web-vitals/dist/web-vitals.umd.cjs"
258
- )
259
- });
260
- await this.page.exposeFunction(
261
- "__reportVitals__",
262
- (data) => {
263
- const measurement = JSON.parse(data);
264
- this.webVitals[measurement.name] = measurement.value;
265
- }
266
- );
267
- await this.page.addInitScript(() => {
268
- const reportVitals = (measurement) => window.__reportVitals__(JSON.stringify(measurement));
269
- window.addEventListener("DOMContentLoaded", () => {
270
- window.webVitals.onCLS(reportVitals);
271
- window.webVitals.onFCP(reportVitals);
272
- window.webVitals.onFID(reportVitals);
273
- window.webVitals.onINP(reportVitals);
274
- window.webVitals.onLCP(reportVitals);
275
- window.webVitals.onTTFB(reportVitals);
276
- });
277
- });
278
- if (reload) {
279
- await this.page.reload();
280
- }
281
- }
282
- /**
283
- * Returns web vitals as collected by the web-vitals library.
284
- *
285
- * If the web-vitals library hasn't been loaded on the current page yet,
286
- * it will be initialized with a page reload.
287
- *
288
- * Reloads the page to force web-vitals to report all collected metrics.
289
- *
290
- * @return {WebVitalsMeasurements} Web vitals measurements.
291
- */
292
- async getWebVitals() {
293
- this.webVitals = {};
294
- const hasScript = await this.page.evaluate(
295
- // @ts-expect-error This is valid but web-vitals does not register the global types.
296
- () => typeof window.webVitals !== "undefined"
297
- );
298
- if (!hasScript) {
299
- await this.initWebVitals();
300
- }
301
- await this.page.reload();
302
- return this.webVitals;
303
- }
304
- };
305
- // Annotate the CommonJS export names for ESM import in node:
306
- 0 && (module.exports = {
307
- Metrics
308
- });
309
- //# sourceMappingURL=index.cjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/metrics/index.ts"],
4
- "sourcesContent": ["/**\n * External dependencies\n */\nimport type { Page, Browser } from '@playwright/test';\nimport { join } from 'path';\n// resolution-mode support in TypeScript 5.3 will resolve this.\n// See https://devblogs.microsoft.com/typescript/announcing-typescript-5-3-beta/\n// @ts-expect-error\nimport type { Metric } from 'web-vitals';\n\ntype EventType =\n\t| 'click'\n\t| 'focus'\n\t| 'focusin'\n\t| 'keydown'\n\t| 'keypress'\n\t| 'keyup'\n\t| 'mouseout'\n\t| 'mouseover';\n\ninterface TraceEvent {\n\tcat: string;\n\tname: string;\n\tdur?: number;\n\targs: {\n\t\tdata?: {\n\t\t\ttype: EventType;\n\t\t};\n\t};\n}\n\ninterface Trace {\n\ttraceEvents: TraceEvent[];\n}\n\ntype MetricsConstructorProps = {\n\tpage: Page;\n};\n\ninterface WebVitalsMeasurements {\n\tCLS?: number;\n\tFCP?: number;\n\tFID?: number;\n\tINP?: number;\n\tLCP?: number;\n\tTTFB?: number;\n}\n\nexport class Metrics {\n\tbrowser: Browser;\n\tpage: Page;\n\ttrace: Trace;\n\n\twebVitals: WebVitalsMeasurements = {};\n\n\tconstructor( { page }: MetricsConstructorProps ) {\n\t\tthis.page = page;\n\t\tthis.browser = page.context().browser()!;\n\t\tthis.trace = { traceEvents: [] };\n\t}\n\n\t/**\n\t * Returns durations from the Server-Timing header.\n\t *\n\t * @param fields Optional fields to filter.\n\t */\n\tasync getServerTiming( fields: string[] = [] ) {\n\t\treturn this.page.evaluate< Record< string, number >, string[] >(\n\t\t\t( f: string[] ) =>\n\t\t\t\t(\n\t\t\t\t\tperformance.getEntriesByType(\n\t\t\t\t\t\t'navigation'\n\t\t\t\t\t) as PerformanceNavigationTiming[]\n\t\t\t\t )[ 0 ].serverTiming.reduce(\n\t\t\t\t\t( acc, entry ) => {\n\t\t\t\t\t\tif ( f.length === 0 || f.includes( entry.name ) ) {\n\t\t\t\t\t\t\tacc[ entry.name ] = entry.duration;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn acc;\n\t\t\t\t\t},\n\t\t\t\t\t{} as Record< string, number >\n\t\t\t\t),\n\t\t\tfields\n\t\t);\n\t}\n\n\t/**\n\t * Returns time to first byte (TTFB) using the Navigation Timing API.\n\t *\n\t * @see https://web.dev/ttfb/#measure-ttfb-in-javascript\n\t *\n\t * @return TTFB value.\n\t */\n\tasync getTimeToFirstByte() {\n\t\treturn await this.page.evaluate< number >( () => {\n\t\t\tconst { responseStart, startTime } = (\n\t\t\t\tperformance.getEntriesByType(\n\t\t\t\t\t'navigation'\n\t\t\t\t) as PerformanceNavigationTiming[]\n\t\t\t )[ 0 ];\n\t\t\treturn responseStart - startTime;\n\t\t} );\n\t}\n\n\t/**\n\t * Returns the Largest Contentful Paint (LCP) value using the dedicated API.\n\t *\n\t * @see https://w3c.github.io/largest-contentful-paint/\n\t * @see https://web.dev/lcp/#measure-lcp-in-javascript\n\t *\n\t * @return LCP value.\n\t */\n\tasync getLargestContentfulPaint() {\n\t\treturn await this.page.evaluate< number >(\n\t\t\t() =>\n\t\t\t\tnew Promise( ( resolve ) => {\n\t\t\t\t\tnew PerformanceObserver( ( entryList ) => {\n\t\t\t\t\t\tconst entries = entryList.getEntries();\n\t\t\t\t\t\t// The last entry is the largest contentful paint.\n\t\t\t\t\t\tconst largestPaintEntry = entries.at( -1 );\n\n\t\t\t\t\t\tresolve( largestPaintEntry?.startTime || 0 );\n\t\t\t\t\t} ).observe( {\n\t\t\t\t\t\ttype: 'largest-contentful-paint',\n\t\t\t\t\t\tbuffered: true,\n\t\t\t\t\t} );\n\t\t\t\t} )\n\t\t);\n\t}\n\n\t/**\n\t * Returns the Cumulative Layout Shift (CLS) value using the dedicated API.\n\t *\n\t * @see https://github.com/WICG/layout-instability\n\t * @see https://web.dev/cls/#measure-layout-shifts-in-javascript\n\t *\n\t * @return CLS value.\n\t */\n\tasync getCumulativeLayoutShift() {\n\t\treturn await this.page.evaluate< number >(\n\t\t\t() =>\n\t\t\t\tnew Promise( ( resolve ) => {\n\t\t\t\t\tlet CLS = 0;\n\n\t\t\t\t\tnew PerformanceObserver( ( l ) => {\n\t\t\t\t\t\tconst entries = l.getEntries() as LayoutShift[];\n\n\t\t\t\t\t\tentries.forEach( ( entry ) => {\n\t\t\t\t\t\t\tif ( ! entry.hadRecentInput ) {\n\t\t\t\t\t\t\t\tCLS += entry.value;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\n\t\t\t\t\t\tresolve( CLS );\n\t\t\t\t\t} ).observe( {\n\t\t\t\t\t\ttype: 'layout-shift',\n\t\t\t\t\t\tbuffered: true,\n\t\t\t\t\t} );\n\t\t\t\t} )\n\t\t);\n\t}\n\n\t/**\n\t * Returns the loading durations using the Navigation Timing API. All the\n\t * durations exclude the server response time.\n\t *\n\t * @return Object with loading metrics durations.\n\t */\n\tasync getLoadingDurations() {\n\t\treturn await this.page.evaluate( () => {\n\t\t\tconst [\n\t\t\t\t{\n\t\t\t\t\trequestStart,\n\t\t\t\t\tresponseStart,\n\t\t\t\t\tresponseEnd,\n\t\t\t\t\tdomContentLoadedEventEnd,\n\t\t\t\t\tloadEventEnd,\n\t\t\t\t},\n\t\t\t] = performance.getEntriesByType(\n\t\t\t\t'navigation'\n\t\t\t) as PerformanceNavigationTiming[];\n\t\t\tconst paintTimings = performance.getEntriesByType(\n\t\t\t\t'paint'\n\t\t\t) as PerformancePaintTiming[];\n\n\t\t\tconst firstPaintStartTime = paintTimings.find(\n\t\t\t\t( { name } ) => name === 'first-paint'\n\t\t\t)!.startTime;\n\n\t\t\tconst firstContentfulPaintStartTime = paintTimings.find(\n\t\t\t\t( { name } ) => name === 'first-contentful-paint'\n\t\t\t)!.startTime;\n\n\t\t\treturn {\n\t\t\t\t// Server side metric.\n\t\t\t\tserverResponse: responseStart - requestStart,\n\t\t\t\t// For client side metrics, consider the end of the response (the\n\t\t\t\t// browser receives the HTML) as the start time (0).\n\t\t\t\tfirstPaint: firstPaintStartTime - responseEnd,\n\t\t\t\tdomContentLoaded: domContentLoadedEventEnd - responseEnd,\n\t\t\t\tloaded: loadEventEnd - responseEnd,\n\t\t\t\tfirstContentfulPaint:\n\t\t\t\t\tfirstContentfulPaintStartTime - responseEnd,\n\t\t\t\ttimeSinceResponseEnd: performance.now() - responseEnd,\n\t\t\t};\n\t\t} );\n\t}\n\n\t/**\n\t * Starts Chromium tracing with predefined options for performance testing.\n\t *\n\t * @param options Options to pass to `browser.startTracing()`.\n\t */\n\tasync startTracing( options = {} ) {\n\t\treturn await this.browser.startTracing( this.page, {\n\t\t\tscreenshots: false,\n\t\t\tcategories: [ 'devtools.timeline' ],\n\t\t\t...options,\n\t\t} );\n\t}\n\n\t/**\n\t * Stops Chromium tracing and saves the trace.\n\t */\n\tasync stopTracing() {\n\t\tconst traceBuffer = await this.browser.stopTracing();\n\t\tconst traceJSON = JSON.parse( traceBuffer.toString() );\n\n\t\tthis.trace = traceJSON;\n\t}\n\n\t/**\n\t * @return Durations of all traced `keydown`, `keypress`, and `keyup`\n\t * events.\n\t */\n\tgetTypingEventDurations() {\n\t\treturn [\n\t\t\tthis.getEventDurations( 'keydown' ),\n\t\t\tthis.getEventDurations( 'keypress' ),\n\t\t\tthis.getEventDurations( 'keyup' ),\n\t\t];\n\t}\n\n\t/**\n\t * @return Durations of all traced `focus` and `focusin` events.\n\t */\n\tgetSelectionEventDurations() {\n\t\treturn [\n\t\t\tthis.getEventDurations( 'focus' ),\n\t\t\tthis.getEventDurations( 'focusin' ),\n\t\t];\n\t}\n\n\t/**\n\t * @return Durations of all traced `click` events.\n\t */\n\tgetClickEventDurations() {\n\t\treturn [ this.getEventDurations( 'click' ) ];\n\t}\n\n\t/**\n\t * @return Durations of all traced `mouseover` and `mouseout` events.\n\t */\n\tgetHoverEventDurations() {\n\t\treturn [\n\t\t\tthis.getEventDurations( 'mouseover' ),\n\t\t\tthis.getEventDurations( 'mouseout' ),\n\t\t];\n\t}\n\n\t/**\n\t * @param eventType Type of event to filter.\n\t * @return Durations of all events of a given type.\n\t */\n\tgetEventDurations( eventType: EventType ) {\n\t\tif ( this.trace.traceEvents.length === 0 ) {\n\t\t\tthrow new Error(\n\t\t\t\t'No trace events found. Did you forget to call stopTracing()?'\n\t\t\t);\n\t\t}\n\n\t\treturn this.trace.traceEvents\n\t\t\t.filter(\n\t\t\t\t( item: TraceEvent ): boolean =>\n\t\t\t\t\titem.cat === 'devtools.timeline' &&\n\t\t\t\t\titem.name === 'EventDispatch' &&\n\t\t\t\t\titem?.args?.data?.type === eventType &&\n\t\t\t\t\t!! item.dur\n\t\t\t)\n\t\t\t.map( ( item ) => ( item.dur ? item.dur / 1000 : 0 ) );\n\t}\n\n\t/**\n\t * Initializes the web-vitals library upon next page navigation.\n\t *\n\t * Defaults to automatically triggering the navigation,\n\t * but it can also be done manually.\n\t *\n\t * @example\n\t * ```js\n\t * await metrics.initWebVitals();\n\t * console.log( await metrics.getWebVitals() );\n\t * ```\n\t *\n\t * @example\n\t * ```js\n\t * await metrics.initWebVitals( false );\n\t * await page.goto( '/some-other-page' );\n\t * console.log( await metrics.getWebVitals() );\n\t * ```\n\t *\n\t * @param reload Whether to force navigation by reloading the current page.\n\t */\n\tasync initWebVitals( reload = true ) {\n\t\tawait this.page.addInitScript( {\n\t\t\tpath: join(\n\t\t\t\t__dirname,\n\t\t\t\t'../../../../node_modules/web-vitals/dist/web-vitals.umd.cjs'\n\t\t\t),\n\t\t} );\n\n\t\tawait this.page.exposeFunction(\n\t\t\t'__reportVitals__',\n\t\t\t( data: string ) => {\n\t\t\t\tconst measurement: Metric = JSON.parse( data );\n\t\t\t\tthis.webVitals[ measurement.name ] = measurement.value;\n\t\t\t}\n\t\t);\n\n\t\tawait this.page.addInitScript( () => {\n\t\t\tconst reportVitals = ( measurement: unknown ) =>\n\t\t\t\twindow.__reportVitals__( JSON.stringify( measurement ) );\n\n\t\t\twindow.addEventListener( 'DOMContentLoaded', () => {\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onCLS( reportVitals );\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onFCP( reportVitals );\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onFID( reportVitals );\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onINP( reportVitals );\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onLCP( reportVitals );\n\t\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t\twindow.webVitals.onTTFB( reportVitals );\n\t\t\t} );\n\t\t} );\n\n\t\tif ( reload ) {\n\t\t\t// By reloading the page the script will be applied.\n\t\t\tawait this.page.reload();\n\t\t}\n\t}\n\n\t/**\n\t * Returns web vitals as collected by the web-vitals library.\n\t *\n\t * If the web-vitals library hasn't been loaded on the current page yet,\n\t * it will be initialized with a page reload.\n\t *\n\t * Reloads the page to force web-vitals to report all collected metrics.\n\t *\n\t * @return {WebVitalsMeasurements} Web vitals measurements.\n\t */\n\tasync getWebVitals() {\n\t\t// Reset values.\n\t\tthis.webVitals = {};\n\n\t\tconst hasScript = await this.page.evaluate(\n\t\t\t// @ts-expect-error This is valid but web-vitals does not register the global types.\n\t\t\t() => typeof window.webVitals !== 'undefined'\n\t\t);\n\n\t\tif ( ! hasScript ) {\n\t\t\tawait this.initWebVitals();\n\t\t}\n\n\t\t// Trigger navigation so the web-vitals library reports values on unload.\n\t\tawait this.page.reload();\n\n\t\treturn this.webVitals;\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,kBAAqB;AA4Cd,IAAM,UAAN,MAAc;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAmC,CAAC;AAAA,EAEpC,YAAa,EAAE,KAAK,GAA6B;AAChD,SAAK,OAAO;AACZ,SAAK,UAAU,KAAK,QAAQ,EAAE,QAAQ;AACtC,SAAK,QAAQ,EAAE,aAAa,CAAC,EAAE;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBAAiB,SAAmB,CAAC,GAAI;AAC9C,WAAO,KAAK,KAAK;AAAA,MAChB,CAAE,MAEA,YAAY;AAAA,QACX;AAAA,MACD,EACG,CAAE,EAAE,aAAa;AAAA,QACpB,CAAE,KAAK,UAAW;AACjB,cAAK,EAAE,WAAW,KAAK,EAAE,SAAU,MAAM,IAAK,GAAI;AACjD,gBAAK,MAAM,IAAK,IAAI,MAAM;AAAA,UAC3B;AACA,iBAAO;AAAA,QACR;AAAA,QACA,CAAC;AAAA,MACF;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAAqB;AAC1B,WAAO,MAAM,KAAK,KAAK,SAAoB,MAAM;AAChD,YAAM,EAAE,eAAe,UAAU,IAChC,YAAY;AAAA,QACX;AAAA,MACD,EACG,CAAE;AACN,aAAO,gBAAgB;AAAA,IACxB,CAAE;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,4BAA4B;AACjC,WAAO,MAAM,KAAK,KAAK;AAAA,MACtB,MACC,IAAI,QAAS,CAAE,YAAa;AAC3B,YAAI,oBAAqB,CAAE,cAAe;AACzC,gBAAM,UAAU,UAAU,WAAW;AAErC,gBAAM,oBAAoB,QAAQ,GAAI,EAAG;AAEzC,kBAAS,mBAAmB,aAAa,CAAE;AAAA,QAC5C,CAAE,EAAE,QAAS;AAAA,UACZ,MAAM;AAAA,UACN,UAAU;AAAA,QACX,CAAE;AAAA,MACH,CAAE;AAAA,IACJ;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,2BAA2B;AAChC,WAAO,MAAM,KAAK,KAAK;AAAA,MACtB,MACC,IAAI,QAAS,CAAE,YAAa;AAC3B,YAAI,MAAM;AAEV,YAAI,oBAAqB,CAAE,MAAO;AACjC,gBAAM,UAAU,EAAE,WAAW;AAE7B,kBAAQ,QAAS,CAAE,UAAW;AAC7B,gBAAK,CAAE,MAAM,gBAAiB;AAC7B,qBAAO,MAAM;AAAA,YACd;AAAA,UACD,CAAE;AAEF,kBAAS,GAAI;AAAA,QACd,CAAE,EAAE,QAAS;AAAA,UACZ,MAAM;AAAA,UACN,UAAU;AAAA,QACX,CAAE;AAAA,MACH,CAAE;AAAA,IACJ;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBAAsB;AAC3B,WAAO,MAAM,KAAK,KAAK,SAAU,MAAM;AACtC,YAAM;AAAA,QACL;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACD;AAAA,MACD,IAAI,YAAY;AAAA,QACf;AAAA,MACD;AACA,YAAM,eAAe,YAAY;AAAA,QAChC;AAAA,MACD;AAEA,YAAM,sBAAsB,aAAa;AAAA,QACxC,CAAE,EAAE,KAAK,MAAO,SAAS;AAAA,MAC1B,EAAG;AAEH,YAAM,gCAAgC,aAAa;AAAA,QAClD,CAAE,EAAE,KAAK,MAAO,SAAS;AAAA,MAC1B,EAAG;AAEH,aAAO;AAAA;AAAA,QAEN,gBAAgB,gBAAgB;AAAA;AAAA;AAAA,QAGhC,YAAY,sBAAsB;AAAA,QAClC,kBAAkB,2BAA2B;AAAA,QAC7C,QAAQ,eAAe;AAAA,QACvB,sBACC,gCAAgC;AAAA,QACjC,sBAAsB,YAAY,IAAI,IAAI;AAAA,MAC3C;AAAA,IACD,CAAE;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAc,UAAU,CAAC,GAAI;AAClC,WAAO,MAAM,KAAK,QAAQ,aAAc,KAAK,MAAM;AAAA,MAClD,aAAa;AAAA,MACb,YAAY,CAAE,mBAAoB;AAAA,MAClC,GAAG;AAAA,IACJ,CAAE;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc;AACnB,UAAM,cAAc,MAAM,KAAK,QAAQ,YAAY;AACnD,UAAM,YAAY,KAAK,MAAO,YAAY,SAAS,CAAE;AAErD,SAAK,QAAQ;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,0BAA0B;AACzB,WAAO;AAAA,MACN,KAAK,kBAAmB,SAAU;AAAA,MAClC,KAAK,kBAAmB,UAAW;AAAA,MACnC,KAAK,kBAAmB,OAAQ;AAAA,IACjC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,6BAA6B;AAC5B,WAAO;AAAA,MACN,KAAK,kBAAmB,OAAQ;AAAA,MAChC,KAAK,kBAAmB,SAAU;AAAA,IACnC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,yBAAyB;AACxB,WAAO,CAAE,KAAK,kBAAmB,OAAQ,CAAE;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,yBAAyB;AACxB,WAAO;AAAA,MACN,KAAK,kBAAmB,WAAY;AAAA,MACpC,KAAK,kBAAmB,UAAW;AAAA,IACpC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAmB,WAAuB;AACzC,QAAK,KAAK,MAAM,YAAY,WAAW,GAAI;AAC1C,YAAM,IAAI;AAAA,QACT;AAAA,MACD;AAAA,IACD;AAEA,WAAO,KAAK,MAAM,YAChB;AAAA,MACA,CAAE,SACD,KAAK,QAAQ,uBACb,KAAK,SAAS,mBACd,MAAM,MAAM,MAAM,SAAS,aAC3B,CAAC,CAAE,KAAK;AAAA,IACV,EACC,IAAK,CAAE,SAAY,KAAK,MAAM,KAAK,MAAM,MAAO,CAAI;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,cAAe,SAAS,MAAO;AACpC,UAAM,KAAK,KAAK,cAAe;AAAA,MAC9B,UAAM;AAAA,QACL;AAAA,QACA;AAAA,MACD;AAAA,IACD,CAAE;AAEF,UAAM,KAAK,KAAK;AAAA,MACf;AAAA,MACA,CAAE,SAAkB;AACnB,cAAM,cAAsB,KAAK,MAAO,IAAK;AAC7C,aAAK,UAAW,YAAY,IAAK,IAAI,YAAY;AAAA,MAClD;AAAA,IACD;AAEA,UAAM,KAAK,KAAK,cAAe,MAAM;AACpC,YAAM,eAAe,CAAE,gBACtB,OAAO,iBAAkB,KAAK,UAAW,WAAY,CAAE;AAExD,aAAO,iBAAkB,oBAAoB,MAAM;AAElD,eAAO,UAAU,MAAO,YAAa;AAErC,eAAO,UAAU,MAAO,YAAa;AAErC,eAAO,UAAU,MAAO,YAAa;AAErC,eAAO,UAAU,MAAO,YAAa;AAErC,eAAO,UAAU,MAAO,YAAa;AAErC,eAAO,UAAU,OAAQ,YAAa;AAAA,MACvC,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,QAAS;AAEb,YAAM,KAAK,KAAK,OAAO;AAAA,IACxB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,eAAe;AAEpB,SAAK,YAAY,CAAC;AAElB,UAAM,YAAY,MAAM,KAAK,KAAK;AAAA;AAAA,MAEjC,MAAM,OAAO,OAAO,cAAc;AAAA,IACnC;AAEA,QAAK,CAAE,WAAY;AAClB,YAAM,KAAK,cAAc;AAAA,IAC1B;AAGA,UAAM,KAAK,KAAK,OAAO;AAEvB,WAAO,KAAK;AAAA,EACb;AACD;",
6
- "names": []
7
- }
@@ -1,258 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Metrics = void 0;
4
- const path_1 = require("path");
5
- class Metrics {
6
- browser;
7
- page;
8
- trace;
9
- webVitals = {};
10
- constructor({ page }) {
11
- this.page = page;
12
- this.browser = page.context().browser();
13
- this.trace = { traceEvents: [] };
14
- }
15
- /**
16
- * Returns durations from the Server-Timing header.
17
- *
18
- * @param fields Optional fields to filter.
19
- */
20
- async getServerTiming(fields = []) {
21
- return this.page.evaluate((f) => performance.getEntriesByType('navigation')[0].serverTiming.reduce((acc, entry) => {
22
- if (f.length === 0 || f.includes(entry.name)) {
23
- acc[entry.name] = entry.duration;
24
- }
25
- return acc;
26
- }, {}), fields);
27
- }
28
- /**
29
- * Returns time to first byte (TTFB) using the Navigation Timing API.
30
- *
31
- * @see https://web.dev/ttfb/#measure-ttfb-in-javascript
32
- *
33
- * @return TTFB value.
34
- */
35
- async getTimeToFirstByte() {
36
- return await this.page.evaluate(() => {
37
- const { responseStart, startTime } = performance.getEntriesByType('navigation')[0];
38
- return responseStart - startTime;
39
- });
40
- }
41
- /**
42
- * Returns the Largest Contentful Paint (LCP) value using the dedicated API.
43
- *
44
- * @see https://w3c.github.io/largest-contentful-paint/
45
- * @see https://web.dev/lcp/#measure-lcp-in-javascript
46
- *
47
- * @return LCP value.
48
- */
49
- async getLargestContentfulPaint() {
50
- return await this.page.evaluate(() => new Promise((resolve) => {
51
- new PerformanceObserver((entryList) => {
52
- const entries = entryList.getEntries();
53
- // The last entry is the largest contentful paint.
54
- const largestPaintEntry = entries.at(-1);
55
- resolve(largestPaintEntry?.startTime || 0);
56
- }).observe({
57
- type: 'largest-contentful-paint',
58
- buffered: true,
59
- });
60
- }));
61
- }
62
- /**
63
- * Returns the Cumulative Layout Shift (CLS) value using the dedicated API.
64
- *
65
- * @see https://github.com/WICG/layout-instability
66
- * @see https://web.dev/cls/#measure-layout-shifts-in-javascript
67
- *
68
- * @return CLS value.
69
- */
70
- async getCumulativeLayoutShift() {
71
- return await this.page.evaluate(() => new Promise((resolve) => {
72
- let CLS = 0;
73
- new PerformanceObserver((l) => {
74
- const entries = l.getEntries();
75
- entries.forEach((entry) => {
76
- if (!entry.hadRecentInput) {
77
- CLS += entry.value;
78
- }
79
- });
80
- resolve(CLS);
81
- }).observe({
82
- type: 'layout-shift',
83
- buffered: true,
84
- });
85
- }));
86
- }
87
- /**
88
- * Returns the loading durations using the Navigation Timing API. All the
89
- * durations exclude the server response time.
90
- *
91
- * @return Object with loading metrics durations.
92
- */
93
- async getLoadingDurations() {
94
- return await this.page.evaluate(() => {
95
- const [{ requestStart, responseStart, responseEnd, domContentLoadedEventEnd, loadEventEnd, },] = performance.getEntriesByType('navigation');
96
- const paintTimings = performance.getEntriesByType('paint');
97
- const firstPaintStartTime = paintTimings.find(({ name }) => name === 'first-paint').startTime;
98
- const firstContentfulPaintStartTime = paintTimings.find(({ name }) => name === 'first-contentful-paint').startTime;
99
- return {
100
- // Server side metric.
101
- serverResponse: responseStart - requestStart,
102
- // For client side metrics, consider the end of the response (the
103
- // browser receives the HTML) as the start time (0).
104
- firstPaint: firstPaintStartTime - responseEnd,
105
- domContentLoaded: domContentLoadedEventEnd - responseEnd,
106
- loaded: loadEventEnd - responseEnd,
107
- firstContentfulPaint: firstContentfulPaintStartTime - responseEnd,
108
- timeSinceResponseEnd: performance.now() - responseEnd,
109
- };
110
- });
111
- }
112
- /**
113
- * Starts Chromium tracing with predefined options for performance testing.
114
- *
115
- * @param options Options to pass to `browser.startTracing()`.
116
- */
117
- async startTracing(options = {}) {
118
- return await this.browser.startTracing(this.page, {
119
- screenshots: false,
120
- categories: ['devtools.timeline'],
121
- ...options,
122
- });
123
- }
124
- /**
125
- * Stops Chromium tracing and saves the trace.
126
- */
127
- async stopTracing() {
128
- const traceBuffer = await this.browser.stopTracing();
129
- const traceJSON = JSON.parse(traceBuffer.toString());
130
- this.trace = traceJSON;
131
- }
132
- /**
133
- * @return Durations of all traced `keydown`, `keypress`, and `keyup`
134
- * events.
135
- */
136
- getTypingEventDurations() {
137
- return [
138
- this.getEventDurations('keydown'),
139
- this.getEventDurations('keypress'),
140
- this.getEventDurations('keyup'),
141
- ];
142
- }
143
- /**
144
- * @return Durations of all traced `focus` and `focusin` events.
145
- */
146
- getSelectionEventDurations() {
147
- return [
148
- this.getEventDurations('focus'),
149
- this.getEventDurations('focusin'),
150
- ];
151
- }
152
- /**
153
- * @return Durations of all traced `click` events.
154
- */
155
- getClickEventDurations() {
156
- return [this.getEventDurations('click')];
157
- }
158
- /**
159
- * @return Durations of all traced `mouseover` and `mouseout` events.
160
- */
161
- getHoverEventDurations() {
162
- return [
163
- this.getEventDurations('mouseover'),
164
- this.getEventDurations('mouseout'),
165
- ];
166
- }
167
- /**
168
- * @param eventType Type of event to filter.
169
- * @return Durations of all events of a given type.
170
- */
171
- getEventDurations(eventType) {
172
- if (this.trace.traceEvents.length === 0) {
173
- throw new Error('No trace events found. Did you forget to call stopTracing()?');
174
- }
175
- return this.trace.traceEvents
176
- .filter((item) => item.cat === 'devtools.timeline' &&
177
- item.name === 'EventDispatch' &&
178
- item?.args?.data?.type === eventType &&
179
- !!item.dur)
180
- .map((item) => (item.dur ? item.dur / 1000 : 0));
181
- }
182
- /**
183
- * Initializes the web-vitals library upon next page navigation.
184
- *
185
- * Defaults to automatically triggering the navigation,
186
- * but it can also be done manually.
187
- *
188
- * @example
189
- * ```js
190
- * await metrics.initWebVitals();
191
- * console.log( await metrics.getWebVitals() );
192
- * ```
193
- *
194
- * @example
195
- * ```js
196
- * await metrics.initWebVitals( false );
197
- * await page.goto( '/some-other-page' );
198
- * console.log( await metrics.getWebVitals() );
199
- * ```
200
- *
201
- * @param reload Whether to force navigation by reloading the current page.
202
- */
203
- async initWebVitals(reload = true) {
204
- await this.page.addInitScript({
205
- path: (0, path_1.join)(__dirname, '../../../../node_modules/web-vitals/dist/web-vitals.umd.cjs'),
206
- });
207
- await this.page.exposeFunction('__reportVitals__', (data) => {
208
- const measurement = JSON.parse(data);
209
- this.webVitals[measurement.name] = measurement.value;
210
- });
211
- await this.page.addInitScript(() => {
212
- const reportVitals = (measurement) => window.__reportVitals__(JSON.stringify(measurement));
213
- window.addEventListener('DOMContentLoaded', () => {
214
- // @ts-expect-error This is valid but web-vitals does not register the global types.
215
- window.webVitals.onCLS(reportVitals);
216
- // @ts-expect-error This is valid but web-vitals does not register the global types.
217
- window.webVitals.onFCP(reportVitals);
218
- // @ts-expect-error This is valid but web-vitals does not register the global types.
219
- window.webVitals.onFID(reportVitals);
220
- // @ts-expect-error This is valid but web-vitals does not register the global types.
221
- window.webVitals.onINP(reportVitals);
222
- // @ts-expect-error This is valid but web-vitals does not register the global types.
223
- window.webVitals.onLCP(reportVitals);
224
- // @ts-expect-error This is valid but web-vitals does not register the global types.
225
- window.webVitals.onTTFB(reportVitals);
226
- });
227
- });
228
- if (reload) {
229
- // By reloading the page the script will be applied.
230
- await this.page.reload();
231
- }
232
- }
233
- /**
234
- * Returns web vitals as collected by the web-vitals library.
235
- *
236
- * If the web-vitals library hasn't been loaded on the current page yet,
237
- * it will be initialized with a page reload.
238
- *
239
- * Reloads the page to force web-vitals to report all collected metrics.
240
- *
241
- * @return {WebVitalsMeasurements} Web vitals measurements.
242
- */
243
- async getWebVitals() {
244
- // Reset values.
245
- this.webVitals = {};
246
- const hasScript = await this.page.evaluate(
247
- // @ts-expect-error This is valid but web-vitals does not register the global types.
248
- () => typeof window.webVitals !== 'undefined');
249
- if (!hasScript) {
250
- await this.initWebVitals();
251
- }
252
- // Trigger navigation so the web-vitals library reports values on unload.
253
- await this.page.reload();
254
- return this.webVitals;
255
- }
256
- }
257
- exports.Metrics = Metrics;
258
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"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"}