diff --git a/packages/common/src/download-file.function.spec.ts b/packages/common/src/download-file.function.spec.ts index 8e9f388..555b8d0 100644 --- a/packages/common/src/download-file.function.spec.ts +++ b/packages/common/src/download-file.function.spec.ts @@ -1,23 +1,72 @@ -import { blobMock, jsonMock, nameMock } from '@bimeister/utilities.internal'; import { downloadFile } from './download-file.function'; describe('download-file.function.ts', () => { - const fileNameSample: string = nameMock; + const originalCreateObjectURL: (obj: Blob | MediaSource) => string = URL.createObjectURL; + const originalRevokeObjectURL: (url: string) => void = URL.revokeObjectURL; + let anchorClickSpy: jest.SpyInstance; - document.body.innerHTML = ``; - window.URL.createObjectURL = (): string => nameMock; + beforeEach(() => { + URL.createObjectURL = jest.fn(() => 'blob:http://localhost/sample'); + URL.revokeObjectURL = jest.fn(); - it('should create new DOM-element if data is Blob', () => { - const fileSample: Blob = blobMock; - downloadFile(fileNameSample, fileSample); - const aElement: HTMLAnchorElement | null = document.querySelector('a'); - expect(aElement).toBeDefined(); + anchorClickSpy = jest.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(); + + document.body.innerHTML = ''; + }); + + afterEach(() => { + URL.createObjectURL = originalCreateObjectURL; + URL.revokeObjectURL = originalRevokeObjectURL; + anchorClickSpy.mockRestore(); }); - it('should create new DOM-element if data is string', () => { - const fileSample: string = jsonMock; - downloadFile(fileNameSample, fileSample); - const aElement: HTMLAnchorElement | null = document.querySelector('a'); - expect(aElement).toBeDefined(); + it('should create an anchor element and trigger download using a presigned URL', () => { + const presignedUrl: string = 'https://example.com/path/to/file'; + + downloadFile(presignedUrl); + + const anchor: HTMLAnchorElement | null = document.querySelector('a'); + expect(anchor).toBeNull(); + expect(anchorClickSpy).toHaveBeenCalledTimes(1); + }); + + it('should create an anchor element and download a file from a Blob', () => { + const filename: string = 'example.txt'; + const blobData: Blob = new Blob(['Hello, world!'], { type: 'text/plain' }); + + downloadFile(filename, blobData); + + const anchor: HTMLAnchorElement | null = document.querySelector('a'); + expect(anchor).toBeNull(); + expect(URL.createObjectURL).toHaveBeenCalledWith(blobData); + expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:http://localhost/sample'); + expect(anchorClickSpy).toHaveBeenCalledTimes(1); + }); + + it('should create an anchor element and download a file from a string', () => { + const filename: string = 'example.txt'; + const stringData: string = 'Sample data content'; + + downloadFile(filename, stringData); + + const anchor: HTMLAnchorElement | null = document.querySelector('a'); + expect(anchor).toBeNull(); + expect(URL.createObjectURL).toHaveBeenCalledTimes(1); + expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:http://localhost/sample'); + expect(anchorClickSpy).toHaveBeenCalledTimes(1); + }); + + it('should create an anchor element and download a file from a BlobPart (typed array)', () => { + const filename: string = 'example.bin'; + const typedArray: Uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in ASCII + + downloadFile(filename, typedArray); + + const anchor: HTMLAnchorElement | null = document.querySelector('a'); + expect(anchor).toBeNull(); + const expectedBlob: Blob = new Blob([typedArray]); + expect(URL.createObjectURL).toHaveBeenCalledWith(expectedBlob); + expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:http://localhost/sample'); + expect(anchorClickSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/common/src/download-file.function.ts b/packages/common/src/download-file.function.ts index 3eae571..93e01c2 100644 --- a/packages/common/src/download-file.function.ts +++ b/packages/common/src/download-file.function.ts @@ -1,8 +1,49 @@ -export function downloadFile(filename: string, data: Blob | string): void { - const dataBlob: Blob = data instanceof Blob ? data : new Blob([data]); +/* eslint-disable @typescript-eslint/unified-signatures */ +import { isNil } from './is-nil.function'; + +/** + * Initiates a file download using a pre-signed URL. + * + * @param presignedUrl - The pre-signed URL from which the file will be downloaded. + * + * @example + * Download a file from a pre-signed URL: + * downloadFile('https://example.com/path/to/file'); + */ +export function downloadFile(presignedUrl: string): void; + +/** + * Creates and downloads a file from the provided data. + * + * @param filename - The name under which the file will be saved. + * @param data - The data to be downloaded, provided as BlobPart. + * + * @example + * Download a file from Blob data: + * const data = new Blob(['Hello, world!'], { type: 'text/plain' }); + * downloadFile('example.txt', data); + * + * @example + * Download a file from string data: + * const dataString = "Sample data content"; + * downloadFile('example.txt', dataString); + */ +export function downloadFile(filename: string, data: BlobPart): void; +export function downloadFile(filenameOrPresignedUrl: string, data?: BlobPart): void { + if (isNil(data)) { + const anchor: HTMLAnchorElement = document.createElement('a'); + anchor.href = filenameOrPresignedUrl; + anchor.click(); + anchor.remove(); + return; + } + + const dataBlob: Blob = data instanceof Blob ? data : new Blob([data]); const anchor: HTMLAnchorElement = document.createElement('a'); anchor.href = URL.createObjectURL(dataBlob); - anchor.download = filename; + anchor.download = filenameOrPresignedUrl; anchor.click(); + URL.revokeObjectURL(anchor.href); + anchor.remove(); }