diff --git a/lib/core/feature-flags/src/lib/components/feature-flags-wrapper.spec.ts b/lib/core/feature-flags/src/lib/components/feature-flags-wrapper.spec.ts new file mode 100644 index 0000000000..229f740a75 --- /dev/null +++ b/lib/core/feature-flags/src/lib/components/feature-flags-wrapper.spec.ts @@ -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; + + 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(); + }); +}); diff --git a/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.spec.ts b/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.spec.ts new file mode 100644 index 0000000000..f645298725 --- /dev/null +++ b/lib/core/feature-flags/src/lib/components/feature-override-indicator.component.spec.ts @@ -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; + let isEnabledSubject: BehaviorSubject; + + beforeEach(() => { + isEnabledSubject = new BehaviorSubject(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('🔴'); + }); +}); diff --git a/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.spec.ts b/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.spec.ts new file mode 100644 index 0000000000..3085e796c1 --- /dev/null +++ b/lib/core/feature-flags/src/lib/guards/is-feature-off.guard.spec.ts @@ -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); + }); +}); diff --git a/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.spec.ts b/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.spec.ts new file mode 100644 index 0000000000..bbefd00927 --- /dev/null +++ b/lib/core/feature-flags/src/lib/guards/is-feature-on.guard.spec.ts @@ -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); + }); +}); diff --git a/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.spec.ts b/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.spec.ts new file mode 100644 index 0000000000..3d5a62de04 --- /dev/null +++ b/lib/core/feature-flags/src/lib/guards/is-flags-override-on.guard.spec.ts @@ -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); + }); +}); diff --git a/lib/core/feature-flags/src/lib/services/debug-features.service.spec.ts b/lib/core/feature-flags/src/lib/services/debug-features.service.spec.ts index f6b0f9dc09..539c1047b1 100644 --- a/lib/core/feature-flags/src/lib/services/debug-features.service.spec.ts +++ b/lib/core/feature-flags/src/lib/services/debug-features.service.spec.ts @@ -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({}); + }); }); diff --git a/lib/core/feature-flags/src/lib/services/dummy-features.service.spec.ts b/lib/core/feature-flags/src/lib/services/dummy-features.service.spec.ts index f2a779f168..a38264bdd8 100644 --- a/lib/core/feature-flags/src/lib/services/dummy-features.service.spec.ts +++ b/lib/core/feature-flags/src/lib/services/dummy-features.service.spec.ts @@ -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({}); + }); }); diff --git a/lib/core/feature-flags/src/lib/services/qa-features.helper.spec.ts b/lib/core/feature-flags/src/lib/services/qa-features.helper.spec.ts new file mode 100644 index 0000000000..59564abb75 --- /dev/null +++ b/lib/core/feature-flags/src/lib/services/qa-features.helper.spec.ts @@ -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; + + 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); + }); +}); diff --git a/lib/core/feature-flags/src/lib/services/storage-features.service.spec.ts b/lib/core/feature-flags/src/lib/services/storage-features.service.spec.ts index 054f49edf8..b7d91a9e08 100644 --- a/lib/core/feature-flags/src/lib/services/storage-features.service.spec.ts +++ b/lib/core/feature-flags/src/lib/services/storage-features.service.spec.ts @@ -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); + }); }); });