From b8b96607c29aedaffe197943c413a5e7e5ef5f66 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 26 Jun 2026 08:19:56 -0700 Subject: [PATCH 1/4] test(electron): Initial Electron SDK integration test --- .changeset/electron-e2e-tests.md | 2 + .github/workflows/ci.yml | 17 +++- integration/presets/electron.ts | 17 ++++ integration/presets/index.ts | 2 + .../templates/electron-vite/index.html | 18 ++++ integration/templates/electron-vite/main.mjs | 71 ++++++++++++++++ .../templates/electron-vite/package.json | 26 ++++++ .../templates/electron-vite/preload.mjs | 3 + .../templates/electron-vite/src/main.tsx | 46 +++++++++++ .../templates/electron-vite/src/style.css | 25 ++++++ .../templates/electron-vite/src/vite-env.d.ts | 1 + .../templates/electron-vite/tsconfig.json | 20 +++++ .../templates/electron-vite/vite.config.ts | 6 ++ integration/templates/index.ts | 1 + integration/tests/electron/basic.test.ts | 35 ++++++++ integration/tests/electron/fixtures.ts | 82 +++++++++++++++++++ package.json | 1 + turbo.json | 7 ++ 18 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 .changeset/electron-e2e-tests.md create mode 100644 integration/presets/electron.ts create mode 100644 integration/templates/electron-vite/index.html create mode 100644 integration/templates/electron-vite/main.mjs create mode 100644 integration/templates/electron-vite/package.json create mode 100644 integration/templates/electron-vite/preload.mjs create mode 100644 integration/templates/electron-vite/src/main.tsx create mode 100644 integration/templates/electron-vite/src/style.css create mode 100644 integration/templates/electron-vite/src/vite-env.d.ts create mode 100644 integration/templates/electron-vite/tsconfig.json create mode 100644 integration/templates/electron-vite/vite.config.ts create mode 100644 integration/tests/electron/basic.test.ts create mode 100644 integration/tests/electron/fixtures.ts diff --git a/.changeset/electron-e2e-tests.md b/.changeset/electron-e2e-tests.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/electron-e2e-tests.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32478c15868..be0cac70715 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -347,6 +347,7 @@ jobs: "custom", "hono", "chrome-extension", + "electron", ] test-project: ["chrome"] include: @@ -486,10 +487,24 @@ jobs: working-directory: ./integration/certs run: ls -la && pwd + - name: Ensure Xvfb is installed + if: ${{ matrix.test-name == 'electron' }} + run: | + if ! command -v xvfb-run &> /dev/null; then + sudo apt-get update + sudo apt-get install -y xvfb + fi + xvfb-run --help > /dev/null + - name: Run Integration Tests id: integration-tests timeout-minutes: 25 - run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS + run: | + if [ "${{ matrix.test-name }}" = "electron" ]; then + xvfb-run -a pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS + else + pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS + fi env: E2E_DEBUG: "1" E2E_APP_CLERK_JS_DIR: ${{runner.temp}} diff --git a/integration/presets/electron.ts b/integration/presets/electron.ts new file mode 100644 index 00000000000..78bbe9a5132 --- /dev/null +++ b/integration/presets/electron.ts @@ -0,0 +1,17 @@ +import { applicationConfig } from '../models/applicationConfig'; +import { templates } from '../templates'; +import { PKGLAB } from './utils'; + +const vite = applicationConfig() + .setName('electron-vite') + .useTemplate(templates['electron-vite']) + .setEnvFormatter('public', key => `VITE_${key}`) + .addScript('setup', 'pnpm install') + .addScript('dev', 'pnpm build') + .addScript('build', 'pnpm build') + .addScript('serve', 'echo noop') + .addDependency('@clerk/electron', PKGLAB); + +export const electron = { + vite, +} as const; diff --git a/integration/presets/index.ts b/integration/presets/index.ts index f67f3b36385..de6cacfcb03 100644 --- a/integration/presets/index.ts +++ b/integration/presets/index.ts @@ -1,6 +1,7 @@ import { astro } from './astro'; import { chromeExtension } from './chrome-extension'; import { customFlows } from './custom-flows'; +import { electron } from './electron'; import { envs, instanceKeys } from './envs'; import { expo } from './expo'; import { express } from './express'; @@ -17,6 +18,7 @@ import { vue } from './vue'; export const appConfigs = { chromeExtension, customFlows, + electron, envs, express, fastify, diff --git a/integration/templates/electron-vite/index.html b/integration/templates/electron-vite/index.html new file mode 100644 index 00000000000..bf21ab8969d --- /dev/null +++ b/integration/templates/electron-vite/index.html @@ -0,0 +1,18 @@ + + + + + + Clerk Electron E2E + + +
+ + + diff --git a/integration/templates/electron-vite/main.mjs b/integration/templates/electron-vite/main.mjs new file mode 100644 index 00000000000..6b10ef9b081 --- /dev/null +++ b/integration/templates/electron-vite/main.mjs @@ -0,0 +1,71 @@ +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +import { createClerkBridge } from '@clerk/electron'; +import { storage } from '@clerk/electron/storage'; +import { app, BrowserWindow, net, protocol } from 'electron'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const RENDERER_SCHEME = 'clerk'; +const RENDERER_HOST = 'app'; +const rendererRoot = path.resolve(__dirname, 'dist'); + +const clerk = createClerkBridge({ + storage: storage({ unencryptedFallback: true }), + renderer: { + scheme: RENDERER_SCHEME, + host: RENDERER_HOST, + }, +}); + +async function createWindow() { + const win = new BrowserWindow({ + width: 1000, + height: 800, + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + preload: path.resolve(__dirname, 'preload.mjs'), + sandbox: false, + }, + }); + + await win.loadURL(`${RENDERER_SCHEME}://${RENDERER_HOST}/`); +} + +function registerClerkAppProtocol() { + protocol.handle(RENDERER_SCHEME, async request => { + const url = new URL(request.url); + + if (url.host !== RENDERER_HOST) { + return new Response('Not found', { status: 404 }); + } + + const requestedPath = decodeURIComponent(url.pathname); + const resolvedPath = path.resolve(rendererRoot, `.${requestedPath}`); + const relativePath = path.relative(rendererRoot, resolvedPath); + const isSafe = relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)); + + if (!isSafe) { + return new Response('Forbidden', { status: 403 }); + } + + const hasExtension = /\.[^/]+$/.test(url.pathname); + const filePath = hasExtension ? resolvedPath : path.join(rendererRoot, 'index.html'); + + return net.fetch(pathToFileURL(filePath).toString()); + }); +} + +app.whenReady().then(async () => { + registerClerkAppProtocol(); + await createWindow(); +}); + +app.on('window-all-closed', () => { + app.quit(); +}); + +app.on('before-quit', () => { + clerk.cleanup(); +}); diff --git a/integration/templates/electron-vite/package.json b/integration/templates/electron-vite/package.json new file mode 100644 index 00000000000..55a1393117f --- /dev/null +++ b/integration/templates/electron-vite/package.json @@ -0,0 +1,26 @@ +{ + "name": "electron-vite", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + }, + "dependencies": { + "electron-store": "^8.2.0", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@types/node": "^22.12.0", + "@types/react": "18.3.12", + "@types/react-dom": "18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "electron": "^39.2.6", + "typescript": "^5.7.3", + "vite": "^7.3.3" + }, + "engines": { + "node": ">=22.11.0" + } +} diff --git a/integration/templates/electron-vite/preload.mjs b/integration/templates/electron-vite/preload.mjs new file mode 100644 index 00000000000..e95e3ee4061 --- /dev/null +++ b/integration/templates/electron-vite/preload.mjs @@ -0,0 +1,3 @@ +import { exposeClerkBridge } from '@clerk/electron/preload'; + +exposeClerkBridge(); diff --git a/integration/templates/electron-vite/src/main.tsx b/integration/templates/electron-vite/src/main.tsx new file mode 100644 index 00000000000..fdb63d41444 --- /dev/null +++ b/integration/templates/electron-vite/src/main.tsx @@ -0,0 +1,46 @@ +import { ClerkProvider, Show, SignIn, UserButton, useAuth } from '@clerk/electron/react'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './style.css'; + +const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string; +const CLERK_UI_URL = import.meta.env.VITE_CLERK_UI_URL as string; + +function App() { + return ( + {}} + routerReplace={() => {}} + > +
+ + + + + + + +
+
+ ); +} + +function AuthInfo() { + const { sessionId, userId } = useAuth(); + + return ( +
+

{userId}

+

{sessionId}

+
+ ); +} + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/integration/templates/electron-vite/src/style.css b/integration/templates/electron-vite/src/style.css new file mode 100644 index 00000000000..305cccba2aa --- /dev/null +++ b/integration/templates/electron-vite/src/style.css @@ -0,0 +1,25 @@ +* { + box-sizing: border-box; +} + +body { + min-width: 320px; + min-height: 100vh; + margin: 0; + font-family: + Inter, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; + background: #f8fafc; +} + +main { + display: grid; + min-height: 100vh; + place-items: center; + padding: 24px; +} diff --git a/integration/templates/electron-vite/src/vite-env.d.ts b/integration/templates/electron-vite/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/integration/templates/electron-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration/templates/electron-vite/tsconfig.json b/integration/templates/electron-vite/tsconfig.json new file mode 100644 index 00000000000..a4c834a6ca4 --- /dev/null +++ b/integration/templates/electron-vite/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/integration/templates/electron-vite/vite.config.ts b/integration/templates/electron-vite/vite.config.ts new file mode 100644 index 00000000000..fabde1a8f5e --- /dev/null +++ b/integration/templates/electron-vite/vite.config.ts @@ -0,0 +1,6 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [react()], +}); diff --git a/integration/templates/index.ts b/integration/templates/index.ts index 5588158e1f5..2fd05daa6ae 100644 --- a/integration/templates/index.ts +++ b/integration/templates/index.ts @@ -24,6 +24,7 @@ export const templates = { 'react-router-library': resolve(__dirname, './react-router-library'), 'custom-flows-react-vite': resolve(__dirname, './custom-flows-react-vite'), 'chrome-extension-vite': resolve(__dirname, './chrome-extension-vite'), + 'electron-vite': resolve(__dirname, './electron-vite'), } as const; if (new Set([...Object.values(templates)]).size !== Object.values(templates).length) { diff --git a/integration/tests/electron/basic.test.ts b/integration/tests/electron/basic.test.ts new file mode 100644 index 00000000000..5f8def8ce8f --- /dev/null +++ b/integration/tests/electron/basic.test.ts @@ -0,0 +1,35 @@ +import { createPageObjects } from '@clerk/testing/playwright/unstable'; + +import type { FakeUser } from '../../testUtils'; +import { createTestUtils } from '../../testUtils'; +import { expect, test } from './fixtures'; + +test.describe('electron basic auth @electron', () => { + test.describe.configure({ mode: 'serial' }); + + let fakeUser: FakeUser; + + test.beforeAll(async ({ electronTestApp }) => { + const u = createTestUtils({ app: electronTestApp }); + fakeUser = u.services.users.createFakeUser(); + await u.services.users.createBapiUser(fakeUser); + }); + + test.afterAll(async () => { + await fakeUser?.deleteIfExists(); + }); + + test('signs in with email and password', async ({ electronPage }) => { + const { signIn } = createPageObjects({ page: electronPage, useTestingToken: false }); + + await signIn.waitForMounted(); + await expect(electronPage.locator('.cl-signIn-root')).toBeVisible(); + + await signIn.setIdentifier(fakeUser.email!); + await signIn.continue(); + await signIn.setPassword(fakeUser.password); + await signIn.continue(); + + await expect(electronPage.locator('[data-testid="user-id"]')).toHaveText(/^user_/, { timeout: 30_000 }); + }); +}); diff --git a/integration/tests/electron/fixtures.ts b/integration/tests/electron/fixtures.ts new file mode 100644 index 00000000000..8f63ec69fe5 --- /dev/null +++ b/integration/tests/electron/fixtures.ts @@ -0,0 +1,82 @@ +import * as path from 'node:path'; + +import { parsePublishableKey } from '@clerk/shared/keys'; +import { clerkSetup, setupClerkTestingToken } from '@clerk/testing/playwright'; +import type { ElectronApplication, Page } from '@playwright/test'; +import { _electron as electron, test as base } from '@playwright/test'; + +import type { Application } from '../../models/application'; +import type { EnvironmentConfig } from '../../models/environment'; +import { appConfigs } from '../../presets'; +import { run } from '../../scripts'; + +type WorkerFixtures = { + electronTestApp: Application; +}; + +type TestFixtures = { + electronApplication: ElectronApplication; + electronPage: Page; +}; + +const electronExecutable = (app: Application) => + path.resolve(app.appDir, 'node_modules', '.bin', process.platform === 'win32' ? 'electron.cmd' : 'electron'); + +async function setupClerkTestingEnv(env: EnvironmentConfig) { + const publishableKey = env.publicVariables.get('CLERK_PUBLISHABLE_KEY'); + const secretKey = env.privateVariables.get('CLERK_SECRET_KEY'); + const apiUrl = env.privateVariables.get('CLERK_API_URL'); + + if (!publishableKey || !secretKey) { + throw new Error('Missing Clerk integration keys for Electron E2E tests'); + } + + const { frontendApi: frontendApiUrl } = parsePublishableKey(publishableKey, { fatal: true }); + await clerkSetup({ + publishableKey, + frontendApiUrl, + secretKey, + // @ts-expect-error apiUrl is accepted at runtime. + apiUrl, + dotenv: false, + }); +} + +export const test = base.extend({ + electronTestApp: [ + async ({}, provide) => { + const env = appConfigs.envs.withEmailCodes; + const app = await appConfigs.electron.vite.commit(); + + await app.withEnv(env); + await app.setup(); + await run('node node_modules/electron/install.js', { cwd: app.appDir }); + await app.build(); + await setupClerkTestingEnv(env); + + await provide(app); + await app.teardown(); + }, + { scope: 'worker', timeout: 120_000 }, + ], + + electronApplication: async ({ electronTestApp }, provide) => { + const application = await electron.launch({ + args: [path.resolve(electronTestApp.appDir, 'main.mjs')], + cwd: electronTestApp.appDir, + executablePath: electronExecutable(electronTestApp), + }); + + await provide(application); + await application.close(); + }, + + electronPage: async ({ electronApplication }, provide) => { + const page = await electronApplication.firstWindow(); + await setupClerkTestingToken({ context: page.context() }); + await page.reload(); + await provide(page); + }, +}); + +export { expect } from '@playwright/test'; diff --git a/package.json b/package.json index 92768bddf09..b38fa016633 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "test:integration:cleanup": "pnpm playwright test --config integration/playwright.cleanup.config.ts", "test:integration:custom": "pnpm test:integration:base --grep @custom", "test:integration:deployment:nextjs": "pnpm playwright test --config integration/playwright.deployments.config.ts", + "test:integration:electron": "pnpm test:integration:base --grep @electron", "test:integration:expo-web:disabled": "E2E_APP_ID=expo.expo-web pnpm test:integration:base --grep @expo-web", "test:integration:express": "E2E_APP_ID=express.* pnpm test:integration:base --grep @express", "test:integration:fastify": "E2E_APP_ID=fastify.* pnpm test:integration:base --grep @fastify", diff --git a/turbo.json b/turbo.json index 3b062a3445a..75b370c9338 100644 --- a/turbo.json +++ b/turbo.json @@ -368,6 +368,13 @@ "outputs": ["integration/playwright-report/**"], "outputLogs": "new-only" }, + "//#test:integration:electron": { + "dependsOn": ["@clerk/electron#build"], + "env": ["CLEANUP", "DEBUG", "E2E_*", "INTEGRATION_INSTANCE_KEYS", "INTEGRATION_STAGING_INSTANCE_KEYS"], + "inputs": ["integration/**", "packages/electron/**"], + "outputs": ["integration/playwright-report/**"], + "outputLogs": "new-only" + }, "//#typedoc:generate": { "dependsOn": ["@clerk/nextjs#build", "@clerk/react#build", "@clerk/shared#build"], "inputs": ["tsconfig.typedoc.json", "typedoc.config.mjs", ".typedoc/**/*.mjs"], From 7dd38a71a15e490b0ada1dbc7ed5524ae4777e54 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 26 Jun 2026 09:20:21 -0700 Subject: [PATCH 2/4] chore: address coderabbit comments --- integration/templates/electron-vite/package.json | 2 +- integration/templates/electron-vite/tsconfig.json | 2 +- integration/tests/electron/fixtures.ts | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/integration/templates/electron-vite/package.json b/integration/templates/electron-vite/package.json index 55a1393117f..a1491475520 100644 --- a/integration/templates/electron-vite/package.json +++ b/integration/templates/electron-vite/package.json @@ -21,6 +21,6 @@ "vite": "^7.3.3" }, "engines": { - "node": ">=22.11.0" + "node": ">=24.15.0" } } diff --git a/integration/templates/electron-vite/tsconfig.json b/integration/templates/electron-vite/tsconfig.json index a4c834a6ca4..df0556f33d7 100644 --- a/integration/templates/electron-vite/tsconfig.json +++ b/integration/templates/electron-vite/tsconfig.json @@ -16,5 +16,5 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] + "include": ["src", "vite.config.ts"] } diff --git a/integration/tests/electron/fixtures.ts b/integration/tests/electron/fixtures.ts index 8f63ec69fe5..7cf3ee3eecb 100644 --- a/integration/tests/electron/fixtures.ts +++ b/integration/tests/electron/fixtures.ts @@ -31,7 +31,12 @@ async function setupClerkTestingEnv(env: EnvironmentConfig) { throw new Error('Missing Clerk integration keys for Electron E2E tests'); } - const { frontendApi: frontendApiUrl } = parsePublishableKey(publishableKey, { fatal: true }); + const { frontendApi: frontendApiUrl, instanceType } = parsePublishableKey(publishableKey, { fatal: true }); + + if (instanceType !== 'development') { + return; + } + await clerkSetup({ publishableKey, frontendApiUrl, From 6a98c41ef616475deb5512910bab562639bb638e Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 26 Jun 2026 09:31:59 -0700 Subject: [PATCH 3/4] chore: more tests --- integration/tests/electron/basic.test.ts | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/integration/tests/electron/basic.test.ts b/integration/tests/electron/basic.test.ts index 5f8def8ce8f..e793dab95e5 100644 --- a/integration/tests/electron/basic.test.ts +++ b/integration/tests/electron/basic.test.ts @@ -1,9 +1,17 @@ +import { clerk } from '@clerk/testing/playwright'; import { createPageObjects } from '@clerk/testing/playwright/unstable'; import type { FakeUser } from '../../testUtils'; import { createTestUtils } from '../../testUtils'; import { expect, test } from './fixtures'; +type ElectronWindow = Window & { + __clerk_internal_electron?: { + tokenCache?: Partial>; + oauthTransport?: Partial>; + }; +}; + test.describe('electron basic auth @electron', () => { test.describe.configure({ mode: 'serial' }); @@ -19,6 +27,24 @@ test.describe('electron basic auth @electron', () => { await fakeUser?.deleteIfExists(); }); + test('exposes the preload bridge to the renderer', async ({ electronPage }) => { + await expect(electronPage.locator('[data-testid="electron-app"]')).toBeVisible(); + + await expect( + electronPage.evaluate(() => { + const bridge = (window as ElectronWindow).__clerk_internal_electron; + + return ( + typeof bridge?.tokenCache?.clearToken === 'function' && + typeof bridge?.tokenCache?.getToken === 'function' && + typeof bridge?.tokenCache?.saveToken === 'function' && + typeof bridge?.oauthTransport?.getRedirectUrl === 'function' && + typeof bridge?.oauthTransport?.open === 'function' + ); + }), + ).resolves.toBe(true); + }); + test('signs in with email and password', async ({ electronPage }) => { const { signIn } = createPageObjects({ page: electronPage, useTestingToken: false }); @@ -32,4 +58,20 @@ test.describe('electron basic auth @electron', () => { await expect(electronPage.locator('[data-testid="user-id"]')).toHaveText(/^user_/, { timeout: 30_000 }); }); + + test('persists the signed-in session after relaunch', async ({ electronPage }) => { + await expect(electronPage.locator('[data-testid="user-id"]')).toHaveText(/^user_/, { timeout: 30_000 }); + await expect(electronPage.locator('[data-testid="session-id"]')).toHaveText(/^sess_/, { timeout: 30_000 }); + }); + + test('signs out and clears the session', async ({ electronPage }) => { + await expect(electronPage.locator('.cl-userButtonTrigger')).toBeVisible({ timeout: 30_000 }); + await clerk.signOut({ page: electronPage }); + + await expect(electronPage.locator('.cl-signIn-root')).toBeVisible({ timeout: 30_000 }); + }); + + test('keeps the signed-out state after relaunch', async ({ electronPage }) => { + await expect(electronPage.locator('.cl-signIn-root')).toBeVisible({ timeout: 30_000 }); + }); }); From 4f179e2ef76d8f98ceb5983fee3ff8acc00e98af Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 26 Jun 2026 09:59:47 -0700 Subject: [PATCH 4/4] chore: address jeremy review --- .github/workflows/ci.yml | 1 - integration/presets/electron.ts | 2 +- integration/templates/electron-vite/main.mjs | 8 +++++++- integration/tests/electron/fixtures.ts | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be0cac70715..58eaa8bc4bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -494,7 +494,6 @@ jobs: sudo apt-get update sudo apt-get install -y xvfb fi - xvfb-run --help > /dev/null - name: Run Integration Tests id: integration-tests diff --git a/integration/presets/electron.ts b/integration/presets/electron.ts index 78bbe9a5132..5276e4ee521 100644 --- a/integration/presets/electron.ts +++ b/integration/presets/electron.ts @@ -7,7 +7,7 @@ const vite = applicationConfig() .useTemplate(templates['electron-vite']) .setEnvFormatter('public', key => `VITE_${key}`) .addScript('setup', 'pnpm install') - .addScript('dev', 'pnpm build') + .addScript('dev', 'echo noop') .addScript('build', 'pnpm build') .addScript('serve', 'echo noop') .addDependency('@clerk/electron', PKGLAB); diff --git a/integration/templates/electron-vite/main.mjs b/integration/templates/electron-vite/main.mjs index 6b10ef9b081..7763211349d 100644 --- a/integration/templates/electron-vite/main.mjs +++ b/integration/templates/electron-vite/main.mjs @@ -41,7 +41,13 @@ function registerClerkAppProtocol() { return new Response('Not found', { status: 404 }); } - const requestedPath = decodeURIComponent(url.pathname); + let requestedPath; + try { + requestedPath = decodeURIComponent(url.pathname); + } catch { + return new Response('Bad request', { status: 400 }); + } + const resolvedPath = path.resolve(rendererRoot, `.${requestedPath}`); const relativePath = path.relative(rendererRoot, resolvedPath); const isSafe = relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)); diff --git a/integration/tests/electron/fixtures.ts b/integration/tests/electron/fixtures.ts index 7cf3ee3eecb..4b88d655f4d 100644 --- a/integration/tests/electron/fixtures.ts +++ b/integration/tests/electron/fixtures.ts @@ -62,7 +62,7 @@ export const test = base.extend({ await provide(app); await app.teardown(); }, - { scope: 'worker', timeout: 120_000 }, + { scope: 'worker', timeout: 180_000 }, ], electronApplication: async ({ electronTestApp }, provide) => {