Skip to content

Commit

Permalink
Add tests (#145)
Browse files Browse the repository at this point in the history
* Added tests and made a start to auth.ts

* Add tests for cookie and callback route

* Tests for session and actions

* Add jsdom tests for tsx files

* Add new workflow

* Clean up jest config file

* Didn't mean to add this

* Add jest config and setup scripts to ts exclude

* Impersonation shouldn't be a client component for now

* 100% test coverage

* Add debug flag

* Add another test and change coverage engine to have local and github show the same results

* Should actually add the test

* Address feedback

* Also run prettier on test files
  • Loading branch information
PaulAsjes authored Dec 27, 2024
1 parent 565f393 commit 2d5204e
Show file tree
Hide file tree
Showing 26 changed files with 6,071 additions and 195 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches:
- 'main'
pull_request: {}

defaults:
run:
shell: bash

jobs:
test:
name: Test Node ${{ matrix.node }}
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Install Dependencies
run: |
npm install
- name: Prettier
run: |
npm run prettier
- name: Lint
run: |
npm run lint
- name: Build
run: |
npm run build
- name: Test
run: |
npm run test -- --coverage
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
node_modules
dist
coverage
22 changes: 22 additions & 0 deletions __tests__/actions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { checkSessionAction, handleSignOutAction } from '../src/actions.js';
import { signOut } from '../src/auth.js';

jest.mock('../src/auth.js', () => ({
signOut: jest.fn().mockResolvedValue(true),
}));

describe('actions', () => {
describe('checkSessionAction', () => {
it('should return true for authenticated users', async () => {
const result = await checkSessionAction();
expect(result).toBe(true);
});
});

describe('handleSignOutAction', () => {
it('should call signOut', async () => {
await handleSignOutAction();
expect(signOut).toHaveBeenCalled();
});
});
});
137 changes: 137 additions & 0 deletions __tests__/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';

import { getSignInUrl, getSignUpUrl, signOut } from '../src/auth.js';
import { workos } from '../src/workos.js';

// These are mocked in jest.setup.ts
import { cookies, headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { sealData } from 'iron-session';
import { generateTestToken } from './test-helpers.js';
import { User } from '@workos-inc/node';

// jest.mock('../src/workos', () => ({
// workos: {
// userManagement: {
// getLogoutUrl: jest.fn().mockReturnValue('https://example.com/logout'),
// getJwksUrl: jest.fn().mockReturnValue('https://api.workos.com/sso/jwks/client_1234567890'),
// },
// },
// }));

describe('auth.ts', () => {
const mockSession = {
accessToken: 'access-token',
oauthTokens: undefined,
sessionId: 'session_123',
organizationId: 'org_123',
role: 'member',
permissions: ['posts:create', 'posts:delete'],
entitlements: ['audit-logs'],
impersonator: undefined,
user: {
object: 'user',
id: 'user_123',
email: '[email protected]',
emailVerified: true,
profilePictureUrl: null,
firstName: null,
lastName: null,
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
} as User,
};

beforeEach(async () => {
// Clear all mocks between tests
jest.clearAllMocks();

// Reset the cookie store
const nextCookies = await cookies();
// @ts-expect-error - _reset is part of the mock
nextCookies._reset();

const nextHeaders = await headers();
// @ts-expect-error - _reset is part of the mock
nextHeaders._reset();
});

describe('getSignInUrl', () => {
it('should return a valid URL', async () => {
const url = await getSignInUrl();
expect(url).toBeDefined();
expect(() => new URL(url)).not.toThrow();
});

it('should use the organizationId if provided', async () => {
const url = await getSignInUrl({ organizationId: 'org_123' });
expect(url).toContain('organization_id=org_123');
expect(url).toBeDefined();
expect(() => new URL(url)).not.toThrow();
});
});

describe('getSignUpUrl', () => {
it('should return a valid URL', async () => {
const url = await getSignUpUrl();
expect(url).toBeDefined();
expect(() => new URL(url)).not.toThrow();
});
});

describe('signOut', () => {
it('should delete the cookie and redirect to the logout url if there is a session', async () => {
const nextCookies = await cookies();
const nextHeaders = await headers();

mockSession.accessToken = await generateTestToken();

nextHeaders.set('x-workos-middleware', 'true');
nextHeaders.set(
'x-workos-session',
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
);

nextCookies.set('wos-session', 'foo');

jest.spyOn(workos.userManagement, 'getLogoutUrl').mockReturnValue('https://example.com/logout');

await signOut();

const sessionCookie = nextCookies.get('wos-session');

expect(sessionCookie).toBeUndefined();
expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith('https://example.com/logout');
});

it('should delete the cookie and redirect to the root path if there is no session', async () => {
const nextCookies = await cookies();
const nextHeaders = await headers();

nextHeaders.set('x-workos-middleware', 'true');
nextCookies.set('wos-session', 'foo');

await signOut();

const sessionCookie = nextCookies.get('wos-session');

expect(sessionCookie).toBeUndefined();
expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith('/');
});

it('should delete the cookie with a specific domain', async () => {
const nextCookies = await cookies();
const nextHeaders = await headers();

nextHeaders.set('x-workos-middleware', 'true');
nextCookies.set('wos-session', 'foo', { domain: 'example.com' });

await signOut();

const sessionCookie = nextCookies.get('wos-session');
expect(sessionCookie).toBeUndefined();
});
});
});
Loading

0 comments on commit 2d5204e

Please sign in to comment.