diff --git a/lib/core/src/lib/common/services/storage.service.spec.ts b/lib/core/src/lib/common/services/storage.service.spec.ts index 667c7ecff5..1f57efa576 100644 --- a/lib/core/src/lib/common/services/storage.service.spec.ts +++ b/lib/core/src/lib/common/services/storage.service.spec.ts @@ -26,42 +26,57 @@ describe('StorageService', () => { const key = 'test_key'; const value = 'test_value'; - describe('with prefix', () => { - beforeEach(() => { + describe('with local storage and prefix', () => { + beforeEach(async () => { TestBed.configureTestingModule({ imports: [NoopTranslateModule, NoopAuthModule] }); appConfig = TestBed.inject(AppConfigService); storage = TestBed.inject(StorageService); + await appConfig.load(); + storage.clear(); }); - it('should get the prefix for the storage from app config', async () => { - await appConfig.load(); + it('should get the prefix for the storage from app config', () => { expect(storage.prefix).toBe('ADF_APP_'); }); - it('should set a property with the prefix in the local storage', async () => { - await appConfig.load(); - - storage.clear(); + it('should set a property with the prefix in the local storage', () => { storage.setItem(key, value); const storageKey = localStorage.key(0); expect(storageKey).toBe('ADF_APP_' + key); expect(localStorage.getItem(storageKey)).toBe(value); }); - it('should be able to get a property from the local storage', async () => { - storage.clear(); - - await appConfig.load(); + it('should be able to get a property from the local storage', () => { storage.setItem(key, value); expect(storage.getItem(key)).toBe(value); }); + + it('should retrieve all items with the prefix from local storage', () => { + localStorage.setItem('ADF_APP_key1', 'value1'); + localStorage.setItem('ADF_APP_key2', 'value2'); + + const items = storage.getItems(); + + expect(items).toEqual({ + key1: 'value1', + key2: 'value2' + }); + }); + + it('should return an empty object if no items match the prefix in local storage', () => { + localStorage.setItem('other_key', 'value'); + + const items = storage.getItems(); + + expect(items).toEqual({}); + }); }); - describe('without prefix', () => { - beforeEach(() => { + describe('without local storage and prefix', () => { + beforeEach(async () => { TestBed.configureTestingModule({ imports: [NoopAuthModule] }); @@ -73,18 +88,64 @@ describe('StorageService', () => { } }; storage = TestBed.inject(StorageService); + await appConfig.load(); + storage.clear(); }); - it('should set an empty prefix when the it is not defined in the app config', async () => { - await appConfig.load(); + it('should set an empty prefix when the it is not defined in the app config', () => { expect(storage.prefix).toBe(''); }); - it('should set a property without a prefix in the local storage', async () => { - await appConfig.load(); + it('should set a property without a prefix in the local storage', () => { storage.setItem(key, value); expect(localStorage.getItem(key)).toBe(value); }); + + it('should retrieve all items without a prefix from local storage', () => { + localStorage.setItem('key1', 'value1'); + localStorage.setItem('key2', 'value2'); + + const items = storage.getItems(); + expect(items).toEqual({ + key1: 'value1', + key2: 'value2' + }); + }); + }); + + describe('with memory storage', () => { + beforeEach(async () => { + Object.defineProperty(window, 'localStorage', { + value: undefined, + configurable: true + }); + + TestBed.configureTestingModule({ + imports: [NoopTranslateModule, NoopAuthModule] + }); + appConfig = TestBed.inject(AppConfigService); + storage = TestBed.inject(StorageService); + await appConfig.load(); + storage.clear(); + }); + + it('should be able to get a property from storage', () => { + storage.setItem(key, value); + + expect(storage.getItem(key)).toBe(value); + }); + + it('should retrieve all items from storage', () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + + const items = storage.getItems(); + + expect(items).toEqual({ + key1: 'value1', + key2: 'value2' + }); + }); }); }); diff --git a/lib/core/src/lib/common/services/storage.service.ts b/lib/core/src/lib/common/services/storage.service.ts index 60a0a39583..1f174004fc 100644 --- a/lib/core/src/lib/common/services/storage.service.ts +++ b/lib/core/src/lib/common/services/storage.service.ts @@ -51,6 +51,33 @@ export class StorageService { } } + /** + * Gets all items from the storage. + * + * @returns All items stored + */ + getItems(): { [key: string]: any } { + const items: { [key: string]: any } = {}; + if (this.useLocalStorage) { + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key?.startsWith(this.prefix)) { + const keyWithoutPrefix = key.slice(this.prefix.length); + items[keyWithoutPrefix] = localStorage.getItem(key); + } + } + } else { + Object.keys(this.memoryStore).forEach((key) => { + if (key.startsWith(this.prefix)) { + const unprefixedKey = key.slice(this.prefix.length); + items[unprefixedKey] = this.memoryStore[key]; + } + }); + } + + return items; + } + /** * Stores an item * diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts index 66f702779f..7dd5536743 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts @@ -33,7 +33,7 @@ import { } from '@alfresco/adf-core'; import { ProcessListCloudService } from '../services/process-list-cloud.service'; import { ProcessListCloudComponent } from './process-list-cloud.component'; -import { of } from 'rxjs'; +import { of, throwError } from 'rxjs'; import { shareReplay, skip } from 'rxjs/operators'; import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module'; import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model'; @@ -272,6 +272,67 @@ describe('ProcessListCloudComponent', () => { configureTestingModule('GET'); }); + it('should load preferences', () => { + const columnsOrder = ['startDate', 'id']; + const columnsVisibility = { startDate: true, id: false }; + const columnsWidths = { startDate: 100, id: 200 }; + + spyOn(preferencesService, 'getPreferences').and.returnValue( + of({ + list: { + entries: [ + { + entry: { + key: ProcessListCloudPreferences.columnOrder, + value: JSON.stringify(columnsOrder) + } + }, + { + entry: { + key: ProcessListCloudPreferences.columnsWidths, + value: JSON.stringify(columnsWidths) + } + }, + { + entry: { + key: ProcessListCloudPreferences.columnsVisibility, + value: JSON.stringify(columnsVisibility) + } + } + ] + } + }) + ); + + fixture.detectChanges(); + + const firstColumn = component.columns[0]; + expect(firstColumn.id).toBe('startDate'); + expect(firstColumn.isHidden).toBe(false); + expect(firstColumn.width).toBe(100); + + const secondColumn = component.columns[1]; + expect(secondColumn.id).toBe('id'); + expect(secondColumn.isHidden).toBe(true); + expect(secondColumn.width).toBe(200); + }); + + it('should load table when loading preferences throws an error', () => { + spyOn(preferencesService, 'getPreferences').and.returnValue(throwError(() => ({ status: 404 }))); + + fixture.detectChanges(); + + const firstColumn = component.columns[0]; + expect(firstColumn.id).toBe('id'); + expect(firstColumn.isHidden).toBe(false); + expect(firstColumn.width).toBe(undefined); + + const secondColumn = component.columns[1]; + expect(secondColumn.id).toBe('startDate'); + expect(secondColumn.isHidden).toBe(false); + expect(secondColumn.width).toBe(undefined); + }); + it('should load spinner and show the content', async () => { spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList)); const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts index 614e43d443..d0589385e0 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts @@ -49,11 +49,11 @@ import { UserPreferenceValues } from '@alfresco/adf-core'; import { ProcessListCloudService } from '../services/process-list-cloud.service'; -import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; +import { BehaviorSubject, combineLatest, of, Subject, throwError } from 'rxjs'; import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model'; import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model'; import { ProcessListCloudSortingModel, ProcessListRequestSortingModel } from '../models/process-list-sorting.model'; -import { filter, map, switchMap, take, tap } from 'rxjs/operators'; +import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service'; import { ProcessListCloudPreferences } from '../models/process-cloud-preferences'; @@ -407,6 +407,17 @@ export class ProcessListCloudComponent columnsVisibility: columnsVisibility ? JSON.parse(columnsVisibility.entry.value) : this.columnsVisibility, columnsWidths: columnsWidths ? JSON.parse(columnsWidths.entry.value) : undefined }; + }), + catchError((error) => { + if (error.status === 404) { + return of({ + columnsOrder: undefined, + columnsVisibility: this.columnsVisibility, + columnsWidths: undefined + }); + } else { + return throwError(error); + } }) ) .subscribe(({ columnsOrder, columnsVisibility, columnsWidths }) => { diff --git a/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.spec.ts new file mode 100644 index 0000000000..8442470a56 --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.spec.ts @@ -0,0 +1,65 @@ +/*! + * @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 { LocalPreferenceCloudService } from './local-preference-cloud.service'; +import { StorageService } from '@alfresco/adf-core'; + +describe('LocalPreferenceCloudService', () => { + let service: LocalPreferenceCloudService; + let storageServiceSpy: jasmine.SpyObj; + + beforeEach(() => { + const spy = jasmine.createSpyObj('StorageService', ['getItem', 'getItems', 'setItem']); + + TestBed.configureTestingModule({ + providers: [LocalPreferenceCloudService, { provide: StorageService, useValue: spy }] + }); + + service = TestBed.inject(LocalPreferenceCloudService); + storageServiceSpy = TestBed.inject(StorageService) as jasmine.SpyObj; + }); + + it('should return preferences for a given key', (done) => { + const key = 'testKey'; + const value = '[{"name": "test"}]'; + storageServiceSpy.getItem.and.returnValue(value); + + service.getPreferences('', key).subscribe((result) => { + expect(result.list.entries[0].entry.key).toBe(key); + expect(result.list.entries[0].entry.value).toBe(value); + done(); + }); + }); + + it('should return all preferences if no key is provided', (done) => { + const items = { + key1: '[{"name": "test1"}]', + key2: '[{"name": "test2"}]' + }; + storageServiceSpy.getItems.and.returnValue(items); + + service.getPreferences('', undefined).subscribe((result) => { + expect(result.list.entries.length).toBe(2); + expect(result.list.entries[0].entry.key).toBe('key1'); + expect(result.list.entries[0].entry.value).toBe(items['key1']); + expect(result.list.entries[1].entry.key).toBe('key2'); + expect(result.list.entries[1].entry.value).toBe(items['key2']); + done(); + }); + }); +}); diff --git a/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts index 7f3a208805..81db87c4b5 100644 --- a/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/services/local-preference-cloud.service.ts @@ -35,9 +35,18 @@ export class LocalPreferenceCloudService implements PreferenceCloudServiceInterf if (key || key === '') { return of(this.prepareLocalPreferenceResponse(key)); } + + const items = this.storage.getItems(); + const entries = Object.keys(items).map((k) => ({ + entry: { + key: k, + value: items[k] || '[]' + } + })); + return of({ list: { - entries: [] + entries } }); }