feat: add Jest configuration and tests for playlist shared utility

This commit is contained in:
4gray committed 2026-04-04 20:41:07 +02:00
1 parent c935c8616d
commit 02c773170e
6 files changed
+245 -10

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
export default {
displayName: 'playlist-shared-util',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/playlist/shared/util',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
+8
View File
@@ -6,6 +6,14 @@
"projectType": "library",
"tags": ["scope:playlist", "domain:m3u", "type:util"],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/playlist/shared/util/jest.config.ts",
"tsConfig": "libs/playlist/shared/util/tsconfig.spec.json"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
@@ -0,0 +1,182 @@
import { TestBed } from '@angular/core/testing';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { DialogService } from 'components';
import { DatabaseService, PlaylistRefreshService } from 'services';
import { PlaylistActions } from 'm3u-state';
import { PlaylistMeta } from 'shared-interfaces';
import { PlaylistRefreshActionService } from './playlist-refresh-action.service';
function createPlaylistMeta(
overrides: Partial<PlaylistMeta> = {}
): PlaylistMeta {
return {
_id: 'playlist-1',
title: 'Xtream Playlist',
serverUrl: 'http://localhost:8080',
username: 'demo',
password: 'secret',
...overrides,
} as PlaylistMeta;
}
describe('PlaylistRefreshActionService', () => {
let service: PlaylistRefreshActionService;
let databaseService: {
createOperationId: jest.Mock;
deleteXtreamPlaylistContent: jest.Mock;
updateXtreamPlaylistDetails: jest.Mock;
};
let dialogService: {
openConfirmDialog: jest.Mock;
};
let router: {
navigate: jest.Mock;
};
let snackBar: {
open: jest.Mock;
};
let store: {
dispatch: jest.Mock;
};
beforeEach(() => {
localStorage.clear();
databaseService = {
createOperationId: jest.fn((prefix: string) => `${prefix}-op`),
deleteXtreamPlaylistContent: jest.fn().mockResolvedValue({
success: true,
favoritedXtreamIds: [101, 202],
recentlyViewedXtreamIds: [
{
xtreamId: 303,
viewedAt: '2026-04-04T08:00:00.000Z',
},
],
hiddenCategories: [{ xtreamId: 404, type: 'live' }],
}),
updateXtreamPlaylistDetails: jest.fn().mockResolvedValue(true),
};
dialogService = {
openConfirmDialog: jest.fn(),
};
router = {
navigate: jest.fn().mockResolvedValue(true),
};
snackBar = {
open: jest.fn(),
};
store = {
dispatch: jest.fn(),
};
TestBed.configureTestingModule({
providers: [
PlaylistRefreshActionService,
{
provide: Router,
useValue: router,
},
{
provide: Store,
useValue: store,
},
{
provide: TranslateService,
useValue: {
instant: jest.fn((key: string) => key),
},
},
{
provide: MatSnackBar,
useValue: snackBar,
},
{
provide: DialogService,
useValue: dialogService,
},
{
provide: DatabaseService,
useValue: databaseService,
},
{
provide: PlaylistRefreshService,
useValue: {
refreshPlaylist: jest.fn(),
},
},
],
});
service = TestBed.inject(PlaylistRefreshActionService);
});
afterEach(() => {
jest.restoreAllMocks();
localStorage.clear();
});
it('stores Xtream restore data before updating playlist meta and navigating', async () => {
const item = createPlaylistMeta();
const executionOrder: string[] = [];
let confirmPromise: Promise<void> | undefined;
const dateNowSpy = jest
.spyOn(Date, 'now')
.mockReturnValue(1712217600000);
const originalSetItem = Storage.prototype.setItem;
const setItemSpy = jest
.spyOn(Storage.prototype, 'setItem')
.mockImplementation(function (key: string, value: string) {
executionOrder.push('setItem');
return originalSetItem.call(this, key, value);
});
store.dispatch.mockImplementation((action: unknown) => {
executionOrder.push('dispatch');
return action;
});
router.navigate.mockImplementation((commands: unknown[]) => {
executionOrder.push('navigate');
return Promise.resolve(Boolean(commands));
});
dialogService.openConfirmDialog.mockImplementation(
({ onConfirm }: { onConfirm?: () => Promise<void> }) => {
confirmPromise = onConfirm?.();
}
);
service.refresh(item);
await confirmPromise;
expect(setItemSpy).toHaveBeenCalledWith(
`xtream-restore-${item._id}`,
JSON.stringify({
favoritedXtreamIds: [101, 202],
recentlyViewedXtreamIds: [
{
xtreamId: 303,
viewedAt: '2026-04-04T08:00:00.000Z',
},
],
hiddenCategories: [{ xtreamId: 404, type: 'live' }],
})
);
expect(store.dispatch).toHaveBeenCalledWith(
PlaylistActions.updatePlaylistMeta({
playlist: { ...item, updateDate: 1712217600000 },
})
);
expect(router.navigate).toHaveBeenCalledWith([
'/workspace',
'xtreams',
item._id,
]);
expect(executionOrder).toEqual(['setItem', 'dispatch', 'navigate']);
setItemSpy.mockRestore();
dateNowSpy.mockRestore();
});
});
@@ -90,22 +90,23 @@ export class PlaylistRefreshActionService {
}),
]);
const restoreKey = `xtream-restore-${item._id}`;
const restorePayload = {
favoritedXtreamIds,
recentlyViewedXtreamIds,
hiddenCategories,
};
localStorage.setItem(
restoreKey,
JSON.stringify(restorePayload)
);
this.store.dispatch(
PlaylistActions.updatePlaylistMeta({
playlist: { ...item, updateDate },
})
);
const restoreKey = `xtream-restore-${item._id}`;
localStorage.setItem(
restoreKey,
JSON.stringify({
favoritedXtreamIds,
recentlyViewedXtreamIds,
hiddenCategories,
})
);
await this.router.navigate([
'/workspace',
'xtreams',
@@ -0,0 +1,6 @@
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv({
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
});
@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"],
"moduleResolution": "node10"
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}