[ADF-4457] StorageService should be independent of AppConfigService (#4712)

* [ADF-4457] StorageService should be independent of AppConfigService

* [ADF-4457] Fix e2e tests

* [ADF-4457] Fix e2e tests

* [ADF-4457] Improve storage service workflow

* Fix linting

* Fix unit tests

* Fix e2e test

* Add missing class to constructor

* Fix e2e test

* Rebase branch

* Improve unit test

* fix test
This commit is contained in:
davidcanonieto
2019-06-25 16:21:13 +01:00
committed by Eugenio Romano
parent 90c403ae9e
commit 5c07d5b3e6
29 changed files with 432 additions and 369 deletions

View File

@@ -39,6 +39,7 @@ export enum AppConfigValues {
DISABLECSRF = 'disableCSRF',
AUTH_WITH_CREDENTIALS = 'auth.withCredentials',
APPLICATION = 'application',
STORAGE_PREFIX = 'application.storagePrefix',
NOTIFY_DURATION = 'notificationDefaultDuration'
}

View File

@@ -17,26 +17,23 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { StorageService } from '../services/storage.service';
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
@Injectable()
export class DebugAppConfigService extends AppConfigService {
constructor(http: HttpClient) {
constructor(private storage: StorageService, http: HttpClient) {
super(http);
}
/** @override */
get<T>(key: string, defaultValue?: T): T {
if (key === AppConfigValues.OAUTHCONFIG) {
return <T> (JSON.parse(this.getItem(key)) || super.get<T>(key, defaultValue));
return <T> (JSON.parse(this.storage.getItem(key)) || super.get<T>(key, defaultValue));
} else if (key === AppConfigValues.APPLICATION) {
return undefined;
} else {
return <T> (<any> this.getItem(key) || super.get<T>(key, defaultValue));
return <T> (<any> this.storage.getItem(key) || super.get<T>(key, defaultValue));
}
}
getItem(key: string): string | null {
return localStorage.getItem(key);
}
}

View File

@@ -17,14 +17,14 @@
import { DateCellComponent } from './date-cell.component';
import { Subject } from 'rxjs';
import { AlfrescoApiServiceMock, AppConfigService } from '@alfresco/adf-core';
import { AlfrescoApiServiceMock, AppConfigService, StorageService } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
describe('DataTableCellComponent', () => {
let alfrescoApiService: AlfrescoApiServiceMock;
beforeEach(() => {
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null));
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
});
it('should use medium format by default', () => {

View File

@@ -102,7 +102,7 @@ describe('DownloadZipDialogComponent', () => {
it('should call cancelDownload when CANCEL button is clicked', () => {
fixture.detectChanges();
spyOn(component, 'cancelDownload').and.returnValue('');
spyOn(component, 'cancelDownload');
const cancelButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#cancel-button');
cancelButton.click();
@@ -118,7 +118,7 @@ describe('DownloadZipDialogComponent', () => {
it('should close dialog when download is completed', () => {
component.download('fakeUrl', 'fileName');
spyOn(component, 'cancelDownload').and.returnValue('');
spyOn(component, 'cancelDownload');
fixture.detectChanges();
expect(dialogRef.close).toHaveBeenCalled();
});
@@ -134,7 +134,7 @@ describe('DownloadZipDialogComponent', () => {
it('should interrupt download when cancel button is clicked', () => {
spyOn(component, 'downloadZip');
spyOn(component, 'download');
spyOn(component, 'cancelDownload').and.returnValue('');
spyOn(component, 'cancelDownload');
fixture.detectChanges();

View File

@@ -22,6 +22,7 @@ import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
import { AppConfigService } from '../app-config/app-config.service';
import { setupTestBed } from '../testing/setupTestBed';
import { CoreTestingModule } from '../testing/core.testing.module';
import { StorageService } from '../services/storage.service';
describe('NodeFavoriteDirective', () => {
@@ -33,7 +34,7 @@ describe('NodeFavoriteDirective', () => {
});
beforeEach(() => {
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null));
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
directive = new NodeFavoriteDirective( alfrescoApiService);
});

View File

@@ -18,15 +18,24 @@
import { Injectable } from '@angular/core';
import { AppConfigService } from '../app-config/app-config.service';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { StorageService } from '../services/storage.service';
/* tslint:disable:adf-file-name */
@Injectable()
export class AlfrescoApiServiceMock extends AlfrescoApiService {
constructor(protected appConfig: AppConfigService) {
super(appConfig);
constructor(protected appConfig: AppConfigService,
protected storageService: StorageService) {
super(appConfig, storageService);
if (!this.alfrescoApi) {
this.initAlfrescoApi();
}
}
initialize(): Promise<any> {
return new Promise((resolve) => {
this.alfrescoApiInitializedSubject.next();
resolve();
});
}
}

View File

@@ -25,8 +25,9 @@ import {
} from '@alfresco/js-api';
import { AlfrescoApiCompatibility, AlfrescoApiConfig } from '@alfresco/js-api';
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
import { Subject } from 'rxjs';
import { Subject, Observable } from 'rxjs';
import { OauthConfigModel } from '../models/oauth-config.model';
import { StorageService } from './storage.service';
/* tslint:disable:adf-file-name */
@@ -39,6 +40,9 @@ export class AlfrescoApiService {
*/
nodeUpdated = new Subject<Node>();
protected alfrescoApiInitializedSubject: Subject<any>;
alfrescoApiInitialized: Observable<any>;
protected alfrescoApi: AlfrescoApiCompatibility;
lastConfig: AlfrescoApiConfig;
@@ -95,12 +99,18 @@ export class AlfrescoApiService {
return this.getInstance().core.groupsApi;
}
constructor(protected appConfig: AppConfigService) {
constructor(
protected appConfig: AppConfigService,
protected storageService: StorageService) {
this.alfrescoApiInitializedSubject = new Subject();
this.alfrescoApiInitialized = this.alfrescoApiInitializedSubject.asObservable();
}
async load() {
await this.appConfig.load().then(() => {
this.storageService.prefix = this.appConfig.get<string>(AppConfigValues.STORAGE_PREFIX, '');
this.initAlfrescoApi();
this.alfrescoApiInitializedSubject.next();
});
}

View File

@@ -40,7 +40,7 @@ describe('Auth Guard SSO role service', () => {
});
it('Should canActivate be true if the Role is present int the JWT token', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1'] } });
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
@@ -50,7 +50,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should canActivate be false if the Role is not present int the JWT token', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role3'] } });
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
@@ -60,7 +60,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should not redirect if canActivate is', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1'] } });
spyOn(routerService, 'navigate').and.stub();
@@ -72,7 +72,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should canActivate return false if the data Role to check is empty', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({ 'realm_access': { roles: ['role1', 'role3'] } });
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
@@ -81,7 +81,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should canActivate return false if the realm_access is not present', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
const router: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
@@ -90,7 +90,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should redirect to the redirectURL if canActivate is false and redirectUrl is in data', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
spyOn(routerService, 'navigate').and.stub();
@@ -102,7 +102,7 @@ describe('Auth Guard SSO role service', () => {
}));
it('Should not redirect if canActivate is false and redirectUrl is not in data', async(() => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({});
spyOn(routerService, 'navigate').and.stub();
@@ -137,7 +137,7 @@ describe('Auth Guard SSO role service', () => {
it('Should canActivate be true if both Real Role and Client Role are present int the JWT token', () => {
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
'realm_access': { roles: ['role1'] },
@@ -152,7 +152,7 @@ describe('Auth Guard SSO role service', () => {
it('Should canActivate be false if the Client Role is not present int the JWT token with the correct role', () => {
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue({
'realm_access': { roles: ['role1'] },
@@ -168,7 +168,7 @@ describe('Auth Guard SSO role service', () => {
describe('ClientRole ', () => {
it('Should be true if the resource_access contains the single role', () => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
@@ -180,7 +180,7 @@ describe('Auth Guard SSO role service', () => {
});
it('Should be true if the resource_access contains at least one of the roles', () => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
@@ -192,7 +192,7 @@ describe('Auth Guard SSO role service', () => {
});
it('Should be false if the resource_access does not contain the role', () => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
'resource_access': { fakeapp: { roles: ['role3'] } }
@@ -202,7 +202,7 @@ describe('Auth Guard SSO role service', () => {
});
it('Should be false if the resource_access does not contain the client role related to the app', () => {
spyOn(localStorage, 'getItem').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'getAccessToken').and.returnValue('my-access_token');
spyOn(jwtHelperService, 'decodeToken').and.returnValue(
{
'resource_access': { anotherfakeapp: { roles: ['role1'] } }

View File

@@ -18,7 +18,6 @@
import { Injectable } from '@angular/core';
import { JwtHelperService } from './jwt-helper.service';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { StorageService } from './storage.service';
@Injectable({
providedIn: 'root'
@@ -52,28 +51,22 @@ export class AuthGuardSsoRoleService implements CanActivate {
return hasRole;
}
constructor(private storageService: StorageService, private jwtHelperService: JwtHelperService, private router: Router) {
constructor(private jwtHelperService: JwtHelperService, private router: Router) {
}
getRealmRoles(): string[] {
const access = this.jwtHelperService.getValueFromLocalAccessToken<any>('realm_access');
const roles = access ? access['roles'] : [];
return roles;
return access ? access['roles'] : [];
}
getClientRoles(client: string): string[] {
const clientRole = this.jwtHelperService.getValueFromLocalAccessToken<any>('resource_access')[client];
const roles = clientRole ? clientRole['roles'] : [];
return roles;
}
getAccessToken(): string {
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
return clientRole ? clientRole['roles'] : [];
}
hasRealmRole(role: string): boolean {
let hasRole = false;
if (this.getAccessToken()) {
if (this.jwtHelperService.getAccessToken()) {
const realmRoles = this.getRealmRoles();
hasRole = realmRoles.some((currentRole) => {
return currentRole === role;
@@ -96,7 +89,7 @@ export class AuthGuardSsoRoleService implements CanActivate {
hasClientRole(clientRole, role: string): boolean {
let hasRole = false;
if (this.getAccessToken()) {
if (this.jwtHelperService.getAccessToken()) {
const clientRoles = this.getClientRoles(clientRole);
hasRole = clientRoles.some((currentRole) => {
return currentRole === role;

View File

@@ -79,8 +79,15 @@ export class JwtHelperService {
* @returns Value from the token
*/
getValueFromLocalAccessToken<T>(key: string): T {
const accessToken = localStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
return this.getValueFromToken(accessToken, key);
return this.getValueFromToken(this.getAccessToken(), key);
}
/**
* Gets access token
* @returns access token
*/
getAccessToken(): string {
return localStorage.getItem(JwtHelperService.USER_ACCESS_TOKEN);
}
/**

View File

@@ -29,69 +29,81 @@ describe('StorageService', () => {
const key = 'test_key';
const value = 'test_value';
setupTestBed({
imports: [CoreTestingModule],
providers: [
{ provide: AppConfigService, useClass: AppConfigServiceMock }
]
});
describe('StorageService', () => {
setupTestBed({
imports: [CoreTestingModule]
});
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
appConfig.config = {
application: {
storagePrefix: 'ADF_APP'
}
};
storage = TestBed.get(StorageService);
});
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
appConfig.config = {
application: {
storagePrefix: 'ADF_APP'
}
};
storage = TestBed.get(StorageService);
});
it('should get the prefix for the storage from app config', (done) => {
appConfig.load().then(() => {
expect(storage.storagePrefix).toBe('ADF_APP_');
done();
it('should get the prefix for the storage from app config', (done) => {
appConfig.load().then(() => {
expect(storage.prefix).toBe('ADF_APP_');
done();
});
});
it('should set a property with the prefix in the local storage', (done) => {
storage.clear();
appConfig.load().then(() => {
storage.setItem(key, value);
const storageKey = localStorage.key(0);
expect(storageKey).toBe('ADF_APP_' + key);
expect(localStorage.getItem(storageKey)).toBe(value);
done();
});
});
it('should be able to get a property from the local storage', (done) => {
storage.clear();
appConfig.load().then(() => {
storage.setItem(key, value);
expect(storage.getItem(key)).toBe(value);
done();
});
});
});
it('should set an empty prefix when the it is not defined in the app config', (done) => {
appConfig.config.application.storagePrefix = '';
appConfig.load().then(() => {
expect(storage.storagePrefix).toBe('');
done();
describe('StorageService', () => {
setupTestBed({
imports: [CoreTestingModule]
});
});
it('should set a property with the prefix in the local storage', (done) => {
storage.clear();
appConfig.load().then(() => {
storage.setItem(key, value);
expect(localStorage.hasOwnProperty('ADF_APP_' + key)).not.toBe(null);
expect(localStorage.getItem('ADF_APP_' + key)).toBe(value);
done();
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
appConfig.config = {
application: {
storagePrefix: ''
}
};
storage = TestBed.get(StorageService);
});
});
it('should set a property without a prefix in the local storage', (done) => {
storage.clear();
appConfig.config.application.storagePrefix = '';
appConfig.load().then(() => {
storage.setItem(key, value);
expect(localStorage.hasOwnProperty(key)).not.toBe(null);
expect(localStorage.getItem(key)).toBe(value);
done();
it('should set an empty prefix when the it is not defined in the app config', (done) => {
appConfig.load().then(() => {
expect(storage.prefix).toBe('');
done();
});
});
});
it('should be able to get a property from the local storage', (done) => {
storage.clear();
it('should set a property without a prefix in the local storage', (done) => {
appConfig.load().then(() => {
storage.setItem(key, value);
appConfig.load().then(() => {
storage.setItem(key, value);
expect(storage.getItem(key)).toBe(value);
done();
expect(localStorage.getItem(key)).toBe(value);
done();
});
});
});
});

View File

@@ -16,7 +16,6 @@
*/
import { Injectable } from '@angular/core';
import { AppConfigService } from '../app-config/app-config.service';
@Injectable({
providedIn: 'root'
@@ -25,11 +24,18 @@ export class StorageService {
private memoryStore: { [key: string]: any } = {};
private useLocalStorage: boolean = false;
storagePrefix: string;
private _prefix: string = '';
constructor(private appConfigService: AppConfigService) {
get prefix() {
return this._prefix;
}
set prefix(prefix: string) {
this._prefix = prefix ? prefix + '_' : '';
}
constructor() {
this.useLocalStorage = this.storageAvailable('localStorage');
this.appConfigService.onLoad.subscribe(this.getAppPrefix.bind(this));
}
/**
@@ -39,9 +45,9 @@ export class StorageService {
*/
getItem(key: string): string | null {
if (this.useLocalStorage) {
return localStorage.getItem(this.storagePrefix + key);
return localStorage.getItem(this.prefix + key);
} else {
return this.memoryStore.hasOwnProperty(this.storagePrefix + key) ? this.memoryStore[this.storagePrefix + key] : null;
return this.memoryStore.hasOwnProperty(this.prefix + key) ? this.memoryStore[this.prefix + key] : null;
}
}
@@ -52,9 +58,9 @@ export class StorageService {
*/
setItem(key: string, data: string) {
if (this.useLocalStorage) {
localStorage.setItem(this.storagePrefix + key, data);
localStorage.setItem(this.prefix + key, data);
} else {
this.memoryStore[this.storagePrefix + key] = data.toString();
this.memoryStore[this.prefix + key] = data.toString();
}
}
@@ -73,9 +79,9 @@ export class StorageService {
*/
removeItem(key: string) {
if (this.useLocalStorage) {
localStorage.removeItem(this.storagePrefix + key);
localStorage.removeItem(this.prefix + key);
} else {
delete this.memoryStore[this.storagePrefix + key];
delete this.memoryStore[this.prefix + key];
}
}
@@ -86,7 +92,7 @@ export class StorageService {
*/
hasItem(key: string): boolean {
if (this.useLocalStorage) {
return localStorage.getItem(this.storagePrefix + key) ? true : false;
return localStorage.getItem(this.prefix + key) ? true : false;
} else {
return this.memoryStore.hasOwnProperty(key);
}
@@ -103,19 +109,4 @@ export class StorageService {
return false;
}
}
/**
* Sets the prefix that is used for the local storage of the app
* It assigns the string that is defined i the app config,
* empty prefix otherwise.
*/
getAppPrefix() {
const appConfiguration = this.appConfigService.get<any>('application');
if (appConfiguration && appConfiguration.storagePrefix) {
this.storagePrefix = appConfiguration.storagePrefix + '_';
} else {
this.storagePrefix = '';
}
}
}

View File

@@ -23,6 +23,8 @@ import { UserPreferencesService, UserPreferenceValues } from './user-preferences
import { setupTestBed } from '../testing/setupTestBed';
import { CoreTestingModule } from '../testing/core.testing.module';
import { AppConfigServiceMock } from '../mock/app-config.service.mock';
import { AlfrescoApiService } from './alfresco-api.service';
import { AlfrescoApiServiceMock } from '../mock/alfresco-api.service.mock';
describe('UserPreferencesService', () => {
@@ -30,13 +32,15 @@ describe('UserPreferencesService', () => {
let preferences: UserPreferencesService;
let storage: StorageService;
let appConfig: AppConfigServiceMock;
let alfrescoApiService: AlfrescoApiServiceMock;
let translate: TranslateService;
let changeDisposable: any;
setupTestBed({
imports: [CoreTestingModule],
providers: [
{ provide: AppConfigService, useClass: AppConfigServiceMock }
{ provide: AppConfigService, useClass: AppConfigServiceMock },
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
]
});
@@ -51,6 +55,7 @@ describe('UserPreferencesService', () => {
storage = TestBed.get(StorageService);
translate = TestBed.get(TranslateService);
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
});
beforeEach(() => {
@@ -65,176 +70,170 @@ describe('UserPreferencesService', () => {
storage.clear();
});
it('should get default pagination from app config', (done) => {
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.config.pagination.size = 0;
appConfig.load().then(() => {
expect(preferences.paginationSize).toBe(0);
done();
describe(' with pagination config', () => {
beforeEach(() => {
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
});
});
it('should return supported page sizes defined in the app config', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
const supportedPages = preferences.supportedPageSizes;
appConfig.load();
expect(supportedPages).toEqual(supportedPaginationSize);
});
it('should use [GUEST] as default storage prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.setStoragePrefix(null);
expect(preferences.getStoragePrefix()).toBe('GUEST');
});
it('should change storage prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.setStoragePrefix('USER_A');
expect(preferences.getStoragePrefix()).toBe('USER_A');
});
it('should format property key for default prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.setStoragePrefix(null);
expect(preferences.getPropertyKey('propertyA')).toBe('GUEST__propertyA');
});
it('should format property key for custom prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.setStoragePrefix('USER_A');
expect(preferences.getPropertyKey('propertyA')).toBe('USER_A__propertyA');
});
it('should save value with default prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should null value return default prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
storage.setItem('paginationSize', null);
const paginationSize = preferences.getPropertyKey('paginationSize');
expect(preferences.get(paginationSize, 'default')).toBe('default');
});
it('should save value with custom prefix', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.setStoragePrefix('USER_A');
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should return as default locale the app.config locate as first', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-config');
});
it('should return as default locale the browser locale as second', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-browser');
});
it('should return as default locale the component property as third ', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
spyOn(translate, 'getBrowserCultureLang').and.stub();
expect(preferences.getDefaultLocale()).toBe('en');
});
it('should return as locale the store locate', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.locale = 'fake-store-locate';
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.locale).toBe('fake-store-locate');
});
it('should store default textOrientation based on language ', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-config'
}
];
appConfig.config.locale = 'fake-locale-config';
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.load();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe('ltr');
}));
it('should store textOrientation based on language config direction', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-config',
direction: 'rtl'
}
];
appConfig.config.locale = 'fake-locale-config';
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.load();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe('rtl');
}));
it('should not store textOrientation based on language ', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-browser'
}
];
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.load();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe(null);
}));
it('should default to browser locale for textOrientation when locale is not defined in configuration', (done) => {
appConfig.config.languages = [
{
key: 'fake-locale-browser',
direction: 'rtl'
}
];
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locale-browser');
preferences = new UserPreferencesService(translate, appConfig, storage);
appConfig.load();
changeDisposable = preferences.onChange
.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus['textOrientation']).toBe('rtl');
it('should get default pagination from app config', (done) => {
appConfig.config.pagination.size = 0;
appConfig.load().then(() => {
expect(preferences.paginationSize).toBe(0);
done();
});
});
it('should return supported page sizes defined in the app config', () => {
const supportedPages = preferences.supportedPageSizes;
appConfig.load();
expect(supportedPages).toEqual(supportedPaginationSize);
});
it('should use [GUEST] as default storage prefix', () => {
preferences.setStoragePrefix(null);
expect(preferences.getStoragePrefix()).toBe('GUEST');
});
it('should change storage prefix', () => {
preferences.setStoragePrefix('USER_A');
expect(preferences.getStoragePrefix()).toBe('USER_A');
});
it('should format property key for default prefix', () => {
preferences.setStoragePrefix(null);
expect(preferences.getPropertyKey('propertyA')).toBe('GUEST__propertyA');
});
it('should format property key for custom prefix', () => {
preferences.setStoragePrefix('USER_A');
expect(preferences.getPropertyKey('propertyA')).toBe('USER_A__propertyA');
});
it('should save value with default prefix', () => {
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should null value return default prefix', () => {
storage.setItem('paginationSize', null);
const paginationSize = preferences.getPropertyKey('paginationSize');
expect(preferences.get(paginationSize, 'default')).toBe('default');
});
it('should save value with custom prefix', () => {
preferences.setStoragePrefix('USER_A');
preferences.set('propertyA', 'valueA');
const propertyKey = preferences.getPropertyKey('propertyA');
expect(storage.getItem(propertyKey)).toBe('valueA');
});
it('should return as default locale the app.config locate as first', () => {
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-config');
});
it('should return as default locale the browser locale as second', () => {
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.getDefaultLocale()).toBe('fake-locate-browser');
});
it('should return as default locale the component property as third ', () => {
spyOn(translate, 'getBrowserCultureLang').and.stub();
expect(preferences.getDefaultLocale()).toBe('en');
});
it('should return as locale the store locate', () => {
preferences.locale = 'fake-store-locate';
appConfig.config.locale = 'fake-locate-config';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.locale).toBe('fake-store-locate');
});
it('should not store in the storage the locale if the app.config.json does not have a value', () => {
preferences.locale = 'fake-store-locate';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.locale).toBe('fake-store-locate');
expect(storage.getItem(UserPreferenceValues.Locale)).toBe(null);
});
it('should stream the page size value when is set', (done) => {
preferences.paginationSize = 5;
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus[UserPreferenceValues.PaginationSize]).toBe(5);
done();
});
});
it('should stream the user preference status when changed', (done) => {
preferences.set('propertyA', 'valueA');
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus.propertyA).toBe('valueA');
done();
});
});
});
it('should not store in the storage the locale if the app.config.json does not have a value', () => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.locale = 'fake-store-locate';
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locate-browser');
expect(preferences.locale).toBe('fake-store-locate');
expect(storage.getItem(UserPreferenceValues.Locale)).toBe(null);
});
describe('with language config', () => {
it('should stream the page size value when is set', (done) => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.paginationSize = 5;
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus[UserPreferenceValues.PaginationSize]).toBe(5);
done();
});
});
it('should store default textOrientation based on language', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-config'
}
];
appConfig.config.locale = 'fake-locale-config';
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
alfrescoApiService.initialize();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe('ltr');
}));
it('should stream the user preference status when changed', (done) => {
preferences = new UserPreferencesService(translate, appConfig, storage);
preferences.set('propertyA', 'valueA');
changeDisposable = preferences.onChange.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus.propertyA).toBe('valueA');
done();
it('should store textOrientation based on language config direction', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-config',
direction: 'rtl'
}
];
appConfig.config.locale = 'fake-locale-config';
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
alfrescoApiService.initialize();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe('rtl');
}));
it('should not store textOrientation based on language ', async(() => {
appConfig.config.languages = [
{
key: 'fake-locale-browser'
}
];
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
alfrescoApiService.initialize();
const textOrientation = preferences.getPropertyKey('textOrientation');
expect(storage.getItem(textOrientation)).toBe(null);
}));
it('should default to browser locale for textOrientation when locale is not defined in configuration', (done) => {
appConfig.config.languages = [
{
key: 'fake-locale-browser',
direction: 'rtl'
}
];
spyOn(translate, 'getBrowserCultureLang').and.returnValue('fake-locale-browser');
preferences = new UserPreferencesService(translate, appConfig, storage, alfrescoApiService);
alfrescoApiService.initialize();
changeDisposable = preferences.onChange
.subscribe((userPreferenceStatus) => {
expect(userPreferenceStatus['textOrientation']).toBe('rtl');
done();
});
});
});
});

View File

@@ -22,6 +22,7 @@ import { AppConfigService, AppConfigValues } from '../app-config/app-config.serv
import { LanguageItem } from '../language-menu/language.interface';
import { StorageService } from './storage.service';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { AlfrescoApiService } from './alfresco-api.service';
export enum UserPreferenceValues {
PaginationSize = 'paginationSize',
@@ -48,8 +49,9 @@ export class UserPreferencesService {
constructor(public translate: TranslateService,
private appConfig: AppConfigService,
private storage: StorageService) {
this.appConfig.onLoad.subscribe(this.initUserPreferenceStatus.bind(this));
private storage: StorageService,
private alfrescoApiService: AlfrescoApiService) {
this.alfrescoApiService.alfrescoApiInitialized.subscribe(this.initUserPreferenceStatus.bind(this));
this.onChangeSubject = new BehaviorSubject(this.userPreferenceStatus);
this.onChange = this.onChangeSubject.asObservable();
}