WellNuo/web/__tests__/lib/api.test.ts
Sergei 29af4c0d0b Add data sync tests for web app using same backend as mobile
Add comprehensive tests verifying the web API client uses the same
WellNuo backend (wellnuo.smartlaunchhub.com/api) as the mobile app.
Tests cover beneficiaries CRUD, profile operations, and auth handling.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-01 11:21:28 -08:00

371 lines
10 KiB
TypeScript

import { api } from '@/lib/api';
// Mock localStorage
const localStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem: (key: string) => store[key] || null,
setItem: (key: string, value: string) => {
store[key] = value.toString();
},
removeItem: (key: string) => {
delete store[key];
},
clear: () => {
store = {};
},
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
});
// Mock crypto.getRandomValues
Object.defineProperty(global, 'crypto', {
value: {
getRandomValues: (arr: Uint8Array) => {
for (let i = 0; i < arr.length; i++) {
arr[i] = Math.floor(Math.random() * 256);
}
return arr;
},
},
});
describe('API Service', () => {
beforeEach(() => {
localStorageMock.clear();
jest.clearAllMocks();
});
describe('Token Management', () => {
it('should save and retrieve access token', async () => {
const testToken = 'test-jwt-token';
localStorageMock.setItem('accessToken', testToken);
const token = await api.getToken();
expect(token).toBe(testToken);
});
it('should return null when no token exists', async () => {
const token = await api.getToken();
expect(token).toBeNull();
});
});
describe('Email Management', () => {
it('should save and retrieve email', async () => {
const testEmail = 'test@example.com';
await api.saveEmail(testEmail);
const email = await api.getStoredEmail();
expect(email).toBe(testEmail);
});
it('should return null when no email is stored', async () => {
const email = await api.getStoredEmail();
expect(email).toBeNull();
});
});
describe('Onboarding Status', () => {
it('should save and retrieve onboarding completed flag', async () => {
await api.setOnboardingCompleted(true);
let isCompleted = await api.isOnboardingCompleted();
expect(isCompleted).toBe(true);
await api.setOnboardingCompleted(false);
isCompleted = await api.isOnboardingCompleted();
expect(isCompleted).toBe(false);
});
it('should return false when onboarding flag is not set', async () => {
const isCompleted = await api.isOnboardingCompleted();
expect(isCompleted).toBe(false);
});
});
describe('Authentication Status', () => {
it('should return true when token exists', async () => {
localStorageMock.setItem('accessToken', 'test-token');
const isAuth = await api.isAuthenticated();
expect(isAuth).toBe(true);
});
it('should return false when token does not exist', async () => {
const isAuth = await api.isAuthenticated();
expect(isAuth).toBe(false);
});
});
describe('Logout', () => {
it('should clear all stored data', async () => {
// Set up some data
localStorageMock.setItem('accessToken', 'token');
localStorageMock.setItem('userId', '123');
localStorageMock.setItem('userEmail', 'test@example.com');
localStorageMock.setItem('legacyAccessToken', 'legacy-token');
await api.logout();
// Verify all data is cleared
expect(localStorageMock.getItem('accessToken')).toBeNull();
expect(localStorageMock.getItem('userId')).toBeNull();
expect(localStorageMock.getItem('userEmail')).toBeNull();
expect(localStorageMock.getItem('legacyAccessToken')).toBeNull();
});
});
describe('Check Email', () => {
it('should call the API with correct endpoint', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ exists: true }),
})
) as jest.Mock;
const result = await api.checkEmail('test@example.com');
expect(global.fetch).toHaveBeenCalledWith(
'https://wellnuo.smartlaunchhub.com/api/auth/check-email',
expect.objectContaining({
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email: 'test@example.com' }),
})
);
expect(result.ok).toBe(true);
expect(result.data?.exists).toBe(true);
});
it('should handle network errors', async () => {
global.fetch = jest.fn(() =>
Promise.reject(new Error('Network error'))
) as jest.Mock;
const result = await api.checkEmail('test@example.com');
expect(result.ok).toBe(false);
expect(result.error?.message).toContain('Network error');
});
});
describe('Data Sync - Same Backend as Mobile App', () => {
const WELLNUO_API_URL = 'https://wellnuo.smartlaunchhub.com/api';
it('should fetch beneficiaries from WellNuo API', async () => {
const testToken = 'valid-jwt-token';
localStorageMock.setItem('accessToken', testToken);
const mockBeneficiaries = {
beneficiaries: [
{
id: 1,
name: 'John Doe',
displayName: 'John Doe',
email: 'john@example.com',
equipmentStatus: 'active',
hasDevices: true,
},
],
};
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockBeneficiaries),
})
) as jest.Mock;
const result = await api.getAllBeneficiaries();
expect(global.fetch).toHaveBeenCalledWith(
`${WELLNUO_API_URL}/me/beneficiaries`,
expect.objectContaining({
method: 'GET',
headers: {
'Authorization': `Bearer ${testToken}`,
},
})
);
expect(result.ok).toBe(true);
expect(result.data).toHaveLength(1);
expect(result.data?.[0].id).toBe(1);
expect(result.data?.[0].displayName).toBe('John Doe');
});
it('should fetch user profile from WellNuo API', async () => {
const testToken = 'valid-jwt-token';
localStorageMock.setItem('accessToken', testToken);
const mockProfile = {
id: 42,
email: 'user@example.com',
firstName: 'Test',
lastName: 'User',
phone: '+1234567890',
user: {
id: 42,
email: 'user@example.com',
firstName: 'Test',
lastName: 'User',
},
};
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockProfile),
})
) as jest.Mock;
const result = await api.getProfile();
expect(global.fetch).toHaveBeenCalledWith(
`${WELLNUO_API_URL}/auth/me`,
expect.objectContaining({
method: 'GET',
headers: {
'Authorization': `Bearer ${testToken}`,
},
})
);
expect(result.ok).toBe(true);
expect(result.data?.email).toBe('user@example.com');
expect(result.data?.firstName).toBe('Test');
});
it('should update profile via WellNuo API', async () => {
const testToken = 'valid-jwt-token';
localStorageMock.setItem('accessToken', testToken);
const mockResponse = {
id: 42,
email: 'user@example.com',
firstName: 'Updated',
lastName: 'Name',
phone: '+1234567890',
};
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockResponse),
})
) as jest.Mock;
const result = await api.updateProfile({
firstName: 'Updated',
lastName: 'Name',
});
expect(global.fetch).toHaveBeenCalledWith(
`${WELLNUO_API_URL}/auth/profile`,
expect.objectContaining({
method: 'PATCH',
headers: {
'Authorization': `Bearer ${testToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ firstName: 'Updated', lastName: 'Name' }),
})
);
expect(result.ok).toBe(true);
expect(result.data?.firstName).toBe('Updated');
});
it('should create beneficiary via WellNuo API', async () => {
const testToken = 'valid-jwt-token';
localStorageMock.setItem('accessToken', testToken);
const mockResponse = {
beneficiary: {
id: 123,
name: 'New Beneficiary',
},
};
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockResponse),
})
) as jest.Mock;
const result = await api.createBeneficiary({
name: 'New Beneficiary',
phone: '+1234567890',
});
expect(global.fetch).toHaveBeenCalledWith(
`${WELLNUO_API_URL}/me/beneficiaries`,
expect.objectContaining({
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${testToken}`,
},
})
);
expect(result.ok).toBe(true);
expect(result.data?.id).toBe(123);
expect(result.data?.name).toBe('New Beneficiary');
});
it('should delete beneficiary via WellNuo API', async () => {
const testToken = 'valid-jwt-token';
localStorageMock.setItem('accessToken', testToken);
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ success: true }),
})
) as jest.Mock;
const result = await api.deleteBeneficiary(123);
expect(global.fetch).toHaveBeenCalledWith(
`${WELLNUO_API_URL}/me/beneficiaries/123`,
expect.objectContaining({
method: 'DELETE',
headers: {
'Authorization': `Bearer ${testToken}`,
},
})
);
expect(result.ok).toBe(true);
expect(result.data?.success).toBe(true);
});
it('should handle 401 unauthorized by triggering callback', async () => {
const testToken = 'expired-token';
localStorageMock.setItem('accessToken', testToken);
global.fetch = jest.fn(() =>
Promise.resolve({
ok: false,
status: 401,
json: () => Promise.resolve({ error: 'Unauthorized' }),
})
) as jest.Mock;
const result = await api.getAllBeneficiaries();
expect(result.ok).toBe(false);
expect(result.error?.code).toBe('UNAUTHORIZED');
});
});
});