add unit tests
This commit is contained in:
@@ -1,23 +1,45 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Storage} from '@angular/fire/storage';
|
||||
|
||||
import {FileService} from './file.service';
|
||||
import {FileDataService} from './file-data.service';
|
||||
import {FileService} from './file.service';
|
||||
|
||||
describe('FileService', () => {
|
||||
let service: FileService;
|
||||
let fileDataServiceSpy: jasmine.SpyObj<FileDataService>;
|
||||
|
||||
beforeEach(() => {
|
||||
fileDataServiceSpy = jasmine.createSpyObj<FileDataService>('FileDataService', ['delete']);
|
||||
fileDataServiceSpy.delete.and.resolveTo();
|
||||
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: Storage, useValue: {}},
|
||||
{provide: FileDataService, useValue: {delete: () => Promise.resolve()}},
|
||||
{provide: Storage, useValue: {app: 'test-storage'}},
|
||||
{provide: FileDataService, useValue: fileDataServiceSpy},
|
||||
],
|
||||
});
|
||||
|
||||
service = TestBed.inject(FileService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
void expect(service).toBeTruthy();
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should resolve download urls via AngularFire storage helpers', async () => {
|
||||
const resolveSpy = spyOn<any>(service, 'resolveDownloadUrl').and.resolveTo('https://cdn.example/file.pdf');
|
||||
|
||||
await expectAsync(service.getDownloadUrl('songs/song-1/file.pdf').toPromise()).toBeResolvedTo('https://cdn.example/file.pdf');
|
||||
|
||||
expect(resolveSpy).toHaveBeenCalledWith('songs/song-1/file.pdf');
|
||||
});
|
||||
|
||||
it('should delete the file from storage and metadata from firestore', async () => {
|
||||
const deleteFromStorageSpy = spyOn<any>(service, 'deleteFromStorage').and.resolveTo();
|
||||
|
||||
service.delete('songs/song-1/file.pdf', 'song-1', 'file-1');
|
||||
await Promise.resolve();
|
||||
|
||||
expect(deleteFromStorageSpy).toHaveBeenCalledWith('songs/song-1/file.pdf');
|
||||
expect(fileDataServiceSpy.delete).toHaveBeenCalledWith('song-1', 'file-1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {EnvironmentInjector, Injectable, inject, runInInjectionContext} from '@angular/core';
|
||||
import {EnvironmentInjector, inject, Injectable, runInInjectionContext} from '@angular/core';
|
||||
import {deleteObject, getDownloadURL, ref, Storage} from '@angular/fire/storage';
|
||||
import {from, Observable} from 'rxjs';
|
||||
import {FileDataService} from './file-data.service';
|
||||
@@ -12,11 +12,19 @@ export class FileService {
|
||||
private environmentInjector = inject(EnvironmentInjector);
|
||||
|
||||
public getDownloadUrl(path: string): Observable<string> {
|
||||
return from(runInInjectionContext(this.environmentInjector, () => getDownloadURL(ref(this.storage, path))));
|
||||
return from(runInInjectionContext(this.environmentInjector, () => this.resolveDownloadUrl(path)));
|
||||
}
|
||||
|
||||
public delete(path: string, songId: string, fileId: string): void {
|
||||
void runInInjectionContext(this.environmentInjector, () => deleteObject(ref(this.storage, path)));
|
||||
void runInInjectionContext(this.environmentInjector, () => this.deleteFromStorage(path));
|
||||
void this.fileDataService.delete(songId, fileId);
|
||||
}
|
||||
|
||||
private resolveDownloadUrl(path: string): Promise<string> {
|
||||
return getDownloadURL(ref(this.storage, path));
|
||||
}
|
||||
|
||||
private deleteFromStorage(path: string): Promise<void> {
|
||||
return deleteObject(ref(this.storage, path));
|
||||
}
|
||||
}
|
||||
|
||||
32
src/app/modules/songs/services/song-list.resolver.spec.ts
Normal file
32
src/app/modules/songs/services/song-list.resolver.spec.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {of} from 'rxjs';
|
||||
import {SongService} from './song.service';
|
||||
import {SongListResolver} from './song-list.resolver';
|
||||
|
||||
describe('SongListResolver', () => {
|
||||
let resolver: SongListResolver;
|
||||
let songServiceSpy: jasmine.SpyObj<SongService>;
|
||||
|
||||
beforeEach(() => {
|
||||
songServiceSpy = jasmine.createSpyObj<SongService>('SongService', ['list$']);
|
||||
songServiceSpy.list$.and.returnValue(of([{id: 'song-1', title: 'Amazing Grace'}] as never));
|
||||
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [{provide: SongService, useValue: songServiceSpy}],
|
||||
});
|
||||
|
||||
resolver = TestBed.inject(SongListResolver);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(resolver).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should resolve the first emitted song list from the service', done => {
|
||||
resolver.resolve().subscribe(songs => {
|
||||
expect(songServiceSpy.list$).toHaveBeenCalled();
|
||||
expect(songs).toEqual([{id: 'song-1', title: 'Amazing Grace'}] as never);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,54 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Storage} from '@angular/fire/storage';
|
||||
|
||||
import {UploadService} from './upload.service';
|
||||
import {FileDataService} from './file-data.service';
|
||||
import {Upload} from './upload';
|
||||
import {UploadService} from './upload.service';
|
||||
|
||||
describe('UploadServiceService', () => {
|
||||
beforeEach(
|
||||
() =>
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: Storage, useValue: {}},
|
||||
{provide: FileDataService, useValue: {set: () => Promise.resolve('')}},
|
||||
],
|
||||
})
|
||||
);
|
||||
describe('UploadService', () => {
|
||||
let service: UploadService;
|
||||
let fileDataServiceSpy: jasmine.SpyObj<FileDataService>;
|
||||
|
||||
beforeEach(() => {
|
||||
fileDataServiceSpy = jasmine.createSpyObj<FileDataService>('FileDataService', ['set']);
|
||||
fileDataServiceSpy.set.and.resolveTo('file-1');
|
||||
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: Storage, useValue: {app: 'test-storage'}},
|
||||
{provide: FileDataService, useValue: fileDataServiceSpy},
|
||||
],
|
||||
});
|
||||
|
||||
service = TestBed.inject(UploadService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
const service: UploadService = TestBed.inject(UploadService);
|
||||
void expect(service).toBeTruthy();
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should upload the file, update progress and persist file metadata on success', async () => {
|
||||
const task = {
|
||||
on: (event: string, progress: (snapshot: {bytesTransferred: number; totalBytes: number}) => void, error: () => void, success: () => void) => {
|
||||
progress({bytesTransferred: 50, totalBytes: 100});
|
||||
success();
|
||||
},
|
||||
};
|
||||
const uploadSpy = spyOn<any>(service, 'startUpload').and.returnValue(task as never);
|
||||
const upload = new Upload(new File(['content'], 'test.pdf', {type: 'application/pdf'}));
|
||||
|
||||
service.pushUpload('song-1', upload);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(uploadSpy).toHaveBeenCalledWith('/attachments/song-1/test.pdf', upload.file);
|
||||
expect(upload.progress).toBe(50);
|
||||
expect(upload.path).toBe('/attachments/song-1');
|
||||
expect(fileDataServiceSpy.set).toHaveBeenCalledWith(
|
||||
'song-1',
|
||||
jasmine.objectContaining({
|
||||
name: 'test.pdf',
|
||||
path: '/attachments/song-1',
|
||||
createdAt: jasmine.any(Date),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,12 +18,7 @@ export class UploadService extends FileBase {
|
||||
const filePath = `${directory}/${upload.file.name}`;
|
||||
upload.path = directory;
|
||||
|
||||
const {task} = runInInjectionContext(this.environmentInjector, () => {
|
||||
const storageRef = ref(this.storage, filePath);
|
||||
return {
|
||||
task: uploadBytesResumable(storageRef, upload.file),
|
||||
};
|
||||
});
|
||||
const task = runInInjectionContext(this.environmentInjector, () => this.startUpload(filePath, upload.file));
|
||||
|
||||
task.on(
|
||||
'state_changed',
|
||||
@@ -45,4 +40,9 @@ export class UploadService extends FileBase {
|
||||
};
|
||||
await this.fileDataService.set(songId, file);
|
||||
}
|
||||
|
||||
private startUpload(filePath: string, file: File) {
|
||||
const storageRef = ref(this.storage, filePath);
|
||||
return uploadBytesResumable(storageRef, file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditSongGuard} from './edit-song.guard';
|
||||
|
||||
describe('EditSongGuard', () => {
|
||||
@@ -11,6 +10,22 @@ describe('EditSongGuard', () => {
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
void expect(guard).toBeTruthy();
|
||||
expect(guard).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should allow navigation when there is no nested editSongComponent', () => {
|
||||
const result = guard.canDeactivate({editSongComponent: null} as never, {} as never, {} as never, {} as never);
|
||||
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
it('should delegate to askForSave on the nested editSongComponent', async () => {
|
||||
const nextState = {url: '/songs'} as never;
|
||||
const askForSave = jasmine.createSpy('askForSave').and.resolveTo(true);
|
||||
|
||||
const result = await guard.canDeactivate({editSongComponent: {askForSave}} as never, {} as never, {} as never, nextState);
|
||||
|
||||
expect(askForSave).toHaveBeenCalledWith(nextState);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,74 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditService} from './edit.service';
|
||||
|
||||
describe('EditService', () => {
|
||||
beforeEach(() => void TestBed.configureTestingModule({}));
|
||||
let service: EditService;
|
||||
|
||||
beforeEach(() => {
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(EditService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
const service: EditService = TestBed.inject(EditService);
|
||||
void expect(service).toBeTruthy();
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create a form with all editable song fields populated', () => {
|
||||
const form = service.createSongForm({
|
||||
text: 'Line 1',
|
||||
title: 'Amazing Grace',
|
||||
comment: 'Comment',
|
||||
flags: 'fast',
|
||||
key: 'G',
|
||||
tempo: 90,
|
||||
type: 'Praise',
|
||||
status: 'final',
|
||||
legalType: 'allowed',
|
||||
legalOwner: 'CCLI',
|
||||
legalOwnerId: '123',
|
||||
artist: 'Artist',
|
||||
label: 'Label',
|
||||
termsOfUse: 'Use it',
|
||||
origin: 'Origin',
|
||||
} as never);
|
||||
|
||||
expect(form.getRawValue()).toEqual({
|
||||
text: 'Line 1',
|
||||
title: 'Amazing Grace',
|
||||
comment: 'Comment',
|
||||
flags: 'fast',
|
||||
key: 'G',
|
||||
tempo: 90,
|
||||
type: 'Praise',
|
||||
status: 'final',
|
||||
legalType: 'allowed',
|
||||
legalOwner: 'CCLI',
|
||||
legalOwnerId: '123',
|
||||
artist: 'Artist',
|
||||
label: 'Label',
|
||||
termsOfUse: 'Use it',
|
||||
origin: 'Origin',
|
||||
});
|
||||
});
|
||||
|
||||
it('should default the status control to draft when the song has no status', () => {
|
||||
const form = service.createSongForm({
|
||||
text: '',
|
||||
title: 'Untitled',
|
||||
comment: '',
|
||||
flags: '',
|
||||
key: 'C',
|
||||
tempo: 80,
|
||||
type: 'Misc',
|
||||
legalType: 'open',
|
||||
legalOwner: 'other',
|
||||
legalOwnerId: '',
|
||||
artist: '',
|
||||
label: '',
|
||||
termsOfUse: '',
|
||||
origin: '',
|
||||
} as never);
|
||||
|
||||
expect(form.get('status')?.value).toBe('draft');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user