From 13b72d3dd27a97c1e7f95527276bcd91f373bf16 Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 12:31:16 +0530 Subject: [PATCH 1/6] feat: add slider push mode and trigger toggle - sliderMode "overlay" | "push" (default overlay); push shifts page content aside instead of overlaying, keeps page interactive, falls back to overlay on narrow viewports - toggle open slider closed on repeat trigger click (JS + React) - fix slider close-button position --- .changeset/slider-push-mode.md | 10 +++ packages/sdk-react/src/hooks/useSlider.ts | 3 + packages/sdk/src/browser.test.ts | 21 ++++++ packages/sdk/src/browser.ts | 25 ++++++- packages/sdk/src/constants.ts | 1 + packages/sdk/src/slider.test.ts | 91 +++++++++++++++++++++++ packages/sdk/src/slider.ts | 46 +++++++++++- packages/sdk/src/styles.ts | 4 +- packages/sdk/src/types.ts | 8 ++ 9 files changed, 201 insertions(+), 8 deletions(-) create mode 100644 .changeset/slider-push-mode.md diff --git a/.changeset/slider-push-mode.md b/.changeset/slider-push-mode.md new file mode 100644 index 0000000..e673068 --- /dev/null +++ b/.changeset/slider-push-mode.md @@ -0,0 +1,10 @@ +--- +"@perspective-ai/sdk": minor +"@perspective-ai/sdk-react": minor +--- + +Add slider `push` mode and trigger toggle + +- **`sliderMode: "overlay" | "push"`** option (default `"overlay"`, backward compatible). In `push` mode the slider shifts page content aside so it occupies real layout space instead of overlaying it — no backdrop, the page stays interactive, and clicking the page no longer closes the slider. Falls back to `"overlay"` on narrow viewports. Available via the JS API, the `data-perspective-slider-mode="push"` attribute, and the `useSlider` React hook. +- **Trigger toggle**: clicking the same `data-perspective-slider` trigger again now closes an open slider instead of re-opening it (respects `disableClose`). The React `useSlider` hook continues to expose `toggle()`. +- **Fix**: adjust the slider close-button position. diff --git a/packages/sdk-react/src/hooks/useSlider.ts b/packages/sdk-react/src/hooks/useSlider.ts index d7a9e10..7847956 100644 --- a/packages/sdk-react/src/hooks/useSlider.ts +++ b/packages/sdk-react/src/hooks/useSlider.ts @@ -48,6 +48,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn { theme, host, disableClose, + sliderMode, onReady, onSubmit, onNavigate, @@ -102,6 +103,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn { theme, host, disableClose, + sliderMode, _apiConfig: embedConfigRef.current, onReady: stableOnReady, onSubmit: stableOnSubmit, @@ -120,6 +122,7 @@ export function useSlider(options: UseSliderOptions): UseSliderReturn { theme, host, disableClose, + sliderMode, stableOnReady, stableOnSubmit, stableOnNavigate, diff --git a/packages/sdk/src/browser.test.ts b/packages/sdk/src/browser.test.ts index a3c22a1..4f66e30 100644 --- a/packages/sdk/src/browser.test.ts +++ b/packages/sdk/src/browser.test.ts @@ -302,6 +302,27 @@ describe("browser entry", () => { expect(document.querySelector(".perspective-slider")).toBeTruthy(); }); + + it("toggles the slider closed on a second trigger click", async () => { + document.body.innerHTML = ` + + `; + autoInit(); + await flushConfigFetch(); + + const button = document.querySelector( + "[data-perspective-slider]" + ) as HTMLButtonElement; + + button.click(); + expect(document.querySelector(".perspective-slider")).toBeTruthy(); + + button.click(); + expect(document.querySelector(".perspective-slider")).toBeFalsy(); + + button.click(); + expect(document.querySelector(".perspective-slider")).toBeTruthy(); + }); }); describe("destroyAll", () => { diff --git a/packages/sdk/src/browser.ts b/packages/sdk/src/browser.ts index ae0ad01..6d4067f 100644 --- a/packages/sdk/src/browser.ts +++ b/packages/sdk/src/browser.ts @@ -642,25 +642,38 @@ function autoInit(): void { const params = parseParamsAttr(el); const brandConfig = extractBrandConfig(el); const disableClose = el.hasAttribute(DATA_ATTRS.disableClose); + const sliderMode = + el.getAttribute(DATA_ATTRS.sliderMode) === "push" + ? "push" + : undefined; const persistedOpen = getPersistedOpenState({ researchId, type: "slider", }); let sliderConfig: EmbedApiConfig | undefined; + // Tracks the live handle so a second click on the same trigger toggles + // the slider closed instead of re-opening it. + let sliderHandle: EmbedHandle | null = null; const disableJsonLdAttribution = el.hasAttribute( DATA_ATTRS.disableJsonLdAttribution ); - const initSlider = () => - init({ + const initSlider = () => { + sliderHandle = init({ researchId, type: "slider", params, disableClose, disableJsonLdAttribution, + sliderMode, ...brandConfig, ...(sliderConfig && { _apiConfig: sliderConfig }), - } as InternalEmbedConfig); + onClose: () => { + sliderHandle = null; + }, + } as InternalEmbedConfig) as EmbedHandle; + return sliderHandle; + }; const dg = globalDestroyGen; const ig = idDestroyGen.get(researchId) ?? 0; @@ -682,6 +695,12 @@ function autoInit(): void { // Cancel any pending API auto-trigger (manual open takes precedence) triggerCleanups.get(researchId)?.(); triggerCleanups.delete(researchId); + // Toggle: a second click on the same trigger closes the open slider + // (unless disableClose, which forbids dismissal entirely). + if (sliderHandle) { + if (!disableClose) sliderHandle.destroy(); + return; + } initSlider(); }); diff --git a/packages/sdk/src/constants.ts b/packages/sdk/src/constants.ts index 2752a5b..0168138 100644 --- a/packages/sdk/src/constants.ts +++ b/packages/sdk/src/constants.ts @@ -91,6 +91,7 @@ export const DATA_ATTRS = { autoOpen: "data-perspective-auto-open", showOnce: "data-perspective-show-once", disableClose: "data-perspective-disable-close", + sliderMode: "data-perspective-slider-mode", launcherIcon: "data-perspective-launcher-icon", launcherStyle: "data-perspective-launcher-style", launcherClass: "data-perspective-launcher-class", diff --git a/packages/sdk/src/slider.test.ts b/packages/sdk/src/slider.test.ts index 56d184d..4ae7187 100644 --- a/packages/sdk/src/slider.test.ts +++ b/packages/sdk/src/slider.test.ts @@ -277,6 +277,97 @@ describe("openSlider", () => { }); }); + describe("sliderMode: push", () => { + const setViewport = (width: number) => { + Object.defineProperty(window, "innerWidth", { + configurable: true, + writable: true, + value: width, + }); + }; + + afterEach(() => { + setViewport(1024); + document.documentElement.style.marginRight = ""; + document.documentElement.style.transition = ""; + }); + + it("omits backdrop and pushes the page in push mode", () => { + setViewport(1024); + const handle = openSlider({ + researchId: "test-research-id", + sliderMode: "push", + }); + + expect( + document.querySelector(".perspective-slider-backdrop") + ).toBeFalsy(); + expect(document.querySelector(".perspective-slider-push")).toBeTruthy(); + expect(document.documentElement.style.marginRight).not.toBe(""); + + handle.unmount(); + }); + + it("restores page margin on close", () => { + setViewport(1024); + const handle = openSlider({ + researchId: "test-research-id", + sliderMode: "push", + }); + + handle.unmount(); + + expect(document.documentElement.style.marginRight).toBe(""); + }); + + it("does not close on page interaction (no backdrop)", () => { + setViewport(1024); + const onClose = vi.fn(); + const handle = openSlider({ + researchId: "test-research-id", + sliderMode: "push", + onClose, + }); + + document.body.click(); + + expect(onClose).not.toHaveBeenCalled(); + expect(document.querySelector(".perspective-slider")).toBeTruthy(); + + handle.unmount(); + }); + + it("falls back to overlay on narrow viewports", () => { + setViewport(480); + const handle = openSlider({ + researchId: "test-research-id", + sliderMode: "push", + }); + + expect( + document.querySelector(".perspective-slider-backdrop") + ).toBeTruthy(); + expect(document.querySelector(".perspective-slider-push")).toBeFalsy(); + expect(document.documentElement.style.marginRight).toBe(""); + + handle.unmount(); + }); + + it("overlay mode is unchanged (default)", () => { + const handle = openSlider({ + researchId: "test-research-id", + sliderMode: "overlay", + }); + + expect( + document.querySelector(".perspective-slider-backdrop") + ).toBeTruthy(); + expect(document.documentElement.style.marginRight).toBe(""); + + handle.unmount(); + }); + }); + describe("update() behavior", () => { const host = "https://getperspective.ai"; const researchId = "test-research-id"; diff --git a/packages/sdk/src/slider.ts b/packages/sdk/src/slider.ts index 16a45bd..ad62fb2 100644 --- a/packages/sdk/src/slider.ts +++ b/packages/sdk/src/slider.ts @@ -19,6 +19,9 @@ import { cn, getThemeClass } from "./utils"; import { enrichContainer } from "./attribution"; import { perfLog } from "./perf"; +/** Below this viewport width, "push" mode falls back to "overlay" so content isn't shoved off-screen. */ +const PUSH_MIN_VIEWPORT = 640; + function createNoOpHandle(researchId: string): EmbedHandle { return { unmount: () => {}, @@ -44,7 +47,12 @@ export function openSlider(config: InternalEmbedConfig): EmbedHandle { injectStyles(); ensureGlobalListeners(); - // Create backdrop + // Push mode shifts page content aside instead of overlaying it. Falls back + // to overlay on narrow viewports where there's no room to push. + const isPush = + config.sliderMode === "push" && window.innerWidth >= PUSH_MIN_VIEWPORT; + + // Create backdrop (overlay mode only — push mode keeps the page interactive) const backdrop = document.createElement("div"); backdrop.className = cn( "perspective-slider-backdrop perspective-embed-root", @@ -55,6 +63,7 @@ export function openSlider(config: InternalEmbedConfig): EmbedHandle { const slider = document.createElement("div"); slider.className = cn( "perspective-slider perspective-embed-root", + isPush && "perspective-slider-push", getThemeClass(config.theme) ); @@ -88,10 +97,37 @@ export function openSlider(config: InternalEmbedConfig): EmbedHandle { slider.appendChild(closeBtn); slider.appendChild(loading); slider.appendChild(iframe); - document.body.appendChild(backdrop); + if (!isPush) { + document.body.appendChild(backdrop); + } document.body.appendChild(slider); enrichContainer(slider, "slider", config); + // Push mode: shrink the page by the slider's width, animated in sync with the + // slide-in. Margin lives on to avoid clobbering site-set body margins. + const root = document.documentElement; + const prevRootMarginRight = root.style.marginRight; + const prevRootTransition = root.style.transition; + const syncPush = () => { + const fits = window.innerWidth >= PUSH_MIN_VIEWPORT; + root.style.marginRight = fits + ? `${slider.getBoundingClientRect().width}px` + : "0px"; + }; + if (isPush) { + root.style.transition = "margin-right 0.3s ease-out"; + syncPush(); + window.addEventListener("resize", syncPush); + } + const removePush = () => { + if (!isPush) return; + window.removeEventListener("resize", syncPush); + root.style.marginRight = prevRootMarginRight; + setTimeout(() => { + root.style.transition = prevRootTransition; + }, 300); + }; + // Mutable config reference for updates let currentConfig = { ...config }; let isOpen = true; @@ -126,6 +162,7 @@ export function openSlider(config: InternalEmbedConfig): EmbedHandle { isOpen = false; messageCleanup?.(); unregisterIframe(); + removePush(); slider.remove(); backdrop.remove(); document.removeEventListener("keydown", escHandler); @@ -184,7 +221,10 @@ export function openSlider(config: InternalEmbedConfig): EmbedHandle { if (!config.disableClose) { closeBtn.addEventListener("click", destroy); - backdrop.addEventListener("click", destroy); + // Push mode has no backdrop — page stays interactive and clicks don't close. + if (!isPush) { + backdrop.addEventListener("click", destroy); + } document.addEventListener("keydown", escHandler); } diff --git a/packages/sdk/src/styles.ts b/packages/sdk/src/styles.ts index 4cb4709..623458b 100644 --- a/packages/sdk/src/styles.ts +++ b/packages/sdk/src/styles.ts @@ -221,8 +221,8 @@ export function injectStyles(): void { } .perspective-slider .perspective-close { - top: 1rem; - right: 2rem; + top: 1.2rem; + right: 1.5rem; } /* Slider backdrop */ diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 791a0d2..b26dbdb 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -112,6 +112,14 @@ export interface EmbedConfig { autoOpen?: AutoOpenConfig; /** When true, prevents the user from closing the popup/slider (hides close button, disables overlay click and ESC key) */ disableClose?: boolean; + /** + * Slider display mode. `"overlay"` (default) floats the panel over the page + * with a dimming backdrop that closes on outside click. `"push"` shifts the + * page content aside so the slider occupies real layout space — no backdrop, + * and clicking the page does not close it. Falls back to `"overlay"` on narrow + * viewports. Only used for slider-type embeds. + */ + sliderMode?: "overlay" | "push"; /** When true, skips JSON-LD structured data injection into the parent page. Other attribution signals (data attributes, global metadata, HTML comments) remain active. */ disableJsonLdAttribution?: boolean; /** Customize the floating launcher button appearance. Only used for float-type embeds. */ From 720e0fb88e162dcbd791156ef24ea685d0dbfb50 Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 16:56:48 +0530 Subject: [PATCH 2/6] ci: run e2e in playwright container to fix install hang playwright install --with-deps deterministically stalled right after the chromium download hit 100% (no extraction, no progress) and reruns never helped because the browser cache only saves in the post-step, which never ran on the cancelled job. Use the prebuilt mcr.microsoft.com/playwright image so browsers are baked in and there's no CDN download to stall on. --- .github/workflows/ci.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61daf23..6302228 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,11 @@ jobs: name: E2E Tests runs-on: ubuntu-latest needs: ci + # Browsers + system deps are pre-baked into this image, so there is no + # `playwright install` download step (it deterministically stalled on the CDN). + # Keep the tag in sync with the @playwright/test version in the root package.json. + container: + image: mcr.microsoft.com/playwright:v1.57.0-noble steps: - uses: actions/checkout@v4 @@ -74,15 +79,6 @@ jobs: - name: Build run: pnpm build - - name: Cache Playwright browsers - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ hashFiles('packages/sdk/package.json') }} - - - name: Install Playwright - run: pnpm --filter @perspective-ai/sdk exec playwright install --with-deps chromium - - name: Test E2E run: pnpm test:e2e From 0c7f57bad3f58d442b6b90043ae6973432e9e6bc Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 16:59:12 +0530 Subject: [PATCH 3/6] ci: trust workspace dir for git in e2e container The playwright container runs as root while the mounted workspace is owned by the host runner UID, so git rejects the lefthook postinstall hook with 'detected dubious ownership'. Add safe.directory before install. --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6302228..f9b0ea9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,12 @@ jobs: steps: - uses: actions/checkout@v4 + # The container runs as root but the mounted workspace is owned by the + # host runner UID, so git's dubious-ownership guard blocks the lefthook + # postinstall hook. Trust the workspace before install runs. + - name: Trust workspace for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 From 9d91dc9ebe71039ec2dd2a0d825ceb5b014eb8b8 Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 17:03:10 +0530 Subject: [PATCH 4/6] ci: point playwright at the image's baked-in browsers GitHub overrides HOME to /github/home in container jobs, so the test process looked for browsers under ~/.cache/ms-playwright (default) instead of /ms-playwright where the image installs them, failing every test with 'Executable doesn't exist'. Set PLAYWRIGHT_BROWSERS_PATH explicitly. --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9b0ea9..3258e09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,11 @@ jobs: # Keep the tag in sync with the @playwright/test version in the root package.json. container: image: mcr.microsoft.com/playwright:v1.57.0-noble + # GitHub overrides HOME to /github/home in container jobs, so Playwright + # looks for browsers in the default ~/.cache path instead of where the + # image bakes them. Point it at the image's browser location. + env: + PLAYWRIGHT_BROWSERS_PATH: /ms-playwright steps: - uses: actions/checkout@v4 From abaed307630947ce157ccef5b53583679cb9c5db Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 17:09:06 +0530 Subject: [PATCH 5/6] ci: revert e2e to non-container, harden playwright install Container approach hit a wall: Playwright launched against the default ~/.cache path even with PLAYWRIGHT_BROWSERS_PATH=/ms-playwright set, so every test failed with 'Executable doesn't exist'. Back to runs-on: ubuntu-latest, but make the original download hang survivable instead of infinite: - split apt system-deps (never hung) from the browser download - per-attempt timeout 240s + 3x retry: a stalled download is killed and retried on a fresh connection; already-fetched artifacts are skipped so retries make incremental progress - cache key on pnpm-lock.yaml so the browser cache tracks the Playwright version and actually persists once populated - job timeout-minutes: 20 as a backstop --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3258e09..4b89d2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,26 +58,11 @@ jobs: name: E2E Tests runs-on: ubuntu-latest needs: ci - # Browsers + system deps are pre-baked into this image, so there is no - # `playwright install` download step (it deterministically stalled on the CDN). - # Keep the tag in sync with the @playwright/test version in the root package.json. - container: - image: mcr.microsoft.com/playwright:v1.57.0-noble - # GitHub overrides HOME to /github/home in container jobs, so Playwright - # looks for browsers in the default ~/.cache path instead of where the - # image bakes them. Point it at the image's browser location. - env: - PLAYWRIGHT_BROWSERS_PATH: /ms-playwright + timeout-minutes: 20 steps: - uses: actions/checkout@v4 - # The container runs as root but the mounted workspace is owned by the - # host runner UID, so git's dubious-ownership guard blocks the lefthook - # postinstall hook. Trust the workspace before install runs. - - name: Trust workspace for git - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 @@ -90,6 +75,28 @@ jobs: - name: Build run: pnpm build + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + # Key on the lockfile so the cache tracks the resolved Playwright version. + key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + + # System deps (apt) install cleanly on their own — kept separate from the + # browser download, which previously stalled after the Chromium binary + # finished. Each download attempt is killed if it stalls and retried on a + # fresh connection; cached/already-fetched browsers make retries no-ops. + - name: Install Playwright system deps + run: pnpm --filter @perspective-ai/sdk exec playwright install-deps chromium + + - name: Install Playwright browsers + run: | + for i in 1 2 3; do + timeout 240 pnpm --filter @perspective-ai/sdk exec playwright install chromium && exit 0 + echo "::warning::playwright browser download attempt $i stalled or failed; retrying" + done + exit 1 + - name: Test E2E run: pnpm test:e2e From def189d6cb7cf701700e60a753b4d0339fe1d277 Mon Sep 17 00:00:00 2001 From: vinaypuppal Date: Fri, 29 May 2026 17:20:13 +0530 Subject: [PATCH 6/6] ci: run e2e in playwright container + pass browser path through turbo Root cause of the e2e failures was two-fold: - playwright install --with-deps deterministically stalled right after the chromium download hit 100%, and reruns never helped (cache only saves in the post-step, which never ran on the cancelled job). - the container fix exposed a second issue: turbo 2.x strict env mode strips PLAYWRIGHT_BROWSERS_PATH before invoking 'playwright test', so the tests looked in ~/.cache instead of the image's /ms-playwright. Fix: run e2e in mcr.microsoft.com/playwright (browsers baked in, no CDN download to stall on) and allowlist PLAYWRIGHT_BROWSERS_PATH via passThroughEnv so it reaches the test process. Also trust the workspace dir (root container vs host-owned mount) for the lefthook postinstall hook. Bump @playwright/test 1.52 -> 1.60 and pin the image tag to match. --- .github/workflows/ci.yml | 41 ++++++++++++++++++---------------------- package.json | 2 +- pnpm-lock.yaml | 26 ++++++++++++------------- turbo.json | 3 ++- 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b89d2e..7528a35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,11 +58,28 @@ jobs: name: E2E Tests runs-on: ubuntu-latest needs: ci - timeout-minutes: 20 + timeout-minutes: 15 + # Browsers + system deps are pre-baked into this image, so there is no + # `playwright install` download step (it deterministically stalled on the CDN). + # Keep the tag in sync with the @playwright/test version in the root package.json. + container: + image: mcr.microsoft.com/playwright:v1.60.0-noble + # GitHub overrides HOME to /github/home in container jobs, so Playwright looks + # for browsers in the default ~/.cache path instead of where the image bakes + # them. Point it at the image's location — and note turbo.json must allowlist + # this var via passThroughEnv, or strict env mode strips it from `playwright test`. + env: + PLAYWRIGHT_BROWSERS_PATH: /ms-playwright steps: - uses: actions/checkout@v4 + # The container runs as root but the mounted workspace is owned by the host + # runner UID, so git's dubious-ownership guard blocks the lefthook postinstall + # hook. Trust the workspace before install runs. + - name: Trust workspace for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 @@ -75,28 +92,6 @@ jobs: - name: Build run: pnpm build - - name: Cache Playwright browsers - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - # Key on the lockfile so the cache tracks the resolved Playwright version. - key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - - # System deps (apt) install cleanly on their own — kept separate from the - # browser download, which previously stalled after the Chromium binary - # finished. Each download attempt is killed if it stalls and retried on a - # fresh connection; cached/already-fetched browsers make retries no-ops. - - name: Install Playwright system deps - run: pnpm --filter @perspective-ai/sdk exec playwright install-deps chromium - - - name: Install Playwright browsers - run: | - for i in 1 2 3; do - timeout 240 pnpm --filter @perspective-ai/sdk exec playwright install chromium && exit 0 - echo "::warning::playwright browser download attempt $i stalled or failed; retrying" - done - exit 1 - - name: Test E2E run: pnpm test:e2e diff --git a/package.json b/package.json index 3ddc54f..92af6d1 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@arethetypeswrong/cli": "^0.18.2", "@changesets/cli": "^2.30.0", - "@playwright/test": "^1.52.0", + "@playwright/test": "^1.60.0", "@vitest/coverage-v8": "^4.0.17", "happy-dom": "^20.8.9", "lefthook": "^2.0.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae6471c..0297344 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,8 +20,8 @@ importers: specifier: ^2.30.0 version: 2.30.0(@types/node@25.5.0) '@playwright/test': - specifier: ^1.52.0 - version: 1.57.0 + specifier: ^1.60.0 + version: 1.60.0 '@vitest/coverage-v8': specifier: ^4.0.17 version: 4.0.17(vitest@4.0.17(@types/node@25.5.0)(happy-dom@20.8.9)) @@ -437,8 +437,8 @@ packages: cpu: [x64] os: [win32] - '@playwright/test@1.57.0': - resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + '@playwright/test@1.60.0': + resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} engines: {node: '>=18'} hasBin: true @@ -1426,13 +1426,13 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + playwright-core@1.60.0: + resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} engines: {node: '>=18'} hasBin: true - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + playwright@1.60.0: + resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} engines: {node: '>=18'} hasBin: true @@ -2256,9 +2256,9 @@ snapshots: '@oxlint/win32-x64@1.39.0': optional: true - '@playwright/test@1.57.0': + '@playwright/test@1.60.0': dependencies: - playwright: 1.57.0 + playwright: 1.60.0 '@rollup/rollup-android-arm-eabi@4.60.1': optional: true @@ -3128,11 +3128,11 @@ snapshots: mlly: 1.8.0 pathe: 2.0.3 - playwright-core@1.57.0: {} + playwright-core@1.60.0: {} - playwright@1.57.0: + playwright@1.60.0: dependencies: - playwright-core: 1.57.0 + playwright-core: 1.60.0 optionalDependencies: fsevents: 2.3.2 diff --git a/turbo.json b/turbo.json index 5ad574d..104acf7 100644 --- a/turbo.json +++ b/turbo.json @@ -27,7 +27,8 @@ "test:e2e": { "dependsOn": ["build"], "inputs": ["$TURBO_DEFAULT$", "src/**", "e2e/**", "playwright.config.ts"], - "outputs": ["playwright-report/**", "test-results/**"] + "outputs": ["playwright-report/**", "test-results/**"], + "passThroughEnv": ["PLAYWRIGHT_BROWSERS_PATH"] }, "test:ssr": { "dependsOn": ["build"],