Skip to content

Commit

Permalink
Merge pull request #1439 from BlueBrain/fix/download-double-extension
Browse files Browse the repository at this point in the history
Fix double-extension on file download
  • Loading branch information
danburonline authored Dec 14, 2023
2 parents b8fa139 + de04188 commit 4e20579
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/shared/containers/ResourceActionsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const ResourceActionsContainer: React.FunctionComponent<{
deprecateMethod = nexus.File.deprecate;
}

const deprectatedResource = await deprecateMethod(
const deprecatedResource = await deprecateMethod(
orgLabel,
projectLabel,
encodeURIComponent(resourceId),
Expand All @@ -130,7 +130,7 @@ const ResourceActionsContainer: React.FunctionComponent<{
message: `Deprecated ${getResourceLabel(resource)}`,
});

const { _rev } = deprectatedResource;
const { _rev } = deprecatedResource;
goToResource(
orgLabel,
projectLabel,
Expand Down
142 changes: 142 additions & 0 deletions src/shared/utils/__tests__/download.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { download } from '../download';
import { fileExtensionFromResourceEncoding } from '../../../utils/contentTypes';

// Mock fileExtensionFromResourceEncoding
jest.mock('../../../utils/contentTypes', () => ({
fileExtensionFromResourceEncoding: jest.fn(),
}));

describe('download function', () => {
const mockCreateObjectURL = jest.fn();
const mockRevokeObjectURL = jest.fn();

beforeAll(() => {
globalThis.Blob = jest.fn(() => ({})) as any;

// Mock URL.createObjectURL and URL.revokeObjectURL
globalThis.URL.createObjectURL = mockCreateObjectURL;
globalThis.URL.revokeObjectURL = mockRevokeObjectURL;

(fileExtensionFromResourceEncoding as jest.Mock).mockImplementation(
(mediaType: string) => {
if (mediaType === 'application/json') {
return 'json';
}
return '';
}
);
});

beforeEach(() => {
mockCreateObjectURL.mockReset();
mockRevokeObjectURL.mockReset();
});

it('should handle filename with existing correct extension', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example.json', 'application/json', 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});

it('should handle filename with duplicate extension', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example.json.json', 'application/json', 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});

it('should append extension if missing', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example', 'application/json', 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});

it('should handle if filename is another type than extension', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example.png', 'application/json', 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});

it('should handle if filename is another type than extension', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example.png', 'application/json', 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});

it('should download without a mediaType', () => {
const linkClickMock = jest.fn();
jest.spyOn(document, 'createElement').mockImplementation(
() =>
({
set href(url: string) {
mockCreateObjectURL(url);
},
click: linkClickMock,
download: '',
} as any)
);

download('example', undefined, 'test data');
expect(linkClickMock).toHaveBeenCalled();
expect(mockCreateObjectURL).toHaveBeenCalled();
});
});
21 changes: 16 additions & 5 deletions src/shared/utils/download.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { fileExtensionFromResourceEncoding } from '../../utils/contentTypes';

export const download = (filename: string, mediaType: string, data: any) => {
export const download = (
filename: string,
mediaType: string | undefined,
data: any
) => {
const blob = new Blob([data], { type: mediaType });
const extention = fileExtensionFromResourceEncoding(mediaType);
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, filename);
const extension = fileExtensionFromResourceEncoding(mediaType);

// Check if filename already ends with the correct extension
const hasCorrectExtension = filename.endsWith(`.${extension}`);

if ((window.navigator as any).msSaveBlob) {
(window.navigator as any).msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = extention ? `${filename}.${extention}` : filename;

// Only append extension if it's not already present or no mediaType is passed
link.download =
hasCorrectExtension || !mediaType ? filename : `${filename}.${extension}`;
link.click();
}
};
Expand Down

0 comments on commit 4e20579

Please sign in to comment.