refactor(runtime): gate settings backup file save by capability

This commit is contained in:
4gray committed 2026-05-22 13:41:24 +03:00
1 parent c9a0d5210e
commit 582422c512
4 files changed
+81 -2

No files matched your search

@@ -911,6 +911,65 @@ describe('SettingsComponent', () => {
expect(component.isExportingData()).toBe(false);
});
it('falls back to browser backup download when desktop file-save preload is incomplete', async () => {
fixture.destroy();
const saveFileDialog = jest.fn().mockResolvedValue('/tmp/backup.json');
window.electron = {
platform: 'linux',
saveFileDialog,
} as unknown as typeof window.electron;
const createObjectURL = jest.fn().mockReturnValue('blob:backup');
const revokeObjectURL = jest.fn();
const originalCreateObjectURL = window.URL.createObjectURL;
const originalRevokeObjectURL = window.URL.revokeObjectURL;
Object.defineProperty(window.URL, 'createObjectURL', {
configurable: true,
value: createObjectURL,
});
Object.defineProperty(window.URL, 'revokeObjectURL', {
configurable: true,
value: revokeObjectURL,
});
const clickSpy = jest
.spyOn(HTMLAnchorElement.prototype, 'click')
.mockImplementation();
try {
const partialFileSaveFixture =
TestBed.createComponent(SettingsComponent);
const partialFileSaveComponent =
partialFileSaveFixture.componentInstance;
partialFileSaveComponent.checkAppVersion = jest.fn();
partialFileSaveComponent.fetchLocalIpAddresses = jest
.fn()
.mockResolvedValue(undefined);
partialFileSaveFixture.detectChanges();
expect(partialFileSaveComponent.isDesktop).toBe(true);
expect(partialFileSaveComponent.supportsDesktopFileSave).toBe(
false
);
await partialFileSaveComponent.exportData();
expect(saveFileDialog).not.toHaveBeenCalled();
expect(createObjectURL).toHaveBeenCalledWith(expect.any(Blob));
expect(clickSpy).toHaveBeenCalled();
expect(revokeObjectURL).toHaveBeenCalledWith('blob:backup');
} finally {
clickSpy.mockRestore();
Object.defineProperty(window.URL, 'createObjectURL', {
configurable: true,
value: originalCreateObjectURL,
});
Object.defineProperty(window.URL, 'revokeObjectURL', {
configurable: true,
value: originalRevokeObjectURL,
});
}
});
it('shows a failure snackbar and skips refresh when clearing EPG data rejects', async () => {
(window.electron.clearEpgData as jest.Mock).mockRejectedValueOnce(
new Error('boom')
@@ -137,6 +137,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
/** Flag that indicates whether the app runs in electron environment */
readonly isDesktop = this.runtime.isElectron;
readonly supportsDesktopFileSave = this.runtime.supportsDesktopFileSave;
readonly supportsEpg = this.runtime.supportsEpg;
readonly supportsExternalPlayerPathSettings =
this.runtime.supportsExternalPlayerPathSettings;
@@ -699,7 +700,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
try {
const backup = await this.playlistBackupService.exportBackup();
if (this.isDesktop && window.electron?.saveFileDialog) {
if (this.supportsDesktopFileSave && window.electron) {
const savePath = await window.electron.saveFileDialog(
backup.defaultFileName,
[