Skip to content

Commit 4686538

Browse files
authored
test(e2e): Initial Chrome Extension integration tests (#8075)
1 parent a5d3ef2 commit 4686538

22 files changed

Lines changed: 685 additions & 0 deletions

.changeset/yummy-hoops-drum.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ jobs:
306306
"react-router",
307307
"custom",
308308
"hono",
309+
"chrome-extension",
309310
]
310311
test-project: ["chrome"]
311312
include:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineConfig } from '@playwright/test';
2+
import { config } from 'dotenv';
3+
import * as path from 'path';
4+
5+
import { common } from './playwright.config';
6+
7+
config({ path: path.resolve(__dirname, '.env.local') });
8+
9+
export default defineConfig({
10+
...common,
11+
testDir: './tests/chrome-extension',
12+
// No global setup/teardown — extension build is handled by worker-scoped fixtures
13+
projects: [
14+
{
15+
name: 'chrome-extension',
16+
// Extension loading uses chromium.launchPersistentContext in fixtures
17+
// with --load-extension flags. No channel override needed — Playwright's
18+
// bundled Chromium supports extensions when launched this way.
19+
},
20+
],
21+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { applicationConfig } from '../models/applicationConfig';
2+
import { templates } from '../templates';
3+
import { PKGLAB } from './utils';
4+
5+
const vite = applicationConfig()
6+
.setName('chrome-extension-vite')
7+
.useTemplate(templates['chrome-extension-vite'])
8+
.setEnvFormatter('public', key => `VITE_${key}`)
9+
.addScript('setup', 'pnpm install')
10+
.addScript('dev', 'pnpm build')
11+
.addScript('build', 'pnpm build')
12+
.addScript('serve', 'echo noop')
13+
.addDependency('@clerk/chrome-extension', PKGLAB)
14+
.addDependency('@clerk/clerk-js', PKGLAB)
15+
.addDependency('@clerk/ui', PKGLAB);
16+
17+
export const chromeExtension = {
18+
vite,
19+
} as const;

integration/presets/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { astro } from './astro';
2+
import { chromeExtension } from './chrome-extension';
23
import { customFlows } from './custom-flows';
34
import { envs, instanceKeys } from './envs';
45
import { expo } from './expo';
@@ -14,6 +15,7 @@ import { tanstack } from './tanstack';
1415
import { vue } from './vue';
1516

1617
export const appConfigs = {
18+
chromeExtension,
1719
customFlows,
1820
envs,
1921
express,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Clerk Test Extension",
4+
"version": "1.0.0",
5+
"action": {
6+
"default_popup": "popup.html"
7+
},
8+
"permissions": ["storage", "cookies"],
9+
"host_permissions": ["http://localhost/*"],
10+
"background": {
11+
"service_worker": "background.js",
12+
"type": "module"
13+
},
14+
"content_security_policy": {
15+
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
16+
}
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "chrome-extension-vite",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"build": "vite build && vite build --config vite.background.config.ts && cp manifest.json dist/manifest.json"
8+
},
9+
"dependencies": {
10+
"react": "18.3.1",
11+
"react-dom": "18.3.1"
12+
},
13+
"devDependencies": {
14+
"@types/chrome": "^0.0.268",
15+
"@types/react": "18.3.12",
16+
"@types/react-dom": "18.3.1",
17+
"@vitejs/plugin-react": "^4.3.4",
18+
"typescript": "^5.7.3",
19+
"vite": "^4.3.9"
20+
},
21+
"engines": {
22+
"node": ">=20.9.0"
23+
}
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Clerk Test Extension</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/popup.tsx"></script>
11+
</body>
12+
</html>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { createClerkClient } from '@clerk/chrome-extension/client';
2+
3+
const PUBLISHABLE_KEY = (globalThis as any).__CLERK_PUBLISHABLE_KEY__ as string;
4+
5+
let clerkPromise: Promise<any> | null = null;
6+
7+
function getClerk() {
8+
if (!clerkPromise) {
9+
clerkPromise = createClerkClient({
10+
publishableKey: PUBLISHABLE_KEY,
11+
background: true,
12+
});
13+
}
14+
return clerkPromise;
15+
}
16+
17+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
18+
if (message.type === 'GET_AUTH') {
19+
getClerk()
20+
.then(clerk => {
21+
sendResponse({
22+
userId: clerk.user?.id ?? null,
23+
sessionId: clerk.session?.id ?? null,
24+
});
25+
})
26+
.catch(err => {
27+
sendResponse({ error: err.message });
28+
});
29+
return true; // Keep message channel open for async response
30+
}
31+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { ClerkProvider, Show, SignIn, UserButton, useAuth } from '@clerk/chrome-extension';
2+
import React from 'react';
3+
import ReactDOM from 'react-dom/client';
4+
5+
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string;
6+
7+
function App() {
8+
return (
9+
<ClerkProvider
10+
publishableKey={PUBLISHABLE_KEY}
11+
routerPush={() => {}}
12+
routerReplace={() => {}}
13+
>
14+
<main>
15+
<Show when='signed-out'>
16+
<SignIn />
17+
</Show>
18+
<Show when='signed-in'>
19+
<UserButton />
20+
<AuthInfo />
21+
</Show>
22+
</main>
23+
</ClerkProvider>
24+
);
25+
}
26+
27+
function AuthInfo() {
28+
const { userId, sessionId } = useAuth();
29+
return (
30+
<div>
31+
<p data-testid='user-id'>{userId}</p>
32+
<p data-testid='session-id'>{sessionId}</p>
33+
</div>
34+
);
35+
}
36+
37+
ReactDOM.createRoot(document.getElementById('root')!).render(
38+
<React.StrictMode>
39+
<App />
40+
</React.StrictMode>,
41+
);

0 commit comments

Comments
 (0)