347 lines
12 KiB
TypeScript
347 lines
12 KiB
TypeScript
import {
|
|
getFormattedDateTime,
|
|
convertActivityToCsv,
|
|
saveCsvFile,
|
|
CSV_KEYS
|
|
} from '.././utils/ActivityCsvUtils';
|
|
import RNFS from 'react-native-fs';
|
|
import { Platform } from 'react-native';
|
|
|
|
jest.mock('react-native-fs', () => ({
|
|
DownloadDirectoryPath: '/mock/download/path',
|
|
DocumentDirectoryPath: '/mock/document/path',
|
|
writeFile: jest.fn(),
|
|
exists: jest.fn()
|
|
}));
|
|
|
|
jest.mock('react-native', () => ({
|
|
Platform: { OS: 'android' }
|
|
}));
|
|
|
|
describe('activityCsvUtils', () => {
|
|
describe('getFormattedDateTime', () => {
|
|
it('returns a properly formatted timestamp', () => {
|
|
const result = getFormattedDateTime();
|
|
expect(result).toMatch(/^\d{8}_\d{6}$/); // Example: 20250212_140719
|
|
});
|
|
});
|
|
|
|
describe('convertActivityToCsv', () => {
|
|
it('correctly formats Invoice CSV data', async () => {
|
|
const mockInvoices = [
|
|
{
|
|
getAmount: 1500,
|
|
getPaymentRequest: 'inv_req123',
|
|
getRHash: 'hash_inv1',
|
|
getMemo: 'Test Memo',
|
|
getNote: 'Test Note',
|
|
getCreationDate: '2024-02-10',
|
|
formattedTimeUntilExpiry: '30 min'
|
|
},
|
|
{
|
|
getAmount: 3000,
|
|
getPaymentRequest: 'inv_req456',
|
|
getRHash: 'hash_inv2',
|
|
getMemo: '',
|
|
getNote: '',
|
|
getCreationDate: '2024-02-11',
|
|
formattedTimeUntilExpiry: '1 hour'
|
|
}
|
|
];
|
|
|
|
const result = await convertActivityToCsv(
|
|
mockInvoices,
|
|
CSV_KEYS.invoice
|
|
);
|
|
expect(result).toContain(
|
|
'"1500","inv_req123","hash_inv1","Test Memo","Test Note","2024-02-10","30 min"'
|
|
);
|
|
expect(result).toContain(
|
|
'"3000","inv_req456","hash_inv2","","","2024-02-11","1 hour"'
|
|
);
|
|
});
|
|
|
|
it('correctly formats Payment CSV data', async () => {
|
|
const mockPayments = [
|
|
{
|
|
getDestination: 'dest123',
|
|
getPaymentRequest: 'pay_req123',
|
|
paymentHash: 'hash_pay1',
|
|
getAmount: 800,
|
|
getMemo: 'Payment Memo',
|
|
getNote: 'Payment Note',
|
|
getDate: '2024-02-09'
|
|
},
|
|
{
|
|
getDestination: 'dest456',
|
|
getPaymentRequest: 'pay_req456',
|
|
paymentHash: 'hash_pay2',
|
|
getAmount: 1600,
|
|
getMemo: '',
|
|
getNote: '',
|
|
getDate: '2024-02-08'
|
|
}
|
|
];
|
|
|
|
const result = await convertActivityToCsv(
|
|
mockPayments,
|
|
CSV_KEYS.payment
|
|
);
|
|
expect(result).toContain(
|
|
'"dest123","pay_req123","hash_pay1","800","Payment Memo","Payment Note","2024-02-09"'
|
|
);
|
|
expect(result).toContain(
|
|
'"dest456","pay_req456","hash_pay2","1600","","","2024-02-08"'
|
|
);
|
|
});
|
|
|
|
it('correctly formats Transaction CSV data', async () => {
|
|
const mockTransactions = [
|
|
{
|
|
tx: 'txhash1',
|
|
getAmount: 2000,
|
|
getFee: 50,
|
|
getNote: 'Tx Note1',
|
|
getDate: '2024-02-07'
|
|
},
|
|
{
|
|
tx: 'txhash2',
|
|
getAmount: 5000,
|
|
getFee: 100,
|
|
getNote: '',
|
|
getDate: '2024-02-06'
|
|
}
|
|
];
|
|
|
|
const result = await convertActivityToCsv(
|
|
mockTransactions,
|
|
CSV_KEYS.transaction
|
|
);
|
|
expect(result).toContain(
|
|
'"txhash1","2000","50","Tx Note1","2024-02-07"'
|
|
);
|
|
expect(result).toContain('"txhash2","5000","100","","2024-02-06"');
|
|
});
|
|
|
|
it('handles missing fields for Invoice CSV', async () => {
|
|
const mockInvoices = [{ getAmount: 1500 }];
|
|
const result = await convertActivityToCsv(
|
|
mockInvoices,
|
|
CSV_KEYS.invoice
|
|
);
|
|
expect(result).toContain('"1500","","","","","",""');
|
|
});
|
|
|
|
it('handles missing fields for Payment CSV', async () => {
|
|
const mockPayments = [{ getDestination: 'dest123' }];
|
|
const result = await convertActivityToCsv(
|
|
mockPayments,
|
|
CSV_KEYS.payment
|
|
);
|
|
expect(result).toContain('"dest123","","","","","",""');
|
|
});
|
|
|
|
it('handles missing fields for Transaction CSV', async () => {
|
|
const mockTransactions = [{ tx: 'txhash1', getAmount: 2000 }];
|
|
const result = await convertActivityToCsv(
|
|
mockTransactions,
|
|
CSV_KEYS.transaction
|
|
);
|
|
expect(result).toContain('"txhash1","2000","","",""');
|
|
});
|
|
});
|
|
|
|
describe('saveCsvFile', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
(RNFS.exists as jest.Mock).mockResolvedValue(false);
|
|
});
|
|
|
|
it('writes the CSV file to the correct path on Android', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('test.csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test.csv'
|
|
);
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/test.csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('writes the CSV file to the correct path on iOS', async () => {
|
|
(Platform.OS as any) = 'ios';
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('test.csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/document/path/test.csv'
|
|
);
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/document/path/test.csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('appends (1) suffix when file already exists', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('test.csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test.csv'
|
|
);
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (1).csv'
|
|
);
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (1).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('appends (2) suffix when file and (1) already exist', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('test.csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test.csv'
|
|
);
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (1).csv'
|
|
);
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (2).csv'
|
|
);
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (2).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('handles uppercase CSV extension', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('test.CSV', 'mock,csv,data');
|
|
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/test (1).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('handles filename with non-CSV extension correctly', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('export.txt', 'mock,csv,data');
|
|
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (1).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('handles filename without extension', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('export', 'mock,csv,data');
|
|
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (1).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('strips existing numbered suffix from filename', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('export (1).csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (2).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('strips existing numbered suffix and finds next available number', async () => {
|
|
(Platform.OS as any) = 'android';
|
|
(RNFS.exists as jest.Mock)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(true)
|
|
.mockResolvedValueOnce(false);
|
|
(RNFS.writeFile as jest.Mock).mockResolvedValue(undefined);
|
|
|
|
await saveCsvFile('export (1).csv', 'mock,csv,data');
|
|
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (1).csv'
|
|
);
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (2).csv'
|
|
);
|
|
expect(RNFS.exists).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (3).csv'
|
|
);
|
|
expect(RNFS.writeFile).toHaveBeenCalledWith(
|
|
'/mock/download/path/export (3).csv',
|
|
'mock,csv,data',
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
it('throws an error when file writing fails (but suppresses console error)', async () => {
|
|
const consoleErrorSpy = jest
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
(RNFS.writeFile as jest.Mock).mockRejectedValue(
|
|
new Error('File write failed')
|
|
);
|
|
|
|
await expect(
|
|
saveCsvFile('test.csv', 'mock,csv,data')
|
|
).rejects.toThrow('File write failed');
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
});
|
|
});
|