Simplify ServersImporter using file.text() instead of a FileReader

This commit is contained in:
Alejandro Celaya 2023-09-02 19:44:29 +02:00
parent a11a2c84fe
commit 973f3e3c8b
3 changed files with 19 additions and 37 deletions

View file

@ -8,32 +8,20 @@ const validateServers = (servers: any): servers is ServerData[] =>
Array.isArray(servers) && servers.every(validateServer); Array.isArray(servers) && servers.every(validateServer);
export class ServersImporter { export class ServersImporter {
public constructor(private readonly csvToJson: CsvToJson, private readonly fileReaderFactory: () => FileReader) {} public constructor(private readonly csvToJson: CsvToJson) {}
public readonly importServersFromFile = async (file?: File | null): Promise<ServerData[]> => { public async importServersFromFile(file: File | null | undefined): Promise<ServerData[]> {
if (!file) { if (!file) {
throw new Error('No file provided'); throw new Error('No file provided');
} }
const reader = this.fileReaderFactory(); const content = await file.text();
const servers = await this.csvToJson(content);
return new Promise((resolve, reject) => { if (!validateServers(servers)) {
reader.addEventListener('loadend', async (e: ProgressEvent<FileReader>) => { throw new Error('Provided file does not have the right format.');
try { }
// TODO Read as stream, otherwise, if the file is too big, this will block the browser tab
const content = e.target?.result?.toString() ?? '';
const servers = await this.csvToJson(content);
if (!validateServers(servers)) { return servers;
throw new Error('Provided file does not have the right format.'); }
}
resolve(servers);
} catch (error) {
reject(error);
}
});
reader.readAsText(file);
});
};
} }

View file

@ -62,8 +62,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.decorator('ServerError', connect(['servers', 'selectedServer'])); bottle.decorator('ServerError', connect(['servers', 'selectedServer']));
// Services // Services
bottle.constant('fileReaderFactory', () => new FileReader()); bottle.service('ServersImporter', ServersImporter, 'csvToJson');
bottle.service('ServersImporter', ServersImporter, 'csvToJson', 'fileReaderFactory');
bottle.service('ServersExporter', ServersExporter, 'Storage', 'window', 'jsonToCsv'); bottle.service('ServersExporter', ServersExporter, 'Storage', 'window', 'jsonToCsv');
// Actions // Actions

View file

@ -5,18 +5,13 @@ import { ServersImporter } from '../../../src/servers/services/ServersImporter';
describe('ServersImporter', () => { describe('ServersImporter', () => {
const servers: RegularServer[] = [fromPartial<RegularServer>({}), fromPartial<RegularServer>({})]; const servers: RegularServer[] = [fromPartial<RegularServer>({}), fromPartial<RegularServer>({})];
const csvjsonMock = vi.fn().mockResolvedValue(servers); const csvjsonMock = vi.fn().mockResolvedValue(servers);
const readAsText = vi.fn(); const text = vi.fn().mockReturnValue('');
const fileReaderMock = fromPartial<FileReader>({ const fileMock = () => fromPartial<File>({ text });
readAsText, const importer = new ServersImporter(csvjsonMock);
addEventListener: ((_eventName: string, listener: (e: ProgressEvent<FileReader>) => void) => listener(
fromPartial({ target: { result: '' } }),
)) as any,
});
const importer = new ServersImporter(csvjsonMock, () => fileReaderMock);
describe('importServersFromFile', () => { describe('importServersFromFile', () => {
it('rejects with error if no file was provided', async () => { it.each([[null], [undefined]])('rejects with error if no file was provided', async (file) => {
await expect(importer.importServersFromFile()).rejects.toEqual( await expect(importer.importServersFromFile(file)).rejects.toEqual(
new Error('No file provided'), new Error('No file provided'),
); );
}); });
@ -26,7 +21,7 @@ describe('ServersImporter', () => {
csvjsonMock.mockRejectedValue(expectedError); csvjsonMock.mockRejectedValue(expectedError);
await expect(importer.importServersFromFile(fromPartial({ type: 'text/html' }))).rejects.toEqual(expectedError); await expect(importer.importServersFromFile(fileMock())).rejects.toEqual(expectedError);
}); });
it.each([ it.each([
@ -55,7 +50,7 @@ describe('ServersImporter', () => {
])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => { ])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => {
csvjsonMock.mockResolvedValue(parsedObject); csvjsonMock.mockResolvedValue(parsedObject);
await expect(importer.importServersFromFile(fromPartial({ type: 'text/html' }))).rejects.toEqual( await expect(importer.importServersFromFile(fileMock())).rejects.toEqual(
new Error('Provided file does not have the right format.'), new Error('Provided file does not have the right format.'),
); );
}); });
@ -76,10 +71,10 @@ describe('ServersImporter', () => {
csvjsonMock.mockResolvedValue(expectedServers); csvjsonMock.mockResolvedValue(expectedServers);
const result = await importer.importServersFromFile(fromPartial({})); const result = await importer.importServersFromFile(fileMock());
expect(result).toEqual(expectedServers); expect(result).toEqual(expectedServers);
expect(readAsText).toHaveBeenCalledTimes(1); expect(text).toHaveBeenCalledTimes(1);
expect(csvjsonMock).toHaveBeenCalledTimes(1); expect(csvjsonMock).toHaveBeenCalledTimes(1);
}); });
}); });