Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 committed Jun 20, 2024
1 parent dd10075 commit dd5af8b
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 11 deletions.
190 changes: 180 additions & 10 deletions apps/extension/src/approve-origin.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { approveOrigin } from './approve-origin';
import { localExtStorage } from './storage/local';
import { UserChoice } from '@penumbra-zone/types/user-choice';
import { OriginRecord } from './storage/types';
import { PopupType } from './message/popup';

// Mocks
vi.mock('./storage/local', () => ({
const mockLocalStorage = vi.hoisted(() => ({
get: vi.fn(),
set: vi.fn(),
}));
vi.mock('./storage/local', () => ({ localExtStorage: mockLocalStorage }));

const mockPopup = vi.hoisted(() => vi.fn());
vi.mock('./popup', () => ({
popup: vi.fn(),
popup: mockPopup,
}));

const mockTab = {
Expand All @@ -23,7 +27,9 @@ const mockTab = {
discarded: false,
autoDiscardable: true,
groupId: -1,
};
favIconUrl: 'https://image.com/favicon.ico',
title: 'Penumbra | xyz',
} satisfies chrome.tabs.Tab;

describe('originHandlers', () => {
beforeEach(() => {
Expand All @@ -36,20 +42,184 @@ describe('originHandlers', () => {
await expect(approveOrigin(messageSender)).rejects.toThrow('Unsupported sender');
});

it('throws an error if the tab ID is missing', async () => {
const messageSender = { origin: 'https://example.com' };
await expect(approveOrigin(messageSender)).rejects.toThrow('Unsupported sender');
});

it('throws an error if a frame ID is present', async () => {
const messageSender = { origin: 'https://example.com', tab: mockTab, frameId: 123 };
await expect(approveOrigin(messageSender)).rejects.toThrow('Unsupported sender');
});

it('prompts user for approval if the origin is not known', async () => {
mockLocalStorage.get.mockReturnValue(Promise.resolve([]));
const messageSender = { origin: 'https://newsite.com', tab: mockTab };
mockPopup.mockResolvedValue({
choice: UserChoice.Approved,
date: 123,
origin: 'https://newsite.com',
} satisfies OriginRecord);

const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Approved);
});

it('returns the stored choice if the origin is already known and approved', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Approved }]),
);

const messageSender = { origin: 'https://example.com', tab: mockTab };
const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Approved);
});

it('throws an error if multiple records exist for the same origin', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([
{ origin: 'https://example.com', choice: UserChoice.Approved },
{ origin: 'https://example.com', choice: UserChoice.Denied },
]),
);

const messageSender = { origin: 'https://example.com', tab: mockTab };
await expect(approveOrigin(messageSender)).rejects.toThrow(
'There are multiple records for origin: https://example.com',
);
});

it('returns Denied if the user denies the request', async () => {
mockLocalStorage.get.mockReturnValue(Promise.resolve([]));
const messageSender = { origin: 'https://newsite.com', tab: mockTab };
mockPopup.mockResolvedValue({
choice: UserChoice.Denied,
date: 123,
origin: 'https://newsite.com',
} satisfies OriginRecord);

const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Denied);
});

it('updates an existing record if the user changes their choice from denied to approve', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Denied }]),
);
const messageSender = { origin: 'https://example.com', tab: mockTab };
mockPopup.mockResolvedValue({
choice: UserChoice.Approved,
date: 123,
origin: 'https://example.com',
} satisfies OriginRecord);

const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Approved);
});

it('returns the previously approved choice if one exists', async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
localExtStorage.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: 'Approved' }]),
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Approved }]),
);

const messageSender = {
origin: 'https://example.com',
tab: mockTab,
};
const choice = await approveOrigin(messageSender);
expect(choice).toBe('Approved');
expect(choice).toBe(UserChoice.Approved);
});

it('correctly updates the persisted known sites after user interaction', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Denied }]),
);
const messageSender = { origin: 'https://example.com', tab: mockTab };
const newOriginRecord = {
choice: UserChoice.Approved,
date: 123,
origin: 'https://example.com',
} satisfies OriginRecord;
mockPopup.mockResolvedValue(newOriginRecord);

await approveOrigin(messageSender);

expect(mockLocalStorage.set).toHaveBeenCalledWith('knownSites', [newOriginRecord]);
});

// More tests based on different scenarios...
it('returns the stored choice if the origin is already known and ignored', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Ignored }]),
);

const messageSender = { origin: 'https://example.com', tab: mockTab };
const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Ignored);
});

it('returns UserChoice.Denied if the user closes the popup without making a choice', async () => {
mockLocalStorage.get.mockReturnValue(Promise.resolve([]));
const messageSender = { origin: 'https://newsite.com', tab: mockTab };
mockPopup.mockResolvedValue(undefined);

const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Denied);
});

it('correctly handles trailing slashes in the origin', async () => {
mockLocalStorage.get.mockReturnValue(
Promise.resolve([{ origin: 'https://example.com', choice: UserChoice.Approved }]),
);

const messageSender = { origin: 'https://example.com/', tab: mockTab };
const choice = await approveOrigin(messageSender);
expect(choice).toBe(UserChoice.Approved);
});

it('shows the popup with the correct parameters', async () => {
mockLocalStorage.get.mockReturnValue(Promise.resolve([]));
const messageSender = { origin: 'https://newsite.com', tab: mockTab };
mockPopup.mockResolvedValue({
choice: UserChoice.Approved,
date: 123,
origin: 'https://newsite.com',
} satisfies OriginRecord);

await approveOrigin(messageSender);

expect(mockPopup).toHaveBeenCalledWith({
type: PopupType.OriginApproval,
request: {
origin: 'https://newsite.com',
favIconUrl: mockTab.favIconUrl,
title: mockTab.title,
lastRequest: undefined,
},
});
});

it('correctly persists the known sites when a new site is added', async () => {
const existingOriginRecord = {
choice: UserChoice.Approved,
date: 456,
origin: 'https://existingsite.com',
} satisfies OriginRecord;
mockLocalStorage.get.mockReturnValue(Promise.resolve([existingOriginRecord]));

const messageSender = { origin: 'https://newsite.com', tab: mockTab };
const newOriginRecord = {
choice: UserChoice.Approved,
date: 123,
origin: 'https://newsite.com',
} satisfies OriginRecord;
mockPopup.mockResolvedValue(newOriginRecord);

await approveOrigin(messageSender);

expect(mockLocalStorage.set).toHaveBeenCalledWith('knownSites', [
existingOriginRecord,
newOriginRecord,
]);
});
});
});
2 changes: 1 addition & 1 deletion apps/extension/src/approve-origin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const approveOrigin = async ({

// if user interacted with popup, update record
if (popupResponse) {
void addOrUpdateSiteRecord(popupResponse);
await addOrUpdateSiteRecord(popupResponse);
}

return popupResponse?.choice ?? UserChoice.Denied;
Expand Down

0 comments on commit dd5af8b

Please sign in to comment.