Files
iptvnator/apps/web/src/app/settings/settings-server-section.component.spec.ts
T
4grayandClaude Opus 5.5 676e3adcd4 fix(server-storage): keep Xtream data, ordered sync, sign-in and stale-cache fixes
- Moving browser playlists matches them to the server's by id or source and
  carries the legacy Xtream maps under the server id; it no longer goes
  through the backup format, whose PWA export has no Xtream user data. A
  partial move stays on offer.
- User-data batches go out in order, and a re-read keeps entries edited
  while it was in flight.
- A keepPayload write keeps its cached copy only when the server replaced
  the version its payload came from (new X-Replaced-ETag header).
- 502/503/504 from the proxy count as offline.
- The start-up screen lets the sign-in page through while a token is
  required.
- Settings → Server offers a reload after a move.
- The probe detects Electron through RuntimeCapabilitiesService.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:16:56 +02:00

129 lines
4.5 KiB
TypeScript

import { signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { ServerStorageStatusService } from '@iptvnator/services';
import {
SERVER_STORAGE_CONFIG,
ServerMigrationService,
ServerStorageSession,
} from '@iptvnator/services/server-storage';
import { SettingsServerSectionComponent } from './settings-server-section.component';
describe('SettingsServerSectionComponent', () => {
let fixture: ComponentFixture<SettingsServerSectionComponent>;
const session = { fetchStatus: jest.fn(), signOut: jest.fn() };
const migration = {
browserPlaylists: jest.fn(),
browserDataMoved: jest.fn(),
moveBrowserData: jest.fn(),
};
async function render(
auth: 'none' | 'token',
browserPlaylists = [{ id: 'a', title: 'A', kind: 'm3u' }]
) {
session.fetchStatus.mockResolvedValue({
playlists: 4,
databaseBytes: 38 * 1024 * 1024,
databasePath: '/data/databases/iptvnator.db',
startedAt: '2026-10-11T08:00:00.000Z',
});
migration.browserDataMoved.mockReturnValue(false);
migration.browserPlaylists.mockResolvedValue(browserPlaylists);
await TestBed.configureTestingModule({
imports: [
SettingsServerSectionComponent,
TranslateModule.forRoot(),
],
providers: [
{ provide: ServerStorageSession, useValue: session },
{ provide: ServerMigrationService, useValue: migration },
{
provide: SERVER_STORAGE_CONFIG,
useValue: { baseUrl: 'http://nas.local:4333/api' },
},
{
provide: ServerStorageStatusService,
useValue: {
state: signal('online'),
instance: signal({ auth, instanceId: 'i-1' }),
},
},
],
}).compileComponents();
fixture = TestBed.createComponent(SettingsServerSectionComponent);
await fixture.componentInstance.load();
fixture.detectChanges();
}
const byTestId = (id: string): HTMLElement | null =>
fixture.nativeElement.querySelector(`[data-test-id="${id}"]`);
afterEach(() => {
TestBed.resetTestingModule();
jest.resetAllMocks();
});
it('shows the storage figures and the database path', async () => {
await render('none');
const text = fixture.nativeElement.textContent;
expect(text).toContain('/data/databases/iptvnator.db');
expect(byTestId('server-address')?.textContent?.trim()).toBe(
'http://nas.local:4333'
);
expect(byTestId('server-state')?.dataset['state']).toBe('online');
expect(fixture.componentInstance.sizeLabel()).toBe('38.0 MB');
expect(byTestId('server-sign-out')).toBeNull();
});
it('moves browser playlists and reports the result', async () => {
await render('none');
migration.moveBrowserData.mockResolvedValue({
imported: 1,
merged: 0,
failed: 0,
});
migration.browserDataMoved.mockReturnValue(true);
byTestId('server-import-browser')?.click();
await fixture.whenStable();
fixture.detectChanges();
expect(migration.moveBrowserData).toHaveBeenCalled();
expect(byTestId('server-import-browser')).toBeNull();
expect(byTestId('server-import-reload')).not.toBeNull();
expect(fixture.nativeElement.textContent).toContain(
'SERVER_STORAGE.MIGRATION_DONE'
);
});
it('hides the browser import when there is nothing to move', async () => {
await render('none', []);
expect(
fixture.nativeElement.querySelector(
'[data-setting-id="server-import-browser"]'
)
).toBeNull();
});
it('offers sign out when a token is in use', async () => {
await render('token');
byTestId('server-sign-out')?.click();
expect(session.signOut).toHaveBeenCalled();
});
it('asks the page to export a backup', async () => {
await render('none');
const exported = jest.fn();
fixture.componentInstance.exportData.subscribe(exported);
byTestId('server-export-backup')?.click();
expect(exported).toHaveBeenCalled();
});
});