Skip to content

test(e2e): Add an e2e test for Handshake (prod) #6072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ const withEmailCodes = base
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-email-codes').pk)
.setEnvVariable('private', 'CLERK_ENCRYPTION_KEY', constants.E2E_CLERK_ENCRYPTION_KEY || 'a-key');

const sessionsProd1 = base
.clone()
.setId('sessionsProd1')
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('sessions-prod-1').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('sessions-prod-1').pk)
.setEnvVariable('public', 'CLERK_JS_URL', '');

const withEmailCodes_destroy_client = withEmailCodes
.clone()
.setEnvVariable('public', 'EXPERIMENTAL_PERSIST_CLIENT', 'false');
Expand Down Expand Up @@ -187,4 +194,5 @@ export const envs = {
withBillingStaging,
withBilling,
withWhatsappPhoneCode,
sessionsProd1,
} as const;
1 change: 1 addition & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const createLongRunningApps = () => {
{ id: 'react.vite.withEmailCodes_persist_client', config: react.vite, env: envs.withEmailCodes_destroy_client },
{ id: 'react.vite.withEmailLinks', config: react.vite, env: envs.withEmailLinks },
{ id: 'next.appRouter.withEmailCodes', config: next.appRouter, env: envs.withEmailCodes },
{ id: 'next.appRouter.sessionsProd1', config: next.appRouter, env: envs.sessionsProd1 },
{
id: 'next.appRouter.withEmailCodes_persist_client',
config: next.appRouter,
Expand Down
4 changes: 2 additions & 2 deletions integration/testUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import type { Application } from '../models/application';
import { createEmailService } from './emailService';
import { createInvitationService } from './invitationsService';
import { createOrganizationsService } from './organizationsService';
import type { FakeOrganization, FakeUser } from './usersService';
import type { FakeOrganization, FakeUser, FakeUserWithEmail } from './usersService';
import { createUserService } from './usersService';

export type { FakeUser, FakeOrganization };
export type { FakeUser, FakeUserWithEmail, FakeOrganization };
const createClerkClient = (app: Application) => {
return backendCreateClerkClient({
apiUrl: app.env.privateVariables.get('CLERK_API_URL'),
Expand Down
2 changes: 2 additions & 0 deletions integration/testUtils/usersService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export type FakeUser = {
deleteIfExists: () => Promise<void>;
};

export type FakeUserWithEmail = FakeUser & { email: string };

export type FakeOrganization = {
name: string;
organization: { id: string };
Expand Down
81 changes: 81 additions & 0 deletions integration/tests/handshake/handshake.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Server, ServerOptions } from 'node:https';

import { expect, test } from '@playwright/test';

import { constants } from '../../constants';
import { fs } from '../../scripts';
import { createProxyServer } from '../../scripts/proxyServer';
import type { FakeUserWithEmail } from '../../testUtils';
import { createTestUtils, testAgainstRunningApps } from '../../testUtils';

testAgainstRunningApps({ withPattern: ['next.appRouter.sessionsProd1'] })('handshake flow @handshake', ({ app }) => {
test.describe.configure({ mode: 'serial' });

test.describe('with Production instance', () => {
// TODO: change host name
const host = 'multiple-apps-e2e.clerk.app:8443';

let fakeUser: FakeUserWithEmail;
let server: Server;

test.afterAll(async () => {
await fakeUser.deleteIfExists();
server.close();
});

test.beforeAll(async () => {
// GIVEN a Production App and Clerk instance
// TODO: Factor out proxy server creation to helper
const ssl: Pick<ServerOptions, 'ca' | 'cert' | 'key'> = {
cert: fs.readFileSync(constants.CERTS_DIR + '/sessions.pem'),
key: fs.readFileSync(constants.CERTS_DIR + '/sessions-key.pem'),
};

server = createProxyServer({
ssl,
targets: {
[host]: app.serverUrl,
},
});

const u = createTestUtils({ app, useTestingToken: false });
// AND an existing user in the instance
fakeUser = u.services.users.createFakeUser({ withEmail: true }) as FakeUserWithEmail;
await u.services.users.createBapiUser(fakeUser);
});

test('when the client uat cookies are deleted', async ({ context }) => {
const page = await context.newPage();
const u = createTestUtils({ app, page, context, useTestingToken: false });

// GIVEN the user is signed into the app on the app homepage
await u.page.goto(`https://${host}`);
await u.po.signIn.goTo();
await u.po.signIn.signInWithEmailAndInstantPassword(fakeUser);
await u.po.expect.toBeSignedIn();

// AND the user has no client uat cookies
// (which forces a handshake flow)
await context.clearCookies({ name: /__client_uat.*/ });

// WHEN the user goes to the protected page
// (the handshake should happen here)
await u.page.goToRelative('/protected');

// THEN the user is signed in
await u.po.expect.toBeSignedIn();
// AND the user is on the protected page
expect(u.page.url()).toBe(`https://${host}/protected`);
// AND the user has valid cookies (session, client_uat, etc)
const cookies = await u.page.context().cookies();
const clientUatCookies = cookies.filter(c => c.name.startsWith('__client_uat'));
// TODO: should we be more specific about the number of cookies?
expect(clientUatCookies.length).toBeGreaterThan(0);
const sessionCookies = cookies.filter(c => c.name.startsWith('__session'));
expect(sessionCookies.length).toBeGreaterThan(0);
// AND the user does not have temporary cookies (e.g. __clerk_handshake, __clerk_handshake_nonce)
const handshakeCookies = cookies.filter(c => c.name.includes('handshake'));
expect(handshakeCookies.length).toBe(0);
});
});
});
102 changes: 0 additions & 102 deletions integration/tests/sessions/prod-app-migration.test.ts

This file was deleted.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"test:integration:expo-web": "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:generic": "E2E_APP_ID=react.vite.*,next.appRouter.withEmailCodes* pnpm test:integration:base --grep @generic",
"test:integration:handshake": "DISABLE_WEB_SECURITY=true E2E_APP_ID=next.appRouter.sessionsProd1 pnpm test:integration:base --grep @handshake",
"test:integration:localhost": "pnpm test:integration:base --grep @localhost",
"test:integration:nextjs": "E2E_APP_ID=next.appRouter.* pnpm test:integration:base --grep @nextjs",
"test:integration:nuxt": "E2E_APP_ID=nuxt.node npm run test:integration:base -- --grep @nuxt",
Expand Down