AAE-36660 Test coverage

This commit is contained in:
Wojciech Duda
2025-10-31 16:39:32 +01:00
parent 66b4983a75
commit b31d851e0f
9 changed files with 680 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FeatureFlagsWrapperComponent } from './feature-flags-wrapper';
import { provideMockFeatureFlags } from '../mocks/features-service-mock.factory';
import { WritableFeaturesServiceToken } from '../interfaces/features.interface';
import { StorageFeaturesService } from '../services/storage-features.service';
import { TranslateModule } from '@ngx-translate/core';
describe('FeatureFlagsWrapperComponent', () => {
let fixture: ComponentFixture<FeatureFlagsWrapperComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FeatureFlagsWrapperComponent, TranslateModule.forRoot()],
providers: [{ provide: WritableFeaturesServiceToken, useClass: StorageFeaturesService }, provideMockFeatureFlags({})]
});
const storageFeaturesService = TestBed.inject(WritableFeaturesServiceToken);
storageFeaturesService.init();
fixture = TestBed.createComponent(FeatureFlagsWrapperComponent);
fixture.detectChanges();
});
it('should render the wrapper div', () => {
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.adf-feature-flags-wrapper')).toBeTruthy();
});
it('should render the feature flags overrides component', () => {
const compiled = fixture.nativeElement;
expect(compiled.querySelector('adf-feature-flags-overrides')).toBeTruthy();
});
});

View File

@@ -0,0 +1,118 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FlagsOverrideComponent } from './feature-override-indicator.component';
import { FeaturesServiceToken } from '../interfaces/features.interface';
import { BehaviorSubject } from 'rxjs';
describe('FlagsOverrideComponent', () => {
let component: FlagsOverrideComponent;
let fixture: ComponentFixture<FlagsOverrideComponent>;
let isEnabledSubject: BehaviorSubject<boolean>;
beforeEach(() => {
isEnabledSubject = new BehaviorSubject<boolean>(false);
TestBed.configureTestingModule({
imports: [FlagsOverrideComponent],
providers: [
{
provide: FeaturesServiceToken,
useValue: {
isEnabled$: () => isEnabledSubject.asObservable()
}
}
]
});
fixture = TestBed.createComponent(FlagsOverrideComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should show red indicator when disabled', () => {
const compiled = fixture.nativeElement;
expect(component.isEnabled).toBe(false);
expect(compiled.textContent).toContain('🔴');
});
it('should show green indicator when enabled', () => {
isEnabledSubject.next(true);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(component.isEnabled).toBe(true);
expect(compiled.textContent).toContain('🟢');
});
it('should update when isEnabled$ emits new value', () => {
expect(component.isEnabled).toBe(false);
isEnabledSubject.next(true);
fixture.detectChanges();
expect(component.isEnabled).toBe(true);
isEnabledSubject.next(false);
fixture.detectChanges();
expect(component.isEnabled).toBe(false);
});
it('should support small size', () => {
component.size = 'small';
expect(component.size).toBe('small');
});
it('should have medium size by default', () => {
expect(component.size).toBe('medium');
});
it('should support large size', () => {
component.size = 'large';
expect(component.size).toBe('large');
});
it('should trigger change detection when isEnabled changes', () => {
isEnabledSubject.next(true);
fixture.detectChanges();
expect(component.isEnabled).toBe(true);
});
it('should create component when isEnabled$ is not available', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [FlagsOverrideComponent],
providers: [
{
provide: FeaturesServiceToken,
useValue: {}
}
]
});
const testFixture = TestBed.createComponent(FlagsOverrideComponent);
testFixture.detectChanges();
expect(testFixture.componentInstance).toBeTruthy();
expect(testFixture.componentInstance.isEnabled).toBe(false);
expect(testFixture.nativeElement.textContent).toContain('🔴');
});
});

View File

@@ -0,0 +1,75 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { IsFeatureOff, isFeatureOff } from './is-feature-off.guard';
import { Route } from '@angular/router';
import { provideMockFeatureFlags } from '../mocks/features-service-mock.factory';
import { firstValueFrom } from 'rxjs';
describe('IsFeatureOff', () => {
let guard: IsFeatureOff;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
IsFeatureOff,
provideMockFeatureFlags({
enabledFeature: true,
disabledFeature: false
})
]
});
guard = TestBed.inject(IsFeatureOff);
});
it('should return true when feature is off using canMatch', async () => {
const route: Route = {
path: 'test',
data: { feature: 'disabledFeature' }
};
const result = await firstValueFrom(guard.canMatch(route));
expect(result).toBe(true);
});
it('should return false when feature is on using canMatch', async () => {
const route: Route = {
path: 'test',
data: { feature: 'enabledFeature' }
};
const result = await firstValueFrom(guard.canMatch(route));
expect(result).toBe(false);
});
it('should return true when feature is off using functional guard', async () => {
const result = await TestBed.runInInjectionContext(() => {
const guardFn = isFeatureOff('disabledFeature');
return firstValueFrom(guardFn());
});
expect(result).toBe(true);
});
it('should return false when feature is on using functional guard', async () => {
const result = await TestBed.runInInjectionContext(() => {
const guardFn = isFeatureOff('enabledFeature');
return firstValueFrom(guardFn());
});
expect(result).toBe(false);
});
});

View File

@@ -0,0 +1,75 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { IsFeatureOn, isFeatureOn } from './is-feature-on.guard';
import { Route } from '@angular/router';
import { provideMockFeatureFlags } from '../mocks/features-service-mock.factory';
import { firstValueFrom } from 'rxjs';
describe('IsFeatureOn', () => {
let guard: IsFeatureOn;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
IsFeatureOn,
provideMockFeatureFlags({
enabledFeature: true,
disabledFeature: false
})
]
});
guard = TestBed.inject(IsFeatureOn);
});
it('should return true when feature is on using canMatch', async () => {
const route: Route = {
path: 'test',
data: { feature: 'enabledFeature' }
};
const result = await firstValueFrom(guard.canMatch(route));
expect(result).toBe(true);
});
it('should return false when feature is off using canMatch', async () => {
const route: Route = {
path: 'test',
data: { feature: 'disabledFeature' }
};
const result = await firstValueFrom(guard.canMatch(route));
expect(result).toBe(false);
});
it('should return true when feature is on using functional guard', async () => {
const result = await TestBed.runInInjectionContext(() => {
const guardFn = isFeatureOn('enabledFeature');
return firstValueFrom(guardFn());
});
expect(result).toBe(true);
});
it('should return false when feature is off using functional guard', async () => {
const result = await TestBed.runInInjectionContext(() => {
const guardFn = isFeatureOn('disabledFeature');
return firstValueFrom(guardFn());
});
expect(result).toBe(false);
});
});

View File

@@ -0,0 +1,94 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { IsFlagsOverrideOn, isFlagsOverrideOn } from './is-flags-override-on.guard';
import { FlagsOverrideToken } from '../interfaces/features.interface';
describe('IsFlagsOverrideOn', () => {
it('should return true when flags override is enabled', () => {
TestBed.configureTestingModule({
providers: [IsFlagsOverrideOn, { provide: FlagsOverrideToken, useValue: true }]
});
const guard = TestBed.inject(IsFlagsOverrideOn);
const result = guard.canMatch();
expect(result).toBe(true);
});
it('should return false when flags override is disabled', () => {
TestBed.configureTestingModule({
providers: [IsFlagsOverrideOn, { provide: FlagsOverrideToken, useValue: false }]
});
const guard = TestBed.inject(IsFlagsOverrideOn);
const result = guard.canMatch();
expect(result).toBe(false);
});
it('should return false when token is not provided', () => {
TestBed.configureTestingModule({
providers: [IsFlagsOverrideOn]
});
const guard = TestBed.inject(IsFlagsOverrideOn);
const result = guard.canMatch();
expect(result).toBe(false);
});
it('should return true using functional guard when override is enabled', () => {
TestBed.configureTestingModule({
providers: [{ provide: FlagsOverrideToken, useValue: true }]
});
const result = TestBed.runInInjectionContext(() => {
const guardFn = isFlagsOverrideOn();
return guardFn();
});
expect(result).toBe(true);
});
it('should return false using functional guard when override is disabled', () => {
TestBed.configureTestingModule({
providers: [{ provide: FlagsOverrideToken, useValue: false }]
});
const result = TestBed.runInInjectionContext(() => {
const guardFn = isFlagsOverrideOn();
return guardFn();
});
expect(result).toBe(false);
});
it('should return false using functional guard when token is not provided', () => {
TestBed.configureTestingModule({
providers: [{ provide: FlagsOverrideToken, useValue: null }]
});
const result = TestBed.runInInjectionContext(() => {
const guardFn = isFlagsOverrideOn();
return guardFn();
});
expect(result).toBe(false);
});
});

View File

@@ -116,4 +116,29 @@ describe('DebugFeaturesService', () => {
done();
});
});
it('should return signal for isOn', () => {
const flagKey = 'feature1';
const signal = TestBed.runInInjectionContext(() => service.isOn(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(false);
});
it('should return signal for isOff', () => {
const flagKey = 'feature1';
const signal = TestBed.runInInjectionContext(() => service.isOff(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(true);
});
it('should return signal for getFlags', () => {
const signal = TestBed.runInInjectionContext(() => service.getFlags());
expect(signal).toBeDefined();
expect(signal()).toEqual({});
});
});

View File

@@ -57,4 +57,25 @@ describe('DummyFeaturesService', () => {
expect(flags).toEqual({});
});
});
it('should return false signal when isOn is called', () => {
const signal = TestBed.runInInjectionContext(() => service.isOn('anyKey'));
expect(signal).toBeDefined();
expect(signal()).toBe(false);
});
it('should return true signal when isOff is called', () => {
const signal = TestBed.runInInjectionContext(() => service.isOff('anyKey'));
expect(signal).toBeDefined();
expect(signal()).toBe(true);
});
it('should return empty object signal when getFlags is called', () => {
const signal = TestBed.runInInjectionContext(() => service.getFlags());
expect(signal).toBeDefined();
expect(signal()).toEqual({});
});
});

View File

@@ -0,0 +1,148 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { QaFeaturesHelper } from './qa-features.helper';
import { FeaturesServiceToken } from '../interfaces/features.interface';
import { DebugFeaturesService } from './debug-features.service';
import { of } from 'rxjs';
describe('QaFeaturesHelper', () => {
let qaHelper: QaFeaturesHelper;
let mockDebugFeaturesService: jasmine.SpyObj<DebugFeaturesService>;
beforeEach(() => {
mockDebugFeaturesService = jasmine.createSpyObj('DebugFeaturesService', ['isOn$', 'resetFlags', 'enable', 'isEnabled$']);
TestBed.configureTestingModule({
providers: [QaFeaturesHelper, { provide: FeaturesServiceToken, useValue: mockDebugFeaturesService }]
});
qaHelper = TestBed.inject(QaFeaturesHelper);
});
it('should return true when feature is on', () => {
mockDebugFeaturesService.isOn$.and.returnValue(of(true));
const result = qaHelper.isOn('testFeature');
expect(result).toBe(true);
expect(mockDebugFeaturesService.isOn$).toHaveBeenCalledWith('testFeature');
});
it('should return false when feature is off', () => {
mockDebugFeaturesService.isOn$.and.returnValue(of(false));
const result = qaHelper.isOn('testFeature');
expect(result).toBe(false);
expect(mockDebugFeaturesService.isOn$).toHaveBeenCalledWith('testFeature');
});
it('should handle different feature keys', () => {
mockDebugFeaturesService.isOn$.and.returnValue(of(true));
qaHelper.isOn('feature1');
qaHelper.isOn('feature2');
expect(mockDebugFeaturesService.isOn$).toHaveBeenCalledWith('feature1');
expect(mockDebugFeaturesService.isOn$).toHaveBeenCalledWith('feature2');
});
it('should reset flags and trigger application tick', () => {
const flags = { feature1: true, feature2: false };
qaHelper.resetFlags(flags);
expect(mockDebugFeaturesService.resetFlags).toHaveBeenCalledWith(flags);
});
it('should reset flags with empty flags object', () => {
const flags = {};
qaHelper.resetFlags(flags);
expect(mockDebugFeaturesService.resetFlags).toHaveBeenCalledWith(flags);
});
it('should reset flags with multiple flags', () => {
const flags = {
feature1: true,
feature2: false,
feature3: true,
feature4: false
};
qaHelper.resetFlags(flags);
expect(mockDebugFeaturesService.resetFlags).toHaveBeenCalledWith(flags);
});
it('should enable debug mode and trigger application tick', () => {
qaHelper.enable();
expect(mockDebugFeaturesService.enable).toHaveBeenCalledWith(true);
});
it('should disable debug mode and trigger application tick', () => {
qaHelper.disable();
expect(mockDebugFeaturesService.enable).toHaveBeenCalledWith(false);
});
it('should return true when debug mode is enabled', () => {
mockDebugFeaturesService.isEnabled$.and.returnValue(of(true));
const result = qaHelper.isEnabled();
expect(result).toBe(true);
expect(mockDebugFeaturesService.isEnabled$).toHaveBeenCalled();
});
it('should return false when debug mode is disabled', () => {
mockDebugFeaturesService.isEnabled$.and.returnValue(of(false));
const result = qaHelper.isEnabled();
expect(result).toBe(false);
expect(mockDebugFeaturesService.isEnabled$).toHaveBeenCalled();
});
it('should allow toggling debug mode and checking status', () => {
mockDebugFeaturesService.isEnabled$.and.returnValue(of(false));
expect(qaHelper.isEnabled()).toBe(false);
qaHelper.enable();
expect(mockDebugFeaturesService.enable).toHaveBeenCalledWith(true);
mockDebugFeaturesService.isEnabled$.and.returnValue(of(true));
expect(qaHelper.isEnabled()).toBe(true);
qaHelper.disable();
expect(mockDebugFeaturesService.enable).toHaveBeenCalledWith(false);
});
it('should allow checking and resetting flags', () => {
mockDebugFeaturesService.isOn$.and.returnValue(of(true));
expect(qaHelper.isOn('feature1')).toBe(true);
const newFlags = { feature1: false, feature2: true };
qaHelper.resetFlags(newFlags);
expect(mockDebugFeaturesService.resetFlags).toHaveBeenCalledWith(newFlags);
});
});

View File

@@ -167,6 +167,41 @@ describe('StorageFeaturesService', () => {
it('should return custom storageFeaturesService key', () => {
expect(storageFeaturesService.storageKey).toEqual('storage-key-test');
});
it('should return signal for isOn', () => {
const flagKey = 'feature1';
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.isOn(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(true);
});
it('should return signal for isOff', () => {
const flagKey = 'feature2';
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.isOff(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(true);
});
it('should return signal for getFlags', () => {
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.getFlags());
expect(signal).toBeDefined();
expect(signal()).toEqual({
feature1: {
current: true,
previous: null
},
feature2: {
current: false,
fictive: true,
previous: null
}
});
});
});
describe('if flags are not present in LocalStorage and no configuration is provided', () => {
@@ -184,5 +219,44 @@ describe('StorageFeaturesService', () => {
it('should return default storageFeaturesService key', () => {
expect(storageFeaturesService.storageKey).toEqual('feature-flags');
});
it('should return signal for isOn with non-existent flag', () => {
const flagKey = 'nonExistentFlag';
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.isOn(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(false);
});
it('should return signal for isOff with non-existent flag', () => {
const flagKey = 'nonExistentFlag';
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.isOff(flagKey));
expect(signal).toBeDefined();
expect(signal()).toBe(true);
});
it('should return signal for getFlags with empty flags', () => {
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.getFlags());
expect(signal).toBeDefined();
expect(signal()).toEqual({});
});
it('should update signal when flag is changed', (done) => {
const flagKey = 'testFlag';
const signal = TestBed.runInInjectionContext(() => storageFeaturesService.isOn(flagKey));
expect(signal()).toBe(false);
storageFeaturesService.setFlag(flagKey, true);
setTimeout(() => {
expect(signal()).toBe(true);
done();
}, 10);
});
});
});