Simplify testing configuration implementation

This commit is contained in:
Yaroslav Admin 2024-05-07 19:47:59 +02:00
parent 14c4812d8e
commit 31ba9799c0
7 changed files with 81 additions and 133 deletions

View File

@ -26,10 +26,7 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"include": [
"src/**/*.spec.ts",
"testing/**/*.spec.ts"
],
"include": ["src/**/*.spec.ts", "testing/src/**/*.spec.ts"],
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js"
@ -38,7 +35,7 @@
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html", "testing/src/**/*.ts", "testing/src/**/*.html"]
}
}
}

View File

@ -1,44 +0,0 @@
import { InjectionToken } from "@angular/core"
/**
* The configuration options that can be passed to the FontAwesomeTestingModule.
*/
// This interface is user-facing and will be converted to the
// _FontAwesomeTestingModuleInternalConfig for internal use by the module.
export interface FontAwesomeTestingModuleConfig extends Partial<_FontAwesomeTestingModuleInternalConfig> { }
/**
* The internal configuration for the FontAwesomeTestingModule.
* This interface is private. Conforming objects should be constructed by the
* _getFontAwesomeTestingModuleInternalConfig() function.
*/
export interface _FontAwesomeTestingModuleInternalConfig {
/**
* What to do when `addIcons()` is invoked on an IconLibrary provided by the FontAwesomeTestingModule.
*
* Possible values are:
* - `'logWarning'`: Writes a warning to the console.
* - `'throwError'`: Throws an error
* - `'noop'`: Does nothing
*
* Note that in any case the icon will not be added to the library.
*/
whenAddingIcons: 'logWarning' | 'throwError' | 'noop'
}
export const FontAwesomeTestingModuleConfigInjectionToken = new InjectionToken<_FontAwesomeTestingModuleInternalConfig>('FontAwesomeTestingModuleInternalConfig')
/**
* The default values used for configuration if the user passes no configuration,
* or an incomplete one.
*/
const DEFAULT_CONFIG = Object.freeze({
// The default value maintains compatibility with versions <= 0.14.1
whenAddingIcons: 'throwError'
})
export function _getFontAwesomeTestingModuleInternalConfig(publicConfig: FontAwesomeTestingModuleConfig = {}): _FontAwesomeTestingModuleInternalConfig {
return {
whenAddingIcons: publicConfig.whenAddingIcons ?? DEFAULT_CONFIG.whenAddingIcons
}
}

21
testing/src/config.ts Normal file
View File

@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class FaTestingConfig {
/**
* What to do when `addIcons()` or `addIconPacks()` is invoked on
* the FaIconLibrary provided by the FontAwesomeTestingModule.
*
* Possible values are:
* - `'throwError'` - Throw an error.
* - `'logWarning'` - Write a warning to the console.
* - `'noop'` - Do nothing.
*
* Note that in any case the icon will not be added to the library.
*
* @default 'throwError'
*/
whenAddingIcons: 'throwError' | 'logWarning' | 'noop' = 'throwError';
}

View File

@ -1,10 +1,6 @@
import { Inject, Injectable, Optional } from '@angular/core';
import { Injectable } from '@angular/core';
import { FaIconLibraryInterface, IconDefinition, IconName, IconPrefix } from '@fortawesome/angular-fontawesome';
import {
FontAwesomeTestingModuleConfigInjectionToken,
_FontAwesomeTestingModuleInternalConfig,
_getFontAwesomeTestingModuleInternalConfig
} from '../TestingModuleConfig';
import { FaTestingConfig } from '../config';
export const dummyIcon: IconDefinition = {
prefix: 'fad',
@ -18,27 +14,14 @@ export const ADD_ICON_MESSAGE = 'Attempt to add an icon to the MockFaIconLibrary
providedIn: 'root',
})
export class MockFaIconLibrary implements FaIconLibraryInterface {
constructor(private config: FaTestingConfig) {}
private config: _FontAwesomeTestingModuleInternalConfig
// The configuration object is optional in order to maintain backwards compatibility with versions <= 0.14.1.
// If the module is unconfigured (that is, FontAwesomeTestingModule.forRoot() has never been called),
// then the dependency injection will provide `null`.
//
// We could alternatively provide a default configuration in the `providers` array of the `NgModule` decorator,
// but that would break the use case of injecting a service directly without providing a configuration,
// as is done in testing/src/icon/mock-icon-library.service.spec.ts
constructor(
@Inject(FontAwesomeTestingModuleConfigInjectionToken) @Optional() config: _FontAwesomeTestingModuleInternalConfig
) {
this.config = config ?? _getFontAwesomeTestingModuleInternalConfig()
}
addIcons() {
if (this.config.whenAddingIcons === 'throwError') {
throw new Error(ADD_ICON_MESSAGE);
}
if (this.config.whenAddingIcons === 'logWarning') {
console.warn(ADD_ICON_MESSAGE)
console.warn(ADD_ICON_MESSAGE);
}
}
@ -47,7 +30,7 @@ export class MockFaIconLibrary implements FaIconLibraryInterface {
throw new Error(ADD_ICON_MESSAGE);
}
if (this.config.whenAddingIcons === 'logWarning') {
console.warn(ADD_ICON_MESSAGE)
console.warn(ADD_ICON_MESSAGE);
}
}

View File

@ -1,3 +1,3 @@
export { FontAwesomeTestingModule } from './testing.module';
export { MockFaIconLibrary } from './icon/mock-icon-library.service'
export { FontAwesomeTestingModuleConfig } from './TestingModuleConfig'
export { MockFaIconLibrary } from './icon/mock-icon-library.service';
export { FaTestingConfig } from './config';

View File

@ -2,8 +2,8 @@ import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faUser } from '@fortawesome/free-solid-svg-icons';
import { ADD_ICON_MESSAGE } from 'testing/src/icon/mock-icon-library.service';
import { FontAwesomeTestingModule } from 'testing/src/public_api';
import { ADD_ICON_MESSAGE } from './icon/mock-icon-library.service';
import { FontAwesomeTestingModule } from './testing.module';
@Component({
selector: 'fa-host',
@ -12,75 +12,73 @@ import { FontAwesomeTestingModule } from 'testing/src/public_api';
class HostComponent {}
describe('Using the `FontAwesomeTestingModule', () => {
describe('Providing no configuration', () => {
// This describe block asserts that the behaviour of versions <= 0.14.1 is maintained
let component: HostComponent;
let fixture: ComponentFixture<HostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FontAwesomeTestingModule],
declarations: [HostComponent],
});
fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should allow you to import the module without errors', () => {
expect(component).toBeTruthy();
});
it('should throw on attempt to add an icon to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error(ADD_ICON_MESSAGE));
});
it('should throw on attempt to add an icon pack to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error(ADD_ICON_MESSAGE));
});
})
});
describe('Providing an empty configuration object', () => {
// This describe block asserts that a partial configuration object
// is correctly filled up to the full internal object.
// The used configuration should mimick the default values for no configuration.
// The used configuration should mimic the default values for no configuration.
let component: HostComponent;
let fixture: ComponentFixture<HostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FontAwesomeTestingModule.forRoot({})],
declarations: [HostComponent],
});
});
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should allow you to import the module without errors', () => {
expect(component).toBeTruthy();
});
it('should throw on attempt to add an icon to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error(ADD_ICON_MESSAGE));
});
it('should throw on attempt to add an icon pack to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error(ADD_ICON_MESSAGE));
});
})
});
describe('Providing {addIcons: "throwError"}', () => {
// This describe block asserts that feature request
@ -89,35 +87,34 @@ describe('Using the `FontAwesomeTestingModule', () => {
let component: HostComponent;
let fixture: ComponentFixture<HostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FontAwesomeTestingModule.forRoot({whenAddingIcons: 'throwError'})],
imports: [FontAwesomeTestingModule.forRoot({ whenAddingIcons: 'throwError' })],
declarations: [HostComponent],
});
});
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should allow you to import the module without errors', () => {
expect(component).toBeTruthy();
});
it('should throw on attempt to add an icon to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error('Attempt to add an icon to the MockFaIconLibrary.'));
});
it('should throw on attempt to add an icon pack to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
expect(() => service.addIcons(faUser)).toThrow(new Error('Attempt to add an icon to the MockFaIconLibrary.'));
});
})
});
describe('Providing {addIcons: "logWarning"}', () => {
// This describe block asserts that feature request
@ -126,40 +123,38 @@ describe('Using the `FontAwesomeTestingModule', () => {
let component: HostComponent;
let fixture: ComponentFixture<HostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FontAwesomeTestingModule.forRoot({whenAddingIcons: 'logWarning'})],
imports: [FontAwesomeTestingModule.forRoot({ whenAddingIcons: 'logWarning' })],
declarations: [HostComponent],
});
});
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should allow you to import the module without errors', () => {
expect(component).toBeTruthy();
});
it('should call console.warn on attempt to add an icon to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
spyOn(console, 'warn')
spyOn(console, 'warn');
expect(() => service.addIcons(faUser)).not.toThrow();
expect(console.warn).toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE)
expect(console.warn).toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE);
});
it('should call console.warn on attempt to add an icon pack to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
spyOn(console, 'warn')
spyOn(console, 'warn');
expect(() => service.addIcons(faUser)).not.toThrow();
expect(console.warn).toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE)
expect(console.warn).toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE);
});
})
});
describe('Providing {addIcons: "noop"}', () => {
// This describe block asserts that feature request
@ -168,36 +163,36 @@ describe('Using the `FontAwesomeTestingModule', () => {
let component: HostComponent;
let fixture: ComponentFixture<HostComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FontAwesomeTestingModule.forRoot({whenAddingIcons: 'noop'})],
imports: [FontAwesomeTestingModule.forRoot({ whenAddingIcons: 'noop' })],
declarations: [HostComponent],
});
});
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should allow you to import the module without errors', () => {
expect(component).toBeTruthy();
});
it('should ignore attempts to add an icon to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
spyOn(console, 'warn')
spyOn(console, 'warn');
expect(() => service.addIcons(faUser)).not.toThrow();
expect(console.warn).not.toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE)
expect(console.warn).not.toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE);
});
it('should ignore attempts to add an icon pack to the mocked icon library', () => {
const service = TestBed.inject(FaIconLibrary);
spyOn(console, 'warn')
spyOn(console, 'warn');
expect(() => service.addIcons(faUser)).not.toThrow();
expect(console.warn).not.toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE)
expect(console.warn).not.toHaveBeenCalledOnceWith(ADD_ICON_MESSAGE);
});
})
});
});

View File

@ -1,11 +1,7 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { FaTestingConfig } from './config';
import { MockFaIconLibrary } from './icon/mock-icon-library.service';
import {
FontAwesomeTestingModuleConfig,
FontAwesomeTestingModuleConfigInjectionToken,
_getFontAwesomeTestingModuleInternalConfig,
} from './TestingModuleConfig';
@NgModule({
exports: [FontAwesomeModule],
@ -16,7 +12,7 @@ export class FontAwesomeTestingModule {
* Use this method to configure the modules behaviour when trying to add icons
* and icon packs to the mock icon library.
*/
public static forRoot(config: FontAwesomeTestingModuleConfig = {}): ModuleWithProviders<FontAwesomeTestingModule> {
static forRoot(config: Partial<FaTestingConfig> = {}): ModuleWithProviders<FontAwesomeTestingModule> {
return {
ngModule: FontAwesomeTestingModule,
providers: [
@ -25,10 +21,10 @@ export class FontAwesomeTestingModule {
useExisting: MockFaIconLibrary,
},
{
provide: FontAwesomeTestingModuleConfigInjectionToken,
useValue: _getFontAwesomeTestingModuleInternalConfig(config),
}
]
}
provide: FaTestingConfig,
useFactory: () => Object.assign(new FaTestingConfig(), config),
},
],
};
}
}