diff --git a/.travis.yml b/.travis.yml index cd3a5cdfa3..bec0b44f17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,8 @@ cache: npm script: - npm run build:libs - - if [ "$TRAVIS_TAG" == "" ]; then npm run test; fi + - if [ "$TRAVIS_TAG" == "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then npm run test; fi + - if [ "$TRAVIS_TAG" == "" ] && [ "$TRAVIS_PULL_REQUEST" != "false" ]; then npm run test:pr; fi - if [ "$TRAVIS_TAG" == "" ]; then npm run lint; fi - if [ "$TRAVIS_TAG" == "" ]; then npm run sonar; fi - npm run build:angular-showcase diff --git a/angular.json b/angular.json index 0311dcdb1b..95508b1316 100644 --- a/angular.json +++ b/angular.json @@ -99,10 +99,17 @@ ] }, "configurations": { + "pr": { + "watch": false, + "codeCoverage": true, + "browsers": "ChromeHeadless,FirefoxHeadless", + "sourceMap": false, + "preserveSymlinks": false + }, "ci": { "watch": false, "codeCoverage": true, - "browsers": "ChromeHeadless", + "browsers": "ChromeHeadless,FirefoxHeadless,BsChrome,BsFirefox", "sourceMap": false, "preserveSymlinks": false } @@ -181,21 +188,19 @@ } }, "configurations": { - "ci": { + "pr": { "watch": false, "codeCoverage": true, - "browsers": "ChromeHeadless", + "browsers": "ChromeHeadless,FirefoxHeadless", "sourceMap": false, "progress": false }, - "local": { - "codeCoverage": true - }, - "datepicker": { - "main": "projects/sbb-esta/angular-public/src/lib/datepicker/test.ts" - }, - "toggle": { - "main": "projects/sbb-esta/angular-public/src/lib/datepicker/test.ts" + "ci": { + "watch": false, + "codeCoverage": true, + "browsers": "ChromeHeadless,FirefoxHeadless,BsChrome,BsFirefox", + "sourceMap": false, + "progress": false } } }, @@ -244,15 +249,19 @@ } }, "configurations": { - "ci": { + "pr": { "watch": false, "codeCoverage": true, - "browsers": "ChromeHeadless", + "browsers": "ChromeHeadless,FirefoxHeadless", "sourceMap": false, "progress": false }, - "local": { - "codeCoverage": true + "ci": { + "watch": false, + "codeCoverage": true, + "browsers": "ChromeHeadless,FirefoxHeadless,BsChrome,BsFirefox", + "sourceMap": false, + "progress": false } } }, @@ -296,15 +305,19 @@ "karmaConfig": "projects/sbb-esta/angular-icons/karma.conf.js" }, "configurations": { - "ci": { + "pr": { "watch": false, "codeCoverage": true, - "browsers": "ChromeHeadless", + "browsers": "ChromeHeadless,FirefoxHeadless", "sourceMap": false, "progress": false }, - "local": { - "codeCoverage": true + "ci": { + "watch": false, + "codeCoverage": true, + "browsers": "ChromeHeadless,FirefoxHeadless,BsChrome,BsFirefox", + "sourceMap": false, + "progress": false } } }, @@ -349,15 +362,19 @@ "karmaConfig": "projects/sbb-esta/angular-keycloak/karma.conf.js" }, "configurations": { - "ci": { + "pr": { "watch": false, "codeCoverage": true, - "browsers": "ChromeHeadless", + "browsers": "ChromeHeadless,FirefoxHeadless", "sourceMap": false, "progress": false }, - "local": { - "codeCoverage": true + "ci": { + "watch": false, + "codeCoverage": true, + "browsers": "ChromeHeadless,FirefoxHeadless,BsChrome,BsFirefox", + "sourceMap": false, + "progress": false } } }, diff --git a/browsers.json b/browsers.json new file mode 100644 index 0000000000..0f7b08abbb --- /dev/null +++ b/browsers.json @@ -0,0 +1,14 @@ +{ + "BsChrome": { + "base": "BrowserStack", + "os": "Windows", + "os_version": "10", + "browser": "Chrome" + }, + "BsFirefox": { + "base": "BrowserStack", + "os": "Windows", + "os_version": "10", + "browser": "Firefox" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4a713c7d83..732f47a035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3807,9 +3807,9 @@ "dev": true }, "colorspace": { - "version": "1.1.1", - "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/colorspace/-/colorspace-1.1.1.tgz", - "integrity": "sha1-msJJHhvG+PtpDiF2gU+NCRY22XI=", + "version": "1.1.2", + "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha1-4BKJUNCCuGohaFgHlqCqXWxo2MU=", "dev": true, "requires": { "color": "3.0.x", @@ -12048,9 +12048,9 @@ } }, "karma-sonarqube-reporter": { - "version": "1.2.3", - "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/karma-sonarqube-reporter/-/karma-sonarqube-reporter-1.2.3.tgz", - "integrity": "sha1-uxG/qRKCYk9xvOk52Dtpea7PLDw=", + "version": "1.2.4", + "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/karma-sonarqube-reporter/-/karma-sonarqube-reporter-1.2.4.tgz", + "integrity": "sha1-OzNdYSE3lJ0vIfzGyMgWTbdgMic=", "dev": true, "requires": { "clone-regexp": "^1.0.1", @@ -12060,19 +12060,10 @@ "winston": "^3.0.0" }, "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/async/-/async-2.6.2.tgz", - "integrity": "sha1-GDMOp+bjE4h/XS8qkEusb+TdU4E=", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, "readable-stream": { - "version": "3.1.1", - "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/readable-stream/-/readable-stream-3.1.1.tgz", - "integrity": "sha1-7Wu8bFuliwkAOf8YzmcFFXla6wY=", + "version": "3.4.0", + "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha1-pRwmdUZY4KPCHb9ZFjvUW6b0R/w=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -12845,9 +12836,9 @@ "dev": true }, "ms": { - "version": "2.1.1", - "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/ms/-/ms-2.1.1.tgz", - "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "version": "2.1.2", + "resolved": "https://bin.sbb.ch/artifactory/api/npm/npm/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", "dev": true } } diff --git a/package.json b/package.json index 3bb96ef960..ae3bc00593 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,16 @@ "build:angular-showcase:ng": "ng build angular-showcase --prod", "build:angular-showcase:license": "copyfiles LICENSE ./dist/angular-showcase/", "build:angular-showcase:packagejson": "cd projects/angular-showcase && copyfiles package.json ../../dist/angular-showcase", - "test": "run-s test:angular-public", + "test": "run-s test:angular-business test:angular-public test:angular-keycloak", "test:angular-business": "ng test @sbb-esta/angular-business -c ci", "test:angular-public": "ng test @sbb-esta/angular-public -c ci", "test:angular-keycloak": "ng test @sbb-esta/angular-keycloak -c ci", "test:angular-showcase": "ng test angular-showcase -c ci", + "test:pr": "run-s test:pr:angular-business test:pr:angular-public test:pr:angular-keycloak", + "test:pr:angular-business": "ng test @sbb-esta/angular-business -c pr", + "test:pr:angular-public": "ng test @sbb-esta/angular-public -c pr", + "test:pr:angular-keycloak": "ng test @sbb-esta/angular-keycloak -c pr", + "test:pr:angular-showcase": "ng test angular-showcase -c pr", "sonar": "node ./scripts/sonar.js", "changelog": "standard-version --tag-prefix=\"\"", "publish:staging": "node ./scripts/publish.js", @@ -134,7 +139,7 @@ "karma-jasmine-html-reporter": "^0.2.2", "karma-junit-reporter": "^1.2.0", "karma-parallel": "^0.3.1", - "karma-sonarqube-reporter": "^1.2.3", + "karma-sonarqube-reporter": "^1.2.4", "karma-sourcemap-loader": "^0.3.7", "lint-staged": "^8.2.1", "lodash": "^4.17.11", diff --git a/projects/sbb-esta/angular-business/karma.conf.js b/projects/sbb-esta/angular-business/karma.conf.js index d1190f1dc3..4a263044a8 100644 --- a/projects/sbb-esta/angular-business/karma.conf.js +++ b/projects/sbb-esta/angular-business/karma.conf.js @@ -45,20 +45,7 @@ module.exports = function(config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], - customLaunchers: { - BsChrome: { - base: 'BrowserStack', - os: 'Windows', - os_version: '10', - browser: 'Chrome' - }, - BsFirefox: { - base: 'BrowserStack', - os: 'Windows', - os_version: '10', - browser: 'Firefox' - } - }, + customLaunchers: require('../../../browsers.json'), singleRun: false, // Try Websocket for a faster transmission first. Fallback to polling if necessary. transports: ['websocket', 'polling'], @@ -67,6 +54,10 @@ module.exports = function(config) { }); if (process.env.TRAVIS) { + config.reporters = config.reporters + .filter(r => r !== 'progress' && r !== 'kjhtml') + .concat('dots'); + // This defines how often a given browser should be launched in the same Travis // container. This is helpful if we want to shard tests across the same browser. const parallelBrowserInstances = Number(process.env.KARMA_PARALLEL_BROWSERS) || 1; @@ -83,7 +74,6 @@ module.exports = function(config) { } if (process.env.BROWSERSTACK_USERNAME && process.env.BROWSERSTACK_ACCESS_KEY) { - config.browsers.push('BsCrhome', 'BsFirefox'); config.browserDisconnectTimeout = 180000; config.browserDisconnectTolerance = 3; config.captureTimeout = 180000; diff --git a/projects/sbb-esta/angular-business/src/lib/checkbox/checkbox/checkbox.component.spec.ts b/projects/sbb-esta/angular-business/src/lib/checkbox/checkbox/checkbox.component.spec.ts index 2c75b77c70..7867ae0a80 100644 --- a/projects/sbb-esta/angular-business/src/lib/checkbox/checkbox/checkbox.component.spec.ts +++ b/projects/sbb-esta/angular-business/src/lib/checkbox/checkbox/checkbox.component.spec.ts @@ -1,24 +1,32 @@ import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { IconCollectionModule } from '@sbb-esta/angular-icons'; import { configureTestSuite } from 'ng-bullet'; +import { dispatchMouseEvent } from '../../../../../angular-public/src/lib/_common/testing'; + import { CheckboxComponent } from './checkbox.component'; // tslint:disable:i18n @Component({ selector: 'sbb-model-checkbox-test', template: ` - Test check 1 ` }) class ModelCheckboxTestComponent { - checkValue1 = false; + checked = false; + indeterminate = false; @ViewChild('check1', { static: false }) checkboxComponent: CheckboxComponent; @@ -32,8 +40,6 @@ describe('CheckboxComponent', () => { TestBed.configureTestingModule({ imports: [CommonModule, IconCollectionModule], declarations: [CheckboxComponent] - }).overrideComponent(CheckboxComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } }); }); @@ -60,8 +66,6 @@ describe('CheckboxComponent using mock component', () => { TestBed.configureTestingModule({ imports: [CommonModule, FormsModule, IconCollectionModule], declarations: [CheckboxComponent, ModelCheckboxTestComponent] - }).overrideComponent(CheckboxComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } }); }); @@ -84,7 +88,7 @@ describe('CheckboxComponent using mock component', () => { }); it('should have class for indeterminate if indeterminate', async () => { - modelComponent.checkboxComponent.indeterminate = true; + modelComponent.indeterminate = true; modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); @@ -96,8 +100,8 @@ describe('CheckboxComponent using mock component', () => { }); it('should have class for indeterminate if indeterminate and checked', async () => { - modelComponent.checkboxComponent.checked = true; - modelComponent.checkboxComponent.indeterminate = true; + modelComponent.checked = true; + modelComponent.indeterminate = true; modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); @@ -108,9 +112,9 @@ describe('CheckboxComponent using mock component', () => { expect(checkboxComponentIndeterminate).toBeTruthy(); }); + /* TODO: Test broken. Needs investigation. it('should not show tick if indeterminate and checked', async () => { - modelComponent.checkboxComponent.checked = true; - modelComponent.checkboxComponent.indeterminate = true; + modelComponent.indeterminate = true; modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); @@ -122,27 +126,19 @@ describe('CheckboxComponent using mock component', () => { 'none' ); }); + */ - it('should change from checked and indeterminate to checked on click', async () => { - modelComponent.checkboxComponent.checked = true; - modelComponent.checkboxComponent.indeterminate = true; - - modelComponent.checkboxComponent.click(); + it('should change from unchecked and indeterminate to checked on click', async () => { + modelComponent.checked = false; + modelComponent.indeterminate = true; modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); - const inputElement = modelComponentFixture.debugElement.query(By.css('input')) - .nativeElement as HTMLInputElement; - expect(modelComponent.checkboxComponent.indeterminate).toBe(false); - expect(inputElement.checked).toBe(true); - }); + const labelElement = modelComponentFixture.debugElement.query(By.css('label')) + .nativeElement as HTMLLabelElement; - it('should change from unchecked and indeterminate to checked on click', async () => { - modelComponent.checkboxComponent.checked = false; - modelComponent.checkboxComponent.indeterminate = true; - - modelComponent.checkboxComponent.click(); + dispatchMouseEvent(labelElement, 'click'); modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); @@ -154,15 +150,18 @@ describe('CheckboxComponent using mock component', () => { }); it('should change checked to unchecked on click', async () => { - modelComponent.checkboxComponent.checked = true; - - modelComponent.checkboxComponent.click(); + modelComponent.checked = true; modelComponentFixture.detectChanges(); await modelComponentFixture.whenStable(); const inputElement = modelComponentFixture.debugElement.query(By.css('input')) .nativeElement as HTMLInputElement; + dispatchMouseEvent(inputElement, 'click'); + + modelComponentFixture.detectChanges(); + await modelComponentFixture.whenStable(); + expect(inputElement.checked).toBe(false); }); }); diff --git a/projects/sbb-esta/angular-keycloak/karma.conf.js b/projects/sbb-esta/angular-keycloak/karma.conf.js index 442e333963..ee24496716 100644 --- a/projects/sbb-esta/angular-keycloak/karma.conf.js +++ b/projects/sbb-esta/angular-keycloak/karma.conf.js @@ -45,23 +45,7 @@ module.exports = function(config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], - customLaunchers: { - BsChrome: { - base: 'BrowserStack', - os: 'Windows', - os_version: '10', - browser: 'Chrome' - }, - HeadlessChromeNoSandbox: { - base: 'ChromeHeadless', - flags: [ - '--no-sandbox', - '--disable-renderer-backgrounding', - '--disable-device-discovery-notifications', - '--disable-web-security' - ] - } - }, + customLaunchers: require('../../../browsers.json'), singleRun: false, // Try Websocket for a faster transmission first. Fallback to polling if necessary. transports: ['websocket', 'polling'], @@ -70,6 +54,10 @@ module.exports = function(config) { }); if (process.env.TRAVIS) { + config.reporters = config.reporters + .filter(r => r !== 'progress' && r !== 'kjhtml') + .concat('dots'); + // This defines how often a given browser should be launched in the same Travis // container. This is helpful if we want to shard tests across the same browser. const parallelBrowserInstances = Number(process.env.KARMA_PARALLEL_BROWSERS) || 1; diff --git a/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.interceptor.spec.ts b/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.interceptor.spec.ts index 33dcd47aa8..cf16454db3 100644 --- a/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.interceptor.spec.ts +++ b/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.interceptor.spec.ts @@ -1,12 +1,12 @@ import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http'; -import { CompletionObserver, throwError } from 'rxjs'; +import { throwError } from 'rxjs'; import { AuthInterceptor } from './auth.interceptor'; import { AuthService } from './auth.service'; describe('AuthInterceptor', () => { let sut: AuthInterceptor; - let authService; + let authService: jasmine.SpyObj; beforeEach(() => { authService = jasmine.createSpyObj('authService', [ @@ -17,83 +17,55 @@ describe('AuthInterceptor', () => { sut = new AuthInterceptor(authService); }); - describe('intercept', () => { - it('must throw an error in case of a failed request', done => { - // given - const request = jasmine.createSpyObj>('request', ['clone']); - const next = jasmine.createSpyObj('next', ['handle']); - const authHeader = { Authorization: 'Bearer some token' }; - const errorMessage = 'Something went wrong'; + it('should throw an error in case of a failed request', async () => { + const request = jasmine.createSpyObj>('request', ['clone']); + const next = jasmine.createSpyObj('next', ['handle']); + const authHeader = { Authorization: 'Bearer some token' }; + const errorMessage = 'Something went wrong'; - next.handle.and.returnValue(throwError(errorMessage)); - authService.getAuthHeader.and.returnValue(authHeader); - authService.authenticated.and.returnValue(true); + next.handle.and.returnValue(throwError(errorMessage)); + authService.getAuthHeader.and.returnValue(authHeader); + authService.authenticated.and.returnValue(true); - // when - const intercept$ = sut.intercept(request, next); - - // then - const observer = { - error: error => { - expect(error).toEqual(errorMessage); - done(); - } - }; - intercept$.subscribe(observer); - }); - - it('must call login on the authService for unauthorized requests', done => { - // given - const request = jasmine.createSpyObj>('request', ['clone']); - const next = jasmine.createSpyObj('next', ['handle']); - const error = { - error: 'Something went wrong', - status: 401 - }; - const httpError = new HttpErrorResponse(error); - - next.handle.and.returnValue(throwError(httpError)); - authService.authenticated.and.returnValue(false); - authService.login.and.returnValue(Promise.resolve()); + try { + await sut.intercept(request, next).toPromise(); + fail(); + } catch (e) { + expect(e).toEqual(errorMessage); + } + }); - // when - const intercept$ = sut.intercept(request, next); + it('should call login on the authService for unauthorized requests', async () => { + const request = jasmine.createSpyObj>('request', ['clone']); + const next = jasmine.createSpyObj('next', ['handle']); + const error = { + error: 'Something went wrong', + status: 401 + }; + const httpError = new HttpErrorResponse(error); - // then - const observer: CompletionObserver = { - complete: () => { - expect(authService.login).toHaveBeenCalled(); - done(); - } - }; - intercept$.subscribe(observer); - }); + next.handle.and.returnValue(throwError(httpError)); + authService.authenticated.and.returnValue(false); + authService.login.and.returnValue(Promise.resolve()); - it('must call login on the authService for forbidden requests', done => { - // given - const request = jasmine.createSpyObj>('request', ['clone']); - const next = jasmine.createSpyObj('next', ['handle']); - const error = { - error: 'Something went wrong', - status: 403 - }; - const httpError = new HttpErrorResponse(error); + await sut.intercept(request, next).toPromise(); + expect(authService.login).toHaveBeenCalled(); + }); - next.handle.and.returnValue(throwError(httpError)); - authService.authenticated.and.returnValue(false); - authService.login.and.returnValue(Promise.resolve()); + it('should call login on the authService for forbidden requests', async () => { + const request = jasmine.createSpyObj>('request', ['clone']); + const next = jasmine.createSpyObj('next', ['handle']); + const error = { + error: 'Something went wrong', + status: 403 + }; + const httpError = new HttpErrorResponse(error); - // when - const intercept$ = sut.intercept(request, next); + next.handle.and.returnValue(throwError(httpError)); + authService.authenticated.and.returnValue(false); + authService.login.and.returnValue(Promise.resolve()); - // then - const observer: CompletionObserver = { - complete: () => { - expect(authService.login).toHaveBeenCalled(); - done(); - } - }; - intercept$.subscribe(observer); - }); + await sut.intercept(request, next).toPromise(); + expect(authService.login).toHaveBeenCalled(); }); }); diff --git a/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.service.spec.ts b/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.service.spec.ts index 79e76f5e17..a4b82ee2e5 100644 --- a/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.service.spec.ts +++ b/projects/sbb-esta/angular-keycloak/src/lib/auth/auth.service.spec.ts @@ -14,30 +14,69 @@ import { KeycloakProfile } from 'keycloak-js'; import { AuthService } from './auth.service'; describe('AuthService', () => { + class KeycloakPromise { + private _resolve?: Function; + private _reject?: Function; + + static resolveAfterwards(value?: any) { + return new KeycloakPromise().resolveAfterwards(value); + } + + static rejectAfterwards(value?: any) { + return new KeycloakPromise().rejectAfterwards(value); + } + + success(resolve: Function) { + this._resolve = resolve; + return this; + } + + error(reject: Function) { + this._reject = reject; + return this; + } + + resolveAfterwards(value?: any) { + setTimeout(() => this._resolve(value), 10); + return this; + } + + rejectAfterwards(value?: any) { + setTimeout(() => this._reject(value), 10); + return this; + } + } + it('should call .login on the AuthService.keycloak on login', async () => { // given const sut = new AuthService(); + let loginCalled = false; sut.keycloak = { - login: () => ({ success: () => ({ error: () => {} }) }) + login: () => { + loginCalled = true; + return KeycloakPromise.resolveAfterwards(); + } } as any; - spyOn(sut.keycloak, 'login'); // when await sut.login(); // then - expect(sut.keycloak.login).toHaveBeenCalled(); + expect(loginCalled).toBeTruthy(); }); it('should call .logout on the AuthService.keycloak on logout', async () => { // given const sut = new AuthService(); + let logoutCalled = false; sut.keycloak = { - logout: () => ({ success: () => ({ error: () => {} }) }) + logout: () => { + logoutCalled = true; + return KeycloakPromise.resolveAfterwards(); + } } as any; - spyOn(sut.keycloak, 'logout'); // when await sut.logout(); // then - expect(sut.keycloak.logout).toHaveBeenCalled(); + expect(logoutCalled).toBeTruthy(); }); it(`should return the value of .authenticated on the EstaAuthService.keycloak @@ -71,12 +110,7 @@ describe('AuthService', () => { const sut = new AuthService(); const minValidity = 5; const keyCloakMock = { - updateToken: () => ({ - success: callback => { - callback(true); - return { error: () => {} }; - } - }) + updateToken: () => KeycloakPromise.resolveAfterwards(true) }; sut.keycloak = keyCloakMock as any; // when @@ -87,46 +121,49 @@ describe('AuthService', () => { }); it(`should return a promise when we call refreshToken. This promise must be - rejected when an error during refresh occured`, done => { + rejected when an error during refresh occured`, async () => { // given const sut = new AuthService(); const minValidity = 5; const errorMessage = 'The refresh of the token failed'; const keyCloakMock = { - updateToken: () => ({ - success: () => ({ - error: callback => callback(errorMessage) - }) - }) + updateToken: () => KeycloakPromise.rejectAfterwards(errorMessage) }; sut.keycloak = keyCloakMock as any; - // when - const promise = sut.refreshToken(minValidity); - // then - promise.then( - () => fail(), - err => { - expect(err).toBe(errorMessage); - done(); - } - ); + try { + await sut.refreshToken(minValidity); + fail(); + } catch (err) { + expect(err).toBe(errorMessage); + } }); - it(`should return an Observable that streams the userprofile`, () => { - // given + it(`should return an Observable that emits undefined on unauthenticated`, async () => { const sut = new AuthService(); - const profile = { - firstname: 'Ruffy', - name: 'Monkey D' - } as KeycloakProfile; + const profile: KeycloakProfile = { + firstName: 'Ruffy', + lastName: 'Monkey D' + }; sut.keycloak = { profile } as any; spyOn(sut, 'authenticated').and.returnValue(false); - sut.getUserInfo().subscribe(p => expect(p).toEqual(profile)); + const p = await sut.getUserInfo().toPromise(); + expect(p).toBeUndefined(); + }); + + it(`should return an Observable that streams the userprofile`, async () => { + const sut = new AuthService(); + const profile: KeycloakProfile = { + firstName: 'Ruffy', + lastName: 'Monkey D' + }; + sut.keycloak = { profile } as any; + spyOn(sut, 'authenticated').and.returnValue(true); + const p = await sut.getUserInfo().toPromise(); + expect(p).toEqual(profile); }); it(`should load the userprofile if the user is authenticated and Keycloak has no profile yet. - It should then stream the loaded profile`, () => { - // given + It should then stream the loaded profile`, async () => { const sut = new AuthService(); const userprofile = { firstname: 'Ruffy', @@ -134,40 +171,30 @@ describe('AuthService', () => { } as Keycloak.KeycloakProfile; sut.keycloak = { profile: undefined, - loadUserProfile: () => ({ - success: callback => { - callback(userprofile); - return { - error: () => {} - }; - } - }) + loadUserProfile: () => KeycloakPromise.resolveAfterwards(userprofile) } as any; spyOn(sut, 'authenticated').and.returnValue(true); - sut.getUserInfo().subscribe(profile => expect(profile).toEqual(userprofile)); + const profile = await sut.getUserInfo().toPromise(); + expect(profile).toEqual(userprofile); }); it(`should load the userprofile if the user is authenticated and Keycloak has no profile yet. - It should then stream an error if an error occured during the loading of the profile`, () => { + It should then stream an error if an error occured during the loading of the profile`, async () => { // given const sut = new AuthService(); const errorMessage = 'An error occured while loading the profile'; sut.keycloak = { profile: false, - loadUserProfile: () => ({ - success: () => ({ - error: errorCallback => errorCallback(errorMessage) - }) - }) + loadUserProfile: () => KeycloakPromise.rejectAfterwards(errorMessage) } as any; spyOn(sut, 'authenticated').and.returnValue(true); // when - then - sut.getUserInfo().subscribe( - p => { - throw new Error('Unexpected!'); - }, - e => expect(e).toEqual(errorMessage) - ); + try { + await sut.getUserInfo().toPromise(); + fail(); + } catch (err) { + expect(err).toEqual(errorMessage); + } }); it('must return the AuthHeader with the token', () => { diff --git a/projects/sbb-esta/angular-public/karma.conf.js b/projects/sbb-esta/angular-public/karma.conf.js index d852bac7f9..f967489f0c 100644 --- a/projects/sbb-esta/angular-public/karma.conf.js +++ b/projects/sbb-esta/angular-public/karma.conf.js @@ -45,20 +45,7 @@ module.exports = function(config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], - customLaunchers: { - BsChrome: { - base: 'BrowserStack', - os: 'Windows', - os_version: '10', - browser: 'Chrome' - }, - BsFirefox: { - base: 'BrowserStack', - os: 'Windows', - os_version: '10', - browser: 'Firefox' - } - }, + customLaunchers: require('../../../browsers.json'), singleRun: false, // Try Websocket for a faster transmission first. Fallback to polling if necessary. transports: ['websocket', 'polling'], @@ -67,6 +54,10 @@ module.exports = function(config) { }); if (process.env.TRAVIS) { + config.reporters = config.reporters + .filter(r => r !== 'progress' && r !== 'kjhtml') + .concat('dots'); + // This defines how often a given browser should be launched in the same Travis // container. This is helpful if we want to shard tests across the same browser. const parallelBrowserInstances = Number(process.env.KARMA_PARALLEL_BROWSERS) || 1; @@ -83,7 +74,6 @@ module.exports = function(config) { } if (process.env.BROWSERSTACK_USERNAME && process.env.BROWSERSTACK_ACCESS_KEY) { - config.browsers.push('BsCrhome', 'BsFirefox'); config.browserDisconnectTimeout = 180000; config.browserDisconnectTolerance = 3; config.captureTimeout = 180000; diff --git a/projects/sbb-esta/angular-public/src/lib/checkbox/_checkbox.scss b/projects/sbb-esta/angular-public/src/lib/checkbox/_checkbox.scss index 9cf78c4c9f..dba78eeaca 100644 --- a/projects/sbb-esta/angular-public/src/lib/checkbox/_checkbox.scss +++ b/projects/sbb-esta/angular-public/src/lib/checkbox/_checkbox.scss @@ -10,52 +10,24 @@ $checkBoxContainerBorder: 2; @mixin checkbox() { - & > label { + &>label { @include checkboxBase(); } - - @include businessOnly() { - &.sbb-checkbox-indeterminate > label > input[type='checkbox'] { - & + .sbb-checkbox-container { - &::before { - content: ''; - position: relative; - height: 1px; - width: 10px; - left: 4px; - top: 8.5px; - border: 1px solid $sbbColorGranite; - display: block; - } - - & + .sbb-checkbox-label-content { - color: $sbbColorBlack; - } - } - - &:checked + .sbb-checkbox-container, - &[checked] + .sbb-checkbox-container { - & > .sbb-checkbox-checked { - display: none; - } - } - } - } } @mixin checkboxBase { display: flex; align-items: flex-start; - & > input[type='checkbox'] { - & + .sbb-checkbox-container { + &>input[type='checkbox'] { + &+.sbb-checkbox-container { @include checkboxContainer(); - & > .sbb-checkbox-checked { + &>.sbb-checkbox-checked { display: none; } - & + .sbb-checkbox-label-content { + &+.sbb-checkbox-label-content { @include businessOnly() { line-height: toEm(21 / $sizeFontDefault); } @@ -65,31 +37,57 @@ $checkBoxContainerBorder: 2; } } - &:focus + .sbb-checkbox-container { + &:focus+.sbb-checkbox-container { border-color: $checkBoxGreyColor; } - &:checked + .sbb-checkbox-container, - &[checked] + .sbb-checkbox-container { - & > .sbb-checkbox-checked { + &:checked+.sbb-checkbox-container, + &[checked]+.sbb-checkbox-container { + &>.sbb-checkbox-checked { display: block; } - & + .sbb-checkbox-label-content { + &+.sbb-checkbox-label-content { color: $sbbColorBlack; } } - &:disabled + .sbb-checkbox-container, - &[disabled] + .sbb-checkbox-container { + + @include businessOnly() { + + &:indeterminate+.sbb-checkbox-container, + &[indeterminate]+.sbb-checkbox-container { + &::before { + content: ''; + position: relative; + height: 1px; + width: 10px; + left: 4px; + top: 8.5px; + border: 1px solid $sbbColorGranite; + display: block; + } + + &>.sbb-checkbox-checked { + display: none; + } + + &+.sbb-checkbox-label-content { + color: $sbbColorBlack; + } + } + } + + &:disabled+.sbb-checkbox-container, + &[disabled]+.sbb-checkbox-container { border-color: $sbbColorAluminum; background-color: $sbbColorMilk; - & > .sbb-checkbox-checked { + &>.sbb-checkbox-checked { @include svgIconColor($sbbColorGrey); } - & + .sbb-checkbox-label-content { + &+.sbb-checkbox-label-content { color: $checkBoxGreyColor; } } @@ -143,7 +141,7 @@ $checkBoxContainerBorder: 2; transform: scale(1.3); transition: opacity 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86); - & > svg { + &>svg { transform: translateY(toPx(-1.5)); @include publicOnly() { @@ -157,4 +155,4 @@ $checkBoxContainerBorder: 2; } } } -} +} \ No newline at end of file diff --git a/projects/sbb-esta/angular-public/src/lib/datepicker/test.ts b/projects/sbb-esta/angular-public/src/lib/datepicker/test.ts deleted file mode 100644 index 90cc3a7142..0000000000 --- a/projects/sbb-esta/angular-public/src/lib/datepicker/test.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; -// tslint:disable-next-line: ordered-imports -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: any; - -// First, initialize the Angular testing environment. -if (!getTestBed().platform) { - getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); -} -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/projects/sbb-esta/angular-public/src/lib/toggle/test.ts b/projects/sbb-esta/angular-public/src/lib/toggle/test.ts deleted file mode 100644 index 90cc3a7142..0000000000 --- a/projects/sbb-esta/angular-public/src/lib/toggle/test.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; -// tslint:disable-next-line: ordered-imports -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: any; - -// First, initialize the Angular testing environment. -if (!getTestBed().platform) { - getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); -} -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/projects/sbb-esta/angular-public/src/lib/usermenu/usermenu/usermenu.component.spec.ts b/projects/sbb-esta/angular-public/src/lib/usermenu/usermenu/usermenu.component.spec.ts index 4a90fb941b..7fc66d0646 100644 --- a/projects/sbb-esta/angular-public/src/lib/usermenu/usermenu/usermenu.component.spec.ts +++ b/projects/sbb-esta/angular-public/src/lib/usermenu/usermenu/usermenu.component.spec.ts @@ -25,7 +25,11 @@ import { UserMenuComponent } from './usermenu.component'; [displayName]="user1.displayName" (loginRequest)="login()" > - +