diff --git a/src/app/core/end-user-agreement/end-user-agreement.guard.spec.ts b/src/app/core/end-user-agreement/end-user-agreement.guard.spec.ts new file mode 100644 index 0000000000..589b227b5a --- /dev/null +++ b/src/app/core/end-user-agreement/end-user-agreement.guard.spec.ts @@ -0,0 +1,48 @@ +import { EndUserAgreementGuard } from './end-user-agreement.guard'; +import { EndUserAgreementService } from './end-user-agreement.service'; +import { Router, UrlTree } from '@angular/router'; +import { of as observableOf } from 'rxjs'; + +describe('EndUserAgreementGuard', () => { + let guard: EndUserAgreementGuard; + + let endUserAgreementService: EndUserAgreementService; + let router: Router; + + beforeEach(() => { + endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', { + hasCurrentUserAcceptedAgreement: observableOf(true) + }); + router = jasmine.createSpyObj('router', { + navigateByUrl: {}, + parseUrl: new UrlTree() + }); + + guard = new EndUserAgreementGuard(endUserAgreementService, router); + }); + + describe('canActivate', () => { + describe('when the user has accepted the agreement', () => { + it('should return true', (done) => { + guard.canActivate(undefined, undefined).subscribe((result) => { + expect(result).toEqual(true); + done(); + }); + }); + }); + + describe('when the user hasn\'t accepted the agreement', () => { + beforeEach(() => { + (endUserAgreementService.hasCurrentUserAcceptedAgreement as jasmine.Spy).and.returnValue(observableOf(false)); + }); + + it('should navigate the user with a redirect url', (done) => { + const redirect = 'redirect/url'; + guard.canActivate(undefined, Object.assign({ url: redirect })).subscribe(() => { + expect(router.navigateByUrl).toHaveBeenCalledWith(jasmine.anything(), { state: { redirect } }); + done(); + }); + }); + }); + }); +}); diff --git a/src/app/core/end-user-agreement/end-user-agreement.service.spec.ts b/src/app/core/end-user-agreement/end-user-agreement.service.spec.ts new file mode 100644 index 0000000000..a292b2371c --- /dev/null +++ b/src/app/core/end-user-agreement/end-user-agreement.service.spec.ts @@ -0,0 +1,144 @@ +import { + END_USER_AGREEMENT_COOKIE, + END_USER_AGREEMENT_METADATA_FIELD, + EndUserAgreementService +} from './end-user-agreement.service'; +import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; +import { of as observableOf } from 'rxjs'; +import { EPerson } from '../eperson/models/eperson.model'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; + +describe('EndUserAgreementService', () => { + let service: EndUserAgreementService; + + let userWithMetadata: EPerson; + let userWithoutMetadata: EPerson; + + let cookie; + let authService; + let ePersonService; + + beforeEach(() => { + userWithMetadata = Object.assign(new EPerson(), { + metadata: { + [END_USER_AGREEMENT_METADATA_FIELD]: [ + { + value: 'true' + } + ] + } + }); + userWithoutMetadata = Object.assign(new EPerson()); + + cookie = new CookieServiceMock(); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true), + getAuthenticatedUserFromStore: observableOf(userWithMetadata) + }); + ePersonService = jasmine.createSpyObj('ePersonService', { + update: createSuccessfulRemoteDataObject$(userWithMetadata) + }); + + service = new EndUserAgreementService(cookie, authService, ePersonService); + }); + + describe('when the cookie is set to true', () => { + beforeEach(() => { + cookie.set(END_USER_AGREEMENT_COOKIE, true); + }); + + it('hasCurrentUserAcceptedAgreement should return true', (done) => { + service.hasCurrentUserAcceptedAgreement().subscribe((result) => { + expect(result).toEqual(true); + done(); + }); + }); + + it('isCookieAccepted should return true', () => { + expect(service.isCookieAccepted()).toEqual(true); + }); + + it('removeCookieAccepted should remove the cookie', () => { + service.removeCookieAccepted(); + expect(cookie.get(END_USER_AGREEMENT_COOKIE)).toBeUndefined(); + }); + }); + + describe('when the cookie isn\'t set', () => { + describe('and the user is authenticated', () => { + beforeEach(() => { + (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(true)); + }); + + describe('and the user contains agreement metadata', () => { + beforeEach(() => { + (authService.getAuthenticatedUserFromStore as jasmine.Spy).and.returnValue(observableOf(userWithMetadata)); + }); + + it('hasCurrentUserAcceptedAgreement should return true', (done) => { + service.hasCurrentUserAcceptedAgreement().subscribe((result) => { + expect(result).toEqual(true); + done(); + }); + }); + }); + + describe('and the user doesn\'t contain agreement metadata', () => { + beforeEach(() => { + (authService.getAuthenticatedUserFromStore as jasmine.Spy).and.returnValue(observableOf(userWithoutMetadata)); + }); + + it('hasCurrentUserAcceptedAgreement should return false', (done) => { + service.hasCurrentUserAcceptedAgreement().subscribe((result) => { + expect(result).toEqual(false); + done(); + }); + }); + }); + + it('setUserAcceptedAgreement should update the user with new metadata', (done) => { + service.setUserAcceptedAgreement(true).subscribe(() => { + expect(ePersonService.update).toHaveBeenCalledWith(jasmine.objectContaining({ + metadata: jasmine.objectContaining({ + [END_USER_AGREEMENT_METADATA_FIELD]: [ + { + value: 'true' + } + ] + }) + })); + done(); + }); + }); + }); + + describe('and the user is not authenticated', () => { + beforeEach(() => { + (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false)); + }); + + it('hasCurrentUserAcceptedAgreement should return false', (done) => { + service.hasCurrentUserAcceptedAgreement().subscribe((result) => { + expect(result).toEqual(false); + done(); + }); + }); + + it('setUserAcceptedAgreement should set the cookie to true', (done) => { + service.setUserAcceptedAgreement(true).subscribe(() => { + expect(cookie.get(END_USER_AGREEMENT_COOKIE)).toEqual(true); + done(); + }); + }); + }); + + it('isCookieAccepted should return false', () => { + expect(service.isCookieAccepted()).toEqual(false); + }); + + it('setCookieAccepted should set the cookie', () => { + service.setCookieAccepted(true); + expect(cookie.get(END_USER_AGREEMENT_COOKIE)).toEqual(true); + }); + }); +}); diff --git a/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.spec.ts b/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.spec.ts index c95e60846e..d2290cd01c 100644 --- a/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.spec.ts +++ b/src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.spec.ts @@ -1,5 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EndUserAgreementContentComponent } from './end-user-agreement-content.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('EndUserAgreementContentComponent', () => { let component: EndUserAgreementContentComponent; @@ -7,9 +9,10 @@ describe('EndUserAgreementContentComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ EndUserAgreementContentComponent ] - }) - .compileComponents(); + imports: [ TranslateModule.forRoot() ], + declarations: [ EndUserAgreementContentComponent ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.html b/src/app/info/end-user-agreement/end-user-agreement.component.html index 628718cdcd..2338bfa460 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.html +++ b/src/app/info/end-user-agreement/end-user-agreement.component.html @@ -6,8 +6,8 @@
- - + +
diff --git a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts index 6b87a0fccb..5d6b3f904c 100644 --- a/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts +++ b/src/app/info/end-user-agreement/end-user-agreement.component.spec.ts @@ -1,15 +1,58 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EndUserAgreementComponent } from './end-user-agreement.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { TranslateModule } from '@ngx-translate/core'; +import { AuthService } from '../../core/auth/auth.service'; +import { Router } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { Store } from '@ngrx/store'; +import { By } from '@angular/platform-browser'; +import { LogOutAction } from '../../core/auth/auth.actions'; describe('EndUserAgreementComponent', () => { let component: EndUserAgreementComponent; let fixture: ComponentFixture; + let endUserAgreementService: EndUserAgreementService; + let notificationsService: NotificationsService; + let authService: AuthService; + let store; + let router: Router; + + let redirectUrl; + + function init() { + endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', { + hasCurrentUserAcceptedAgreement: observableOf(false), + setUserAcceptedAgreement: observableOf(true) + }); + notificationsService = jasmine.createSpyObj('notificationsService', ['success', 'error']); + authService = jasmine.createSpyObj('authService', { + isAuthenticated: observableOf(true) + }); + store = jasmine.createSpyObj('store', ['dispatch']); + router = jasmine.createSpyObj('router', ['navigate', 'navigateByUrl']); + + redirectUrl = 'redirect/url'; + window.history.pushState({ redirect: redirectUrl }, ''); + } + beforeEach(async(() => { + init(); TestBed.configureTestingModule({ - declarations: [ EndUserAgreementComponent ] - }) - .compileComponents(); + imports: [ TranslateModule.forRoot() ], + declarations: [ EndUserAgreementComponent ], + providers: [ + { provide: EndUserAgreementService, useValue: endUserAgreementService }, + { provide: NotificationsService, useValue: notificationsService }, + { provide: AuthService, useValue: authService }, + { provide: Store, useValue: store }, + { provide: Router, useValue: router } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -18,7 +61,89 @@ describe('EndUserAgreementComponent', () => { fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('when the user hasn\'t accepted the agreement', () => { + beforeEach(() => { + (endUserAgreementService.hasCurrentUserAcceptedAgreement as jasmine.Spy).and.returnValue(observableOf(false)); + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should initialize the accepted property', () => { + expect(component.accepted).toEqual(false); + }); + + it('should disable the save button', () => { + const button = fixture.debugElement.query(By.css('#button-save')).nativeElement; + expect(button.disabled).toBeTruthy(); + }); + }); + + describe('when the user has accepted the agreement', () => { + beforeEach(() => { + (endUserAgreementService.hasCurrentUserAcceptedAgreement as jasmine.Spy).and.returnValue(observableOf(true)); + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should initialize the accepted property', () => { + expect(component.accepted).toEqual(true); + }); + + it('should enable the save button', () => { + const button = fixture.debugElement.query(By.css('#button-save')).nativeElement; + expect(button.disabled).toBeFalsy(); + }); + + describe('submit', () => { + describe('when accepting the agreement was successful', () => { + beforeEach(() => { + (endUserAgreementService.setUserAcceptedAgreement as jasmine.Spy).and.returnValue(observableOf(true)); + component.submit(); + }); + + it('should display a success notification', () => { + expect(notificationsService.success).toHaveBeenCalled(); + }); + + it('should navigate the user to the redirect url', () => { + expect(router.navigateByUrl).toHaveBeenCalledWith(redirectUrl); + }); + }); + + describe('when accepting the agreement was unsuccessful', () => { + beforeEach(() => { + (endUserAgreementService.setUserAcceptedAgreement as jasmine.Spy).and.returnValue(observableOf(false)); + component.submit(); + }); + + it('should display an error notification', () => { + expect(notificationsService.error).toHaveBeenCalled(); + }); + }); + }); + }); + + describe('cancel', () => { + describe('when the user is authenticated', () => { + beforeEach(() => { + (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(true)); + component.cancel(); + }); + + it('should logout the user', () => { + expect(store.dispatch).toHaveBeenCalledWith(new LogOutAction()); + }); + }); + + describe('when the user is not authenticated', () => { + beforeEach(() => { + (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false)); + component.cancel(); + }); + + it('should navigate the user to the homepage', () => { + expect(router.navigate).toHaveBeenCalledWith(['home']); + }); + }); }); }); diff --git a/src/app/info/privacy/privacy-content/privacy-content.component.spec.ts b/src/app/info/privacy/privacy-content/privacy-content.component.spec.ts index eca0659147..a77e809dc3 100644 --- a/src/app/info/privacy/privacy-content/privacy-content.component.spec.ts +++ b/src/app/info/privacy/privacy-content/privacy-content.component.spec.ts @@ -1,5 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { PrivacyContentComponent } from './privacy-content.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('PrivacyContentComponent', () => { let component: PrivacyContentComponent; @@ -7,9 +9,10 @@ describe('PrivacyContentComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ PrivacyContentComponent ] - }) - .compileComponents(); + imports: [ TranslateModule.forRoot() ], + declarations: [ PrivacyContentComponent ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/app/info/privacy/privacy.component.spec.ts b/src/app/info/privacy/privacy.component.spec.ts index b7daa30029..a3d47e82f9 100644 --- a/src/app/info/privacy/privacy.component.spec.ts +++ b/src/app/info/privacy/privacy.component.spec.ts @@ -1,5 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { PrivacyComponent } from './privacy.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('PrivacyComponent', () => { let component: PrivacyComponent; @@ -7,9 +9,10 @@ describe('PrivacyComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ PrivacyComponent ] - }) - .compileComponents(); + imports: [ TranslateModule.forRoot() ], + declarations: [ PrivacyComponent ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/app/register-page/create-profile/create-profile.component.spec.ts b/src/app/register-page/create-profile/create-profile.component.spec.ts index a435e1143e..00c2eef99d 100644 --- a/src/app/register-page/create-profile/create-profile.component.spec.ts +++ b/src/app/register-page/create-profile/create-profile.component.spec.ts @@ -18,6 +18,10 @@ import { EPerson } from '../../core/eperson/models/eperson.model'; import { AuthenticateAction } from '../../core/auth/auth.actions'; import { RouterStub } from '../../shared/testing/router.stub'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; +import { + END_USER_AGREEMENT_METADATA_FIELD, + EndUserAgreementService +} from '../../core/end-user-agreement/end-user-agreement.service'; describe('CreateProfileComponent', () => { let comp: CreateProfileComponent; @@ -28,40 +32,80 @@ describe('CreateProfileComponent', () => { let ePersonDataService: EPersonDataService; let notificationsService; let store: Store; + let endUserAgreementService: EndUserAgreementService; const registration = Object.assign(new Registration(), {email: 'test@email.org', token: 'test-token'}); - const values = { - metadata: { - 'eperson.firstname': [ - { - value: 'First' - } - ], - 'eperson.lastname': [ - { - value: 'Last' - }, - ], - 'eperson.phone': [ - { - value: 'Phone' - } - ], - 'eperson.language': [ - { - value: 'en' - } - ] - }, - email: 'test@email.org', - password: 'password', - canLogIn: true, - requireCertificate: false - }; - const eperson = Object.assign(new EPerson(), values); + let values; + let eperson: EPerson; + let valuesWithAgreement; + let epersonWithAgreement: EPerson; beforeEach(async(() => { + values = { + metadata: { + 'eperson.firstname': [ + { + value: 'First' + } + ], + 'eperson.lastname': [ + { + value: 'Last' + }, + ], + 'eperson.phone': [ + { + value: 'Phone' + } + ], + 'eperson.language': [ + { + value: 'en' + } + ] + }, + email: 'test@email.org', + password: 'password', + canLogIn: true, + requireCertificate: false + }; + eperson = Object.assign(new EPerson(), values); + valuesWithAgreement = { + metadata: { + 'eperson.firstname': [ + { + value: 'First' + } + ], + 'eperson.lastname': [ + { + value: 'Last' + }, + ], + 'eperson.phone': [ + { + value: 'Phone' + } + ], + 'eperson.language': [ + { + value: 'en' + } + ], + [END_USER_AGREEMENT_METADATA_FIELD]: [ + { + value: 'true' + } + ] + }, + email: 'test@email.org', + password: 'password', + canLogIn: true, + requireCertificate: false + }; + epersonWithAgreement = Object.assign(new EPerson(), valuesWithAgreement); + route = {data: observableOf({registration: registration})}; router = new RouterStub(); notificationsService = new NotificationsServiceStub(); @@ -74,6 +118,11 @@ describe('CreateProfileComponent', () => { dispatch: {}, }); + endUserAgreementService = jasmine.createSpyObj('endUserAgreementService', { + isCookieAccepted: false, + removeCookieAccepted: {} + }); + TestBed.configureTestingModule({ imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), ReactiveFormsModule], declarations: [CreateProfileComponent], @@ -84,6 +133,7 @@ describe('CreateProfileComponent', () => { {provide: EPersonDataService, useValue: ePersonDataService}, {provide: FormBuilder, useValue: new FormBuilder()}, {provide: NotificationsService, useValue: notificationsService}, + {provide: EndUserAgreementService, useValue: endUserAgreementService}, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); @@ -131,6 +181,41 @@ describe('CreateProfileComponent', () => { expect(notificationsService.success).toHaveBeenCalled(); }); + describe('when the end-user-agreement cookie is accepted', () => { + beforeEach(() => { + (endUserAgreementService.isCookieAccepted as jasmine.Spy).and.returnValue(true); + }); + + it('should submit an eperson with agreement metadata for creation and log in on success', () => { + comp.firstName.patchValue('First'); + comp.lastName.patchValue('Last'); + comp.contactPhone.patchValue('Phone'); + comp.language.patchValue('en'); + comp.password = 'password'; + comp.isInValidPassword = false; + + comp.submitEperson(); + + expect(ePersonDataService.createEPersonForToken).toHaveBeenCalledWith(epersonWithAgreement, 'test-token'); + expect(store.dispatch).toHaveBeenCalledWith(new AuthenticateAction('test@email.org', 'password')); + expect(router.navigate).toHaveBeenCalledWith(['/home']); + expect(notificationsService.success).toHaveBeenCalled(); + }); + + it('should remove the cookie', () => { + comp.firstName.patchValue('First'); + comp.lastName.patchValue('Last'); + comp.contactPhone.patchValue('Phone'); + comp.language.patchValue('en'); + comp.password = 'password'; + comp.isInValidPassword = false; + + comp.submitEperson(); + + expect(endUserAgreementService.removeCookieAccepted).toHaveBeenCalled(); + }); + }); + it('should submit an eperson for creation and stay on page on error', () => { (ePersonDataService.createEPersonForToken as jasmine.Spy).and.returnValue(observableOf(new RestResponse(false, 500, 'Error'))); diff --git a/src/app/shared/mocks/cookie.service.mock.ts b/src/app/shared/mocks/cookie.service.mock.ts index f94f3d4a7d..17e1b36981 100644 --- a/src/app/shared/mocks/cookie.service.mock.ts +++ b/src/app/shared/mocks/cookie.service.mock.ts @@ -16,7 +16,8 @@ export class CookieServiceMock { return this.cookies.get(name); } - remove() { + remove(name) { + this.cookies.delete(name); return jasmine.createSpy('remove'); }