Commit 5c969487 authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Replace all file-downloading function with one which expects the type

parent 4ce317f5
function downloadFile(
export function downloadFile(
content: string,
type: string,
filename: string,
document: Document,
/* istanbul ignore next - test seam */
document_ = document,
): void {
const blob = new Blob([content], { type });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
const link = document_.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
document_.body.appendChild(link);
link.click();
document.body.removeChild(link);
document_.body.removeChild(link);
URL.revokeObjectURL(url);
}
function buildTextFileDownloader(type: string) {
return (
text: string,
filename: string,
/* istanbul ignore next - test seam */
_document = document,
) => downloadFile(text, type, filename, _document);
}
export const downloadJSONFile = buildTextFileDownloader('application/json');
export const downloadTextFile = buildTextFileDownloader('text/plain');
export const downloadCSVFile = buildTextFileDownloader('text/csv');
export const downloadHTMLFile = buildTextFileDownloader('text/html');
import {
downloadCSVFile,
downloadHTMLFile,
downloadJSONFile,
downloadTextFile,
} from '../download-file';
import { downloadFile } from '../download-file';
describe('download-file', () => {
let fakeLink;
......@@ -47,39 +42,14 @@ describe('download-file', () => {
assert.equal('hidden', fakeLink.style.visibility);
}
it('downloadJSONFile generates JSON file with provided data', () => {
const data = JSON.stringify({ foo: ['bar', 'baz'] }, null, 2);
const filename = 'my-file.json';
['application/json', 'text/plain', 'text/csv', 'text/html'].forEach(type => {
it('downloadTextFile generates text file with provided data', () => {
const data = 'The content of the file';
const filename = 'my-file.txt';
downloadJSONFile(data, filename, fakeDocument);
downloadFile(data, type, filename, fakeDocument);
assertDownloadHappened(filename, data, 'application/json');
});
it('downloadTextFile generates text file with provided data', () => {
const data = 'The content of the file';
const filename = 'my-file.txt';
downloadTextFile(data, filename, fakeDocument);
assertDownloadHappened(filename, data, 'text/plain');
});
it('downloadCSVFile generates csv file with provided data', () => {
const data = 'foo,bar,baz';
const filename = 'my-file.csv';
downloadCSVFile(data, filename, fakeDocument);
assertDownloadHappened(filename, data, 'text/csv');
});
it('downloadHTMLFile generates HTML file with provided data', () => {
const data = '<p>Hello</p>';
const filename = 'my-file.html';
downloadHTMLFile(data, filename, fakeDocument);
assertDownloadHappened(filename, data, 'text/html');
assertDownloadHappened(filename, data, type);
});
});
});
......@@ -8,12 +8,7 @@ import {
} from '@hypothesis/frontend-shared';
import { useCallback, useId, useMemo, useState } from 'preact/hooks';
import {
downloadCSVFile,
downloadHTMLFile,
downloadJSONFile,
downloadTextFile,
} from '../../../shared/download-file';
import { downloadFile } from '../../../shared/download-file';
import type { APIAnnotationData } from '../../../types/api';
import { annotationDisplayName } from '../../helpers/annotation-user';
import type { UserAnnotations } from '../../helpers/annotations-by-user';
......@@ -74,6 +69,16 @@ const exportFormats: ExportFormat[] = [
},
];
function formatToMimeType(format: ExportFormat['value']): string {
const typeForFormat: Record<ExportFormat['value'], string> = {
json: 'application/json',
txt: 'text/plain',
csv: 'text/csv',
html: 'text/html',
};
return typeForFormat[format];
}
/**
* Render content for "export" tab panel: allow user to export annotations
* with a specified filename.
......@@ -200,25 +205,9 @@ function ExportAnnotations({
const format = exportFormat.value;
const filename = `${customFilename ?? defaultFilename}.${format}`;
const exportData = buildExportContent(format);
const mimeType = formatToMimeType(format);
switch (format) {
case 'json': {
downloadJSONFile(exportData, filename);
break;
}
case 'txt': {
downloadTextFile(exportData, filename);
break;
}
case 'csv': {
downloadCSVFile(exportData, filename);
break;
}
case 'html': {
downloadHTMLFile(exportData, filename);
break;
}
}
downloadFile(exportData, mimeType, filename);
} catch (e) {
toastMessenger.error('Exporting annotations failed');
}
......
......@@ -14,10 +14,7 @@ describe('ExportAnnotations', () => {
let fakeStore;
let fakeAnnotationsExporter;
let fakeToastMessenger;
let fakeDownloadJSONFile;
let fakeDownloadTextFile;
let fakeDownloadCSVFile;
let fakeDownloadHTMLFile;
let fakeDownloadFile;
let fakeSuggestedFilename;
let fakeCopyPlainText;
let fakeCopyHTML;
......@@ -48,10 +45,7 @@ describe('ExportAnnotations', () => {
error: sinon.stub(),
success: sinon.stub(),
};
fakeDownloadJSONFile = sinon.stub();
fakeDownloadTextFile = sinon.stub();
fakeDownloadCSVFile = sinon.stub();
fakeDownloadHTMLFile = sinon.stub();
fakeDownloadFile = sinon.stub();
fakeStore = {
defaultAuthority: sinon.stub().returns('example.com'),
isFeatureEnabled: sinon.stub().returns(true),
......@@ -74,10 +68,7 @@ describe('ExportAnnotations', () => {
$imports.$mock({
'../../../shared/download-file': {
downloadJSONFile: fakeDownloadJSONFile,
downloadTextFile: fakeDownloadTextFile,
downloadCSVFile: fakeDownloadCSVFile,
downloadHTMLFile: fakeDownloadHTMLFile,
downloadFile: fakeDownloadFile,
},
'../../helpers/export-annotations': {
suggestedFilename: fakeSuggestedFilename,
......@@ -408,21 +399,21 @@ describe('ExportAnnotations', () => {
[
{
format: 'json',
getExpectedInvokedDownloader: () => fakeDownloadJSONFile,
expectedMimeType: 'application/json',
},
{
format: 'txt',
getExpectedInvokedDownloader: () => fakeDownloadTextFile,
expectedMimeType: 'text/plain',
},
{
format: 'csv',
getExpectedInvokedDownloader: () => fakeDownloadCSVFile,
expectedMimeType: 'text/csv',
},
{
format: 'html',
getExpectedInvokedDownloader: () => fakeDownloadHTMLFile,
expectedMimeType: 'text/html',
},
].forEach(({ format, getExpectedInvokedDownloader }) => {
].forEach(({ format, expectedMimeType }) => {
it('downloads a file using user-entered filename appended with proper extension', async () => {
const wrapper = createComponent();
const filenameInput = wrapper.find(
......@@ -436,11 +427,11 @@ describe('ExportAnnotations', () => {
submitExportForm(wrapper);
const invokedDownloader = getExpectedInvokedDownloader();
assert.calledOnce(invokedDownloader);
assert.calledOnce(fakeDownloadFile);
assert.calledWith(
invokedDownloader,
fakeDownloadFile,
sinon.match.any,
expectedMimeType,
`my-filename.${format}`,
);
});
......@@ -456,7 +447,7 @@ describe('ExportAnnotations', () => {
submitExportForm(wrapper);
assert.notCalled(fakeDownloadJSONFile);
assert.notCalled(fakeDownloadFile);
assert.calledOnce(fakeAnnotationsExporter.buildJSONExportContent);
assert.calledWith(
fakeToastMessenger.error,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment