@qoretechnologies/qlip 0.1.3-pr.30.gc7b05dc → 0.1.3-pr.31.g4b62f97

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 (35) hide show
  1. package/README.md +16 -1
  2. package/dist/config/parameters.d.ts +1 -0
  3. package/dist/config/parameters.d.ts.map +1 -1
  4. package/dist/config/parameters.js +3 -0
  5. package/dist/plugin/vitestPlugin.d.ts.map +1 -1
  6. package/dist/plugin/vitestPlugin.js +7 -3
  7. package/dist/runtime/fullPage.d.ts +5 -3
  8. package/dist/runtime/fullPage.d.ts.map +1 -1
  9. package/dist/runtime/fullPage.js +5 -3
  10. package/dist/runtime/screenshot.d.ts.map +1 -1
  11. package/dist/runtime/screenshot.js +8 -4
  12. package/dist/stories/Page.stories.d.ts +7 -0
  13. package/dist/stories/Page.stories.d.ts.map +1 -1
  14. package/dist/stories/Page.stories.js +11 -0
  15. package/dist/stories/TallPage.stories.d.ts +11 -1
  16. package/dist/stories/TallPage.stories.d.ts.map +1 -1
  17. package/dist/stories/TallPage.stories.js +16 -1
  18. package/dist/test-runner/index.d.ts.map +1 -1
  19. package/dist/test-runner/index.js +3 -0
  20. package/dist/types.d.ts +40 -10
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/upload/autodetect.d.ts +23 -0
  23. package/dist/upload/autodetect.d.ts.map +1 -1
  24. package/dist/upload/autodetect.js +19 -0
  25. package/dist/upload/finalize.d.ts.map +1 -1
  26. package/dist/upload/finalize.js +12 -1
  27. package/dist/upload/upload.d.ts.map +1 -1
  28. package/dist/upload/upload.js +12 -0
  29. package/dist-cjs/config/parameters.js +3 -0
  30. package/dist-cjs/runtime/fullPage.js +5 -3
  31. package/dist-cjs/test-runner/index.js +3 -0
  32. package/dist-cjs/upload/autodetect.js +21 -1
  33. package/dist-cjs/upload/finalize.js +11 -0
  34. package/dist-cjs/upload/upload.js +12 -0
  35. package/package.json +1 -1
package/README.md CHANGED
@@ -165,6 +165,15 @@ qlip prints a one-time warning when it detects a shallow clone. It
165
165
  never fails the upload over this — accuracy degrades, the run still
166
166
  succeeds.
167
167
 
168
+ ### Who triggered the build
169
+
170
+ On GitHub Actions qlip also sends the user who triggered the run
171
+ (`$GITHUB_ACTOR`, with `$GITHUB_ACTOR_ID` for a rename-proof avatar), so
172
+ the dashboard shows who pushed each build. Nothing to configure; set
173
+ `upload.triggeredByLogin` (and optionally `upload.triggeredById`) to
174
+ override it, for example from a CI system that isn't GitHub Actions.
175
+ Outside CI the field is simply omitted.
176
+
168
177
  ## Manual screenshots inside play
169
178
 
170
179
  Use the existing **"Logged In"** story as a real-world example:
@@ -194,6 +203,7 @@ export const LoggedIn = {
194
203
  qlip: {
195
204
  skip: false,
196
205
  viewport: { width: 1280, height: 720 },
206
+ fullSizeCaptures: false,
197
207
  disableAnimations: true,
198
208
  pauseAnimationsAtEnd: false,
199
209
  fullPage: false,
@@ -219,6 +229,7 @@ Options precedence:
219
229
  qlipVitestPlugin({
220
230
  outputDir: './qlip/screenshots',
221
231
  viewport: { width: 1280, height: 720 },
232
+ fullSizeCaptures: false,
222
233
  disableAnimations: false,
223
234
  pauseAnimationsAtEnd: false,
224
235
  fullPage: false,
@@ -231,7 +242,11 @@ qlipVitestPlugin({
231
242
  });
232
243
  ```
233
244
 
234
- `fullPage` grows the viewport to the page's full content height for the capture and puts it back afterwards. A story that pins a phone viewport is otherwise photographed one screen tall; a browser "full page" screenshot would not help, because an app shell usually scrolls an inner box rather than the document. qlip measures the document and every scrolling element, grows the viewport by what they hide, and re-measures after each growth until nothing scrolls. Two kinds of box are not chased. A box that keeps its height when the viewport grows (a fixed-height code block, a capped list) stays scrolled, and the growth it asked for is given back, so the capture is not padded with blank space. Content that grows with the viewport (a tile sized to its column, a `vh` hero) would ask for more every round; it stops at the height its real content asked for. The height is capped at `fullPageMaxHeight` (default 10000); a page taller than that is captured at the cap with a `[qlip] fullPage:` warning naming the story. Under Vitest browser mode the test iframe is scaled down to fit the browser window, so the plugin also grows the window — the Playwright context viewport — through a browser command it registers (`qlipBrowserViewport`), and restores it first; the capture is 1:1, not a shrunk picture of a tall page. The same holds for any pinned `viewport` the window cannot hold: an 844px-tall phone story in a 720px window used to come back at 85%, and now grows the window too. The test-runner path grows its Playwright viewport the same way. The manifest keeps reporting the pinned `viewport` — that is the baseline key; the image is simply taller, and a capture whose content height changed shows on the server as a dimensions change rather than a pixel diff.
245
+ `fullPage` grows the viewport to the page's full content height for the capture and puts it back afterwards. A story that pins a phone viewport is otherwise photographed one screen tall; a browser "full page" screenshot would not help, because an app shell usually scrolls an inner box rather than the document. qlip measures the document and every scrolling element, grows the viewport by what they hide, and re-measures after each growth until nothing scrolls. Two kinds of box are not chased. A box that keeps its height when the viewport grows (a fixed-height code block, a capped list) stays scrolled, and the growth it asked for is given back, so the capture is not padded with blank space. Content that grows with the viewport (a tile sized to its column, a `vh` hero) would ask for more every round; it stops at the height its real content asked for. The height is capped at `fullPageMaxHeight` (default 10000); a page taller than that is captured at the cap with a `[qlip] fullPage:` warning naming the story. Under Vitest browser mode the test iframe is scaled down to fit the browser window, so a `fullPage` capture also grows the window — the Playwright context viewport — through a browser command the plugin registers (`qlipBrowserViewport`), and restores it first; the capture is 1:1, not a shrunk picture of a tall page. `fullSizeCaptures` does the same for every capture (below). The test-runner path grows its Playwright viewport the same way. The manifest keeps reporting the pinned `viewport` — that is the baseline key; the image is simply taller, and a capture whose content height changed shows on the server as a dimensions change rather than a pixel diff.
246
+
247
+ `fullSizeCaptures` captures every story at the full size of its `viewport`. Under Vitest browser mode the browser window stays at the Playwright context viewport — 1280×720 headless, unless you configure it — whatever the story's `viewport` says, and Vitest scales the test iframe down to fit the window. The layout is the story's, but the pixels are shrunk: a 1920×1080 story comes back 1280×720, a 390×844 phone story 333×720, with its text and any fine difference shrunk with it. With `fullSizeCaptures: true` the window grows to hold the viewport for each capture and is put back afterwards, so the PNG is exactly the viewport. The runner path always captures at full size, and `fullPage` always grows the window.
248
+
249
+ It is off by default, so upgrading qlip changes no baselines, and it will become the default in a future minor release. Turning it on changes the size of every capture whose viewport is larger than the window (stories that fit it are unaffected), and the server reports each of those as changed against its baseline, a dimensions change. Turn it on in one change on your default branch and accept those captures once.
235
250
 
236
251
  `waitForIdleMs` waits for DOM mutations to settle before taking a screenshot. This is especially useful for animation libraries like `react-spring` that update inline styles via `requestAnimationFrame`, which bypasses CSS-based animation disabling. Increase it if you still catch mid-transition frames, or lower it for faster runs when your UI is static. `maxWaitForIdleMs` caps the wait so stories with continuously changing UI still complete.
237
252
 
@@ -5,6 +5,7 @@ export interface QlipResolvedOptions {
5
5
  width: number;
6
6
  height: number;
7
7
  };
8
+ fullSizeCaptures: boolean;
8
9
  disableAnimations: boolean;
9
10
  pauseAnimationsAtEnd: boolean;
10
11
  disableBackdropFilter: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"parameters.d.ts","sourceRoot":"","sources":["../../src/config/parameters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5G,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,yBAAyB,EAAE,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,kBAAkB,GAAI,gCAIhC;IACD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC,KAAG,mBAqDH,CAAC"}
1
+ {"version":3,"file":"parameters.d.ts","sourceRoot":"","sources":["../../src/config/parameters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5G,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,yBAAyB,EAAE,MAAM,EAAE,CAAC;CACrC;AAED,eAAO,MAAM,kBAAkB,GAAI,gCAIhC;IACD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC,KAAG,mBAyDH,CAAC"}
@@ -2,6 +2,9 @@ export const resolveQlipOptions = ({ defaults, story, override, }) => {
2
2
  return {
3
3
  skip: override?.skip ?? story?.skip ?? defaults.skip,
4
4
  viewport: override?.viewport ?? story?.viewport ?? defaults.viewport,
5
+ fullSizeCaptures: override?.fullSizeCaptures ??
6
+ story?.fullSizeCaptures ??
7
+ defaults.fullSizeCaptures,
5
8
  disableAnimations: override?.disableAnimations ??
6
9
  story?.disableAnimations ??
7
10
  defaults.disableAnimations,
@@ -1 +1 @@
1
- {"version":3,"file":"vitestPlugin.d.ts","sourceRoot":"","sources":["../../src/plugin/vitestPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAU5C,OAAO,EAAE,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AAanE,eAAO,MAAM,gBAAgB,GAAI,UAAS,iBAAsB,KAAG,MA8QlE,CAAC"}
1
+ {"version":3,"file":"vitestPlugin.d.ts","sourceRoot":"","sources":["../../src/plugin/vitestPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAU5C,OAAO,EAAE,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AAanE,eAAO,MAAM,gBAAgB,GAAI,UAAS,iBAAsB,KAAG,MAkRlE,CAAC"}
@@ -33,6 +33,9 @@ export const qlipVitestPlugin = (options = {}) => {
33
33
  defaults: {
34
34
  outputDir,
35
35
  viewport: options.viewport ?? DEFAULT_VIEWPORT,
36
+ // Off by default — turning it on resizes every capture that was
37
+ // being scaled to fit the window. See QlipCaptureOptions.
38
+ fullSizeCaptures: options.fullSizeCaptures ?? false,
36
39
  skip: false,
37
40
  disableAnimations: options.disableAnimations ?? false,
38
41
  pauseAnimationsAtEnd: options.pauseAnimationsAtEnd ?? false,
@@ -190,9 +193,10 @@ export const qlipVitestPlugin = (options = {}) => {
190
193
  setupFiles: [setupFile],
191
194
  globalSetup: [globalSetupFile],
192
195
  reporters: reportersList,
193
- // The runtime grows the browser window for a `fullPage` capture
194
- // through this command; it runs here, where the Playwright page
195
- // is. `mergeConfig` merges it beside any commands the consumer
196
+ // The runtime grows the browser window for a `fullPage` capture,
197
+ // and for every capture with `fullSizeCaptures`, through this
198
+ // command; it runs here, where the Playwright page is.
199
+ // `mergeConfig` merges it beside any commands the consumer
196
200
  // declares.
197
201
  browser: {
198
202
  commands: {
@@ -10,9 +10,11 @@
10
10
  * viewport) stays as configured, and Vitest scales the iframe down to fit it.
11
11
  * A 3,600px-tall iframe in a 1,440px window is photographed at 40% — the
12
12
  * whole page, unreadable — and so is a plain 844px-tall phone story in a
13
- * 720px window, at 85%. `createCaptureViewport` therefore grows the window
14
- * to hold whatever viewport a capture asks for, pinned or grown, and puts
15
- * both back afterwards, the window first, so the iframe lands at scale 1.
13
+ * 720px window, at 85%. `createCaptureViewport` therefore grows the window,
14
+ * when it is given one, to hold whatever viewport a capture asks for, pinned
15
+ * or grown, and puts both back afterwards, the window first, so the iframe
16
+ * lands at scale 1. The runtime gives it the window for a `fullPage` capture
17
+ * and, with `fullSizeCaptures` on, for every capture.
16
18
  *
17
19
  * This module has no imports: the runtime, the test-runner and the unit
18
20
  * project (which has no DOM and feeds it fakes) all share it.
@@ -1 +1 @@
1
- {"version":3,"file":"fullPage.d.ts","sourceRoot":"","sources":["../../src/runtime/fullPage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,GAAG,kBAAkB,CAAC,CAAC;QACrE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAAC,CAAC;KACvD,CAAC;CACH,KAAG,MAsEH,CAAC;AAEF,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAAU,mEAOzC;IACD,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,KAAG,OAAO,CAAC,cAAc,CAmBzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,QAAQ,cAAc,EACtB,WAAW,MAAM,KAChB,MAAM,GAAG,IAKF,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACnC,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,KACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,6CAInC;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC7C,KAAG;IACF,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CA4C9B,CAAC"}
1
+ {"version":3,"file":"fullPage.d.ts","sourceRoot":"","sources":["../../src/runtime/fullPage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,GAAG,kBAAkB,CAAC,CAAC;QACrE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAAC,CAAC;KACvD,CAAC;CACH,KAAG,MAsEH,CAAC;AAEF,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAAU,mEAOzC;IACD,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,KAAG,OAAO,CAAC,cAAc,CAmBzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,QAAQ,cAAc,EACtB,WAAW,MAAM,KAChB,MAAM,GAAG,IAKF,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACnC,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,KACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,6CAInC;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC7C,KAAG;IACF,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CA4C9B,CAAC"}
@@ -10,9 +10,11 @@
10
10
  * viewport) stays as configured, and Vitest scales the iframe down to fit it.
11
11
  * A 3,600px-tall iframe in a 1,440px window is photographed at 40% — the
12
12
  * whole page, unreadable — and so is a plain 844px-tall phone story in a
13
- * 720px window, at 85%. `createCaptureViewport` therefore grows the window
14
- * to hold whatever viewport a capture asks for, pinned or grown, and puts
15
- * both back afterwards, the window first, so the iframe lands at scale 1.
13
+ * 720px window, at 85%. `createCaptureViewport` therefore grows the window,
14
+ * when it is given one, to hold whatever viewport a capture asks for, pinned
15
+ * or grown, and puts both back afterwards, the window first, so the iframe
16
+ * lands at scale 1. The runtime gives it the window for a `fullPage` capture
17
+ * and, with `fullSizeCaptures` on, for every capture.
16
18
  *
17
19
  * This module has no imports: the runtime, the test-runner and the unit
18
20
  * project (which has no DOM and feeds it fakes) all share it.
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/runtime/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,qBAAqB,EACrB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAqCrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AA8L1C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACrC,SAAS,MAAM;AACf;;;;;;;GAOG;AACH,gBAAgB,MAAM,KACrB;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAiCvD,CAAC;AAwDF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GAAI,qEAIxC;IACD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;CAChC,SA+DA,CAAC;AA4bF,eAAO,MAAM,UAAU,GACrB,KAAK,gBAAgB,EACrB,gBAAgB,MAAM,GAAG,qBAAqB,EAC9C,UAAU,qBAAqB,kBA0BhC,CAAC;AAEF,KAAK,eAAe,GAAG,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AA8ClE,eAAO,MAAM,qBAAqB,GAAU,KAAK,eAAe,kBAiG/D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,KAAK,eAAe,kBAkEhE,CAAC"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/runtime/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,qBAAqB,EACrB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAqCrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AA8L1C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACrC,SAAS,MAAM;AACf;;;;;;;GAOG;AACH,gBAAgB,MAAM,KACrB;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAiCvD,CAAC;AAwDF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GAAI,qEAIxC;IACD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;CAChC,SA+DA,CAAC;AAicF,eAAO,MAAM,UAAU,GACrB,KAAK,gBAAgB,EACrB,gBAAgB,MAAM,GAAG,qBAAqB,EAC9C,UAAU,qBAAqB,kBA0BhC,CAAC;AAEF,KAAK,eAAe,GAAG,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AA8ClE,eAAO,MAAM,qBAAqB,GAAU,KAAK,eAAe,kBAiG/D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,KAAK,eAAe,kBAkEhE,CAAC"}
@@ -491,13 +491,17 @@ const captureScreenshot = async ({ kind, story, screenshotName, options, testErr
491
491
  // throws.
492
492
  let error = testError ?? null;
493
493
  let cleanupMasks = null;
494
- // The browser window grows to hold whatever viewport the capture asks for,
495
- // pinned or full-page, or the capture comes back scaled down to fit the
496
- // window (see ./fullPage.ts).
494
+ // Unless the browser window grows with it, a viewport larger than the
495
+ // window is captured scaled down to fit it (see ./fullPage.ts). Growing it
496
+ // resizes captures that already have baselines, so it is opt-in
497
+ // (`fullSizeCaptures`); a `fullPage` capture always grows it, since scaled
498
+ // it is unreadable.
497
499
  const captureViewport = createCaptureViewport({
498
500
  pinned: resolved.viewport,
499
501
  iframe: (width, height) => page.viewport(width, height),
500
- window: commands.qlipBrowserViewport,
502
+ window: resolved.fullSizeCaptures || resolved.fullPage
503
+ ? commands.qlipBrowserViewport
504
+ : undefined,
501
505
  });
502
506
  try {
503
507
  await captureViewport.set(resolved.viewport.width, resolved.viewport.height);
@@ -9,6 +9,13 @@ declare const meta: {
9
9
  export default meta;
10
10
  type Story = StoryObj<typeof meta>;
11
11
  export declare const LoggedOut: Story;
12
+ /**
13
+ * A desktop viewport wider and taller than the 1280×720 headless browser
14
+ * window. `fullSizeCaptures` grows the window to hold it, so the capture is
15
+ * 1920×1080; without it Vitest would scale the story down to 1280×720. The
16
+ * e2e test reads the PNG.
17
+ */
18
+ export declare const DesktopFullSize: Story;
12
19
  export declare const LoggedIn: Story;
13
20
  export declare const ElementWithFullHeight: Story;
14
21
  export declare const WithConsoleError: Story;
@@ -1 +1 @@
1
- {"version":3,"file":"Page.stories.d.ts","sourceRoot":"","sources":["../../src/stories/Page.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAO5D,QAAA,MAAM,IAAI;;;;;;CAOmB,CAAC;AAE9B,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAGnC,eAAO,MAAM,SAAS,EAAE,KAAU,CAAC;AAGnC,eAAO,MAAM,QAAQ,EAAE,KAatB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,KAQnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAS9B,CAAC"}
1
+ {"version":3,"file":"Page.stories.d.ts","sourceRoot":"","sources":["../../src/stories/Page.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAO5D,QAAA,MAAM,IAAI;;;;;;CAOmB,CAAC;AAE9B,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAGnC,eAAO,MAAM,SAAS,EAAE,KAAU,CAAC;AAEnC;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KAI7B,CAAC;AAGF,eAAO,MAAM,QAAQ,EAAE,KAatB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,KAQnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAS9B,CAAC"}
@@ -12,6 +12,17 @@ const meta = {
12
12
  };
13
13
  export default meta;
14
14
  export const LoggedOut = {};
15
+ /**
16
+ * A desktop viewport wider and taller than the 1280×720 headless browser
17
+ * window. `fullSizeCaptures` grows the window to hold it, so the capture is
18
+ * 1920×1080; without it Vitest would scale the story down to 1280×720. The
19
+ * e2e test reads the PNG.
20
+ */
21
+ export const DesktopFullSize = {
22
+ parameters: {
23
+ qlip: { viewport: { width: 1920, height: 1080 }, fullSizeCaptures: true },
24
+ },
25
+ };
15
26
  // More on component testing: https://storybook.js.org/docs/writing-tests/interaction-testing
16
27
  export const LoggedIn = {
17
28
  play: async (ctx) => {
@@ -15,8 +15,18 @@ type Story = StoryObj<typeof meta>;
15
15
  * The manifest keeps the pinned viewport as the baseline key.
16
16
  */
17
17
  export declare const ScrollsAnInnerBox: Story;
18
- /** The same page without `fullPage`: one screen, the rest scrolled away. */
18
+ /**
19
+ * The same page without `fullPage`: one screen, the rest scrolled away. The
20
+ * 844px-tall viewport does not fit the 720px headless browser window, so
21
+ * Vitest scales the story down to fit it and the capture comes back 333×720
22
+ * — the size such a capture has always had. The e2e test reads it.
23
+ */
19
24
  export declare const OneScreen: Story;
25
+ /**
26
+ * `OneScreen` with `fullSizeCaptures`: qlip grows the browser window to hold
27
+ * the viewport, so the capture is exactly 390×844.
28
+ */
29
+ export declare const OneScreenFullSize: Story;
20
30
  /**
21
31
  * `fullPage` on an ordinary page whose only scroller is a fixed-height box:
22
32
  * more viewport never reveals it, so qlip gives the growth back and the
@@ -1 +1 @@
1
- {"version":3,"file":"TallPage.stories.d.ts","sourceRoot":"","sources":["../../src/stories/TallPage.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAK5D,QAAA,MAAM,IAAI;;;;;;CAMuB,CAAC;AAElC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAgBnC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAK/B,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,SAAS,EAAE,KAKvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,KAU5B,CAAC"}
1
+ {"version":3,"file":"TallPage.stories.d.ts","sourceRoot":"","sources":["../../src/stories/TallPage.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAK5D,QAAA,MAAM,IAAI;;;;;;CAMuB,CAAC;AAElC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAgBnC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAK/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAKvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAK/B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,KAU5B,CAAC"}
@@ -33,13 +33,28 @@ export const ScrollsAnInnerBox = {
33
33
  },
34
34
  play: expectsAnInnerScroller,
35
35
  };
36
- /** The same page without `fullPage`: one screen, the rest scrolled away. */
36
+ /**
37
+ * The same page without `fullPage`: one screen, the rest scrolled away. The
38
+ * 844px-tall viewport does not fit the 720px headless browser window, so
39
+ * Vitest scales the story down to fit it and the capture comes back 333×720
40
+ * — the size such a capture has always had. The e2e test reads it.
41
+ */
37
42
  export const OneScreen = {
38
43
  parameters: {
39
44
  qlip: { viewport: PHONE },
40
45
  },
41
46
  play: expectsAnInnerScroller,
42
47
  };
48
+ /**
49
+ * `OneScreen` with `fullSizeCaptures`: qlip grows the browser window to hold
50
+ * the viewport, so the capture is exactly 390×844.
51
+ */
52
+ export const OneScreenFullSize = {
53
+ parameters: {
54
+ qlip: { viewport: PHONE, fullSizeCaptures: true },
55
+ },
56
+ play: expectsAnInnerScroller,
57
+ };
43
58
  /**
44
59
  * `fullPage` on an ordinary page whose only scroller is a fixed-height box:
45
60
  * more viewport never reveals it, so qlip gives the growth back and the
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAkBH,OAAO,KAAK,EACV,kBAAkB,EAGlB,iBAAiB,EACjB,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,YAAY,EAIb,MAAM,qBAAqB,CAAC;AAS7B;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE,UAAU,CAAC,IAAI,EAAE;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACtE;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,cAAc,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CAC1D;AA+HD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GACtB,MAAM,mBAAmB,EACzB,SAAS,gBAAgB,EACzB,UAAS,mBAAwB,KAChC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAelC,CAAC;AA0JF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAkBH,OAAO,KAAK,EACV,kBAAkB,EAGlB,iBAAiB,EACjB,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,YAAY,EAIb,MAAM,qBAAqB,CAAC;AAS7B;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE,UAAU,CAAC,IAAI,EAAE;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACtE;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;QAAE,UAAU,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,cAAc,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CAC1D;AAkID;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GACtB,MAAM,mBAAmB,EACzB,SAAS,gBAAgB,EACzB,UAAS,mBAAwB,KAChC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAelC,CAAC;AA0JF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -60,6 +60,9 @@ const buildResolvedDefaults = (override) => {
60
60
  return {
61
61
  outputDir: DEFAULT_OUTPUT_DIR,
62
62
  viewport: fallbackViewport,
63
+ // Not read on this path: `page.setViewportSize` sizes the page itself,
64
+ // so every capture here is full size already.
65
+ fullSizeCaptures: override?.fullSizeCaptures ?? false,
63
66
  skip: false,
64
67
  disableAnimations: override?.disableAnimations ?? false,
65
68
  pauseAnimationsAtEnd: override?.pauseAnimationsAtEnd ?? false,
package/dist/types.d.ts CHANGED
@@ -6,12 +6,29 @@ export interface QlipCaptureOptions {
6
6
  skip?: boolean;
7
7
  /**
8
8
  * The capture's viewport, and the baseline key on the server. Under
9
- * Vitest browser mode a viewport the browser window cannot hold grows the
10
- * window for the capture (and puts it back), because Vitest otherwise
11
- * scales the test iframe down to fit and the capture comes back smaller
12
- * than the viewport it names.
9
+ * Vitest browser mode a viewport larger than the browser window is
10
+ * captured scaled down to fit it, unless `fullSizeCaptures` is on.
13
11
  */
14
12
  viewport?: QlipViewport;
13
+ /**
14
+ * Capture at the full size of `viewport`, pixel for pixel. Under Vitest
15
+ * browser mode the browser window (the Playwright context viewport,
16
+ * 1280×720 headless unless configured) does not follow `viewport`: Vitest
17
+ * sizes the test iframe to the viewport and scales the iframe down to fit
18
+ * the window. The layout is the viewport's, but the pixels are shrunk: a
19
+ * 1920×1080 story is captured at 1280×720, a 390×844 phone story at
20
+ * 333×720. With this on, the window grows to hold the viewport for the
21
+ * capture and is put back afterwards, so the capture is exactly `viewport`.
22
+ *
23
+ * Off by default: turning it on changes the size of every capture that was
24
+ * being scaled, and the server reports each one as changed against its
25
+ * baseline, so accept them once. A `fullPage` capture grows the window
26
+ * either way. The Storybook test-runner path sizes the page's own viewport
27
+ * and always captures at full size.
28
+ *
29
+ * @default false
30
+ */
31
+ fullSizeCaptures?: boolean;
15
32
  disableAnimations?: boolean;
16
33
  pauseAnimationsAtEnd?: boolean;
17
34
  /**
@@ -38,12 +55,12 @@ export interface QlipCaptureOptions {
38
55
  * scrolled and gives its growth back, and content that grows with the
39
56
  * viewport is not chased. Capped at `fullPageMaxHeight`. Under Vitest
40
57
  * browser mode the browser window (the Playwright context viewport) grows
41
- * with the test iframe,
42
- * or the capture would be scaled down to fit the window; both are put
43
- * back afterwards. The manifest still reports the pinned `viewport` —
44
- * that is the baseline key; the picture is simply taller. A capture whose
45
- * content height changed shows on the server as a dimensions change
46
- * rather than a pixel diff.
58
+ * with the test iframe whatever `fullSizeCaptures` says, or the capture
59
+ * would be scaled down to fit the window; both are put back afterwards.
60
+ * The manifest still reports the pinned `viewport` — that is the baseline
61
+ * key; the picture is simply taller. A capture whose content height
62
+ * changed shows on the server as a dimensions change rather than a pixel
63
+ * diff.
47
64
  *
48
65
  * @default false
49
66
  */
@@ -132,6 +149,19 @@ export interface QlipUploadOptions {
132
149
  * "View PR" link. See `design/UPLOAD.md`.
133
150
  */
134
151
  pullRequestUrl?: string;
152
+ /**
153
+ * GitHub login of the user who triggered the build — shown as the
154
+ * build's author in the dashboard. Auto-detected from `$GITHUB_ACTOR`
155
+ * when omitted. Undefined (not sent) outside GitHub Actions; a
156
+ * dashboard upload is attributed from the signed-in session instead.
157
+ */
158
+ triggeredByLogin?: string;
159
+ /**
160
+ * GitHub account id of that user (`$GITHUB_ACTOR_ID`). Stable across
161
+ * a login rename, so the server prefers it for the avatar. Only sent
162
+ * alongside `triggeredByLogin`.
163
+ */
164
+ triggeredById?: number;
135
165
  /** Skip upload entirely (handy for local-only runs). */
136
166
  disabled?: boolean;
137
167
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,29 @@ export declare const autodetectBaseBranch: () => string | undefined;
38
38
  * `qlip-server/design/UPLOAD.md`.
39
39
  */
40
40
  export declare const autodetectPullRequestUrl: () => string | undefined;
41
+ /** The GitHub user whose action produced the build. */
42
+ export interface IUploadTriggeredBy {
43
+ /** GitHub login (`$GITHUB_ACTOR`). */
44
+ login: string;
45
+ /**
46
+ * GitHub account id (`$GITHUB_ACTOR_ID`). Stable across a rename, so
47
+ * the server prefers it for the avatar; omitted on an older runner
48
+ * that doesn't set it.
49
+ */
50
+ id?: number;
51
+ }
52
+ /**
53
+ * Who triggered the CI run — the user the dashboard shows as the
54
+ * build's author. GitHub Actions sets `$GITHUB_ACTOR` (the login) and
55
+ * `$GITHUB_ACTOR_ID` (the numeric account id) on every run.
56
+ *
57
+ * Env-only by design: outside CI there is no GitHub identity to read
58
+ * (a git author is an email, not a login), and a dashboard upload is
59
+ * attributed server-side from the signed-in session. Returns undefined
60
+ * for local runs; the server stores it as nullable, so absence just
61
+ * means "author unknown". See `qlip-server/design/UPLOAD.md`.
62
+ */
63
+ export declare const autodetectTriggeredBy: () => IUploadTriggeredBy | undefined;
41
64
  /**
42
65
  * Commit SHAs from HEAD backward, nearest-first, capped at
43
66
  * MAX_ANCESTOR_COMMITS. The server walks this list to find the nearest
@@ -1 +1 @@
1
- {"version":3,"file":"autodetect.d.ts","sourceRoot":"","sources":["../../src/upload/autodetect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAqBH,eAAO,MAAM,gBAAgB,QAAO,MAAM,GAAG,SAW5C,CAAC;AAkCF,eAAO,MAAM,gBAAgB,QAAO,MAAM,GAAG,SAS5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SAGhD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAAO,MAAM,GAAG,SAcpD,CAAC;AA0BF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,QAAO,MAAM,EAAE,GAAG,SAgBvD,CAAC"}
1
+ {"version":3,"file":"autodetect.d.ts","sourceRoot":"","sources":["../../src/upload/autodetect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAqBH,eAAO,MAAM,gBAAgB,QAAO,MAAM,GAAG,SAW5C,CAAC;AAkCF,eAAO,MAAM,gBAAgB,QAAO,MAAM,GAAG,SAS5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SAGhD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAAO,MAAM,GAAG,SAcpD,CAAC;AAEF,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,QAAO,kBAAkB,GAAG,SAM7D,CAAC;AA0BF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,QAAO,MAAM,EAAE,GAAG,SAgBvD,CAAC"}
@@ -127,6 +127,25 @@ export const autodetectPullRequestUrl = () => {
127
127
  const base = serverUrl.replace(/\/+$/, '');
128
128
  return `${base}/${repo}/pull/${prNumber}`;
129
129
  };
130
+ /**
131
+ * Who triggered the CI run — the user the dashboard shows as the
132
+ * build's author. GitHub Actions sets `$GITHUB_ACTOR` (the login) and
133
+ * `$GITHUB_ACTOR_ID` (the numeric account id) on every run.
134
+ *
135
+ * Env-only by design: outside CI there is no GitHub identity to read
136
+ * (a git author is an email, not a login), and a dashboard upload is
137
+ * attributed server-side from the signed-in session. Returns undefined
138
+ * for local runs; the server stores it as nullable, so absence just
139
+ * means "author unknown". See `qlip-server/design/UPLOAD.md`.
140
+ */
141
+ export const autodetectTriggeredBy = () => {
142
+ const login = process.env['GITHUB_ACTOR']?.trim();
143
+ if (!login || login.length === 0)
144
+ return undefined;
145
+ const rawId = process.env['GITHUB_ACTOR_ID']?.trim();
146
+ const id = rawId && /^\d+$/.test(rawId) ? Number(rawId) : undefined;
147
+ return id !== undefined ? { login, id } : { login };
148
+ };
130
149
  let shallowWarningEmitted = false;
131
150
  /**
132
151
  * A shallow clone (CI default for `actions/checkout`) only has the tip
@@ -1 +1 @@
1
- {"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../src/upload/finalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoBxE;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AA6BD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,eAAO,MAAM,aAAa,GACxB,MAAM,qBAAqB,KAC1B,OAAO,CAAC,IAAI,CA6Gd,CAAC"}
1
+ {"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../src/upload/finalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqBxE;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AA6BD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,eAAO,MAAM,aAAa,GACxB,MAAM,qBAAqB,KAC1B,OAAO,CAAC,IAAI,CA2Hd,CAAC"}
@@ -17,7 +17,7 @@
17
17
  * dedupes — if both paths fire in the same run, only the first
18
18
  * actually does the work.
19
19
  */
20
- import { autodetectAncestorCommits, autodetectBaseBranch, autodetectBranch, autodetectCommit, autodetectPullRequestUrl, } from './autodetect.js';
20
+ import { autodetectAncestorCommits, autodetectBaseBranch, autodetectBranch, autodetectCommit, autodetectPullRequestUrl, autodetectTriggeredBy, } from './autodetect.js';
21
21
  import { buildCaptureReport, describeBaselineCollisions, describeCollisions, describeMissingCaptures, describePartialBuild, writeCaptureReport, } from './capture-report.js';
22
22
  import { mergeManifestFragments } from './manifest.js';
23
23
  import { QlipUploadError, uploadBuild, resolveServerUrl } from './upload.js';
@@ -118,6 +118,15 @@ export const finalizeBuild = async (opts) => {
118
118
  const baseBranch = opts.upload.baseBranch ?? autodetectBaseBranch();
119
119
  const ancestorCommits = opts.upload.ancestorCommits ?? autodetectAncestorCommits();
120
120
  const pullRequestUrl = opts.upload.pullRequestUrl ?? autodetectPullRequestUrl();
121
+ // An explicit login wins outright (with whatever id came with it);
122
+ // otherwise both come from the CI env together.
123
+ const detectedTriggeredBy = opts.upload.triggeredByLogin === undefined
124
+ ? autodetectTriggeredBy()
125
+ : undefined;
126
+ const triggeredByLogin = opts.upload.triggeredByLogin ?? detectedTriggeredBy?.login;
127
+ const triggeredById = opts.upload.triggeredByLogin !== undefined
128
+ ? opts.upload.triggeredById
129
+ : detectedTriggeredBy?.id;
121
130
  const resolved = {
122
131
  ...opts.upload,
123
132
  ...(branch !== undefined ? { branch } : {}),
@@ -125,6 +134,8 @@ export const finalizeBuild = async (opts) => {
125
134
  ...(baseBranch !== undefined ? { baseBranch } : {}),
126
135
  ...(ancestorCommits !== undefined ? { ancestorCommits } : {}),
127
136
  ...(pullRequestUrl !== undefined ? { pullRequestUrl } : {}),
137
+ ...(triggeredByLogin !== undefined ? { triggeredByLogin } : {}),
138
+ ...(triggeredById !== undefined ? { triggeredById } : {}),
128
139
  };
129
140
  try {
130
141
  const result = await uploadBuild({
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/upload/upload.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGnE,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,IAAI,GAAG,QAAQ,CAAC;IAC1B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,iBAAiB,CAAC;IAC3B,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AAOtE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,KAC5C,MAAsE,CAAC;AAE1E,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;gBAG3B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,YAAuB;CAUjC;AAuJD,eAAO,MAAM,WAAW,GACtB,OAAO,eAAe,KACrB,OAAO,CAAC,gBAAgB,CA4F1B,CAAC"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/upload/upload.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGnE,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,IAAI,GAAG,QAAQ,CAAC;IAC1B,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,iBAAiB,CAAC;IAC3B,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AAOtE,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,KAC5C,MAAsE,CAAC;AAE1E,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;gBAG3B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,YAAuB;CAUjC;AA6JD,eAAO,MAAM,WAAW,GACtB,OAAO,eAAe,KACrB,OAAO,CAAC,gBAAgB,CAkG1B,CAAC"}
@@ -125,6 +125,12 @@ const uploadBuildLegacy = async (fetchImpl, baseUrl, buildDir, options, manifest
125
125
  if (options.pullRequestUrl !== undefined) {
126
126
  form.append('pullRequestUrl', options.pullRequestUrl);
127
127
  }
128
+ if (options.triggeredByLogin !== undefined) {
129
+ form.append('triggeredByLogin', options.triggeredByLogin);
130
+ }
131
+ if (options.triggeredById !== undefined) {
132
+ form.append('triggeredById', String(options.triggeredById));
133
+ }
128
134
  const response = await fetchImpl(`${baseUrl}/api/builds/upload`, {
129
135
  method: 'POST',
130
136
  body: form,
@@ -165,6 +171,12 @@ export const uploadBuild = async (input) => {
165
171
  ...(options.pullRequestUrl !== undefined
166
172
  ? { pullRequestUrl: options.pullRequestUrl }
167
173
  : {}),
174
+ ...(options.triggeredByLogin !== undefined
175
+ ? { triggeredByLogin: options.triggeredByLogin }
176
+ : {}),
177
+ ...(options.triggeredById !== undefined
178
+ ? { triggeredById: options.triggeredById }
179
+ : {}),
168
180
  }),
169
181
  });
170
182
  // 404 ⇒ server predates the v2 routes; fall back to legacy multipart.
@@ -5,6 +5,9 @@ const resolveQlipOptions = ({ defaults, story, override, }) => {
5
5
  return {
6
6
  skip: override?.skip ?? story?.skip ?? defaults.skip,
7
7
  viewport: override?.viewport ?? story?.viewport ?? defaults.viewport,
8
+ fullSizeCaptures: override?.fullSizeCaptures ??
9
+ story?.fullSizeCaptures ??
10
+ defaults.fullSizeCaptures,
8
11
  disableAnimations: override?.disableAnimations ??
9
12
  story?.disableAnimations ??
10
13
  defaults.disableAnimations,
@@ -11,9 +11,11 @@
11
11
  * viewport) stays as configured, and Vitest scales the iframe down to fit it.
12
12
  * A 3,600px-tall iframe in a 1,440px window is photographed at 40% — the
13
13
  * whole page, unreadable — and so is a plain 844px-tall phone story in a
14
- * 720px window, at 85%. `createCaptureViewport` therefore grows the window
15
- * to hold whatever viewport a capture asks for, pinned or grown, and puts
16
- * both back afterwards, the window first, so the iframe lands at scale 1.
14
+ * 720px window, at 85%. `createCaptureViewport` therefore grows the window,
15
+ * when it is given one, to hold whatever viewport a capture asks for, pinned
16
+ * or grown, and puts both back afterwards, the window first, so the iframe
17
+ * lands at scale 1. The runtime gives it the window for a `fullPage` capture
18
+ * and, with `fullSizeCaptures` on, for every capture.
17
19
  *
18
20
  * This module has no imports: the runtime, the test-runner and the unit
19
21
  * project (which has no DOM and feeds it fakes) all share it.
@@ -67,6 +67,9 @@ const buildResolvedDefaults = (override) => {
67
67
  return {
68
68
  outputDir: output_js_1.DEFAULT_OUTPUT_DIR,
69
69
  viewport: fallbackViewport,
70
+ // Not read on this path: `page.setViewportSize` sizes the page itself,
71
+ // so every capture here is full size already.
72
+ fullSizeCaptures: override?.fullSizeCaptures ?? false,
70
73
  skip: false,
71
74
  disableAnimations: override?.disableAnimations ?? false,
72
75
  pauseAnimationsAtEnd: override?.pauseAnimationsAtEnd ?? false,
@@ -15,7 +15,7 @@
15
15
  * `qlip-server/design/UPLOAD.md`.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.autodetectAncestorCommits = exports.autodetectPullRequestUrl = exports.autodetectBaseBranch = exports.autodetectCommit = exports.autodetectBranch = void 0;
18
+ exports.autodetectAncestorCommits = exports.autodetectTriggeredBy = exports.autodetectPullRequestUrl = exports.autodetectBaseBranch = exports.autodetectCommit = exports.autodetectBranch = void 0;
19
19
  const node_child_process_1 = require("node:child_process");
20
20
  const node_fs_1 = require("node:fs");
21
21
  /** Server-side cap on the ancestry list (`qlip-server/design/UPLOAD.md`). */
@@ -134,6 +134,26 @@ const autodetectPullRequestUrl = () => {
134
134
  return `${base}/${repo}/pull/${prNumber}`;
135
135
  };
136
136
  exports.autodetectPullRequestUrl = autodetectPullRequestUrl;
137
+ /**
138
+ * Who triggered the CI run — the user the dashboard shows as the
139
+ * build's author. GitHub Actions sets `$GITHUB_ACTOR` (the login) and
140
+ * `$GITHUB_ACTOR_ID` (the numeric account id) on every run.
141
+ *
142
+ * Env-only by design: outside CI there is no GitHub identity to read
143
+ * (a git author is an email, not a login), and a dashboard upload is
144
+ * attributed server-side from the signed-in session. Returns undefined
145
+ * for local runs; the server stores it as nullable, so absence just
146
+ * means "author unknown". See `qlip-server/design/UPLOAD.md`.
147
+ */
148
+ const autodetectTriggeredBy = () => {
149
+ const login = process.env['GITHUB_ACTOR']?.trim();
150
+ if (!login || login.length === 0)
151
+ return undefined;
152
+ const rawId = process.env['GITHUB_ACTOR_ID']?.trim();
153
+ const id = rawId && /^\d+$/.test(rawId) ? Number(rawId) : undefined;
154
+ return id !== undefined ? { login, id } : { login };
155
+ };
156
+ exports.autodetectTriggeredBy = autodetectTriggeredBy;
137
157
  let shallowWarningEmitted = false;
138
158
  /**
139
159
  * A shallow clone (CI default for `actions/checkout`) only has the tip
@@ -122,6 +122,15 @@ const finalizeBuild = async (opts) => {
122
122
  const baseBranch = opts.upload.baseBranch ?? (0, autodetect_js_1.autodetectBaseBranch)();
123
123
  const ancestorCommits = opts.upload.ancestorCommits ?? (0, autodetect_js_1.autodetectAncestorCommits)();
124
124
  const pullRequestUrl = opts.upload.pullRequestUrl ?? (0, autodetect_js_1.autodetectPullRequestUrl)();
125
+ // An explicit login wins outright (with whatever id came with it);
126
+ // otherwise both come from the CI env together.
127
+ const detectedTriggeredBy = opts.upload.triggeredByLogin === undefined
128
+ ? (0, autodetect_js_1.autodetectTriggeredBy)()
129
+ : undefined;
130
+ const triggeredByLogin = opts.upload.triggeredByLogin ?? detectedTriggeredBy?.login;
131
+ const triggeredById = opts.upload.triggeredByLogin !== undefined
132
+ ? opts.upload.triggeredById
133
+ : detectedTriggeredBy?.id;
125
134
  const resolved = {
126
135
  ...opts.upload,
127
136
  ...(branch !== undefined ? { branch } : {}),
@@ -129,6 +138,8 @@ const finalizeBuild = async (opts) => {
129
138
  ...(baseBranch !== undefined ? { baseBranch } : {}),
130
139
  ...(ancestorCommits !== undefined ? { ancestorCommits } : {}),
131
140
  ...(pullRequestUrl !== undefined ? { pullRequestUrl } : {}),
141
+ ...(triggeredByLogin !== undefined ? { triggeredByLogin } : {}),
142
+ ...(triggeredById !== undefined ? { triggeredById } : {}),
132
143
  };
133
144
  try {
134
145
  const result = await (0, upload_js_1.uploadBuild)({
@@ -133,6 +133,12 @@ const uploadBuildLegacy = async (fetchImpl, baseUrl, buildDir, options, manifest
133
133
  if (options.pullRequestUrl !== undefined) {
134
134
  form.append('pullRequestUrl', options.pullRequestUrl);
135
135
  }
136
+ if (options.triggeredByLogin !== undefined) {
137
+ form.append('triggeredByLogin', options.triggeredByLogin);
138
+ }
139
+ if (options.triggeredById !== undefined) {
140
+ form.append('triggeredById', String(options.triggeredById));
141
+ }
136
142
  const response = await fetchImpl(`${baseUrl}/api/builds/upload`, {
137
143
  method: 'POST',
138
144
  body: form,
@@ -173,6 +179,12 @@ const uploadBuild = async (input) => {
173
179
  ...(options.pullRequestUrl !== undefined
174
180
  ? { pullRequestUrl: options.pullRequestUrl }
175
181
  : {}),
182
+ ...(options.triggeredByLogin !== undefined
183
+ ? { triggeredByLogin: options.triggeredByLogin }
184
+ : {}),
185
+ ...(options.triggeredById !== undefined
186
+ ? { triggeredById: options.triggeredById }
187
+ : {}),
176
188
  }),
177
189
  });
178
190
  // 404 ⇒ server predates the v2 routes; fall back to legacy multipart.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qoretechnologies/qlip",
3
- "version": "0.1.3-pr.30.gc7b05dc",
3
+ "version": "0.1.3-pr.31.g4b62f97",
4
4
  "description": "Storybook screenshot capture for the Vitest addon",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",