Skip to content

Commit 76f9228

Browse files
ghillertilayaperumalg
authored andcommitted
gh-743 Update Karma tests to include new browser versions
* Add `karma-firefox-launcher` - Adds the ability to run unit tests against Firefox using `ng test --browsers=Firefox` * Add `karma-safari-launcher` - Adds the ability to run unit tests against Firefox using `ng test --browsers=Safari` * Add `karma-edge-launcher` - Adds the ability (on Windows 10) to run unit tests against Edge using `ng test --browsers=Edge` * Fix tests for Firefox - Adjust Flo unit tests for FF * Fix tests for Edge * Add SauceLabs Browser Targets for Karma unit tests - Chrome 67, Mac OS - Safari 11, Mac OS - Firefox 59, Mac OS - Edge 16, Windows 10 - Chrome 67, Windows 10 - Firefox 59, Windows 10 * Add BrowserStack Browser Targets for Karma unit tests - Chrome 65, Mac OS - Safari, Mac OS - Firefox 59, Mac OS - Edge 16, Windows 10 - Chrome 67, Windows 10 - Firefox 59, Windows 10 * Update Readme with BrowserStack + SauceLabs badges * Minimize Logging for BrowserStack and Karma - Keep in mind 4MB logging maximum on Travis CI - Use dots reporter for Karma - Only log Warn+Error messages Fix Windows CI build gh-743 Add ability to use stand-alone SauceConnect tunnel * Add support for boolean environment variable `SAUCE_CONNECT_USE_EMBEDDED` * Add documentation on how to use stand-alone SauceConnect tunnel Fix Browserstack test failures
1 parent 9fbee39 commit 76f9228

34 files changed

+426
-131
lines changed

README.md

+25
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
[![Build status](https://ci.appveyor.com/api/projects/status/7pqco2aqjyaphp36/branch/master?svg=true)](https://ci.appveyor.com/project/ghillert/spring-cloud-dataflow-ui/branch/master)
55
[![Code Coverage](https://codecov.io/gh/spring-cloud/spring-cloud-dataflow-ui/branch/master/graph/badge.svg)](https://codecov.io/gh/spring-cloud/spring-cloud-dataflow-ui/branch/master)
66

7+
**SauceLabs Status**
8+
9+
[![Sauce Test Status](https://saucelabs.com/buildstatus/ghillert)](https://saucelabs.com/u/ghillert)
10+
11+
[![Sauce Test Status](https://saucelabs.com/browser-matrix/ghillert.svg)](https://saucelabs.com/u/ghillert)
12+
13+
**BrowserStack Status**
14+
15+
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=T3pKbzdQK2RpVnkxZ2ZwN2tjeGFUSzdOQUJ2cG1GSDBYSlRvT00zZWV1bz0tLVpuMXk0eTJmN01ienhnbkNPbXJTanc9PQ==--b187f26b476b4d3f262b837e13f4be593c41e44c)](https://www.browserstack.com/automate/public-build/T3pKbzdQK2RpVnkxZ2ZwN2tjeGFUSzdOQUJ2cG1GSDBYSlRvT00zZWV1bz0tLVpuMXk0eTJmN01ienhnbkNPbXJTanc9PQ==--b187f26b476b4d3f262b837e13f4be593c41e44c)
16+
717
This is the *Spring Cloud Data Flow Dashboard* user interface (UI). The UI uses [Angular][]. Source code documentation is available at http://cloud.spring.io/spring-cloud-dataflow-ui/.
818

919
> The Git repository for the main *Spring Cloud Data Flow* project is at: https://github.com/spring-cloud/spring-cloud-dataflow
@@ -96,6 +106,21 @@ For E2E tests, developers should refrain from using `localhost`. Instead, add `d
96106
$ npm run e2e-saucelabs-local
97107
```
98108

109+
## Slow Internet Connection
110+
111+
By default the tests use an embedded version of Sauce Connect. In case you enounter test failures due to bandwidth constraints,
112+
you may consider establishing a SauceLabs tunnel using the stand-alone **[Sauce Connect](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy)**.
113+
114+
Setup instructions can be found [here](https://wiki.saucelabs.com/display/DOCS/Basic+Sauce+Connect+Proxy+Setup).
115+
116+
Make sure you have at least the following environment variables defined:
117+
118+
export SAUCE_CONNECT_USE_EMBEDDED=false
119+
export SAUCE_USER=
120+
export SAUCE_API_KEY=
121+
122+
For a list of further supported configuration options, check the `config_examples/sc_configs` folder in the downloaded Sauce Connect binary.
123+
99124
## Thank You
100125

101126
![SauceLabs Logo](master-ui-assets/images/saucelabs-logo-600x315.png)

appveyor.yml

+1-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@ install:
1414
- cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;%JAVA_HOME%\bin;%PATH:C:\Ruby193\bin;=%
1515
- cmd: SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g
1616
- cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g
17-
- cmd: mvn --version
1817
- cmd: java -version
1918
build_script:
20-
- mvn clean package -B -Dmaven.test.skip=true
21-
test_script:
22-
- mvn clean install --batch-mode
19+
- mvnw clean install --batch-mode -Dmaven.test.skip=false
2320
cache:
2421
- C:\Users\appveyor\.m2

pom.xml

+8-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
<goal>install-node-and-npm</goal>
102102
</goals>
103103
<configuration>
104-
<nodeVersion>v8.9.4</nodeVersion>
104+
<nodeVersion>v10.8.0</nodeVersion>
105105
</configuration>
106106
</execution>
107107
<execution>
@@ -155,6 +155,13 @@
155155
<directory>ui/node_modules</directory>
156156
<followSymlinks>false</followSymlinks>
157157
</fileset>
158+
<fileset>
159+
<directory>ui</directory>
160+
<includes>
161+
<include>package-lock.json</include>
162+
</includes>
163+
<followSymlinks>false</followSymlinks>
164+
</fileset>
158165
<fileset>
159166
<directory>ui/dist</directory>
160167
<followSymlinks>false</followSymlinks>

ui/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
[![Build status](https://ci.appveyor.com/api/projects/status/7pqco2aqjyaphp36/branch/master?svg=true)](https://ci.appveyor.com/project/ghillert/spring-cloud-dataflow-ui/branch/master)
55
[![Code Coverage](https://codecov.io/gh/spring-cloud/spring-cloud-dataflow-ui/branch/master/graph/badge.svg)](https://codecov.io/gh/spring-cloud/spring-cloud-dataflow-ui/branch/master)
66

7+
**SauceLabs Status**
8+
9+
[![Sauce Test Status](https://saucelabs.com/buildstatus/ghillert)](https://saucelabs.com/u/ghillert)
10+
11+
[![Sauce Test Status](https://saucelabs.com/browser-matrix/ghillert.svg)](https://saucelabs.com/u/ghillert)
12+
13+
**BrowserStack Status**
14+
15+
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=T3pKbzdQK2RpVnkxZ2ZwN2tjeGFUSzdOQUJ2cG1GSDBYSlRvT00zZWV1bz0tLVpuMXk0eTJmN01ienhnbkNPbXJTanc9PQ==--b187f26b476b4d3f262b837e13f4be593c41e44c)](https://www.browserstack.com/automate/public-build/T3pKbzdQK2RpVnkxZ2ZwN2tjeGFUSzdOQUJ2cG1GSDBYSlRvT00zZWV1bz0tLVpuMXk0eTJmN01ienhnbkNPbXJTanc9PQ==--b187f26b476b4d3f262b837e13f4be593c41e44c)
16+
717
This is the *Spring Cloud Data Flow Dashboard* user interface (UI). The UI uses [Angular][]. Source code documentation is available at http://cloud.spring.io/spring-cloud-dataflow-ui/.
818

919
> The Git repository for the main *Spring Cloud Data Flow* project is at: https://github.com/spring-cloud/spring-cloud-dataflow

ui/e2e/protractor-saucelabs.conf.js

+28-20
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
// https://github.com/angular/protractor/blob/master/lib/config.ts
33

44
const { SpecReporter } = require('jasmine-spec-reporter');
5+
const useEmbeddedSauceConnect =
6+
(process.env.SAUCE_CONNECT_USE_EMBEDDED && process.env.SAUCE_CONNECT_USE_EMBEDDED === 'false') ? false : true;
7+
console.log('Use embedded Sauce Connect client?: ' + useEmbeddedSauceConnect);
58

69
exports.config = {
10+
711
jasmineNodeOpts: {
812
defaultTimeoutInterval: 5000000
913
},
@@ -56,25 +60,29 @@ exports.config = {
5660
};
5761

5862
function startSauceConnect(deferred) {
59-
var sauceConnectLauncher = require('sauce-connect-launcher');
60-
console.log('Launching Sauce Connect...')
61-
sauceConnectLauncher(
62-
{
63-
username: process.env.SAUCE_USERNAME,
64-
accessKey: process.env.SAUCE_ACCESS_KEY,
65-
// verbose: true,
66-
// logger: console.log,
67-
// tunnelIdentifier: 'npm-build',
68-
// doctor: false
69-
}, function (err, sauceConnectProcess) {
70-
if (err) {
71-
console.error('Error', err.message);
63+
if (useEmbeddedSauceConnect) {
64+
var sauceConnectLauncher = require('sauce-connect-launcher');
65+
console.log('Launching Sauce Connect...')
66+
sauceConnectLauncher(
67+
{
68+
username: process.env.SAUCE_USERNAME,
69+
accessKey: process.env.SAUCE_ACCESS_KEY,
70+
// verbose: true,
71+
// logger: console.log,
72+
// tunnelIdentifier: 'npm-build',
73+
// doctor: false
74+
}, function (err, sauceConnectProcess) {
75+
if (err) {
76+
console.error('Error', err.message);
77+
}
78+
deferred.resolve();
79+
console.log("Sauce Connect ready");
80+
// sauceConnectProcess.close(function () {
81+
// console.log("Closed Sauce Connect process");
82+
// })
7283
}
73-
deferred.resolve();
74-
console.log("Sauce Connect ready");
75-
// sauceConnectProcess.close(function () {
76-
// console.log("Closed Sauce Connect process");
77-
// })
78-
}
79-
);
84+
);
85+
} else {
86+
deferred.resolve();
87+
}
8088
};

ui/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@
6767
"codelyzer": "4.2.1",
6868
"jasmine-core": "2.99.1",
6969
"jasmine-spec-reporter": "4.2.1",
70-
"karma": "2.0.0",
70+
"karma": "2.0.5",
7171
"karma-chrome-launcher": "2.2.0",
72+
"karma-edge-launcher": "0.4.2",
73+
"karma-firefox-launcher": "1.1.0",
74+
"karma-safari-launcher": "1.0.0",
7275
"karma-sauce-launcher": "1.2.0",
7376
"karma-browserstack-launcher": "1.3.0",
7477
"karma-coverage-istanbul-reporter": "2.0.1",
@@ -83,7 +86,7 @@
8386
"browserstack-local": "1.3.3",
8487
"napa": "3.0.0",
8588
"webpack-bundle-analyzer": "2.13.1",
86-
"protractor-docker-plugin": "./protractor-docker-plugin"
89+
"protractor-docker-plugin": "file:protractor-docker-plugin"
8790
},
8891
"napa-config": {
8992
"cache": false

ui/src/app/analytics/analytics.service.spec.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import { ErrorHandler } from '../shared/model/error-handler';
33
import { AnalyticsService } from './analytics.service';
44
import { LoggerService } from '../shared/services/logger.service';
55

6-
xdescribe('AnalyticsService', () => {
6+
describe('AnalyticsService', () => {
77

88
const errorHandler = new ErrorHandler();
99
const notificationService = new MockNotificationService();
1010
const loggerService = new LoggerService();
1111

1212
beforeEach(() => {
13-
this.mockHttp = jasmine.createSpyObj('mockHttp', ['get']);
13+
this.mockHttp = {
14+
get: jasmine.createSpy('get'),
15+
};
1416
this.jsonData = {};
1517
this.analyticsService = new AnalyticsService(this.mockHttp, errorHandler, loggerService, notificationService);
1618
});
@@ -21,13 +23,14 @@ xdescribe('AnalyticsService', () => {
2123
});
2224

2325
it('interval change is correctly set', () => {
26+
const spy = spyOn(this.analyticsService, 'startPollingForCounters');
2427
this.analyticsService.counterInterval = 1;
2528
expect(this.analyticsService._counterInterval).toBe(1);
29+
expect(spy).toHaveBeenCalled();
2630
});
2731

2832
it('zero interval stops polling', () => {
2933
const spy = spyOn(this.analyticsService, 'stopPollingForCounters');
30-
3134
this.analyticsService.counterInterval = 0;
3235
// interval is not set below 1 but spy that polling stops
3336
expect(this.analyticsService._counterInterval).toBe(2);

ui/src/app/analytics/analytics.service.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,10 @@ export class AnalyticsService {
281281
* All pollers of all {@link DashboarItem}s are stopped.
282282
*/
283283
stopAllDashboardPollers() {
284-
for (const dashboardItemToDisable of this.dashboardItems) {
285-
this.stopPollingOfSingleDashboardItem(dashboardItemToDisable);
284+
if (this.dashboardItems) {
285+
for (const dashboardItemToDisable of this.dashboardItems) {
286+
this.stopPollingOfSingleDashboardItem(dashboardItemToDisable);
287+
}
286288
}
287289
}
288290

ui/src/app/apps/app-details/app-details.component.spec.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@ import { ConfirmService } from '../../shared/components/confirm/confirm.service'
2222
import { AppVersionLabelComponent } from '../components/app-versions-label/app-versions-label.component';
2323
import { SortComponent } from '../../shared/components/sort/sort.component';
2424
import { OrderByPipe } from '../../shared/pipes/orderby.pipe';
25-
import { MockModalService } from '../../tests/mocks/modal';
2625
import { AppVersionsComponent } from '../app-versions/app-versions.component';
2726
import { BusyService } from '../../shared/services/busy.service';
2827
import { RoutingStateService } from '../../shared/services/routing-state.service';
2928
import { MockRoutingStateService } from '../../tests/mocks/routing-state';
3029
import { NotificationService } from '../../shared/services/notification.service';
3130
import { LoggerService } from '../../shared/services/logger.service';
31+
import { Observable } from 'rxjs';
3232

3333
/**
3434
* Test {@link AppDetailsComponent}.
3535
*
3636
* @author Damien Vitrac
37+
* @author Gunnar Hillert
3738
*/
3839
describe('AppDetailsComponent', () => {
3940
let component: AppDetailsComponent;
@@ -42,7 +43,8 @@ describe('AppDetailsComponent', () => {
4243
const appsService = new MockAppsService();
4344
const authService = new MockAuthService();
4445
const sharedAboutService = new MocksSharedAboutService();
45-
const modalService = new MockModalService();
46+
47+
let modalService;
4648
const confirmService = new MockConfirmService();
4749
const routingStateService = new MockRoutingStateService();
4850
let activeRoute: MockActivatedRoute;
@@ -77,7 +79,7 @@ describe('AppDetailsComponent', () => {
7779
{ provide: AppsService, useValue: appsService },
7880
{ provide: AuthService, useValue: authService },
7981
{ provide: ActivatedRoute, useValue: activeRoute },
80-
{ provide: BsModalService, useValue: modalService },
82+
BsModalService,
8183
{ provide: ConfirmService, useValue: confirmService },
8284
{ provide: BusyService, useValue: new BusyService() },
8385
{ provide: RoutingStateService, useValue: routingStateService },
@@ -95,6 +97,7 @@ describe('AppDetailsComponent', () => {
9597
component = fixture.componentInstance;
9698
notificationService.clearAll();
9799
appsService.mock = Object.assign({}, sourceMock);
100+
modalService = TestBed.get(BsModalService);
98101
});
99102

100103
describe('Application details', () => {
@@ -273,8 +276,13 @@ describe('AppDetailsComponent', () => {
273276
expect(message).toBeTruthy();
274277
});
275278

276-
it('should open the modal versions', () => {
277-
const spy = spyOn(modalService, 'show');
279+
it('should open the modal versions', () => {
280+
const mockBsModalRef = new BsModalRef();
281+
mockBsModalRef.content = {
282+
open: () => Observable.of('testing')
283+
};
284+
const spy = spyOn(modalService, 'show').and.returnValue(mockBsModalRef);
285+
278286
fixture.debugElement.query(By.css('#no-default-version a')).nativeElement.click();
279287
fixture.detectChanges();
280288
expect(spy).toHaveBeenCalledWith(AppVersionsComponent, { class: 'modal-xl' });

ui/src/app/apps/app-versions/app-versions.component.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ describe('AppVersionsComponent', () => {
124124

125125
it('should display the confirm modal when unregister a version', () => {
126126
const btn: HTMLElement = fixture.debugElement.queryAll(By.css('#table-versions tbody tr .btn-default'))[1].nativeElement;
127-
const spy = spyOn(confirmService, 'open');
127+
const spy = spyOn(confirmService, 'open').and.callThrough();
128128
btn.click();
129129
fixture.detectChanges();
130130
expect(spy).toHaveBeenCalled();

ui/src/app/apps/app-versions/app-versions.component.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Component, EventEmitter, OnDestroy } from '@angular/core';
2-
import { Subscription } from 'rxjs/Subscription';
32
import { AppRegistration } from '../../shared/model/app-registration.model';
43
import { AppsService } from '../apps.service';
54
import { ConfirmService } from '../../shared/components/confirm/confirm.service';

ui/src/app/apps/apps-bulk-import/properties/apps-bulk-import-properties.component.spec.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {BusyService} from '../../../shared/services/busy.service';
99
import {By} from '@angular/platform-browser';
1010
import {AppsBulkImportPropertiesComponent} from './apps-bulk-import-properties.component';
1111
import { NotificationService } from '../../../shared/services/notification.service';
12+
import { Router } from '@angular/router';
1213

1314
/**
1415
* Test {@link AppsBulkImportPropertiesComponent}.
@@ -18,6 +19,7 @@ import { NotificationService } from '../../../shared/services/notification.servi
1819
describe('AppsBulkImportPropertiesComponent', () => {
1920
let component: AppsBulkImportPropertiesComponent;
2021
let fixture: ComponentFixture<AppsBulkImportPropertiesComponent>;
22+
let router;
2123
const bsModalRef = new BsModalRef();
2224
const notificationService = new MockNotificationService();
2325
const appsService = new MockAppsService();
@@ -48,6 +50,7 @@ describe('AppsBulkImportPropertiesComponent', () => {
4850
fixture = TestBed.createComponent(AppsBulkImportPropertiesComponent);
4951
component = fixture.componentInstance;
5052
notificationService.clearAll();
53+
router = TestBed.get(Router);
5154
});
5255

5356
it('should be created', () => {
@@ -82,13 +85,15 @@ describe('AppsBulkImportPropertiesComponent', () => {
8285
});
8386

8487
it('should enable the import action and call the appService.bulkImportApps method', () => {
88+
spyOn(router, 'navigateByUrl');
8589
fixture.detectChanges();
8690
const bt = fixture.debugElement.query(By.css('.footer-actions .btn-primary')).nativeElement;
8791
const inputs = {
8892
properties: fixture.debugElement.query(By.css('#propertiesInput')).nativeElement,
8993
force: fixture.debugElement.query(By.css('#forceInput')).nativeElement
9094
};
91-
const spy = spyOn(appsService, 'bulkImportApps');
95+
const spy = spyOn(appsService, 'bulkImportApps').and.callThrough();
96+
9297
[
9398
{properties: 'foo=http://foo.ly/foo-bar-foo', force: true},
9499
{properties: 'foo=http://foo.ly/foo-bar-foo\nbar=http://foo.ly/foo-bar-foo', force: true}
@@ -102,14 +107,22 @@ describe('AppsBulkImportPropertiesComponent', () => {
102107
bt.click();
103108
});
104109
expect(spy).toHaveBeenCalledTimes(2);
110+
111+
fixture.whenStable().then(() => {
112+
expect((<any>router.navigateByUrl).calls.mostRecent().args[0].toString()).toBe('/apps');
113+
});
105114
});
106115
});
107116

108117
it('should display a toast after a success import', () => {
118+
spyOn(router, 'navigateByUrl');
109119
component.form.get('properties').setValue('foo=http://foo.ly/foo-bar-foo');
110120
component.submit();
111121
fixture.detectChanges();
112122
expect(notificationService.testSuccess[0]).toContain('Apps Imported');
123+
fixture.whenStable().then(() => {
124+
expect((<any>router.navigateByUrl).calls.mostRecent().args[0].toString()).toBe('/apps');
125+
});
113126
});
114127

115128
it('should load a file in the properties input', (done) => {

ui/src/app/apps/apps-bulk-import/properties/apps-bulk-import-properties.component.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AppsService } from '../../apps.service';
77
import { BusyService } from '../../../shared/services/busy.service';
88
import { AppsBulkImportValidator } from '../apps-bulk-import.validator';
99
import { NotificationService } from '../../../shared/services/notification.service';
10+
import { BulkImportParams } from '../../components/apps.interface';
1011

1112
/**
1213
* Applications Bulk Import Properties
@@ -79,7 +80,7 @@ export class AppsBulkImportPropertiesComponent implements OnDestroy {
7980
} catch (e) {
8081
}
8182
}
82-
prepareBulkImportRequest(force, importProps: string) {
83+
prepareBulkImportRequest(force, importProps: string): BulkImportParams {
8384
return {
8485
force: force,
8586
properties: importProps.split('\n'),

0 commit comments

Comments
 (0)