From 145b26d2624ecd5fcddbedd77103206a14d3ab30 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 20 Apr 2020 20:06:50 +0200 Subject: [PATCH 01/17] Added LocaleService as localization handler --- src/app/app.component.spec.ts | 38 +++---- src/app/app.component.ts | 60 +++++----- src/app/core/locale/locale.service.spec.ts | 106 ++++++++++++++++++ src/app/core/locale/locale.service.ts | 76 +++++++++++++ .../lang-switch/lang-switch.component.spec.ts | 49 ++++---- .../lang-switch/lang-switch.component.ts | 16 +-- 6 files changed, 254 insertions(+), 91 deletions(-) create mode 100644 src/app/core/locale/locale.service.spec.ts create mode 100644 src/app/core/locale/locale.service.ts diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index a578c0d8c1..09e472650f 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,36 +1,22 @@ -import { - async, - ComponentFixture, - inject, - TestBed -} from '@angular/core/testing'; - -import { - CUSTOM_ELEMENTS_SCHEMA, - DebugElement -} from '@angular/core'; - +import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; import { CommonModule } from '@angular/common'; - import { By } from '@angular/platform-browser'; +import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { Store, StoreModule } from '@ngrx/store'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; // Load the implementations that should be tested import { AppComponent } from './app.component'; - import { HostWindowState } from './shared/search/host-window.reducer'; import { HostWindowResizeAction } from './shared/host-window.actions'; - import { MetadataService } from './core/metadata/metadata.service'; - -import { GLOBAL_CONFIG, ENV_CONFIG } from '../config'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../config'; import { NativeWindowRef, NativeWindowService } from './core/services/window.service'; - import { MockTranslateLoader } from './shared/mocks/mock-translate-loader'; import { MockMetadataService } from './shared/mocks/mock-metadata-service'; -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { AngularticsMock } from './shared/mocks/mock-angulartics.service'; import { AuthServiceMock } from './shared/mocks/mock-auth.service'; import { AuthService } from './core/auth/auth.service'; @@ -40,13 +26,11 @@ import { CSSVariableServiceStub } from './shared/testing/css-variable-service-st import { MenuServiceStub } from './shared/testing/menu-service-stub'; import { HostWindowService } from './shared/host-window.service'; import { HostWindowServiceStub } from './shared/testing/host-window-service-stub'; -import { ActivatedRoute, Router } from '@angular/router'; import { RouteService } from './core/services/route.service'; import { MockActivatedRoute } from './shared/mocks/mock-active-router'; import { MockRouter } from './shared/mocks/mock-router'; -import { MockCookieService } from './shared/mocks/mock-cookie.service'; -import { CookieService } from './core/services/cookie.service'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; +import { LocaleService } from './core/locale/locale.service'; let comp: AppComponent; let fixture: ComponentFixture; @@ -56,6 +40,12 @@ const menuService = new MenuServiceStub(); describe('App component', () => { + function getMockLocaleService(): LocaleService { + return jasmine.createSpyObj('LocaleService', { + setCurrentLanguageCode: jasmine.createSpy('setCurrentLanguageCode') + }) + } + // async beforeEach beforeEach(async(() => { return TestBed.configureTestingModule({ @@ -82,7 +72,7 @@ describe('App component', () => { { provide: MenuService, useValue: menuService }, { provide: CSSVariableService, useClass: CSSVariableServiceStub }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, - { provide: CookieService, useValue: new MockCookieService()}, + { provide: LocaleService, useValue: getMockLocaleService() }, AppComponent, RouteService ], diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4e029af8ca..2bd1b5e2b9 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,33 +1,36 @@ import { delay, filter, map, take } from 'rxjs/operators'; -import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + HostListener, + Inject, + OnInit, + ViewEncapsulation +} from '@angular/core'; import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router'; +import { BehaviorSubject, combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; import { select, Store } from '@ngrx/store'; - import { TranslateService } from '@ngx-translate/core'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { GLOBAL_CONFIG, GlobalConfig } from '../config'; - import { MetadataService } from './core/metadata/metadata.service'; import { HostWindowResizeAction } from './shared/host-window.actions'; import { HostWindowState } from './shared/search/host-window.reducer'; import { NativeWindowRef, NativeWindowService } from './core/services/window.service'; import { isAuthenticated } from './core/auth/selectors'; import { AuthService } from './core/auth/auth.service'; -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import variables from '../styles/_exposed_variables.scss'; import { CSSVariableService } from './shared/sass-helper/sass-helper.service'; import { MenuService } from './shared/menu/menu.service'; import { MenuID } from './shared/menu/initial-menus-state'; -import { BehaviorSubject, combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; import { slideSidebarPadding } from './shared/animations/slide'; import { HostWindowService } from './shared/host-window.service'; import { Theme } from '../config/theme.inferface'; -import { isNotEmpty } from './shared/empty.util'; -import { CookieService } from './core/services/cookie.service'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; - -export const LANG_COOKIE = 'language_cookie'; +import { LocaleService } from './core/locale/locale.service'; @Component({ selector: 'ds-app', @@ -58,29 +61,16 @@ export class AppComponent implements OnInit, AfterViewInit { private cssService: CSSVariableService, private menuService: MenuService, private windowService: HostWindowService, - private cookie: CookieService + private localeService: LocaleService ) { // Load all the languages that are defined as active from the config file translate.addLangs(config.languages.filter((LangConfig) => LangConfig.active === true).map((a) => a.code)); // Load the default language from the config file - translate.setDefaultLang(config.defaultLanguage); + // translate.setDefaultLang(config.defaultLanguage); - // Attempt to get the language from a cookie - const lang = cookie.get(LANG_COOKIE); - if (isNotEmpty(lang)) { - // Cookie found - // Use the language from the cookie - translate.use(lang); - } else { - // Cookie not found - // Attempt to get the browser language from the user - if (translate.getLangs().includes(translate.getBrowserLang())) { - translate.use(translate.getBrowserLang()); - } else { - translate.use(config.defaultLanguage); - } - } + // set the current language code + this.localeService.setCurrentLanguageCode(); angulartics2GoogleAnalytics.startTracking(); angulartics2DSpace.startTracking(); @@ -130,15 +120,15 @@ export class AppComponent implements OnInit, AfterViewInit { // More information on this bug-fix: https://blog.angular-university.io/angular-debugging/ delay(0) ).subscribe((event) => { - if (event instanceof NavigationStart) { - this.isLoading$.next(true); - } else if ( - event instanceof NavigationEnd || - event instanceof NavigationCancel - ) { - this.isLoading$.next(false); - } - }); + if (event instanceof NavigationStart) { + this.isLoading$.next(true); + } else if ( + event instanceof NavigationEnd || + event instanceof NavigationCancel + ) { + this.isLoading$.next(false); + } + }); } @HostListener('window:resize', ['$event']) diff --git a/src/app/core/locale/locale.service.spec.ts b/src/app/core/locale/locale.service.spec.ts new file mode 100644 index 0000000000..c17cdd8089 --- /dev/null +++ b/src/app/core/locale/locale.service.spec.ts @@ -0,0 +1,106 @@ +import { async, TestBed } from '@angular/core/testing'; + +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; + +import { CookieService } from '../services/cookie.service'; +import { MockCookieService } from '../../shared/mocks/mock-cookie.service'; +import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; +import { LANG_COOKIE, LocaleService } from './locale.service'; + +describe('LocaleService test suite', () => { + let service: LocaleService; + let serviceAsAny: any; + let cookieService: CookieService; + let translateService: TranslateService; + let spyOnGet; + let spyOnSet; + + const config: any = { + defaultLanguage: 'en' + }; + + const langList = ['en', 'it', 'de']; + + beforeEach(async(() => { + return TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), + ], + providers: [ + { provide: CookieService, useValue: new MockCookieService() }, + ] + }); + })); + + beforeEach(() => { + cookieService = TestBed.get(CookieService); + translateService = TestBed.get(TranslateService); + service = new LocaleService(config, cookieService, translateService); + serviceAsAny = service; + spyOnGet = spyOn(cookieService, 'get'); + spyOnSet = spyOn(cookieService, 'set'); + }); + + describe('getCurrentLanguageCode', () => { + it('should return language saved on cookie', () => { + spyOnGet.and.returnValue('de'); + expect(service.getCurrentLanguageCode()).toBe('de'); + }); + + describe('', () => { + beforeEach(() => { + spyOn(translateService, 'getLangs').and.returnValue(langList); + }); + + it('should return language from browser setting', () => { + spyOn(translateService, 'getBrowserLang').and.returnValue('it'); + expect(service.getCurrentLanguageCode()).toBe('it'); + }); + + it('should return default language from config', () => { + spyOn(translateService, 'getBrowserLang').and.returnValue('fr'); + expect(service.getCurrentLanguageCode()).toBe('en'); + }); + }); + }); + + describe('getLanguageCodeFromCookie', () => { + it('should return language from cookie', () => { + spyOnGet.and.returnValue('de'); + expect(service.getLanguageCodeFromCookie()).toBe('de'); + }); + + }); + + describe('saveLanguageCodeToCookie', () => { + it('should save language to cookie', () => { + service.saveLanguageCodeToCookie('en'); + expect(spyOnSet).toHaveBeenCalledWith(LANG_COOKIE, 'en'); + }); + }); + + describe('setCurrentLanguageCode', () => { + beforeEach(() => { + spyOn(service, 'saveLanguageCodeToCookie'); + spyOn(translateService, 'use'); + }); + + it('should set the given language', () => { + service.setCurrentLanguageCode('it'); + expect(translateService.use).toHaveBeenCalledWith( 'it'); + expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('it'); + }); + + it('should set the current language', () => { + spyOn(service, 'getCurrentLanguageCode').and.returnValue('es'); + service.setCurrentLanguageCode(); + expect(translateService.use).toHaveBeenCalledWith( 'es'); + expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('es'); + }); + }); +}); diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts new file mode 100644 index 0000000000..f70d7f8765 --- /dev/null +++ b/src/app/core/locale/locale.service.ts @@ -0,0 +1,76 @@ +import { Inject, Injectable } from '@angular/core'; + +import { TranslateService } from '@ngx-translate/core'; + +import { isEmpty } from '../../shared/empty.util'; +import { CookieService } from '../services/cookie.service'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; + +export const LANG_COOKIE = 'language_cookie'; + +/** + * Service to provide localization handler + */ +@Injectable({ + providedIn: 'root' +}) +export class LocaleService { + + constructor( + @Inject(GLOBAL_CONFIG) public config: GlobalConfig, + private cookie: CookieService, + private translate: TranslateService) { + } + + /** + * Get the language currently used + * + * @returns {string} The language code + */ + getCurrentLanguageCode(): string { + // Attempt to get the language from a cookie + let lang = this.getLanguageCodeFromCookie(); + if (isEmpty(lang)) { + // Cookie not found + // Attempt to get the browser language from the user + if (this.translate.getLangs().includes(this.translate.getBrowserLang())) { + lang = this.translate.getBrowserLang(); + } else { + lang = this.config.defaultLanguage; + } + } + + return lang; + } + + /** + * Retrieve the language from a cookie + */ + getLanguageCodeFromCookie(): string { + return this.cookie.get(LANG_COOKIE); + } + + /** + * Set the language currently used + * + * @param lang + * The language to save + */ + saveLanguageCodeToCookie(lang: string): void { + this.cookie.set(LANG_COOKIE, lang); + } + + /** + * Set the language currently used + * + * @param lang + * The language to set, if it's not provided retrieve default one + */ + setCurrentLanguageCode(lang?: string): void { + if (isEmpty(lang)) { + lang = this.getCurrentLanguageCode() + } + this.translate.use(lang); + this.saveLanguageCodeToCookie(lang); + } +} diff --git a/src/app/shared/lang-switch/lang-switch.component.spec.ts b/src/app/shared/lang-switch/lang-switch.component.spec.ts index 3d7aca46b6..b0f242a2e0 100644 --- a/src/app/shared/lang-switch/lang-switch.component.spec.ts +++ b/src/app/shared/lang-switch/lang-switch.component.spec.ts @@ -1,14 +1,15 @@ -import {LangSwitchComponent} from './lang-switch.component'; -import {async, ComponentFixture, TestBed} from '@angular/core/testing'; -import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core'; -import {TranslateLoader, TranslateModule, TranslateService} from '@ngx-translate/core'; -import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; -import { GLOBAL_CONFIG } from '../../../config'; -import {LangConfig} from '../../../config/lang-config.interface'; -import {Observable, of} from 'rxjs'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { MockCookieService } from '../mocks/mock-cookie.service'; -import { CookieService } from '../../core/services/cookie.service'; + +import { Observable, of } from 'rxjs'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { LangSwitchComponent } from './lang-switch.component'; +import { GLOBAL_CONFIG } from '../../../config'; +import { LangConfig } from '../../../config/lang-config.interface'; +import { LocaleService } from '../../core/locale/locale.service'; // This test is completely independent from any message catalogs or keys in the codebase // The translation module is instantiated with these bogus messages that we aren't using anyway. @@ -28,16 +29,19 @@ class CustomLoader implements TranslateLoader { }); } } + /* tslint:enable:quotemark */ /* tslint:enable:object-literal-key-quotes */ -let cookie: CookieService; +let localService: any; describe('LangSwitchComponent', () => { - beforeEach(() => { - cookie = Object.assign(new MockCookieService()); - }); + function getMockLocaleService(): LocaleService { + return jasmine.createSpyObj('LocaleService', { + setCurrentLanguageCode: jasmine.createSpy('setCurrentLanguageCode') + }) + } describe('with English and Deutsch activated, English as default', () => { let component: LangSwitchComponent; @@ -65,7 +69,7 @@ describe('LangSwitchComponent', () => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule, TranslateModule.forRoot( { - loader: {provide: TranslateLoader, useClass: CustomLoader} + loader: { provide: TranslateLoader, useClass: CustomLoader } } )], declarations: [LangSwitchComponent], @@ -73,7 +77,7 @@ describe('LangSwitchComponent', () => { providers: [ TranslateService, { provide: GLOBAL_CONFIG, useValue: mockConfig }, - { provide: CookieService, useValue: cookie } + { provide: LocaleService, useValue: getMockLocaleService() }, ] }).compileComponents() .then(() => { @@ -83,6 +87,7 @@ describe('LangSwitchComponent', () => { translate.use('en'); http = TestBed.get(HttpTestingController); fixture = TestBed.createComponent(LangSwitchComponent); + localService = TestBed.get(LocaleService); component = fixture.componentInstance; de = fixture.debugElement; langSwitchElement = de.nativeElement; @@ -111,19 +116,15 @@ describe('LangSwitchComponent', () => { describe('when selecting a language', () => { beforeEach(() => { spyOn(translate, 'use'); - spyOn(cookie, 'set'); const langItem = fixture.debugElement.query(By.css('.dropdown-item')).nativeElement; langItem.click(); fixture.detectChanges(); }); - it('should translate the app', () => { - expect(translate.use).toHaveBeenCalled(); + it('should translate the app and set the client\'s language cookie', () => { + expect(localService.setCurrentLanguageCode).toHaveBeenCalled(); }); - it('should set the client\'s language cookie', () => { - expect(cookie.set).toHaveBeenCalled(); - }); }); }); @@ -154,7 +155,7 @@ describe('LangSwitchComponent', () => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule, TranslateModule.forRoot( { - loader: {provide: TranslateLoader, useClass: CustomLoader} + loader: { provide: TranslateLoader, useClass: CustomLoader } } )], declarations: [LangSwitchComponent], @@ -162,7 +163,7 @@ describe('LangSwitchComponent', () => { providers: [ TranslateService, { provide: GLOBAL_CONFIG, useValue: mockConfig }, - { provide: CookieService, useValue: cookie } + { provide: LocaleService, useValue: getMockLocaleService() }, ] }).compileComponents(); translate = TestBed.get(TranslateService); diff --git a/src/app/shared/lang-switch/lang-switch.component.ts b/src/app/shared/lang-switch/lang-switch.component.ts index e91ed5c9a2..ee301c8d54 100644 --- a/src/app/shared/lang-switch/lang-switch.component.ts +++ b/src/app/shared/lang-switch/lang-switch.component.ts @@ -1,9 +1,10 @@ -import {Component, Inject, OnInit} from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; + +import { TranslateService } from '@ngx-translate/core'; + import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; -import {TranslateService} from '@ngx-translate/core'; -import {LangConfig} from '../../../config/lang-config.interface'; -import { LANG_COOKIE } from '../../app.component'; -import { CookieService } from '../../core/services/cookie.service'; +import { LangConfig } from '../../../config/lang-config.interface'; +import { LocaleService } from '../../core/locale/locale.service'; @Component({ selector: 'ds-lang-switch', @@ -26,7 +27,7 @@ export class LangSwitchComponent implements OnInit { constructor( @Inject(GLOBAL_CONFIG) public config: GlobalConfig, public translate: TranslateService, - public cookie: CookieService + private localeService: LocaleService ) { } @@ -54,8 +55,7 @@ export class LangSwitchComponent implements OnInit { * @param lang The language to switch to */ useLang(lang: string) { - this.translate.use(lang); - this.cookie.set(LANG_COOKIE, lang); + this.localeService.setCurrentLanguageCode(lang); } } From 616a5fe0bdf35f8e3cb7c7a3c409dfb58322266b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 20 Apr 2020 20:07:24 +0200 Subject: [PATCH 02/17] Added LocaleInterceptor to set Accept-Language header --- src/app/core/core.module.ts | 7 ++ .../core/locale/locale.interceptor.spec.ts | 70 +++++++++++++++++++ src/app/core/locale/locale.interceptor.ts | 31 ++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/app/core/locale/locale.interceptor.spec.ts create mode 100644 src/app/core/locale/locale.interceptor.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 783b169291..3c25ff026b 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -143,6 +143,7 @@ import { Version } from './shared/version.model'; import { VersionHistory } from './shared/version-history.model'; import { WorkflowActionDataService } from './data/workflow-action-data.service'; import { WorkflowAction } from './tasks/models/workflow-action-object.model'; +import { LocaleInterceptor } from './locale/locale.interceptor'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -272,6 +273,12 @@ const PROVIDERS = [ useClass: AuthInterceptor, multi: true }, + // register LocaleInterceptor as HttpInterceptor + { + provide: HTTP_INTERCEPTORS, + useClass: LocaleInterceptor, + multi: true + }, NotificationsService, FilteredDiscoveryPageResponseParsingService, { provide: NativeWindowService, useFactory: NativeWindowFactory } diff --git a/src/app/core/locale/locale.interceptor.spec.ts b/src/app/core/locale/locale.interceptor.spec.ts new file mode 100644 index 0000000000..952cbf9952 --- /dev/null +++ b/src/app/core/locale/locale.interceptor.spec.ts @@ -0,0 +1,70 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController, } from '@angular/common/http/testing'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; + +import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service'; +import { RestRequestMethod } from '../data/rest-request-method'; +import { LocaleService } from './locale.service'; +import { LocaleInterceptor } from './locale.interceptor'; + +describe(`LocaleInterceptor`, () => { + let service: DSpaceRESTv2Service; + let httpMock: HttpTestingController; + let localeService: any; + + function getMockLocaleService(): LocaleService { + return jasmine.createSpyObj('LocaleService', { + getCurrentLanguageCode: jasmine.createSpy('getCurrentLanguageCode') + }) + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DSpaceRESTv2Service, + { + provide: HTTP_INTERCEPTORS, + useClass: LocaleInterceptor, + multi: true, + }, + {provide: LocaleService, useValue: getMockLocaleService()}, + ], + }); + + service = TestBed.get(DSpaceRESTv2Service); + httpMock = TestBed.get(HttpTestingController); + localeService = TestBed.get(LocaleService); + + localeService.getCurrentLanguageCode.and.returnValue('en') + }); + + describe('', () => { + + it('should add an Accept-Language header when we’re sending an HTTP POST request', () => { + service.request(RestRequestMethod.POST, 'server/api/submission/workspaceitems', 'test').subscribe((response) => { + expect(response).toBeTruthy(); + }); + + const httpRequest = httpMock.expectOne(`server/api/submission/workspaceitems`); + + expect(httpRequest.request.headers.has('Accept-Language')); + const lang = httpRequest.request.headers.get('Accept-Language'); + expect(lang).toBe('en'); + }); + + it('should add an Accept-Language header when we’re sending an HTTP GET request', () => { + service.request(RestRequestMethod.GET, 'server/api/submission/workspaceitems/123').subscribe((response) => { + expect(response).toBeTruthy(); + }); + + const httpRequest = httpMock.expectOne(`server/api/submission/workspaceitems/123`); + + expect(httpRequest.request.headers.has('Accept-Language')); + const lang = httpRequest.request.headers.get('Accept-Language'); + expect(lang).toBe('en'); + }); + + }); + +}); diff --git a/src/app/core/locale/locale.interceptor.ts b/src/app/core/locale/locale.interceptor.ts new file mode 100644 index 0000000000..c2a10f5b25 --- /dev/null +++ b/src/app/core/locale/locale.interceptor.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; + +import { Observable } from 'rxjs'; + +import { LocaleService } from './locale.service'; + +@Injectable() +export class LocaleInterceptor implements HttpInterceptor { + + constructor(private localeService: LocaleService) { + } + + /** + * Intercept method + * @param req + * @param next + */ + intercept(req: HttpRequest, next: HttpHandler): Observable> { + let newReq: HttpRequest; + + // Clone the request to add the new header. + newReq = req.clone({ + headers: req.headers + .set('Accept-Language', this.localeService.getCurrentLanguageCode()) + }); + + // Pass on the new request instead of the original request. + return next.handle(newReq); + } +} From 889a91ba522bfa051713029824141fa1fe3180ec Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 11 May 2020 20:28:31 +0200 Subject: [PATCH 03/17] Fixed merge --- src/app/core/locale/locale.service.spec.ts | 14 +++++--------- src/app/core/locale/locale.service.ts | 7 +++---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/app/core/locale/locale.service.spec.ts b/src/app/core/locale/locale.service.spec.ts index c17cdd8089..1c003b5deb 100644 --- a/src/app/core/locale/locale.service.spec.ts +++ b/src/app/core/locale/locale.service.spec.ts @@ -3,8 +3,8 @@ import { async, TestBed } from '@angular/core/testing'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { CookieService } from '../services/cookie.service'; -import { MockCookieService } from '../../shared/mocks/mock-cookie.service'; -import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; +import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; +import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { LANG_COOKIE, LocaleService } from './locale.service'; describe('LocaleService test suite', () => { @@ -15,10 +15,6 @@ describe('LocaleService test suite', () => { let spyOnGet; let spyOnSet; - const config: any = { - defaultLanguage: 'en' - }; - const langList = ['en', 'it', 'de']; beforeEach(async(() => { @@ -27,12 +23,12 @@ describe('LocaleService test suite', () => { TranslateModule.forRoot({ loader: { provide: TranslateLoader, - useClass: MockTranslateLoader + useClass: TranslateLoaderMock } }), ], providers: [ - { provide: CookieService, useValue: new MockCookieService() }, + { provide: CookieService, useValue: new CookieServiceMock() }, ] }); })); @@ -40,7 +36,7 @@ describe('LocaleService test suite', () => { beforeEach(() => { cookieService = TestBed.get(CookieService); translateService = TestBed.get(TranslateService); - service = new LocaleService(config, cookieService, translateService); + service = new LocaleService(cookieService, translateService); serviceAsAny = service; spyOnGet = spyOn(cookieService, 'get'); spyOnSet = spyOn(cookieService, 'set'); diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts index f70d7f8765..3c181e0ab3 100644 --- a/src/app/core/locale/locale.service.ts +++ b/src/app/core/locale/locale.service.ts @@ -1,10 +1,10 @@ -import { Inject, Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { isEmpty } from '../../shared/empty.util'; import { CookieService } from '../services/cookie.service'; -import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { environment } from '../../../environments/environment'; export const LANG_COOKIE = 'language_cookie'; @@ -17,7 +17,6 @@ export const LANG_COOKIE = 'language_cookie'; export class LocaleService { constructor( - @Inject(GLOBAL_CONFIG) public config: GlobalConfig, private cookie: CookieService, private translate: TranslateService) { } @@ -36,7 +35,7 @@ export class LocaleService { if (this.translate.getLangs().includes(this.translate.getBrowserLang())) { lang = this.translate.getBrowserLang(); } else { - lang = this.config.defaultLanguage; + lang = environment.defaultLanguage; } } From d66d5b6a4663acbea6e5f7ee49906b28fe476de6 Mon Sep 17 00:00:00 2001 From: Danilo Di Nuzzo Date: Mon, 22 Jun 2020 17:54:37 +0200 Subject: [PATCH 04/17] [CST-2877] done (angular) --- .../core/locale/locale.interceptor.spec.ts | 20 +++-- src/app/core/locale/locale.interceptor.ts | 22 +++-- src/app/core/locale/locale.service.spec.ts | 24 ++++- src/app/core/locale/locale.service.ts | 89 ++++++++++++++++++- 4 files changed, 133 insertions(+), 22 deletions(-) diff --git a/src/app/core/locale/locale.interceptor.spec.ts b/src/app/core/locale/locale.interceptor.spec.ts index 952cbf9952..4f45c4765a 100644 --- a/src/app/core/locale/locale.interceptor.spec.ts +++ b/src/app/core/locale/locale.interceptor.spec.ts @@ -6,17 +6,19 @@ import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service'; import { RestRequestMethod } from '../data/rest-request-method'; import { LocaleService } from './locale.service'; import { LocaleInterceptor } from './locale.interceptor'; +import { of } from 'rxjs'; describe(`LocaleInterceptor`, () => { let service: DSpaceRESTv2Service; let httpMock: HttpTestingController; let localeService: any; - function getMockLocaleService(): LocaleService { - return jasmine.createSpyObj('LocaleService', { - getCurrentLanguageCode: jasmine.createSpy('getCurrentLanguageCode') - }) - } + const languageList = ['en;q=1', 'it;q=0.9', 'de;q=0.8', 'fr;q=0.7']; + + const mockLocaleService = jasmine.createSpyObj('LocaleService', { + getCurrentLanguageCode: jasmine.createSpy('getCurrentLanguageCode'), + getLanguageCodeList: of(languageList) + }) beforeEach(() => { TestBed.configureTestingModule({ @@ -28,7 +30,7 @@ describe(`LocaleInterceptor`, () => { useClass: LocaleInterceptor, multi: true, }, - {provide: LocaleService, useValue: getMockLocaleService()}, + {provide: LocaleService, useValue: mockLocaleService}, ], }); @@ -50,7 +52,8 @@ describe(`LocaleInterceptor`, () => { expect(httpRequest.request.headers.has('Accept-Language')); const lang = httpRequest.request.headers.get('Accept-Language'); - expect(lang).toBe('en'); + expect(lang).toBeDefined(); + expect(lang).toBe(languageList.toString()); }); it('should add an Accept-Language header when we’re sending an HTTP GET request', () => { @@ -62,7 +65,8 @@ describe(`LocaleInterceptor`, () => { expect(httpRequest.request.headers.has('Accept-Language')); const lang = httpRequest.request.headers.get('Accept-Language'); - expect(lang).toBe('en'); + expect(lang).toBeDefined(); + expect(lang).toBe(languageList.toString()); }); }); diff --git a/src/app/core/locale/locale.interceptor.ts b/src/app/core/locale/locale.interceptor.ts index c2a10f5b25..fd32e5c5d4 100644 --- a/src/app/core/locale/locale.interceptor.ts +++ b/src/app/core/locale/locale.interceptor.ts @@ -4,6 +4,7 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/c import { Observable } from 'rxjs'; import { LocaleService } from './locale.service'; +import { reduce, mergeMap, scan } from 'rxjs/operators'; @Injectable() export class LocaleInterceptor implements HttpInterceptor { @@ -18,14 +19,17 @@ export class LocaleInterceptor implements HttpInterceptor { */ intercept(req: HttpRequest, next: HttpHandler): Observable> { let newReq: HttpRequest; - - // Clone the request to add the new header. - newReq = req.clone({ - headers: req.headers - .set('Accept-Language', this.localeService.getCurrentLanguageCode()) - }); - - // Pass on the new request instead of the original request. - return next.handle(newReq); + return this.localeService.getLanguageCodeList() + .pipe( + scan((acc: any, value: any) => [...acc, ...value], []), + mergeMap((languages) => { + // Clone the request to add the new header. + newReq = req.clone({ + headers: req.headers + .set('Accept-Language', languages.toString()) + }); + // Pass on the new request instead of the original request. + return next.handle(newReq); + })) } } diff --git a/src/app/core/locale/locale.service.spec.ts b/src/app/core/locale/locale.service.spec.ts index 1c003b5deb..1310653bce 100644 --- a/src/app/core/locale/locale.service.spec.ts +++ b/src/app/core/locale/locale.service.spec.ts @@ -5,13 +5,16 @@ import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-transla import { CookieService } from '../services/cookie.service'; import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; -import { LANG_COOKIE, LocaleService } from './locale.service'; +import { LANG_COOKIE, LocaleService, LANG_ORIGIN } from './locale.service'; +import { AuthService } from '../auth/auth.service'; +import { AuthServiceMock } from 'src/app/shared/mocks/auth.service.mock'; describe('LocaleService test suite', () => { let service: LocaleService; let serviceAsAny: any; let cookieService: CookieService; let translateService: TranslateService; + let authService: AuthService; let spyOnGet; let spyOnSet; @@ -29,6 +32,7 @@ describe('LocaleService test suite', () => { ], providers: [ { provide: CookieService, useValue: new CookieServiceMock() }, + { provide: AuthService, userValue: AuthServiceMock } ] }); })); @@ -36,7 +40,8 @@ describe('LocaleService test suite', () => { beforeEach(() => { cookieService = TestBed.get(CookieService); translateService = TestBed.get(TranslateService); - service = new LocaleService(cookieService, translateService); + authService = TestBed.get(TranslateService); + service = new LocaleService(cookieService, translateService, authService); serviceAsAny = service; spyOnGet = spyOn(cookieService, 'get'); spyOnSet = spyOn(cookieService, 'set'); @@ -99,4 +104,19 @@ describe('LocaleService test suite', () => { expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('es'); }); }); + + describe('', () => { + it('should set quality to current language list', () => { + const langListWithQuality = ['en;q=1', 'it;q=0.9', 'de;q=0.8']; + spyOn(service, 'setQuality').and.returnValue(langListWithQuality); + service.setQuality(langList, LANG_ORIGIN.BROWSER, false); + expect(service.setQuality).toHaveBeenCalledWith(langList, LANG_ORIGIN.BROWSER, false); + }); + + it('should return the list of language with quality factor', () => { + spyOn(service, 'getLanguageCodeList'); + service.getLanguageCodeList(); + expect(service.getLanguageCodeList).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts index 3c181e0ab3..ee93f7d790 100644 --- a/src/app/core/locale/locale.service.ts +++ b/src/app/core/locale/locale.service.ts @@ -2,12 +2,28 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { isEmpty } from '../../shared/empty.util'; +import { isEmpty, hasValue } from '../../shared/empty.util'; import { CookieService } from '../services/cookie.service'; import { environment } from '../../../environments/environment'; +import { AuthService } from '../auth/auth.service'; +import { BehaviorSubject, Observable } from 'rxjs'; export const LANG_COOKIE = 'language_cookie'; +/** + * This enum defines the possible origin of the languages + */ +export enum LANG_ORIGIN { + UI, + EPERSON, + BROWSER +}; + +/** + * Eperson language metadata + */ +const EPERSON_LANG_METADATA = 'eperson.language'; + /** * Service to provide localization handler */ @@ -18,7 +34,8 @@ export class LocaleService { constructor( private cookie: CookieService, - private translate: TranslateService) { + private translate: TranslateService, + private authService: AuthService) { } /** @@ -38,10 +55,44 @@ export class LocaleService { lang = environment.defaultLanguage; } } - return lang; } + getLanguageCodeList(): Observable { + const bs = new BehaviorSubject([]); + // check if user has set preferred language in UI + if (this.translate.currentLang) { + bs.next( + this.setQuality( + [this.translate.currentLang], + LANG_ORIGIN.UI, + false) + ); + } + // check if user is loggedIn and has language associated + this.authService.getAuthenticatedUserFromStore().subscribe( + (eperson) => { + const ePersonLang = eperson.firstMetadataValue(EPERSON_LANG_METADATA); + bs.next( + this.setQuality( + [ePersonLang], + LANG_ORIGIN.EPERSON, + !isEmpty(this.translate.currentLang)) + ); + } + ); + // get the browser languages + if (navigator.languages) { + bs.next( + this.setQuality( + Object.assign([], navigator.languages), + LANG_ORIGIN.BROWSER, + !isEmpty(this.translate.currentLang)) + ); + } + return bs; + } + /** * Retrieve the language from a cookie */ @@ -72,4 +123,36 @@ export class LocaleService { this.translate.use(lang); this.saveLanguageCodeToCookie(lang); } + + /** + * Set the quality factor for all element of input array. + * Returns a new array that contains the languages list with the quality value. + * The quality factor indicate the relative degree of preference for the language + * @param languages the languages list + * @param origin + * @param hasOther + */ + setQuality(languages: string[], origin: LANG_ORIGIN, hasOther: boolean): string[] { + const langWithPrior = []; + let idx = 0; + const v = languages.length > 10 ? languages.length : 10; + let divisor: number; + switch (origin) { + case LANG_ORIGIN.EPERSON: + divisor = 2; break; + case LANG_ORIGIN.BROWSER: + divisor = (hasOther ? 10 : 1); break; + default: + divisor = 1; + } + languages.forEach( (lang) => { + let value = lang + ';q='; + let quality = (v - idx++) / v; + quality = ((languages.length > 10) ? quality.toFixed(2) : quality) as number; + value += quality / divisor; + langWithPrior.push(value); + }); + return langWithPrior; + } + } From ea5f727449e7ec3c712569946fb6c37eb1257cf5 Mon Sep 17 00:00:00 2001 From: Danilo Di Nuzzo Date: Wed, 24 Jun 2020 10:55:13 +0200 Subject: [PATCH 05/17] [CST-2877] fix method for lang list & add reload after change lang --- src/app/core/auth/auth.service.spec.ts | 6 + src/app/core/auth/auth.service.ts | 13 ++- src/app/core/locale/locale.service.spec.ts | 5 +- src/app/core/locale/locale.service.ts | 104 ++++++++++++------ .../lang-switch/lang-switch.component.spec.ts | 6 +- .../lang-switch/lang-switch.component.ts | 1 + 6 files changed, 95 insertions(+), 40 deletions(-) diff --git a/src/app/core/auth/auth.service.spec.ts b/src/app/core/auth/auth.service.spec.ts index 3b6fae4dd1..a884eba7bb 100644 --- a/src/app/core/auth/auth.service.spec.ts +++ b/src/app/core/auth/auth.service.spec.ts @@ -230,6 +230,12 @@ describe('AuthService test', () => { expect(result).toBe(false); }); + it('should return true when authentication is loaded', () => { + authService.isAuthenticationLoaded().subscribe((status: boolean) => { + expect(status).toBe(true); + }); + }); + }); describe('', () => { diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 588d9e2675..bacbfede10 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -21,7 +21,8 @@ import { getAuthenticationToken, getRedirectUrl, isAuthenticated, - isTokenRefreshing + isTokenRefreshing, + isAuthenticatedLoaded } from './selectors'; import { AppState, routerStateSelector } from '../../app.reducer'; import { @@ -148,6 +149,14 @@ export class AuthService { return this.store.pipe(select(isAuthenticated)); } + /** + * Determines if authentication is loaded + * @returns {Observable} + */ + public isAuthenticationLoaded(): Observable { + return this.store.pipe(select(isAuthenticatedLoaded)); + } + /** * Returns the href link to authenticated user * @returns {string} @@ -197,7 +206,7 @@ export class AuthService { return this.store.pipe( select(getAuthenticatedUserId), hasValueOperator(), - switchMap((id: string) => this.epersonService.findById(id)), + switchMap((id: string) => { console.log('ID: ', id); return this.epersonService.findById(id) }), getAllSucceededRemoteDataPayload() ) } diff --git a/src/app/core/locale/locale.service.spec.ts b/src/app/core/locale/locale.service.spec.ts index 1310653bce..67a4d61bf3 100644 --- a/src/app/core/locale/locale.service.spec.ts +++ b/src/app/core/locale/locale.service.spec.ts @@ -8,6 +8,7 @@ import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { LANG_COOKIE, LocaleService, LANG_ORIGIN } from './locale.service'; import { AuthService } from '../auth/auth.service'; import { AuthServiceMock } from 'src/app/shared/mocks/auth.service.mock'; +import { NativeWindowRef } from '../services/window.service'; describe('LocaleService test suite', () => { let service: LocaleService; @@ -15,6 +16,7 @@ describe('LocaleService test suite', () => { let cookieService: CookieService; let translateService: TranslateService; let authService: AuthService; + let window; let spyOnGet; let spyOnSet; @@ -41,7 +43,8 @@ describe('LocaleService test suite', () => { cookieService = TestBed.get(CookieService); translateService = TestBed.get(TranslateService); authService = TestBed.get(TranslateService); - service = new LocaleService(cookieService, translateService, authService); + window = new NativeWindowRef(); + service = new LocaleService(window, cookieService, translateService, authService); serviceAsAny = service; spyOnGet = spyOn(cookieService, 'get'); spyOnSet = spyOn(cookieService, 'set'); diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts index ee93f7d790..f4343b953a 100644 --- a/src/app/core/locale/locale.service.ts +++ b/src/app/core/locale/locale.service.ts @@ -1,12 +1,14 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { isEmpty, hasValue } from '../../shared/empty.util'; +import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CookieService } from '../services/cookie.service'; import { environment } from '../../../environments/environment'; import { AuthService } from '../auth/auth.service'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { Observable, of as observableOf, combineLatest } from 'rxjs'; +import { map, take, flatMap, tap } from 'rxjs/operators'; +import { NativeWindowService, NativeWindowRef } from '../services/window.service'; export const LANG_COOKIE = 'language_cookie'; @@ -33,6 +35,7 @@ const EPERSON_LANG_METADATA = 'eperson.language'; export class LocaleService { constructor( + @Inject(NativeWindowService) protected _window: NativeWindowRef, private cookie: CookieService, private translate: TranslateService, private authService: AuthService) { @@ -58,39 +61,61 @@ export class LocaleService { return lang; } + /** + * Get the languages list of the user in Accept-Language format + * + * @returns {Observable} + */ getLanguageCodeList(): Observable { - const bs = new BehaviorSubject([]); - // check if user has set preferred language in UI - if (this.translate.currentLang) { - bs.next( - this.setQuality( - [this.translate.currentLang], - LANG_ORIGIN.UI, - false) - ); - } - // check if user is loggedIn and has language associated - this.authService.getAuthenticatedUserFromStore().subscribe( - (eperson) => { - const ePersonLang = eperson.firstMetadataValue(EPERSON_LANG_METADATA); - bs.next( - this.setQuality( - [ePersonLang], - LANG_ORIGIN.EPERSON, - !isEmpty(this.translate.currentLang)) + const obs$ = combineLatest([ + this.authService.isAuthenticated(), + this.authService.isAuthenticationLoaded() + ]); + + return obs$.pipe( + take(1), + flatMap(([isAuthenticated, isLoaded]) => { + let epersonLang$: Observable = observableOf([]); + if (isAuthenticated && isLoaded) { + epersonLang$ = this.authService.getAuthenticatedUserFromStore().pipe( + take(1), + map((eperson) => { + const languages: string[] = []; + const ePersonLang = eperson.firstMetadataValue(EPERSON_LANG_METADATA); + if (ePersonLang) { + languages.push(...this.setQuality( + [ePersonLang], + LANG_ORIGIN.EPERSON, + !isEmpty(this.translate.currentLang))); + } + return languages; + }) ); - } + } + return epersonLang$.pipe( + map((epersonLang: string[]) => { + const languages: string[] = []; + if (this.translate.currentLang) { + languages.push(...this.setQuality( + [this.translate.currentLang], + LANG_ORIGIN.UI, + false)); + } + if (isNotEmpty(epersonLang)) { + languages.push(...epersonLang); + } + if (navigator.languages) { + languages.push(...this.setQuality( + Object.assign([], navigator.languages), + LANG_ORIGIN.BROWSER, + !isEmpty(this.translate.currentLang)) + ); + } + return languages; + }) + ) + }) ); - // get the browser languages - if (navigator.languages) { - bs.next( - this.setQuality( - Object.assign([], navigator.languages), - LANG_ORIGIN.BROWSER, - !isEmpty(this.translate.currentLang)) - ); - } - return bs; } /** @@ -129,8 +154,8 @@ export class LocaleService { * Returns a new array that contains the languages list with the quality value. * The quality factor indicate the relative degree of preference for the language * @param languages the languages list - * @param origin - * @param hasOther + * @param origin origin of language list (UI, EPERSON, BROWSER) + * @param hasOther true if contains other language, false otherwise */ setQuality(languages: string[], origin: LANG_ORIGIN, hasOther: boolean): string[] { const langWithPrior = []; @@ -155,4 +180,13 @@ export class LocaleService { return langWithPrior; } + /** + * Refresh route navigated + */ + public refreshAfterChangeLanguage() { + // Hard redirect to the reload page with a unique number behind it + // so that all state is definitely lost + this._window.nativeWindow.location.href = `/reload/${new Date().getTime()}`; + } + } diff --git a/src/app/shared/lang-switch/lang-switch.component.spec.ts b/src/app/shared/lang-switch/lang-switch.component.spec.ts index c7d785a9e8..072f1bac40 100644 --- a/src/app/shared/lang-switch/lang-switch.component.spec.ts +++ b/src/app/shared/lang-switch/lang-switch.component.spec.ts @@ -34,11 +34,12 @@ class CustomLoader implements TranslateLoader { let localService: any; -describe('LangSwitchComponent', () => { +fdescribe('LangSwitchComponent', () => { function getMockLocaleService(): LocaleService { return jasmine.createSpyObj('LocaleService', { - setCurrentLanguageCode: jasmine.createSpy('setCurrentLanguageCode') + setCurrentLanguageCode: jasmine.createSpy('setCurrentLanguageCode'), + refreshAfterChangeLanguage: jasmine.createSpy('refreshAfterChangeLanguage') }) } @@ -121,6 +122,7 @@ describe('LangSwitchComponent', () => { it('should translate the app and set the client\'s language cookie', () => { expect(localService.setCurrentLanguageCode).toHaveBeenCalled(); + expect(localService.refreshAfterChangeLanguage).toHaveBeenCalled(); }); }); diff --git a/src/app/shared/lang-switch/lang-switch.component.ts b/src/app/shared/lang-switch/lang-switch.component.ts index 5a215cf42a..e28b1d4bb7 100644 --- a/src/app/shared/lang-switch/lang-switch.component.ts +++ b/src/app/shared/lang-switch/lang-switch.component.ts @@ -55,6 +55,7 @@ export class LangSwitchComponent implements OnInit { */ useLang(lang: string) { this.localeService.setCurrentLanguageCode(lang); + this.localeService.refreshAfterChangeLanguage(); } } From c3192047e1c0769c77c6353c9634d94715f0fa04 Mon Sep 17 00:00:00 2001 From: Danilo Di Nuzzo Date: Wed, 24 Jun 2020 12:32:33 +0200 Subject: [PATCH 06/17] [CST-2877] fix tests --- src/app/shared/lang-switch/lang-switch.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/lang-switch/lang-switch.component.spec.ts b/src/app/shared/lang-switch/lang-switch.component.spec.ts index 072f1bac40..9b8ee2e343 100644 --- a/src/app/shared/lang-switch/lang-switch.component.spec.ts +++ b/src/app/shared/lang-switch/lang-switch.component.spec.ts @@ -34,7 +34,7 @@ class CustomLoader implements TranslateLoader { let localService: any; -fdescribe('LangSwitchComponent', () => { +describe('LangSwitchComponent', () => { function getMockLocaleService(): LocaleService { return jasmine.createSpyObj('LocaleService', { From 722fdc01248e6dfef21ccb768e289f7fc7af2d00 Mon Sep 17 00:00:00 2001 From: Danilo Di Nuzzo Date: Tue, 30 Jun 2020 16:42:58 +0200 Subject: [PATCH 07/17] [CST-2877] rm browser lang for SSR reqs --- src/app/core/locale/locale.interceptor.ts | 2 +- src/app/core/locale/locale.service.ts | 20 +++---- src/app/core/locale/server-locale.service.ts | 60 ++++++++++++++++++++ src/modules/app/server-app.module.ts | 6 ++ 4 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/app/core/locale/server-locale.service.ts diff --git a/src/app/core/locale/locale.interceptor.ts b/src/app/core/locale/locale.interceptor.ts index fd32e5c5d4..9327db5d38 100644 --- a/src/app/core/locale/locale.interceptor.ts +++ b/src/app/core/locale/locale.interceptor.ts @@ -4,7 +4,7 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/c import { Observable } from 'rxjs'; import { LocaleService } from './locale.service'; -import { reduce, mergeMap, scan } from 'rxjs/operators'; +import { mergeMap, scan } from 'rxjs/operators'; @Injectable() export class LocaleInterceptor implements HttpInterceptor { diff --git a/src/app/core/locale/locale.service.ts b/src/app/core/locale/locale.service.ts index f4343b953a..b7f9314a33 100644 --- a/src/app/core/locale/locale.service.ts +++ b/src/app/core/locale/locale.service.ts @@ -7,7 +7,7 @@ import { CookieService } from '../services/cookie.service'; import { environment } from '../../../environments/environment'; import { AuthService } from '../auth/auth.service'; import { Observable, of as observableOf, combineLatest } from 'rxjs'; -import { map, take, flatMap, tap } from 'rxjs/operators'; +import { map, take, flatMap } from 'rxjs/operators'; import { NativeWindowService, NativeWindowRef } from '../services/window.service'; export const LANG_COOKIE = 'language_cookie'; @@ -21,11 +21,6 @@ export enum LANG_ORIGIN { BROWSER }; -/** - * Eperson language metadata - */ -const EPERSON_LANG_METADATA = 'eperson.language'; - /** * Service to provide localization handler */ @@ -34,11 +29,16 @@ const EPERSON_LANG_METADATA = 'eperson.language'; }) export class LocaleService { + /** + * Eperson language metadata + */ + EPERSON_LANG_METADATA = 'eperson.language'; + constructor( @Inject(NativeWindowService) protected _window: NativeWindowRef, - private cookie: CookieService, - private translate: TranslateService, - private authService: AuthService) { + protected cookie: CookieService, + protected translate: TranslateService, + protected authService: AuthService) { } /** @@ -81,7 +81,7 @@ export class LocaleService { take(1), map((eperson) => { const languages: string[] = []; - const ePersonLang = eperson.firstMetadataValue(EPERSON_LANG_METADATA); + const ePersonLang = eperson.firstMetadataValue(this.EPERSON_LANG_METADATA); if (ePersonLang) { languages.push(...this.setQuality( [ePersonLang], diff --git a/src/app/core/locale/server-locale.service.ts b/src/app/core/locale/server-locale.service.ts new file mode 100644 index 0000000000..b33338240f --- /dev/null +++ b/src/app/core/locale/server-locale.service.ts @@ -0,0 +1,60 @@ +import { LocaleService, LANG_ORIGIN } from './locale.service'; +import { Injectable } from '@angular/core'; +import { Observable, combineLatest, of as observableOf } from 'rxjs'; +import { take, flatMap, map } from 'rxjs/operators'; +import { isNotEmpty, isEmpty } from 'src/app/shared/empty.util'; + +@Injectable() +export class ServerLocaleService extends LocaleService { + + /** + * Get the languages list of the user in Accept-Language format + * + * @returns {Observable} + */ + getLanguageCodeList(): Observable { + const obs$ = combineLatest([ + this.authService.isAuthenticated(), + this.authService.isAuthenticationLoaded() + ]); + + return obs$.pipe( + take(1), + flatMap(([isAuthenticated, isLoaded]) => { + let epersonLang$: Observable = observableOf([]); + if (isAuthenticated && isLoaded) { + epersonLang$ = this.authService.getAuthenticatedUserFromStore().pipe( + take(1), + map((eperson) => { + const languages: string[] = []; + const ePersonLang = eperson.firstMetadataValue(this.EPERSON_LANG_METADATA); + if (ePersonLang) { + languages.push(...this.setQuality( + [ePersonLang], + LANG_ORIGIN.EPERSON, + !isEmpty(this.translate.currentLang))); + } + return languages; + }) + ); + } + return epersonLang$.pipe( + map((epersonLang: string[]) => { + const languages: string[] = []; + if (this.translate.currentLang) { + languages.push(...this.setQuality( + [this.translate.currentLang], + LANG_ORIGIN.UI, + false)); + } + if (isNotEmpty(epersonLang)) { + languages.push(...epersonLang); + } + return languages; + }) + ) + }) + ); + } + +} diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 5abd8e3aa1..6b58d8663a 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -25,6 +25,8 @@ import { ServerSubmissionService } from '../../app/submission/server-submission. import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { Angulartics2RouterlessModule } from 'angulartics2/routerlessmodule'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; +import { ServerLocaleService } from 'src/app/core/locale/server-locale.service'; +import { LocaleService } from 'src/app/core/locale/locale.service'; export function createTranslateLoader() { return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5'); @@ -73,6 +75,10 @@ export function createTranslateLoader() { { provide: SubmissionService, useClass: ServerSubmissionService + }, + { + provide: LocaleService, + useClass: ServerLocaleService } ] }) From d85b0cb1b1771008a1fed27dffe6eee1448bd139 Mon Sep 17 00:00:00 2001 From: Danilo Di Nuzzo Date: Mon, 6 Jul 2020 18:34:03 +0200 Subject: [PATCH 08/17] From a74dafb9598d9797c56cf052b214baaa21b35259 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 7 Jul 2020 09:51:54 +0200 Subject: [PATCH 09/17] reset scroll position on route change --- src/modules/app/browser-app.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index f43074e033..73a49b0211 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -44,6 +44,7 @@ export function getRequest(transferState: TransferState): any { RouterModule.forRoot([], { // enableTracing: true, useHash: false, + scrollPositionRestoration: 'enabled', preloadingStrategy: IdlePreload }), From 797a640814c0e8413b278b3ebbbf9d8844632eaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 09:00:35 +0000 Subject: [PATCH 10/17] Bump npm-registry-fetch from 4.0.3 to 4.0.5 Bumps [npm-registry-fetch](https://github.com/npm/registry-fetch) from 4.0.3 to 4.0.5. - [Release notes](https://github.com/npm/registry-fetch/releases) - [Changelog](https://github.com/npm/npm-registry-fetch/blob/latest/CHANGELOG.md) - [Commits](https://github.com/npm/registry-fetch/commits) Signed-off-by: dependabot[bot] --- yarn.lock | 58 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index c48e53a226..7a38500e9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2264,7 +2264,28 @@ cacache@12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: +cacache@^12.0.0: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^12.0.2, cacache@^12.0.3: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== @@ -4260,9 +4281,9 @@ faye-websocket@~0.11.1: websocket-driver ">=0.5.1" figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figures@^2.0.0: version "2.0.0" @@ -4797,12 +4818,12 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.2: +graceful-fs@^4.1.11, graceful-fs@^4.1.6, graceful-fs@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -4952,11 +4973,16 @@ hoopy@^0.1.4: resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== -hosted-git-info@^2.1.4, hosted-git-info@^2.6.0, hosted-git-info@^2.7.1: +hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: version "2.8.7" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.7.tgz#4d2e0d5248e1cfabc984b0f6a6d75fe36e679511" integrity sha512-ChkjQtKJ3GI6SsI4O5jwr8q8EPrWCnxuc4Tbx+vRI5x6mDOpjKKltNo1lRlszw3xwgTOSns1ZRBiMmmwpcvLxg== +hosted-git-info@^2.7.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -6869,9 +6895,9 @@ no-case@^2.2.0: lower-case "^1.1.1" node-fetch-npm@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" - integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" + integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== dependencies: encoding "^0.1.11" json-parse-better-errors "^1.0.0" @@ -7056,9 +7082,9 @@ npm-pick-manifest@^2.2.3: semver "^5.4.1" npm-registry-fetch@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz#3c2179e39e04f9348b1c2979545951d36bee8766" - integrity sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw== + version "4.0.5" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz#cb87cf7f25bfb048d6c3ee19d115bebf93ea5bfa" + integrity sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q== dependencies: JSONStream "^1.3.4" bluebird "^3.5.1" @@ -9115,9 +9141,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex@^1.1.0: version "1.1.0" From 6cd6c096e391de12e949b00468d82aff2081a523 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 13 Jul 2020 12:22:11 -0500 Subject: [PATCH 11/17] Move coveralls to after_success --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0d65d76f41..e56707a96c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,10 +35,11 @@ before_install: - google-chrome-stable --version install: - # Start up DSpace 7 using the entities database dump + # Start up a test DSpace 7 REST backend using the entities database dump - docker-compose -f ./docker/docker-compose-travis.yml up -d # Use the dspace-cli image to populate the assetstore. Triggers a discovery and oai update - docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli + # Install all local dependencies (retry if initially fails) - travis_retry yarn install before_script: @@ -49,9 +50,14 @@ before_script: #- curl http://localhost:8080/server/ script: + # build app and run all tests - yarn run ci - - cat coverage/dspace-angular-cli/lcov.info | ./node_modules/coveralls/bin/coveralls.js after_script: # Shutdown docker after everything runs - docker-compose -f ./docker/docker-compose-travis.yml down + +# After a successful build and test (see 'script'), send code coverage reports to coveralls.io +# These code coverage reports are generated by the coveralls node module in our package.json +after_success: + - cat coverage/dspace-angular-cli/lcov.info | ./node_modules/coveralls/bin/coveralls.js From e94fbfdcd0c78fb6766dc868b47daac730f2148d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 13 Jul 2020 12:22:31 -0500 Subject: [PATCH 12/17] Allow for any version of coveralls compatible with 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21a89400bf..0bbf156c9d 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "codelyzer": "^5.0.0", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^5.1.1", - "coveralls": "3.0.0", + "coveralls": "^3.0.0", "css-loader": "3.4.0", "cssnano": "^4.1.10", "deep-freeze": "0.0.1", From 4648348bd17a840faae0a1692d4102c768c84e5d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 13 Jul 2020 14:08:47 -0500 Subject: [PATCH 13/17] Remove deprecated "sudo" key. Add new recommended "os" key --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e56707a96c..90d50d1b08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -sudo: required +os: linux dist: bionic language: node_js From 08484d31cab84337debbcc96ffed6ed0159e12c5 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 14 Jul 2020 15:45:33 +0200 Subject: [PATCH 14/17] renamed project to dspace-angular and fixed broken tests --- .travis.yml | 2 +- angular.json | 16 +++++----- karma.conf.js | 2 +- package.json | 4 +-- .../form/process-form.component.spec.ts | 4 +-- yarn.lock | 32 +++++++++---------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90d50d1b08..b3ed67f221 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,4 +60,4 @@ after_script: # After a successful build and test (see 'script'), send code coverage reports to coveralls.io # These code coverage reports are generated by the coveralls node module in our package.json after_success: - - cat coverage/dspace-angular-cli/lcov.info | ./node_modules/coveralls/bin/coveralls.js + - cat coverage/dspace-angular/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/angular.json b/angular.json index 92c0f27d2b..9c55d648b3 100644 --- a/angular.json +++ b/angular.json @@ -3,7 +3,7 @@ "version": 1, "newProjectRoot": "projects", "projects": { - "dspace-angular-cli": { + "dspace-angular": { "projectType": "application", "schematics": { "@schematics/angular:component": { @@ -21,7 +21,7 @@ "path": "./webpack/webpack.common.ts", "mergeStrategies": { "loaders": "prepend" - }, + } }, "outputPath": "dist/browser", "index": "src/index.html", @@ -65,19 +65,19 @@ "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "dspace-angular-cli:build", + "browserTarget": "dspace-angular:build", "port": 4000 }, "configurations": { "production": { - "browserTarget": "dspace-angular-cli:build:production" + "browserTarget": "dspace-angular:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "dspace-angular-cli:build" + "browserTarget": "dspace-angular:build" } }, "test": { @@ -119,11 +119,11 @@ "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "dspace-angular-cli:serve" + "devServerTarget": "dspace-angular:serve" }, "configurations": { "production": { - "devServerTarget": "dspace-angular-cli:serve:production" + "devServerTarget": "dspace-angular:serve:production" } } }, @@ -153,5 +153,5 @@ } } }, - "defaultProject": "dspace-angular-cli" + "defaultProject": "dspace-angular" } diff --git a/karma.conf.js b/karma.conf.js index 9844d65904..a3b6803e6d 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -17,7 +17,7 @@ module.exports = function (config) { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { - dir: require('path').join(__dirname, './coverage/dspace-angular-cli'), + dir: require('path').join(__dirname, './coverage/dspace-angular'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, diff --git a/package.json b/package.json index 0bbf156c9d..bd52f75b4b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "dspace-angular-cli", + "name": "dspace-angular", "version": "0.0.0", "scripts": { "ng": "ng", @@ -23,7 +23,7 @@ "build": "ng build", "build:prod": "yarn run build:ssr", "build:ssr": "yarn run build:client-and-server-bundles && yarn run compile:server", - "build:client-and-server-bundles": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng build --prod && ng run dspace-angular-cli:server:production --bundleDependencies all", + "build:client-and-server-bundles": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng build --prod && ng run dspace-angular:server:production --bundleDependencies all", "test:watch": "npm-run-all --parallel config:test:watch test", "test": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng test --sourceMap=true --watch=true", "test:headless": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng test --watch=false --sourceMap=true --browsers=ChromeHeadless --code-coverage", diff --git a/src/app/process-page/form/process-form.component.spec.ts b/src/app/process-page/form/process-form.component.spec.ts index 12326111da..95f266ed6f 100644 --- a/src/app/process-page/form/process-form.component.spec.ts +++ b/src/app/process-page/form/process-form.component.spec.ts @@ -59,8 +59,8 @@ describe('ProcessFormComponent', () => { providers: [ { provide: ScriptDataService, useValue: scriptService }, { provide: NotificationsService, useClass: NotificationsServiceStub }, - { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeBySubstring']) }, - { provide: Router, useValue: {} }, + { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeBySubstring', 'removeByHrefSubstring']) }, + { provide: Router, useValue: jasmine.createSpyObj('router', ['navigateByUrl']) }, ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/yarn.lock b/yarn.lock index 7a38500e9e..3a36e692d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3061,16 +3061,16 @@ coverage-istanbul-loader@2.0.3: merge-source-map "^1.1.0" schema-utils "^2.6.1" -coveralls@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.0.tgz#22ef730330538080d29b8c151dc9146afde88a99" - integrity sha512-ZppXR9y5PraUOrf/DzHJY6gzNUhXYE3b9D43xEXs4QYZ7/Oe0Gy0CS+IPKWFfvQFXB3RG9QduaQUFehzSpGAFw== +coveralls@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.0.tgz#13c754d5e7a2dd8b44fe5269e21ca394fb4d615b" + integrity sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ== dependencies: - js-yaml "^3.6.1" - lcov-parse "^0.0.10" - log-driver "^1.2.5" - minimist "^1.2.0" - request "^2.79.0" + js-yaml "^3.13.1" + lcov-parse "^1.0.0" + log-driver "^1.2.7" + minimist "^1.2.5" + request "^2.88.2" create-ecdh@^4.0.0: version "4.0.3" @@ -5928,7 +5928,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.6.1: +js-yaml@^3.13.0, js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -6169,10 +6169,10 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -lcov-parse@^0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" - integrity sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM= +lcov-parse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" + integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= less-loader@5.0.0: version "5.0.0" @@ -6324,7 +6324,7 @@ lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -log-driver@^1.2.5: +log-driver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== @@ -8923,7 +8923,7 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request@^2.79.0, request@^2.83.0, request@^2.87.0, request@^2.88.0: +request@^2.83.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== From 1bfc12fe9b9defb62350da7b0a033cb398e63142 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 14 Jul 2020 16:20:52 +0200 Subject: [PATCH 15/17] added travis_wait to yarn ci command --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b3ed67f221..02c17c3e1b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,7 +51,7 @@ before_script: script: # build app and run all tests - - yarn run ci + - travis_wait yarn run ci after_script: # Shutdown docker after everything runs From d558a056aabda5a718fa21465180b83523d5dd7d Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 14 Jul 2020 16:37:26 +0200 Subject: [PATCH 16/17] updates to travis scripts to prevent timeout --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 02c17c3e1b..54b3c4752a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,7 +51,10 @@ before_script: script: # build app and run all tests - - travis_wait yarn run ci + - ng lint + - travis_wait yarn run build:prod + - yarn test:headless + - yarn run e2e:ci after_script: # Shutdown docker after everything runs From 1d7a9cd78b6eb69d5d774d645b7ee4ae2dbf2b64 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 14 Jul 2020 16:39:04 +0200 Subject: [PATCH 17/17] removed unused ci script from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index bd52f75b4b..c1e5b05010 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "e2e:ci": "ng e2e --protractor-config=./e2e/protractor-ci.conf.js", "compile:server": "webpack --config webpack.server.config.js --progress --colors", "serve:ssr": "node dist/server", - "ci": "ng lint && yarn run build:prod && yarn test:headless && yarn run e2e:ci", "clean:coverage": "rimraf coverage", "clean:dist": "rimraf dist", "clean:doc": "rimraf doc",