Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(components/forms): add provideSkyFileAttachmentTesting to aid in file attachment unit tests (internal) #2941

Merged
merged 8 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/components/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export {
SkyDynamicComponentService,
} from './lib/modules/dynamic-component/dynamic-component.service';

export { SkyFileReaderService } from './lib/modules/file-reader/file-reader.service';

export { SkyAppFormat } from './lib/modules/format/app-format';

export { SkyHelpGlobalOptions } from './lib/modules/help/help-global-options';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { TestBed } from '@angular/core/testing';

import { SkyFileReaderService } from './file-reader.service';

describe('file-reader.service', () => {
function setupTest(options?: {
readFileWithStatus: 'abort' | 'error' | 'load';
}): { fileReaderSvc: SkyFileReaderService } {
const fileReaderSvc = TestBed.inject(SkyFileReaderService);

const { readFileWithStatus } = options || { readFileWithStatus: 'load' };

spyOn(window, 'FileReader').and.returnValue({
addEventListener: (eventName: string, cb: (data?: unknown) => void) => {
if (eventName === 'load' && readFileWithStatus === 'load') {
cb({ target: { result: 'data:MOCK' } });
} else {
if (eventName === readFileWithStatus) {
cb();
}
}
},
readAsDataURL: () => {
/* */
},
} as unknown as FileReader);

return { fileReaderSvc };
}

it('should read a file as data URL', async () => {
const { fileReaderSvc } = setupTest();

const file = new File(['foo'], 'foo.txt', { type: 'text/plain' });

const result = await fileReaderSvc.readAsDataURL(file);

expect(result).toEqual('data:MOCK');
});

it('should reject on error', async () => {
const { fileReaderSvc } = setupTest({ readFileWithStatus: 'error' });

const file = new File(['foo'], 'foo.txt', { type: 'text/plain' });

await expectAsync(fileReaderSvc.readAsDataURL(file)).toBeRejectedWith(file);
});

it('should reject on abort', async () => {
const { fileReaderSvc } = setupTest({ readFileWithStatus: 'abort' });

const file = new File(['foo'], 'foo.txt', { type: 'text/plain' });

await expectAsync(fileReaderSvc.readAsDataURL(file)).toBeRejectedWith(file);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';

/**
* Wraps the FileReader API so it can be mocked in tests.
* @internal
*/
@Injectable({
providedIn: 'root',
})
export class SkyFileReaderService {
public async readAsDataURL(file: File): Promise<string> {
return await new Promise((resolve, reject) => {
const reader = new FileReader();

reader.addEventListener('load', (event) => {
resolve(event.target?.result as string);
});

reader.addEventListener('error', () => {
reject(file);
});

reader.addEventListener('abort', () => {
reject(file);
});

reader.readAsDataURL(file);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { SkyFileReaderService } from '@skyux/core';

/**
* @internal
*/
@Injectable()
export class SkyFileReaderTestingService extends SkyFileReaderService {
public override async readAsDataURL(file: File): Promise<string> {
return await new Promise((resolve) => {
resolve(`data:${file.type};base64,MOCK_DATA`);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { TestBed } from '@angular/core/testing';
import { SkyFileReaderService } from '@skyux/core';

import { SkyFileReaderTestingService } from './file-reader-testing.service';
import { provideSkyFileReaderTesting } from './provide-file-reader-testing';

describe('provideSkyFileReaderTesting', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideSkyFileReaderTesting()],
});
});

it('should mock the service', () => {
const actualSvc = TestBed.inject(SkyFileReaderService);

expect(actualSvc instanceof SkyFileReaderTestingService).toBe(true);
});

it('should return file url', async () => {
const file = new File([''], 'filename', { type: 'text/plain' });

await expectAsync(
TestBed.inject(SkyFileReaderService).readAsDataURL(file),
).toBeResolvedTo('data:text/plain;base64,MOCK_DATA');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Provider } from '@angular/core';
import { SkyFileReaderService } from '@skyux/core';

import { SkyFileReaderTestingService } from './file-reader-testing.service';

/**
* Provides mocks for file reader testing.
* @internal
* @example
* ```typescript
* TestBed.configureTestingModule({
* providers: [provideSkyFileReaderTesting()]
* });
* ```
*/
export function provideSkyFileReaderTesting(): Provider[] {
return [
SkyFileReaderTestingService,
{
provide: SkyFileReaderService,
useClass: SkyFileReaderTestingService,
},
];
}
1 change: 1 addition & 0 deletions libs/components/core/testing/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
mockResizeObserverEntry,
mockResizeObserverHandle,
} from './legacy/resize-observer-mock';
export { provideSkyFileReaderTesting } from './modules/file-reader/provide-file-reader-testing';
export { SkyHelpTestingController } from './modules/help/help-testing-controller';
export { SkyHelpTestingModule } from './modules/help/help-testing.module';
export { SkyMediaQueryTestingController } from './modules/media-query/media-query-testing-controller';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ describe('File attachment', () => {
});

// Maybe some other tests here about dragging
it('should load and emit file on file change event', () => {
it('should load and emit file on file change event', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -576,6 +576,7 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();
Blackbaud-SandhyaRajasabeson marked this conversation as resolved.
Show resolved Hide resolved

expect(fileChangeActual?.file).toBeTruthy();
expect(fileChangeActual?.file?.url).toBe('$/url');
Expand All @@ -585,7 +586,7 @@ describe('File attachment', () => {
expect(liveAnnouncerSpy.calls.count()).toBe(1);
});

it('should load and emit files on file change event when file reader has an error and aborts', () => {
it('should load and emit files on file change event when file reader has an error and aborts', async () => {
let filesChangedActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -606,6 +607,7 @@ describe('File attachment', () => {

fileReaderSpy.abortCallbacks[0]();
fixture.detectChanges();
await fixture.whenStable();

expect(filesChangedActual?.file?.url).toBeFalsy();
expect(filesChangedActual?.file?.file.name).toBe('woo.txt');
Expand All @@ -621,13 +623,14 @@ describe('File attachment', () => {

fileReaderSpy.errorCallbacks[1]();
fixture.detectChanges();
await fixture.whenStable();

expect(filesChangedActual?.file?.url).toBeFalsy();
expect(filesChangedActual?.file?.file.name).toBe('foo.txt');
expect(filesChangedActual?.file?.file.size).toBe(2000);
});

it('should clear file on remove press', () => {
it('should clear file on remove press', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -645,6 +648,9 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();
fixture.detectChanges();

liveAnnouncerSpy.calls.reset();

const deleteEl = getDeleteEl();
Expand Down Expand Up @@ -810,6 +816,7 @@ describe('File attachment', () => {
});

fixture.detectChanges();
await fixture.whenStable();

expect(fileChangeActual?.file).toBeTruthy();
expect(fileChangeActual?.file?.errorType).toBeFalsy();
Expand Down Expand Up @@ -873,6 +880,7 @@ describe('File attachment', () => {
];

const fileReaderSpy = setupStandardFileChangeEvent(initialFile);
await fixture.whenStable();

expect(fileChangeActual?.file).toBeTruthy();
expect(fileChangeActual?.file?.url).toBe('$/url');
Expand Down Expand Up @@ -910,6 +918,7 @@ describe('File attachment', () => {
});

fixture.detectChanges();
await fixture.whenStable();

expect(fileChangeActual?.file).toBeTruthy();
expect(fileChangeActual?.file?.errorType).toBeFalsy();
Expand Down Expand Up @@ -1020,14 +1029,15 @@ describe('File attachment', () => {
expect(fileAttachmentInstance.value).toBeFalsy();
});

it('should respect a default min file size of 0', () => {
it('should respect a default min file size of 0', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
(fileChange: SkyFileAttachmentChange) => (fileChangeActual = fileChange),
);

const spy = setupStandardFileChangeEvent();
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand All @@ -1044,6 +1054,7 @@ describe('File attachment', () => {
fixture.detectChanges();

setupStandardFileChangeEvent(undefined, spy);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand All @@ -1058,6 +1069,7 @@ describe('File attachment', () => {
fixture.detectChanges();

setupStandardFileChangeEvent(undefined, spy);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand Down Expand Up @@ -1171,7 +1183,7 @@ describe('File attachment', () => {
expect(fileAttachmentInstance.value).toBeFalsy();
});

it('should accept if file passes user provided validation function', () => {
it('should accept if file passes user provided validation function', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand Down Expand Up @@ -1201,6 +1213,7 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand All @@ -1209,7 +1222,7 @@ describe('File attachment', () => {
expect(fileAttachmentInstance.value).toBeTruthy();
});

it('should accept a file when type is accepted', () => {
it('should accept a file when type is accepted', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -1229,6 +1242,7 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand Down Expand Up @@ -1288,7 +1302,7 @@ describe('File attachment', () => {
expect(fileChangeActual?.file?.errorParam).toBe('PNG, TIFF');
});

it('should allow the user to specify accepted type with wildcards', () => {
it('should allow the user to specify accepted type with wildcards', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -1308,13 +1322,14 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('woo.txt');
expect(fileChangeActual?.file?.file.size).toBe(2000);
expect(fileChangeActual?.file?.url).toBe('$/url');
});

it('should accept multiple types using a wildcard', () => {
it('should accept multiple types using a wildcard', async () => {
let fileChangeActual: SkyFileAttachmentChange | undefined;

fileAttachmentInstance.fileChange.subscribe(
Expand All @@ -1334,6 +1349,7 @@ describe('File attachment', () => {
];

setupStandardFileChangeEvent(file);
await fixture.whenStable();

expect(fileChangeActual?.file?.file.name).toBe('foo.txt');
expect(fileChangeActual?.file?.file.size).toBe(1000);
Expand Down
Loading
Loading