Skip to content
Merged
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
11 changes: 9 additions & 2 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"test": "vitest run"
},
"dependencies": {
"@stellar/stellar-sdk": "^14.6.1",
Expand All @@ -30,14 +31,20 @@
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@storybook/test": "^8.6.12",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/jest": "^30.0.0",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.27",
"eslint": "^10.4.1",
"jsdom": "^29.1.1",
"postcss": "^8.5.8",
"storybook": "^8.6.12",
"tailwindcss": "^3.4.19",
"typescript": "^5.5.3",
"vite": "^6.4.1"
"vite": "^6.4.1",
"vitest": "^4.1.7"
}
}
77 changes: 77 additions & 0 deletions dashboard/src/lib/wallet/FreighterAdapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { FreighterAdapter } from './FreighterAdapter';

describe('FreighterAdapter', () => {
let adapter: FreighterAdapter;

beforeEach(() => {
adapter = new FreighterAdapter();
// Clear global window property before each test
Reflect.deleteProperty(globalThis, 'window');
globalThis.window = {} as any;
});

describe('isInstalled', () => {
it('returns false when window.freighterApi is undefined', async () => {
const installed = await adapter.isInstalled();
expect(installed).toBe(false);
});

it('returns true when window.freighterApi is defined', async () => {
(globalThis.window as any).freighterApi = {};
const installed = await adapter.isInstalled();
expect(installed).toBe(true);
});
});

describe('connect', () => {
it('throws error if not installed', async () => {
await expect(adapter.connect()).rejects.toThrow('Freighter is not installed');
});

it('throws error if connection cancelled', async () => {
(globalThis.window as any).freighterApi = {
isConnected: vi.fn().mockResolvedValue(false),
};
await expect(adapter.connect()).rejects.toThrow('User cancelled connection');
});

it('returns publicKey and network on successful connection', async () => {
(globalThis.window as any).freighterApi = {
isConnected: vi.fn().mockResolvedValue(true),
getPublicKey: vi.fn().mockResolvedValue('GB...TEST'),
getNetwork: vi.fn().mockResolvedValue('TESTNET'),
};
const result = await adapter.connect();
expect(result).toEqual({ publicKey: 'GB...TEST', network: 'TESTNET' });
});

it('wraps and throws api errors', async () => {
(globalThis.window as any).freighterApi = {
isConnected: vi.fn().mockRejectedValue(new Error('Extension error')),
};
await expect(adapter.connect()).rejects.toThrow('Failed to connect to Freighter: Extension error');
});
});

describe('signTransaction', () => {
it('throws error if not installed', async () => {
await expect(adapter.signTransaction('some_xdr', 'TESTNET')).rejects.toThrow('Freighter is not installed');
});

it('returns signed xdr successfully', async () => {
(globalThis.window as any).freighterApi = {
signTransaction: vi.fn().mockResolvedValue('signed_xdr_data'),
};
const result = await adapter.signTransaction('some_xdr', 'TESTNET');
expect(result).toBe('signed_xdr_data');
expect((globalThis.window as any).freighterApi.signTransaction).toHaveBeenCalledWith('some_xdr', { network: 'TESTNET' });
});
});

describe('disconnect', () => {
it('resolves void', async () => {
await expect(adapter.disconnect()).resolves.toBeUndefined();
});
});
});
52 changes: 52 additions & 0 deletions dashboard/src/lib/wallet/FreighterAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { WalletAdapter } from './types';

export class FreighterAdapter implements WalletAdapter {
id = 'freighter';
name = 'Freighter';
icon = 'freighter-icon-url';

async isInstalled(): Promise<boolean> {
// Check if the Freighter extension is injected
return typeof window !== 'undefined' && !!(window as any).freighterApi;
}

async connect(): Promise<{ publicKey: string; network: string }> {
const installed = await this.isInstalled();
if (!installed) {
throw new Error('Freighter is not installed');
}

const api = (window as any).freighterApi;
try {
if (await api.isConnected()) {
const publicKey = await api.getPublicKey();
const network = await api.getNetwork();
return { publicKey, network };
} else {
throw new Error('User cancelled connection');
}
} catch (error) {
throw new Error(`Failed to connect to Freighter: ${error instanceof Error ? error.message : String(error)}`);
}
}

async disconnect(): Promise<void> {
// Freighter doesn't have a direct disconnect, but we can do local cleanup
return Promise.resolve();
}

async signTransaction(xdr: string, network: string): Promise<string> {
const installed = await this.isInstalled();
if (!installed) {
throw new Error('Freighter is not installed');
}

const api = (window as any).freighterApi;
try {
const signedXdr = await api.signTransaction(xdr, { network });
return signedXdr;
} catch (error) {
throw new Error(`Failed to sign transaction: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
11 changes: 11 additions & 0 deletions dashboard/src/lib/wallet/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type NetworkType = 'TESTNET' | 'PUBLIC' | 'STANDALONE';

export interface WalletAdapter {
id: string;
name: string;
icon: string;
isInstalled(): Promise<boolean>;
connect(): Promise<{ publicKey: string; network: string }>;
disconnect(): Promise<void>;
signTransaction(xdr: string, network: string): Promise<string>;
}
8 changes: 8 additions & 0 deletions dashboard/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
},
})
Loading
Loading