mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
settings.component.spec.ts was 1516 lines and the last settings file in the max-lines baseline. The behaviour that moved into facades now has its own specs, driven directly instead of through the rendered page. - settings-app-update.facade.spec.ts: status polling/retry, bridge actions, release notes dialog, version messaging, dispose - settings-epg.facade.spec.ts: refresh, clear flow, post-save re-fetch - settings-playlist-reset.facade.spec.ts: summary, dialog, Electron progress, browser fallback, failure snackbar - settings-backup.facade.spec.ts: desktop export, browser download fallback - settings.component.spec.ts keeps the page shell, the facade lifecycle seam and runtime capabilities; settings.component.form.spec.ts takes hydration, section outputs, dashboard controls and submit - settings-section-scroll.directive.spec.ts gives the directive its first spec - shared TestBed fixtures live in settings/test-stubs/, kept out of both the app build (.stub.ts) and the coverage ratchet (test-stubs/) 105 settings tests, up from 94; every file is under the 400-line limit, so settings.component.spec.ts leaves the baseline.
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
import { By } from '@angular/platform-browser';
|
|
import { EpgRuntimeBridgeService } from '@iptvnator/epg/data-access';
|
|
import { SettingsContextService } from '@iptvnator/workspace/shell/util';
|
|
import { SettingsComponent } from './settings.component';
|
|
import { SettingsSectionScrollDirective } from './settings-section-scroll.directive';
|
|
import {
|
|
configureSettingsComponentTestBed,
|
|
createElectronStub,
|
|
createEpgBridgeStub,
|
|
stubSettingsSideEffects,
|
|
} from './test-stubs/settings-test-harness.stub';
|
|
|
|
interface SettingsSectionScrollDirectiveTestApi {
|
|
getScrollRoot(): HTMLElement | null;
|
|
}
|
|
|
|
/**
|
|
* The directive only makes sense against a real settings page, so this is an
|
|
* integration spec: it renders the component and drives the section
|
|
* navigation the workspace shell would trigger.
|
|
*/
|
|
describe('SettingsSectionScrollDirective', () => {
|
|
let fixture: ComponentFixture<SettingsComponent>;
|
|
let epgBridge: Partial<EpgRuntimeBridgeService>;
|
|
const originalElectron = window.electron;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
epgBridge = createEpgBridgeStub();
|
|
configureSettingsComponentTestBed(epgBridge);
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
window.electron = createElectronStub();
|
|
});
|
|
|
|
afterEach(() => {
|
|
window.electron = originalElectron;
|
|
});
|
|
|
|
it('should scroll the selected navigation target within the workspace viewport', async () => {
|
|
jest.useFakeTimers();
|
|
|
|
try {
|
|
fixture = TestBed.createComponent(SettingsComponent);
|
|
const component = fixture.componentInstance;
|
|
const settingsContext = TestBed.inject(SettingsContextService);
|
|
const originalGetElementById =
|
|
document.getElementById.bind(document);
|
|
const scrollTo = jest.fn();
|
|
const scrollRoot = {
|
|
scrollTop: 96,
|
|
clientHeight: 885,
|
|
scrollHeight: 2469,
|
|
getBoundingClientRect: () =>
|
|
({
|
|
top: 56,
|
|
}) as DOMRect,
|
|
scrollTo,
|
|
} as unknown as HTMLElement;
|
|
|
|
stubSettingsSideEffects(component);
|
|
fixture.detectChanges();
|
|
const scrollDirective = fixture.debugElement
|
|
.query(By.directive(SettingsSectionScrollDirective))
|
|
.injector.get(SettingsSectionScrollDirective);
|
|
jest.spyOn(
|
|
scrollDirective as unknown as SettingsSectionScrollDirectiveTestApi,
|
|
'getScrollRoot'
|
|
).mockReturnValue(scrollRoot);
|
|
|
|
const getElementByIdSpy = jest
|
|
.spyOn(document, 'getElementById')
|
|
.mockImplementation((id: string) => {
|
|
if (id === 'about') {
|
|
return {
|
|
getBoundingClientRect: () =>
|
|
({
|
|
top: 2050,
|
|
height: 159,
|
|
}) as DOMRect,
|
|
} as HTMLElement;
|
|
}
|
|
|
|
return originalGetElementById(id);
|
|
});
|
|
|
|
await fixture.whenStable();
|
|
fixture.detectChanges();
|
|
settingsContext.navigateToSection('about');
|
|
fixture.detectChanges();
|
|
|
|
expect(getElementByIdSpy).toHaveBeenCalledWith('about');
|
|
expect(scrollTo).toHaveBeenCalledWith({
|
|
behavior: 'smooth',
|
|
top: 1488,
|
|
});
|
|
expect(settingsContext.pendingScrollTarget()).toBe('about');
|
|
|
|
jest.advanceTimersByTime(600);
|
|
|
|
expect(settingsContext.pendingScrollTarget()).toBeNull();
|
|
} finally {
|
|
jest.useRealTimers();
|
|
}
|
|
});
|
|
});
|