From 76dc1b3577bfa04bd7ac66c8e54212a8d4409456 Mon Sep 17 00:00:00 2001 From: Faustin Date: Wed, 16 Oct 2024 11:29:15 +0200 Subject: [PATCH 1/3] feat: download-file service --- .../services/download-file.service.spec.ts | 42 +++++++++++++++++++ src/app/services/download-file.service.ts | 17 ++++++++ src/app/settings/index.component.ts | 11 +---- src/app/types/file.type.ts | 1 + 4 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 src/app/services/download-file.service.spec.ts create mode 100644 src/app/services/download-file.service.ts create mode 100644 src/app/types/file.type.ts diff --git a/src/app/services/download-file.service.spec.ts b/src/app/services/download-file.service.spec.ts new file mode 100644 index 000000000..b9bb9b883 --- /dev/null +++ b/src/app/services/download-file.service.spec.ts @@ -0,0 +1,42 @@ +import { FileFormat } from '@app/types/file.type'; +import { downloadFile } from './download-file.service'; + +describe('download-file function', () => { + const anchor = { + href: '', + download: '', + click: jest.fn() + }; + + const mockId = 1234; + const mockDate = '2024 01 01'; + + global.document.createElement = jest.fn().mockReturnValue(anchor); + global.URL.createObjectURL = jest.fn(); + + global.Date.prototype.getTime = jest.fn(() => mockId); + global.Date.prototype.toISOString = jest.fn(() => mockDate); + + const name = 'settings'; + const format: FileFormat = 'csv'; + + const data = JSON.stringify([1, 2, 3]); + + beforeEach(() => { + }); + + it('should download the settings', () => { + downloadFile(data, name, format); + expect(anchor.download).toEqual(`${mockDate}-${mockId}-${name}.${format}`); + }); + + it('should download the settings with default configuration', () => { + downloadFile(data); + expect(anchor.download).toContain(`${mockDate}-${mockId}.json`); + }); + + it('should click the anchor', () => { + downloadFile(data); + expect(anchor.click).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/app/services/download-file.service.ts b/src/app/services/download-file.service.ts new file mode 100644 index 000000000..418db7750 --- /dev/null +++ b/src/app/services/download-file.service.ts @@ -0,0 +1,17 @@ +import { FileFormat } from '@app/types/file.type'; + +/** + * Download data into a json or csv file. The file name will be as following: "date-id-name.format". + * the Id is the current time in seconds. + */ +export function downloadFile(data: BlobPart, name?: string, format?: FileFormat) { + const blob = new Blob([data], { type: `application/${format ?? 'json'}` }); + const url = URL.createObjectURL(blob); + const date = new Date().toISOString().slice(0, 10); + const id = new Date().getTime(); + + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `${date}-${id}${name ? '-' + name : ''}.${format ?? 'json'}`; + anchor.click(); +} \ No newline at end of file diff --git a/src/app/settings/index.component.ts b/src/app/settings/index.component.ts index 1b5ef2222..db0e33709 100644 --- a/src/app/settings/index.component.ts +++ b/src/app/settings/index.component.ts @@ -15,6 +15,7 @@ import { Sidebar, SidebarItem } from '@app/types/navigation'; import { PageHeaderComponent } from '@components/page-header.component'; import { PageSectionHeaderComponent } from '@components/page-section-header.component'; import { PageSectionComponent } from '@components/page-section.component'; +import { downloadFile } from '@services/download-file.service'; import { IconsService } from '@services/icons.service'; import { NavigationService } from '@services/navigation.service'; import { NotificationService } from '@services/notification.service'; @@ -271,15 +272,7 @@ export class IndexComponent implements OnInit { exportData(): void { const data = JSON.stringify(this.storageService.exportData()); - const blob = new Blob([data], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const date = new Date().toISOString().slice(0, 10); - const id = new Date().getTime(); - - const anchor = document.createElement('a'); - anchor.href = url; - anchor.download = `${date}-${id}-settings.json`; - anchor.click(); + downloadFile(data, 'settings', 'json'); this.notificationService.success('Settings exported'); } diff --git a/src/app/types/file.type.ts b/src/app/types/file.type.ts new file mode 100644 index 000000000..3d0544c3f --- /dev/null +++ b/src/app/types/file.type.ts @@ -0,0 +1 @@ +export type FileFormat = 'json' | 'csv'; \ No newline at end of file From 1b189855c202e54bb3fa6b084c442497b54e34b4 Mon Sep 17 00:00:00 2001 From: Faustin Date: Wed, 16 Oct 2024 11:31:02 +0200 Subject: [PATCH 2/3] test: remove useless tests for settings --- src/app/settings/index.component.spec.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/app/settings/index.component.spec.ts b/src/app/settings/index.component.spec.ts index bcb4cb020..9058154b1 100644 --- a/src/app/settings/index.component.spec.ts +++ b/src/app/settings/index.component.spec.ts @@ -321,14 +321,6 @@ describe('IndexComponent', () => { component.exportData(); }); - it('should download the settings', () => { - expect(anchor.download).toContain('settings.json'); - }); - - it('should click the anchor', () => { - expect(anchor.click).toHaveBeenCalled(); - }); - it('should notify on success', () => { expect(mockNotificationService.success).toHaveBeenCalled(); }); From d992b0878eb5f846a8b6b5b8f98a49ec9334900d Mon Sep 17 00:00:00 2001 From: Faustin Date: Wed, 16 Oct 2024 17:10:23 +0200 Subject: [PATCH 3/3] chore: update the function into a service --- .../services/download-file.service.spec.ts | 12 +++---- src/app/services/download-file.service.ts | 32 +++++++++++-------- src/app/settings/index.component.spec.ts | 22 ++++++------- src/app/settings/index.component.ts | 6 ++-- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/app/services/download-file.service.spec.ts b/src/app/services/download-file.service.spec.ts index b9bb9b883..c96cd9926 100644 --- a/src/app/services/download-file.service.spec.ts +++ b/src/app/services/download-file.service.spec.ts @@ -1,7 +1,8 @@ import { FileFormat } from '@app/types/file.type'; -import { downloadFile } from './download-file.service'; +import DownloadService from './download-file.service'; describe('download-file function', () => { + const service = new DownloadService(); const anchor = { href: '', download: '', @@ -21,22 +22,19 @@ describe('download-file function', () => { const format: FileFormat = 'csv'; const data = JSON.stringify([1, 2, 3]); - - beforeEach(() => { - }); it('should download the settings', () => { - downloadFile(data, name, format); + service.downloadFile(data, name, format); expect(anchor.download).toEqual(`${mockDate}-${mockId}-${name}.${format}`); }); it('should download the settings with default configuration', () => { - downloadFile(data); + service.downloadFile(data); expect(anchor.download).toContain(`${mockDate}-${mockId}.json`); }); it('should click the anchor', () => { - downloadFile(data); + service.downloadFile(data); expect(anchor.click).toHaveBeenCalled(); }); }); \ No newline at end of file diff --git a/src/app/services/download-file.service.ts b/src/app/services/download-file.service.ts index 418db7750..2efbe84ab 100644 --- a/src/app/services/download-file.service.ts +++ b/src/app/services/download-file.service.ts @@ -1,17 +1,21 @@ +import { Injectable } from '@angular/core'; import { FileFormat } from '@app/types/file.type'; -/** - * Download data into a json or csv file. The file name will be as following: "date-id-name.format". - * the Id is the current time in seconds. - */ -export function downloadFile(data: BlobPart, name?: string, format?: FileFormat) { - const blob = new Blob([data], { type: `application/${format ?? 'json'}` }); - const url = URL.createObjectURL(blob); - const date = new Date().toISOString().slice(0, 10); - const id = new Date().getTime(); - - const anchor = document.createElement('a'); - anchor.href = url; - anchor.download = `${date}-${id}${name ? '-' + name : ''}.${format ?? 'json'}`; - anchor.click(); +@Injectable() +export default class DownloadService { + /** + * Download data into a json or csv file. The file name will be as following: "date-id-name.format". + * the Id is the current time in seconds. + */ + downloadFile(data: BlobPart, name?: string, format?: FileFormat) { + const blob = new Blob([data], { type: `application/${format ?? 'json'}` }); + const url = URL.createObjectURL(blob); + const date = new Date().toISOString().slice(0, 10); + const id = new Date().getTime(); + + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `${date}-${id}${name ? '-' + name : ''}.${format ?? 'json'}`; + anchor.click(); + } } \ No newline at end of file diff --git a/src/app/settings/index.component.spec.ts b/src/app/settings/index.component.spec.ts index 9058154b1..d6af6b5a9 100644 --- a/src/app/settings/index.component.spec.ts +++ b/src/app/settings/index.component.spec.ts @@ -5,6 +5,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatDialog } from '@angular/material/dialog'; import { Key } from '@app/types/config'; import { Sidebar, SidebarItem } from '@app/types/navigation'; +import DownloadService from '@services/download-file.service'; import { IconsService } from '@services/icons.service'; import { NavigationService } from '@services/navigation.service'; import { NotificationService } from '@services/notification.service'; @@ -127,6 +128,10 @@ describe('IndexComponent', () => { get: jest.fn() }; + const mockDownloadService = { + downloadFile: jest.fn() + }; + beforeEach(() => { component = TestBed.configureTestingModule({ providers: [ @@ -137,6 +142,7 @@ describe('IndexComponent', () => { { provide: StorageService, useValue: mockStorageService }, { provide: MatDialog, useValue: mockDialog }, { provide: HttpClient, useValue: mockHttpClient }, + { provide: DownloadService, useValue: mockDownloadService }, ] }).inject(IndexComponent); component.ngOnInit(); @@ -306,21 +312,15 @@ describe('IndexComponent', () => { }); }); - describe('exportData', () => { - const anchor = { - href: '', - download: '', - click: jest.fn() - }; - - global.document.createElement = jest.fn().mockReturnValue(anchor); - - global.URL.createObjectURL = jest.fn(); - + describe('exportData', () => { beforeEach(() => { component.exportData(); }); + it('should call downloadFile', () => { + expect(mockDownloadService.downloadFile).toHaveBeenCalled(); + }); + it('should notify on success', () => { expect(mockNotificationService.success).toHaveBeenCalled(); }); diff --git a/src/app/settings/index.component.ts b/src/app/settings/index.component.ts index db0e33709..c87d44541 100644 --- a/src/app/settings/index.component.ts +++ b/src/app/settings/index.component.ts @@ -15,7 +15,7 @@ import { Sidebar, SidebarItem } from '@app/types/navigation'; import { PageHeaderComponent } from '@components/page-header.component'; import { PageSectionHeaderComponent } from '@components/page-section-header.component'; import { PageSectionComponent } from '@components/page-section.component'; -import { downloadFile } from '@services/download-file.service'; +import DownloadService from '@services/download-file.service'; import { IconsService } from '@services/icons.service'; import { NavigationService } from '@services/navigation.service'; import { NotificationService } from '@services/notification.service'; @@ -130,6 +130,7 @@ main { providers: [ QueryParamsService, NotificationService, + DownloadService, ], imports: [ PageHeaderComponent, @@ -160,6 +161,7 @@ export class IndexComponent implements OnInit { private readonly navigationService = inject(NavigationService); private readonly storageService = inject(StorageService); private readonly httpClient = inject(HttpClient); + private readonly downloadService = inject(DownloadService); ngOnInit(): void { this.keys = this.sortKeys(this.storageService.restoreKeys()); @@ -272,7 +274,7 @@ export class IndexComponent implements OnInit { exportData(): void { const data = JSON.stringify(this.storageService.exportData()); - downloadFile(data, 'settings', 'json'); + this.downloadService.downloadFile(data, 'settings', 'json'); this.notificationService.success('Settings exported'); }