@argos-ci/vitest 0.4.2 → 0.4.3

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.
@@ -25,7 +25,7 @@ declare function resetTesterScale(ctx: BrowserCommandContext): Promise<() => Pro
25
25
  * box is not painted, so the iframe must be sized to hold the content.
26
26
  *
27
27
  * @param size - The viewport size, `"default"` to keep the natural size, or
28
- * `"initial"` to restore the size backed up on the first resize.
28
+ * `"initial"` to restore the size the iframe had before Argos resized it.
29
29
  * @param options.fullPage - When `true`, grow the height to fit the content
30
30
  * while keeping the viewport width (Playwright-style full page).
31
31
  */
@@ -35,9 +35,12 @@ declare function setIframeViewportSize(ctx: BrowserCommandContext, size: Viewpor
35
35
  /**
36
36
  * Grow the Vitest iframe to fit its content so nothing is clipped.
37
37
  *
38
- * This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
39
- * because `setIframeViewportSize` sizes the iframe *before* the content's final
40
- * size is known. It only ever grows the iframe, never shrinks it.
38
+ * This must run once the content has reached its final size — after `argosCSS`
39
+ * (which may inject a `zoom`) is applied *and* after stabilization has waited
40
+ * for images and fonts. `setIframeViewportSize` sizes the iframe before any of
41
+ * that, so it can't account for the final content size. It only ever grows the
42
+ * iframe, never shrinks it; use `setIframeViewportSize(ctx, "initial")` to
43
+ * restore the original size afterwards.
41
44
  *
42
45
  * @param options.fitWidth - Also grow the iframe horizontally to paint content
43
46
  * wider than the viewport. When `false`, only the height grows (to match
@@ -55,9 +58,9 @@ declare function fitIframeToContent(ctx: BrowserCommandContext, options: {
55
58
  * on. It:
56
59
  * - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
57
60
  * iframe resize, not Playwright);
58
- * - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
59
- * (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
60
- * content would be clipped;
61
+ * - wraps `beforeScreenshot` so the iframe is grown to fit once the content has
62
+ * settled (see {@link fitIframeToContent}) — otherwise wide/tall content
63
+ * would be clipped;
61
64
  * - captures the iframe's `<body>` via `@argos-ci/playwright`.
62
65
  *
63
66
  * @param config.fitWidth - Grow the iframe horizontally as well as vertically
package/dist/internal.mjs CHANGED
@@ -12,6 +12,15 @@ const VITEST_IFRAME_SELECTOR = "iframe[data-vitest=\"true\"]";
12
12
  */
13
13
  const VITEST_TESTER_ID = "vitest-tester";
14
14
  /**
15
+ * Attribute holding the iframe's inline size from before Argos resized it, as
16
+ * JSON.
17
+ *
18
+ * The presence of the attribute — not the values it holds — is what marks the
19
+ * size as backed up: the original `style.width`/`style.height` are usually
20
+ * empty strings, which are indistinguishable from "nothing was saved yet".
21
+ */
22
+ const SIZE_BACKUP_ATTRIBUTE = "data-argos-size-backup";
23
+ /**
15
24
  * Remove the scale from the Vitest `#vitest-tester` element before taking a
16
25
  * screenshot to avoid ending up with small screenshots.
17
26
  * @returns A function to restore the scale after the screenshot.
@@ -40,26 +49,29 @@ async function resetTesterScale(ctx) {
40
49
  * box is not painted, so the iframe must be sized to hold the content.
41
50
  *
42
51
  * @param size - The viewport size, `"default"` to keep the natural size, or
43
- * `"initial"` to restore the size backed up on the first resize.
52
+ * `"initial"` to restore the size the iframe had before Argos resized it.
44
53
  * @param options.fullPage - When `true`, grow the height to fit the content
45
54
  * while keeping the viewport width (Playwright-style full page).
46
55
  */
47
56
  async function setIframeViewportSize(ctx, size, options = {}) {
48
- await ctx.page.evaluate(({ size, fullPage, selector }) => {
57
+ await ctx.page.evaluate(({ size, fullPage, selector, backupAttribute }) => {
49
58
  const iframe = document.querySelector(selector);
50
59
  if (!(iframe instanceof HTMLIFrameElement)) throw new Error("Vitest iframe not found");
51
60
  if (!iframe.contentDocument) throw new Error("Vitest iframe contentDocument not found");
52
61
  if (size === "initial") {
53
- if (iframe.dataset.initialWidth && iframe.dataset.initialHeight) {
54
- iframe.style.width = iframe.dataset.initialWidth;
55
- iframe.style.height = iframe.dataset.initialHeight;
62
+ const backup = iframe.getAttribute(backupAttribute);
63
+ if (backup !== null) {
64
+ const { width, height } = JSON.parse(backup);
65
+ iframe.style.width = width;
66
+ iframe.style.height = height;
67
+ iframe.removeAttribute(backupAttribute);
56
68
  }
57
69
  return;
58
70
  }
59
- if (!iframe.dataset.initialWidth && !iframe.dataset.initialHeight) {
60
- iframe.dataset.initialWidth = iframe.style.width;
61
- iframe.dataset.initialHeight = iframe.style.height;
62
- }
71
+ if (!iframe.hasAttribute(backupAttribute)) iframe.setAttribute(backupAttribute, JSON.stringify({
72
+ width: iframe.style.width,
73
+ height: iframe.style.height
74
+ }));
63
75
  if (size !== "default") iframe.style.width = `${size.width}px`;
64
76
  if (fullPage) {
65
77
  if (!iframe.contentWindow) throw new Error(`Can't access iframe window`);
@@ -73,24 +85,32 @@ async function setIframeViewportSize(ctx, size, options = {}) {
73
85
  }, {
74
86
  size,
75
87
  fullPage: options.fullPage ?? false,
76
- selector: VITEST_IFRAME_SELECTOR
88
+ selector: VITEST_IFRAME_SELECTOR,
89
+ backupAttribute: SIZE_BACKUP_ATTRIBUTE
77
90
  });
78
91
  }
79
92
  /**
80
93
  * Grow the Vitest iframe to fit its content so nothing is clipped.
81
94
  *
82
- * This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
83
- * because `setIframeViewportSize` sizes the iframe *before* the content's final
84
- * size is known. It only ever grows the iframe, never shrinks it.
95
+ * This must run once the content has reached its final size — after `argosCSS`
96
+ * (which may inject a `zoom`) is applied *and* after stabilization has waited
97
+ * for images and fonts. `setIframeViewportSize` sizes the iframe before any of
98
+ * that, so it can't account for the final content size. It only ever grows the
99
+ * iframe, never shrinks it; use `setIframeViewportSize(ctx, "initial")` to
100
+ * restore the original size afterwards.
85
101
  *
86
102
  * @param options.fitWidth - Also grow the iframe horizontally to paint content
87
103
  * wider than the viewport. When `false`, only the height grows (to match
88
104
  * Playwright's `fullPage` semantics: full height, viewport width).
89
105
  */
90
106
  async function fitIframeToContent(ctx, options) {
91
- await ctx.page.evaluate(({ fitWidth, selector }) => {
107
+ await ctx.page.evaluate(({ fitWidth, selector, backupAttribute }) => {
92
108
  const iframe = document.querySelector(selector);
93
109
  if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return;
110
+ if (!iframe.hasAttribute(backupAttribute)) iframe.setAttribute(backupAttribute, JSON.stringify({
111
+ width: iframe.style.width,
112
+ height: iframe.style.height
113
+ }));
94
114
  const { body, documentElement } = iframe.contentDocument;
95
115
  const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, documentElement.scrollHeight);
96
116
  if (contentHeight > iframe.clientHeight) iframe.style.height = `${contentHeight}px`;
@@ -100,7 +120,8 @@ async function fitIframeToContent(ctx, options) {
100
120
  }
101
121
  }, {
102
122
  fitWidth: options.fitWidth,
103
- selector: VITEST_IFRAME_SELECTOR
123
+ selector: VITEST_IFRAME_SELECTOR,
124
+ backupAttribute: SIZE_BACKUP_ATTRIBUTE
104
125
  });
105
126
  }
106
127
  //#endregion
@@ -112,9 +133,9 @@ async function fitIframeToContent(ctx, options) {
112
133
  * on. It:
113
134
  * - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
114
135
  * iframe resize, not Playwright);
115
- * - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
116
- * (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
117
- * content would be clipped;
136
+ * - wraps `beforeScreenshot` so the iframe is grown to fit once the content has
137
+ * settled (see {@link fitIframeToContent}) — otherwise wide/tall content
138
+ * would be clipped;
118
139
  * - captures the iframe's `<body>` via `@argos-ci/playwright`.
119
140
  *
120
141
  * @param config.fitWidth - Grow the iframe horizontally as well as vertically
@@ -127,6 +148,7 @@ async function screenshotFrame(ctx, name, options, config) {
127
148
  ...rest,
128
149
  beforeScreenshot: async (api) => {
129
150
  await userBeforeScreenshot?.(api);
151
+ await api.runStabilization();
130
152
  await fitIframeToContent(ctx, { fitWidth: config.fitWidth });
131
153
  }
132
154
  };
package/dist/plugin.mjs CHANGED
@@ -16,6 +16,15 @@ const VITEST_IFRAME_SELECTOR = "iframe[data-vitest=\"true\"]";
16
16
  */
17
17
  const VITEST_TESTER_ID = "vitest-tester";
18
18
  /**
19
+ * Attribute holding the iframe's inline size from before Argos resized it, as
20
+ * JSON.
21
+ *
22
+ * The presence of the attribute — not the values it holds — is what marks the
23
+ * size as backed up: the original `style.width`/`style.height` are usually
24
+ * empty strings, which are indistinguishable from "nothing was saved yet".
25
+ */
26
+ const SIZE_BACKUP_ATTRIBUTE = "data-argos-size-backup";
27
+ /**
19
28
  * Remove the scale from the Vitest `#vitest-tester` element before taking a
20
29
  * screenshot to avoid ending up with small screenshots.
21
30
  * @returns A function to restore the scale after the screenshot.
@@ -44,26 +53,29 @@ async function resetTesterScale(ctx) {
44
53
  * box is not painted, so the iframe must be sized to hold the content.
45
54
  *
46
55
  * @param size - The viewport size, `"default"` to keep the natural size, or
47
- * `"initial"` to restore the size backed up on the first resize.
56
+ * `"initial"` to restore the size the iframe had before Argos resized it.
48
57
  * @param options.fullPage - When `true`, grow the height to fit the content
49
58
  * while keeping the viewport width (Playwright-style full page).
50
59
  */
51
60
  async function setIframeViewportSize(ctx, size, options = {}) {
52
- await ctx.page.evaluate(({ size, fullPage, selector }) => {
61
+ await ctx.page.evaluate(({ size, fullPage, selector, backupAttribute }) => {
53
62
  const iframe = document.querySelector(selector);
54
63
  if (!(iframe instanceof HTMLIFrameElement)) throw new Error("Vitest iframe not found");
55
64
  if (!iframe.contentDocument) throw new Error("Vitest iframe contentDocument not found");
56
65
  if (size === "initial") {
57
- if (iframe.dataset.initialWidth && iframe.dataset.initialHeight) {
58
- iframe.style.width = iframe.dataset.initialWidth;
59
- iframe.style.height = iframe.dataset.initialHeight;
66
+ const backup = iframe.getAttribute(backupAttribute);
67
+ if (backup !== null) {
68
+ const { width, height } = JSON.parse(backup);
69
+ iframe.style.width = width;
70
+ iframe.style.height = height;
71
+ iframe.removeAttribute(backupAttribute);
60
72
  }
61
73
  return;
62
74
  }
63
- if (!iframe.dataset.initialWidth && !iframe.dataset.initialHeight) {
64
- iframe.dataset.initialWidth = iframe.style.width;
65
- iframe.dataset.initialHeight = iframe.style.height;
66
- }
75
+ if (!iframe.hasAttribute(backupAttribute)) iframe.setAttribute(backupAttribute, JSON.stringify({
76
+ width: iframe.style.width,
77
+ height: iframe.style.height
78
+ }));
67
79
  if (size !== "default") iframe.style.width = `${size.width}px`;
68
80
  if (fullPage) {
69
81
  if (!iframe.contentWindow) throw new Error(`Can't access iframe window`);
@@ -77,24 +89,32 @@ async function setIframeViewportSize(ctx, size, options = {}) {
77
89
  }, {
78
90
  size,
79
91
  fullPage: options.fullPage ?? false,
80
- selector: VITEST_IFRAME_SELECTOR
92
+ selector: VITEST_IFRAME_SELECTOR,
93
+ backupAttribute: SIZE_BACKUP_ATTRIBUTE
81
94
  });
82
95
  }
83
96
  /**
84
97
  * Grow the Vitest iframe to fit its content so nothing is clipped.
85
98
  *
86
- * This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
87
- * because `setIframeViewportSize` sizes the iframe *before* the content's final
88
- * size is known. It only ever grows the iframe, never shrinks it.
99
+ * This must run once the content has reached its final size — after `argosCSS`
100
+ * (which may inject a `zoom`) is applied *and* after stabilization has waited
101
+ * for images and fonts. `setIframeViewportSize` sizes the iframe before any of
102
+ * that, so it can't account for the final content size. It only ever grows the
103
+ * iframe, never shrinks it; use `setIframeViewportSize(ctx, "initial")` to
104
+ * restore the original size afterwards.
89
105
  *
90
106
  * @param options.fitWidth - Also grow the iframe horizontally to paint content
91
107
  * wider than the viewport. When `false`, only the height grows (to match
92
108
  * Playwright's `fullPage` semantics: full height, viewport width).
93
109
  */
94
110
  async function fitIframeToContent(ctx, options) {
95
- await ctx.page.evaluate(({ fitWidth, selector }) => {
111
+ await ctx.page.evaluate(({ fitWidth, selector, backupAttribute }) => {
96
112
  const iframe = document.querySelector(selector);
97
113
  if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return;
114
+ if (!iframe.hasAttribute(backupAttribute)) iframe.setAttribute(backupAttribute, JSON.stringify({
115
+ width: iframe.style.width,
116
+ height: iframe.style.height
117
+ }));
98
118
  const { body, documentElement } = iframe.contentDocument;
99
119
  const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, documentElement.scrollHeight);
100
120
  if (contentHeight > iframe.clientHeight) iframe.style.height = `${contentHeight}px`;
@@ -104,7 +124,8 @@ async function fitIframeToContent(ctx, options) {
104
124
  }
105
125
  }, {
106
126
  fitWidth: options.fitWidth,
107
- selector: VITEST_IFRAME_SELECTOR
127
+ selector: VITEST_IFRAME_SELECTOR,
128
+ backupAttribute: SIZE_BACKUP_ATTRIBUTE
108
129
  });
109
130
  }
110
131
  //#endregion
@@ -116,9 +137,9 @@ async function fitIframeToContent(ctx, options) {
116
137
  * on. It:
117
138
  * - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
118
139
  * iframe resize, not Playwright);
119
- * - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
120
- * (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
121
- * content would be clipped;
140
+ * - wraps `beforeScreenshot` so the iframe is grown to fit once the content has
141
+ * settled (see {@link fitIframeToContent}) — otherwise wide/tall content
142
+ * would be clipped;
122
143
  * - captures the iframe's `<body>` via `@argos-ci/playwright`.
123
144
  *
124
145
  * @param config.fitWidth - Grow the iframe horizontally as well as vertically
@@ -131,6 +152,7 @@ async function screenshotFrame(ctx, name, options, config) {
131
152
  ...rest,
132
153
  beforeScreenshot: async (api) => {
133
154
  await userBeforeScreenshot?.(api);
155
+ await api.runStabilization();
134
156
  await fitIframeToContent(ctx, { fitWidth: config.fitWidth });
135
157
  }
136
158
  };
@@ -209,6 +231,7 @@ const createArgosScreenshotCommand = (pluginOptions = {}) => {
209
231
  }
210
232
  return attachments;
211
233
  } finally {
234
+ await setIframeViewportSize(ctx, "initial");
212
235
  await restore();
213
236
  }
214
237
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@argos-ci/vitest",
3
3
  "description": "Vitest SDK for visual testing with Argos.",
4
- "version": "0.4.2",
4
+ "version": "0.4.3",
5
5
  "author": "Smooth Code",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "dependencies": {
54
54
  "@argos-ci/browser": "6.4.5",
55
55
  "@argos-ci/core": "6.7.2",
56
- "@argos-ci/playwright": "7.4.2",
56
+ "@argos-ci/playwright": "7.4.3",
57
57
  "@argos-ci/util": "4.1.0"
58
58
  },
59
59
  "peerDependencies": {
@@ -81,5 +81,5 @@
81
81
  "check-format": "prettier --check --ignore-unknown --ignore-path=./.gitignore --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .",
82
82
  "lint": "eslint ."
83
83
  },
84
- "gitHead": "3268a9ff953f019494367e3bf87ea520268b6ed2"
84
+ "gitHead": "fd20b0af878b8186d6950c2e9c4ab381ca3fcaa1"
85
85
  }