Skip to content

Commit

Permalink
Merge branch 'accessibility-settings-7.6' into accessibility-settings…
Browse files Browse the repository at this point in the history
…-7_x
  • Loading branch information
AAwouters committed Dec 11, 2024
2 parents 3c92acb + c71c666 commit 442b4ea
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 100 deletions.
44 changes: 12 additions & 32 deletions src/app/accessibility/accessibility-settings.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
AccessibilitySettingsService,
AccessibilitySetting,
AccessibilitySettings,
ACCESSIBILITY_SETTINGS_METADATA_KEY,
ACCESSIBILITY_COOKIE
Expand Down Expand Up @@ -41,17 +40,6 @@ describe('accessibilitySettingsService', () => {
);
});

describe('getALlAccessibilitySettingsKeys', () => {
it('should return an array containing all accessibility setting names', () => {
const settingNames: AccessibilitySetting[] = [
AccessibilitySetting.NotificationTimeOut,
AccessibilitySetting.LiveRegionTimeOut,
];

expect(service.getAllAccessibilitySettingKeys()).toEqual(settingNames);
});
});

describe('get', () => {
it('should return the setting if it is set', () => {
const settings: AccessibilitySettings = {
Expand All @@ -60,7 +48,7 @@ describe('accessibilitySettingsService', () => {

service.getAll = jasmine.createSpy('getAll').and.returnValue(of(settings));

service.get(AccessibilitySetting.NotificationTimeOut, 'default').subscribe(value =>
service.get('notificationTimeOut', 'default').subscribe(value =>
expect(value).toEqual('1000')
);
});
Expand All @@ -72,7 +60,7 @@ describe('accessibilitySettingsService', () => {

service.getAll = jasmine.createSpy('getAll').and.returnValue(of(settings));

service.get(AccessibilitySetting.LiveRegionTimeOut, 'default').subscribe(value =>
service.get('liveRegionTimeOut', 'default').subscribe(value =>
expect(value).toEqual('default')
);
});
Expand All @@ -82,23 +70,23 @@ describe('accessibilitySettingsService', () => {
it('should return the setting as number if the value for the setting can be parsed to a number', () => {
service.get = jasmine.createSpy('get').and.returnValue(of('1000'));

service.getAsNumber(AccessibilitySetting.NotificationTimeOut).subscribe(value =>
service.getAsNumber('notificationTimeOut').subscribe(value =>
expect(value).toEqual(1000)
);
});

it('should return the default value if no value is set for the setting', () => {
service.get = jasmine.createSpy('get').and.returnValue(of(null));

service.getAsNumber(AccessibilitySetting.NotificationTimeOut, 123).subscribe(value =>
service.getAsNumber('notificationTimeOut', 123).subscribe(value =>
expect(value).toEqual(123)
);
});

it('should return the default value if the value for the setting can not be parsed to a number', () => {
service.get = jasmine.createSpy('get').and.returnValue(of('text'));

service.getAsNumber(AccessibilitySetting.NotificationTimeOut, 123).subscribe(value =>
service.getAsNumber('notificationTimeOut', 123).subscribe(value =>
expect(value).toEqual(123)
);
});
Expand Down Expand Up @@ -176,7 +164,7 @@ describe('accessibilitySettingsService', () => {
it('should correctly update the chosen setting', () => {
service.updateSettings = jasmine.createSpy('updateSettings');

service.set(AccessibilitySetting.LiveRegionTimeOut, '500');
service.set('liveRegionTimeOut', '500');
expect(service.updateSettings).toHaveBeenCalledWith({ liveRegionTimeOut: '500' });
});
});
Expand Down Expand Up @@ -307,7 +295,7 @@ describe('accessibilitySettingsService', () => {
});

it('should set the settings in metadata', () => {
service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe();
service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe();
expect(ePerson.setMetadata).toHaveBeenCalled();
});

Expand All @@ -318,19 +306,19 @@ describe('accessibilitySettingsService', () => {
});

it('should create a patch with the metadata changes', () => {
service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe();
service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe();
expect(ePersonService.createPatchFromCache).toHaveBeenCalled();
});

it('should send the patch request', () => {
service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' }).subscribe();
service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' }).subscribe();
expect(ePersonService.patch).toHaveBeenCalled();
});

it('should emit true when the update succeeded', fakeAsync(() => {
ePersonService.patch = jasmine.createSpy().and.returnValue(createSuccessfulRemoteDataObject$({}));

service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' })
service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' })
.subscribe(value => {
expect(value).toBeTrue();
});
Expand All @@ -341,7 +329,7 @@ describe('accessibilitySettingsService', () => {
it('should emit false when the update failed', fakeAsync(() => {
ePersonService.patch = jasmine.createSpy().and.returnValue(createFailedRemoteDataObject$());

service.setSettingsInMetadata(ePerson, { [AccessibilitySetting.LiveRegionTimeOut]: '500' })
service.setSettingsInMetadata(ePerson, { ['liveRegionTimeOut']: '500' })
.subscribe(value => {
expect(value).toBeFalse();
});
Expand All @@ -357,7 +345,7 @@ describe('accessibilitySettingsService', () => {
});

it('should store the settings in a cookie', () => {
service.setSettingsInCookie({ [AccessibilitySetting.LiveRegionTimeOut]: '500' });
service.setSettingsInCookie({ ['liveRegionTimeOut']: '500' });
expect(cookieService.set).toHaveBeenCalled();
});

Expand All @@ -368,12 +356,4 @@ describe('accessibilitySettingsService', () => {
});
});

describe('getInputType', () => {
it('should correctly return the input type', () => {
expect(service.getInputType(AccessibilitySetting.NotificationTimeOut)).toEqual('number');
expect(service.getInputType(AccessibilitySetting.LiveRegionTimeOut)).toEqual('number');
expect(service.getInputType('unknownValue' as AccessibilitySetting)).toEqual('text');
});
});

});
6 changes: 6 additions & 0 deletions src/app/accessibility/accessibility-settings.service.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ export class AccessibilitySettingsServiceStub {
setSettingsInCookie = jasmine.createSpy('setSettingsInCookie');

getInputType = jasmine.createSpy('getInputType').and.returnValue('text');

convertFormValuesToStoredValues = jasmine.createSpy('convertFormValuesToStoredValues').and.returnValue({});

convertStoredValuesToFormValues = jasmine.createSpy('convertStoredValuesToFormValues').and.returnValue({});

getPlaceholder = jasmine.createSpy('getPlaceholder').and.returnValue('placeholder');
}
113 changes: 91 additions & 22 deletions src/app/accessibility/accessibility-settings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Injectable } from '@angular/core';
import { Observable, of, switchMap } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { CookieService } from '../core/services/cookie.service';
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../shared/empty.util';
import { hasValue, isNotEmpty } from '../shared/empty.util';
import { AuthService } from '../core/auth/auth.service';
import { EPerson } from '../core/eperson/models/eperson.model';
import { EPersonDataService } from '../core/eperson/eperson-data.service';
import { getFirstCompletedRemoteData } from '../core/shared/operators';
import cloneDeep from 'lodash/cloneDeep';
import { environment } from '../../environments/environment';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';

/**
* Name of the cookie used to store the settings locally
Expand All @@ -21,16 +22,30 @@ export const ACCESSIBILITY_COOKIE = 'dsAccessibilityCookie';
export const ACCESSIBILITY_SETTINGS_METADATA_KEY = 'dspace.accessibility.settings';

/**
* Enum containing all possible accessibility settings.
* When adding new settings, the {@link AccessibilitySettingsService#getInputType} method and the i18n keys for the
* accessibility settings page should be updated.
* Type containing all possible accessibility settings.
* When adding new settings, make sure to add the new setting to the accessibility-settings component form.
* The converter methods to convert from stored format to form format (and vice-versa) need to be updated as well.
*/
export enum AccessibilitySetting {
NotificationTimeOut = 'notificationTimeOut',
LiveRegionTimeOut = 'liveRegionTimeOut',
}
export type AccessibilitySetting = 'notificationTimeOut' | 'liveRegionTimeOut';

export type AccessibilitySettings = { [key in AccessibilitySetting]?: any };
/**
* Type representing an object that contains accessibility settings values for all accessibility settings.
*/
export type FullAccessibilitySettings = { [key in AccessibilitySetting]: string };

/**
* Type representing an object that contains accessibility settings values for some accessibility settings.
*/
export type AccessibilitySettings = Partial<FullAccessibilitySettings>;

/**
* The accessibility settings object format used by the accessibility-settings component form.
*/
export interface AccessibilitySettingsFormValues {
notificationTimeOutEnabled: boolean,
notificationTimeOut: string,
liveRegionTimeOut: string,
}

/**
* Service handling the retrieval and configuration of accessibility settings.
Expand All @@ -50,10 +65,6 @@ export class AccessibilitySettingsService {
) {
}

getAllAccessibilitySettingKeys(): AccessibilitySetting[] {
return Object.entries(AccessibilitySetting).map(([_, val]) => val);
}

/**
* Get the stored value for the provided {@link AccessibilitySetting}. If the value does not exist or if it is empty,
* the provided defaultValue is emitted instead.
Expand Down Expand Up @@ -192,8 +203,8 @@ export class AccessibilitySettingsService {

return this.ePersonService.createPatchFromCache(user).pipe(
take(1),
isNotEmptyOperator(),
switchMap(operations => this.ePersonService.patch(user, operations)),
switchMap(operations =>
isNotEmpty(operations) ? this.ePersonService.patch(user, operations) : createSuccessfulRemoteDataObject$({})),
getFirstCompletedRemoteData(),
map(rd => rd.hasSucceeded),
);
Expand All @@ -211,17 +222,75 @@ export class AccessibilitySettingsService {
}

/**
* Returns the input type that a form should use for the provided {@link AccessibilitySetting}
* Clears all settings in the cookie and attempts to clear settings in metadata.
* Emits true if settings in metadata were cleared and false otherwise.
*/
clearSettings(): Observable<boolean> {
this.setSettingsInCookie({});
return this.setSettingsInAuthenticatedUserMetadata({});

Check warning on line 230 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L229-L230

Added lines #L229 - L230 were not covered by tests
}

/**
* Retrieve the placeholder to be used for the provided AccessibilitySetting.
* Returns an empty string when no placeholder is specified for the provided setting.
*/
getInputType(setting: AccessibilitySetting): string {
getPlaceholder(setting: AccessibilitySetting): string {
switch (setting) {

Check warning on line 238 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L238

Added line #L238 was not covered by tests
case AccessibilitySetting.NotificationTimeOut:
return 'number';
case AccessibilitySetting.LiveRegionTimeOut:
return 'number';
case 'notificationTimeOut':
return millisecondsToSeconds(environment.notifications.timeOut.toString());

Check warning on line 240 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L240

Added line #L240 was not covered by tests
case 'liveRegionTimeOut':
return millisecondsToSeconds(environment.liveRegion.messageTimeOutDurationMs.toString());

Check warning on line 242 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L242

Added line #L242 was not covered by tests
default:
return 'text';
return '';

Check warning on line 244 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L244

Added line #L244 was not covered by tests
}
}

/**
* Convert values in the provided accessibility settings object to values ready to be stored.
*/
convertFormValuesToStoredValues(settings: AccessibilitySettingsFormValues): FullAccessibilitySettings {
return {

Check warning on line 252 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L252

Added line #L252 was not covered by tests
notificationTimeOut: settings.notificationTimeOutEnabled ?
secondsToMilliseconds(settings.notificationTimeOut) : '0',
liveRegionTimeOut: secondsToMilliseconds(settings.liveRegionTimeOut),
};
}

/**
* Convert values in the provided accessibility settings object to values ready to show in the form.
*/
convertStoredValuesToFormValues(settings: AccessibilitySettings): AccessibilitySettingsFormValues {
return {

Check warning on line 263 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L263

Added line #L263 was not covered by tests
notificationTimeOutEnabled: parseFloat(settings.notificationTimeOut) !== 0,
notificationTimeOut: millisecondsToSeconds(settings.notificationTimeOut),
liveRegionTimeOut: millisecondsToSeconds(settings.liveRegionTimeOut),
};
}

}

/**
* Converts a string representing seconds to a string representing milliseconds
* Returns null if the input could not be parsed to a float
*/
function secondsToMilliseconds(secondsStr: string): string {
const seconds = parseFloat(secondsStr);

Check warning on line 277 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L277

Added line #L277 was not covered by tests
if (isNaN(seconds)) {
return null;

Check warning on line 279 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L279

Added line #L279 was not covered by tests
} else {
return (seconds * 1000).toString();

Check warning on line 281 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L281

Added line #L281 was not covered by tests
}
}

/**
* Converts a string representing milliseconds to a string representing seconds
* Returns null if the input could not be parsed to a float
*/
function millisecondsToSeconds(millisecondsStr: string): string {
const milliseconds = parseFloat(millisecondsStr);

Check warning on line 290 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L290

Added line #L290 was not covered by tests
if (isNaN(milliseconds)) {
return null;

Check warning on line 292 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L292

Added line #L292 was not covered by tests
} else {
return (milliseconds / 1000).toString();

Check warning on line 294 in src/app/accessibility/accessibility-settings.service.ts

View check run for this annotation

Codecov / codecov/patch

src/app/accessibility/accessibility-settings.service.ts#L294

Added line #L294 was not covered by tests
}
}
Loading

0 comments on commit 442b4ea

Please sign in to comment.