state-scenarios 0.4.0 → 0.5.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.
@@ -21,8 +21,16 @@ declare global {
21
21
  }
22
22
  /** Requests the active scenario didn't cover, e.g. to assert none in strict runs. */
23
23
  export declare const unhandledRequests: (page: Page) => Promise<string[]>;
24
+ /** Options for {@link showCursor}. */
25
+ export interface CursorOptions {
26
+ /** Colour of the arrow, its pulse and the click ripple. Default `#ff2d55`. */
27
+ color?: string;
28
+ /** Your own cursor as SVG markup, drawn with its top-left corner at the mouse. Replaces the arrow. */
29
+ svg?: string;
30
+ }
24
31
  /**
25
32
  * Draw a mouse pointer that follows Playwright's mouse, since recorded videos don't show one.
26
- * It glides between points and pulses on click. Call before the page loads, e.g. before `openScenario`.
33
+ * It's a bright arrow with a pulsing halo that glides between points and sends out a ripple on
34
+ * each click. Call before the page loads, e.g. before `openScenario`.
27
35
  */
28
- export declare function showCursor(page: Page): Promise<void>;
36
+ export declare function showCursor(page: Page, options?: CursorOptions): Promise<void>;
@@ -51,34 +51,67 @@ async function setPanel(page, show) {
51
51
  });
52
52
  }
53
53
  var unhandledRequests = (page) => page.evaluate(() => globalThis.stateScenariosUnhandled ?? []);
54
- async function showCursor(page) {
55
- await page.addInitScript(() => {
54
+ async function showCursor(page, options = {}) {
55
+ await page.addInitScript(({ color = "#ff2d55", svg }) => {
56
+ let show = (pointer) => parent.postMessage({ stateScenariosCursor: pointer }, "*");
57
+ const local = (type) => (e) => show({ type, x: e.clientX, y: e.clientY });
58
+ addEventListener("mousemove", local("move"), true);
59
+ addEventListener("mousedown", local("down"), true);
60
+ addEventListener("mouseup", local("up"), true);
61
+ addEventListener("message", (e) => {
62
+ const pointer = e.data?.stateScenariosCursor;
63
+ const frame = [...document.querySelectorAll("iframe")].find((f) => f.contentWindow === e.source);
64
+ if (!pointer || !frame) return;
65
+ const box = frame.getBoundingClientRect();
66
+ const style2 = getComputedStyle(frame);
67
+ const scaleX = box.width / frame.offsetWidth || 1;
68
+ const scaleY = box.height / frame.offsetHeight || 1;
69
+ show({
70
+ ...pointer,
71
+ x: box.left + (frame.clientLeft + parseFloat(style2.paddingLeft) + pointer.x) * scaleX,
72
+ y: box.top + (frame.clientTop + parseFloat(style2.paddingTop) + pointer.y) * scaleY
73
+ });
74
+ });
75
+ if (window !== window.top) return;
76
+ const style = document.createElement("style");
77
+ style.textContent = `
78
+ .ss-cursor, .ss-ripple {
79
+ position: fixed; left: 0; top: 0; pointer-events: none; z-index: 2147483647;
80
+ }
81
+ .ss-cursor { transition: transform .35s ease-in-out; }
82
+ .ss-cursor > * { display: block; filter: drop-shadow(0 2px 3px rgba(0,0,0,.45)); transition: scale .1s; transform-origin: 0 0; }
83
+ .ss-cursor[data-down] > * { scale: .8; }
84
+ .ss-cursor::before, .ss-ripple { border-radius: 50%; box-sizing: border-box; border: 3px solid ${color}; }
85
+ .ss-cursor::before {
86
+ content: ''; position: absolute; width: 28px; height: 28px; left: -14px; top: -14px; animation: ss-pulse 1.2s ease-out infinite;
87
+ }
88
+ .ss-ripple { width: 64px; height: 64px; margin: -32px 0 0 -32px; animation: ss-ripple .6s ease-out forwards; }
89
+ @keyframes ss-pulse { from { scale: .4; opacity: .9; } to { scale: 1.3; opacity: 0; } }
90
+ @keyframes ss-ripple { from { scale: .3; opacity: 1; } to { scale: 1.6; opacity: 0; } }`;
56
91
  const cursor = document.createElement("div");
92
+ cursor.className = "ss-cursor";
57
93
  cursor.setAttribute("aria-hidden", "true");
58
- cursor.style.cssText = [
59
- "position:fixed",
60
- "left:0",
61
- "top:0",
62
- "width:18px",
63
- "height:18px",
64
- "margin:-9px 0 0 -9px",
65
- "border-radius:50%",
66
- "background:rgba(20,20,20,.55)",
67
- "border:2px solid #fff",
68
- "box-shadow:0 1px 4px rgba(0,0,0,.4)",
69
- "pointer-events:none",
70
- "z-index:2147483647",
71
- "transform:translate(-40px,-40px)",
72
- "transition:transform .25s ease-out,scale .1s"
73
- ].join(";");
74
- const place = (e) => cursor.style.transform = `translate(${e.clientX}px,${e.clientY}px)`;
75
- addEventListener("mousemove", place, true);
76
- addEventListener("mousedown", () => cursor.style.scale = "0.7", true);
77
- addEventListener("mouseup", () => cursor.style.scale = "1", true);
78
- const attach = () => document.documentElement.append(cursor);
94
+ cursor.innerHTML = svg ?? `<svg width="32" height="37" viewBox="0 0 26 30"><path d="M2 2v23l6-6 4.5 9 4-2-4.5-9H21z" fill="${color}" stroke="#fff" stroke-width="2.5" stroke-linejoin="round"/></svg>`;
95
+ cursor.style.transform = `translate(${innerWidth / 2}px,${innerHeight / 2}px)`;
96
+ show = ({ type, x, y }) => {
97
+ if (type === "up") {
98
+ delete cursor.dataset.down;
99
+ return;
100
+ }
101
+ cursor.style.transform = `translate(${x}px,${y}px)`;
102
+ if (type === "move") return;
103
+ cursor.dataset.down = "";
104
+ const ripple = document.createElement("div");
105
+ ripple.className = "ss-ripple";
106
+ ripple.style.left = `${x}px`;
107
+ ripple.style.top = `${y}px`;
108
+ ripple.addEventListener("animationend", () => ripple.remove());
109
+ document.documentElement.append(ripple);
110
+ };
111
+ const attach = () => document.documentElement.append(style, cursor);
79
112
  if (document.documentElement) attach();
80
113
  else addEventListener("DOMContentLoaded", attach);
81
- });
114
+ }, options);
82
115
  }
83
116
  export {
84
117
  openInlineScenario,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/playwright.ts"],"sourcesContent":["import type { Page } from '@playwright/test';\nimport { DATA_KEY, PARAM, STRICT_PARAM } from './link.js';\nimport type { ScenarioInput } from './schema.js';\n\nexport interface OpenOptions {\n /** App path to open. Default \"/\". */\n path?: string;\n /** Fail unmocked requests instead of letting them reach a real backend. Default true. */\n strict?: boolean;\n /** Show the dev panel. False hides it, for screenshots that go into product docs. Default true. */\n panel?: boolean;\n}\n\n/**\n * Open the app in a catalog scenario and wait until the UI shows it: the scenario is\n * active and its `ready` selector, if any, is visible.\n */\nexport async function openScenario(page: Page, name: string, options: OpenOptions = {}) {\n await open(page, name, new URLSearchParams({ [PARAM]: name }), '', options);\n}\n\n/** Like `openScenario`, for a scenario defined in the test rather than the catalog. */\nexport async function openInlineScenario(page: Page, scenario: ScenarioInput, options: OpenOptions = {}) {\n const hash = `#${new URLSearchParams({ [DATA_KEY]: JSON.stringify(scenario) })}`;\n await open(page, scenario.name, new URLSearchParams(), hash, { path: scenario.path, ...options });\n}\n\nasync function open(\n page: Page,\n name: string,\n search: URLSearchParams,\n hash: string,\n { path = '/', strict = true, panel = true }: OpenOptions,\n) {\n if (strict) search.set(STRICT_PARAM, '');\n await setPanel(page, panel);\n // Leave the page first: a hash-only change would not reload the app.\n await page.goto('about:blank');\n await page.goto(`${path}?${search}${hash}`);\n const html = page.locator(`html[data-scenario=\"${name}\"]`);\n await html.waitFor({ state: 'attached' });\n const ready = await html.getAttribute('data-scenario-ready');\n\n if (ready) await page.locator(ready).first().waitFor();\n}\n\ndeclare global {\n /** Exposed by `openScenario({ panel: false })`: whether this page should hide the panel. */\n var stateScenariosPanelHidden: (() => Promise<boolean>) | undefined;\n}\n\n// Per page: whether the panel is hidden. The init script asks on every load, so a later\n// open can show the panel again on any Playwright version.\nconst panelHidden = new WeakMap<Page, { hidden: boolean; }>();\n\nasync function setPanel(page: Page, show: boolean) {\n const state = panelHidden.get(page);\n\n if (state) {\n state.hidden = !show;\n\n return;\n }\n\n if (show) return;\n const created = { hidden: true };\n panelHidden.set(page, created);\n await page.exposeFunction('stateScenariosPanelHidden', () => created.hidden);\n // Hide before the app's scripts run, so the panel never shows, not even in a video;\n // then show it again if this page no longer wants it hidden.\n await page.addInitScript(() => {\n const style = document.createElement('style');\n style.textContent = 'scenario-panel { display: none !important; }';\n // Init scripts can run before <html> exists: attach as soon as it does.\n const attach = () => document.documentElement.append(style);\n\n if (document.documentElement) attach();\n else {\n new MutationObserver((_, observer) => {\n if (!document.documentElement) return;\n observer.disconnect();\n attach();\n }).observe(document, { childList: true });\n }\n\n void globalThis.stateScenariosPanelHidden?.().then((hidden) => {\n if (!hidden) style.remove();\n });\n });\n}\n\n/** Requests the active scenario didn't cover, e.g. to assert none in strict runs. */\nexport const unhandledRequests = (page: Page): Promise<string[]> =>\n page.evaluate(() => globalThis.stateScenariosUnhandled ?? []);\n\n/**\n * Draw a mouse pointer that follows Playwright's mouse, since recorded videos don't show one.\n * It glides between points and pulses on click. Call before the page loads, e.g. before `openScenario`.\n */\nexport async function showCursor(page: Page) {\n await page.addInitScript(() => {\n const cursor = document.createElement('div');\n cursor.setAttribute('aria-hidden', 'true');\n cursor.style.cssText = [\n 'position:fixed',\n 'left:0',\n 'top:0',\n 'width:18px',\n 'height:18px',\n 'margin:-9px 0 0 -9px',\n 'border-radius:50%',\n 'background:rgba(20,20,20,.55)',\n 'border:2px solid #fff',\n 'box-shadow:0 1px 4px rgba(0,0,0,.4)',\n 'pointer-events:none',\n 'z-index:2147483647',\n 'transform:translate(-40px,-40px)',\n 'transition:transform .25s ease-out,scale .1s',\n ].join(';');\n\n const place = (e: MouseEvent) => (cursor.style.transform = `translate(${e.clientX}px,${e.clientY}px)`);\n addEventListener('mousemove', place, true);\n addEventListener('mousedown', () => (cursor.style.scale = '0.7'), true);\n addEventListener('mouseup', () => (cursor.style.scale = '1'), true);\n\n const attach = () => document.documentElement.append(cursor);\n\n if (document.documentElement) attach();\n else addEventListener('DOMContentLoaded', attach);\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/playwright.ts"],"sourcesContent":["import type { Page } from '@playwright/test';\nimport { DATA_KEY, PARAM, STRICT_PARAM } from './link.js';\nimport type { ScenarioInput } from './schema.js';\n\nexport interface OpenOptions {\n /** App path to open. Default \"/\". */\n path?: string;\n /** Fail unmocked requests instead of letting them reach a real backend. Default true. */\n strict?: boolean;\n /** Show the dev panel. False hides it, for screenshots that go into product docs. Default true. */\n panel?: boolean;\n}\n\n/**\n * Open the app in a catalog scenario and wait until the UI shows it: the scenario is\n * active and its `ready` selector, if any, is visible.\n */\nexport async function openScenario(page: Page, name: string, options: OpenOptions = {}) {\n await open(page, name, new URLSearchParams({ [PARAM]: name }), '', options);\n}\n\n/** Like `openScenario`, for a scenario defined in the test rather than the catalog. */\nexport async function openInlineScenario(page: Page, scenario: ScenarioInput, options: OpenOptions = {}) {\n const hash = `#${new URLSearchParams({ [DATA_KEY]: JSON.stringify(scenario) })}`;\n await open(page, scenario.name, new URLSearchParams(), hash, { path: scenario.path, ...options });\n}\n\nasync function open(\n page: Page,\n name: string,\n search: URLSearchParams,\n hash: string,\n { path = '/', strict = true, panel = true }: OpenOptions,\n) {\n if (strict) search.set(STRICT_PARAM, '');\n await setPanel(page, panel);\n // Leave the page first: a hash-only change would not reload the app.\n await page.goto('about:blank');\n await page.goto(`${path}?${search}${hash}`);\n const html = page.locator(`html[data-scenario=\"${name}\"]`);\n await html.waitFor({ state: 'attached' });\n const ready = await html.getAttribute('data-scenario-ready');\n\n if (ready) await page.locator(ready).first().waitFor();\n}\n\ndeclare global {\n /** Exposed by `openScenario({ panel: false })`: whether this page should hide the panel. */\n var stateScenariosPanelHidden: (() => Promise<boolean>) | undefined;\n}\n\n// Per page: whether the panel is hidden. The init script asks on every load, so a later\n// open can show the panel again on any Playwright version.\nconst panelHidden = new WeakMap<Page, { hidden: boolean; }>();\n\nasync function setPanel(page: Page, show: boolean) {\n const state = panelHidden.get(page);\n\n if (state) {\n state.hidden = !show;\n\n return;\n }\n\n if (show) return;\n const created = { hidden: true };\n panelHidden.set(page, created);\n await page.exposeFunction('stateScenariosPanelHidden', () => created.hidden);\n // Hide before the app's scripts run, so the panel never shows, not even in a video;\n // then show it again if this page no longer wants it hidden.\n await page.addInitScript(() => {\n const style = document.createElement('style');\n style.textContent = 'scenario-panel { display: none !important; }';\n // Init scripts can run before <html> exists: attach as soon as it does.\n const attach = () => document.documentElement.append(style);\n\n if (document.documentElement) attach();\n else {\n new MutationObserver((_, observer) => {\n if (!document.documentElement) return;\n observer.disconnect();\n attach();\n }).observe(document, { childList: true });\n }\n\n void globalThis.stateScenariosPanelHidden?.().then((hidden) => {\n if (!hidden) style.remove();\n });\n });\n}\n\n/** Requests the active scenario didn't cover, e.g. to assert none in strict runs. */\nexport const unhandledRequests = (page: Page): Promise<string[]> =>\n page.evaluate(() => globalThis.stateScenariosUnhandled ?? []);\n\n/** Options for {@link showCursor}. */\nexport interface CursorOptions {\n /** Colour of the arrow, its pulse and the click ripple. Default `#ff2d55`. */\n color?: string;\n /** Your own cursor as SVG markup, drawn with its top-left corner at the mouse. Replaces the arrow. */\n svg?: string;\n}\n\n/**\n * Draw a mouse pointer that follows Playwright's mouse, since recorded videos don't show one.\n * It's a bright arrow with a pulsing halo that glides between points and sends out a ripple on\n * each click. Call before the page loads, e.g. before `openScenario`.\n */\nexport async function showCursor(page: Page, options: CursorOptions = {}) {\n await page.addInitScript(({ color = '#ff2d55', svg }) => {\n type Pointer = { type: 'move' | 'down' | 'up'; x: number; y: number; };\n\n // Only the top frame draws. Other frames pass their mouse events to their parent, which adds the\n // iframe's place, so a page with iframes shows one cursor.\n let show = (pointer: Pointer) => parent.postMessage({ stateScenariosCursor: pointer }, '*');\n\n const local = (type: Pointer['type']) => (e: MouseEvent) => show({ type, x: e.clientX, y: e.clientY });\n addEventListener('mousemove', local('move'), true);\n addEventListener('mousedown', local('down'), true);\n addEventListener('mouseup', local('up'), true);\n addEventListener('message', (e) => {\n const pointer: Pointer | undefined = e.data?.stateScenariosCursor;\n const frame = [...document.querySelectorAll('iframe')].find((f) => f.contentWindow === e.source);\n\n if (!pointer || !frame) return;\n\n // Same mapping as the CLI's pagePoint: the measured box over the layout size gives the\n // iframe's CSS scale, which applies to its border, padding and the point itself.\n const box = frame.getBoundingClientRect();\n const style = getComputedStyle(frame);\n const scaleX = box.width / frame.offsetWidth || 1;\n const scaleY = box.height / frame.offsetHeight || 1;\n\n show({\n ...pointer,\n x: box.left + (frame.clientLeft + parseFloat(style.paddingLeft) + pointer.x) * scaleX,\n y: box.top + (frame.clientTop + parseFloat(style.paddingTop) + pointer.y) * scaleY,\n });\n });\n\n if (window !== window.top) return;\n\n const style = document.createElement('style');\n style.textContent = `\n .ss-cursor, .ss-ripple {\n position: fixed; left: 0; top: 0; pointer-events: none; z-index: 2147483647;\n }\n .ss-cursor { transition: transform .35s ease-in-out; }\n .ss-cursor > * { display: block; filter: drop-shadow(0 2px 3px rgba(0,0,0,.45)); transition: scale .1s; transform-origin: 0 0; }\n .ss-cursor[data-down] > * { scale: .8; }\n .ss-cursor::before, .ss-ripple { border-radius: 50%; box-sizing: border-box; border: 3px solid ${color}; }\n .ss-cursor::before {\n content: ''; position: absolute; width: 28px; height: 28px; left: -14px; top: -14px; animation: ss-pulse 1.2s ease-out infinite;\n }\n .ss-ripple { width: 64px; height: 64px; margin: -32px 0 0 -32px; animation: ss-ripple .6s ease-out forwards; }\n @keyframes ss-pulse { from { scale: .4; opacity: .9; } to { scale: 1.3; opacity: 0; } }\n @keyframes ss-ripple { from { scale: .3; opacity: 1; } to { scale: 1.6; opacity: 0; } }`;\n\n const cursor = document.createElement('div');\n cursor.className = 'ss-cursor';\n cursor.setAttribute('aria-hidden', 'true');\n cursor.innerHTML = svg\n ?? `<svg width=\"32\" height=\"37\" viewBox=\"0 0 26 30\"><path d=\"M2 2v23l6-6 4.5 9 4-2-4.5-9H21z\" fill=\"${color}\" stroke=\"#fff\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/></svg>`;\n // Start mid-screen so the first glide to a target shows in the video.\n cursor.style.transform = `translate(${innerWidth / 2}px,${innerHeight / 2}px)`;\n\n show = ({ type, x, y }) => {\n if (type === 'up') {\n delete cursor.dataset.down;\n\n return;\n }\n\n cursor.style.transform = `translate(${x}px,${y}px)`;\n\n if (type === 'move') return;\n\n cursor.dataset.down = '';\n const ripple = document.createElement('div');\n ripple.className = 'ss-ripple';\n ripple.style.left = `${x}px`;\n ripple.style.top = `${y}px`;\n ripple.addEventListener('animationend', () => ripple.remove());\n document.documentElement.append(ripple);\n };\n\n const attach = () => document.documentElement.append(style, cursor);\n\n if (document.documentElement) attach();\n else addEventListener('DOMContentLoaded', attach);\n }, options);\n}\n"],"mappings":"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","names":["style"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-scenarios",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Put a running app into any state from a URL: shareable JSON scenarios for network, URL and app state, with a dev panel, Playwright helpers and adapters.",
5
5
  "license": "MIT",
6
6
  "repository": {