Skip to content

Commit 9022858

Browse files
committed
Use SchedulerService for all setTimeout/setInterval calls
SchedulerService provides more convenient way of running these async callbacks and internally implements setInterval, setTimeout functions to run outside of angular zone. This is required for protractor tests. Also SchedulerService provides easier testability and maintainability.
1 parent ad68d64 commit 9022858

File tree

9 files changed

+111
-34
lines changed

9 files changed

+111
-34
lines changed
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
1-
import {Component, Input, AfterViewInit} from "@angular/core";
1+
import {Component, Input, AfterViewInit, NgZone, OnDestroy} from "@angular/core";
22
import * as $ from "jquery";
33
import {RuleProviderEntity} from "../generated/windup-services";
4+
import {SchedulerService} from "../shared/scheduler.service";
45

56
declare function prettyPrint();
67

78
@Component({
89
selector: 'wu-rules-modal',
910
templateUrl: 'rules-modal.component.html'
1011
})
11-
export class RulesModalComponent {
12+
export class RulesModalComponent implements OnDestroy {
1213
@Input()
1314
ruleProviderEntity: RuleProviderEntity = <RuleProviderEntity>{};
1415

16+
prettyPrintTimeout: any;
17+
18+
public constructor(private _schedulerService: SchedulerService, private _zone: NgZone) {
19+
}
20+
21+
ngOnDestroy(): void {
22+
if (this.prettyPrintTimeout) {
23+
this._schedulerService.clearTimeout(this.prettyPrintTimeout);
24+
this.prettyPrintTimeout = null;
25+
}
26+
}
27+
1528
show(): void {
1629
(<any>$('#rulesModal')).modal();
17-
setTimeout(() => prettyPrint(), 1000);
30+
this.prettyPrintTimeout = this._schedulerService.setTimeout(() => prettyPrint(), 1000);
1831
}
1932
}

ui/src/main/webapp/src/app/executions/execution-detail.component.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, OnDestroy, OnInit} from "@angular/core";
1+
import {Component, NgZone, OnDestroy, OnInit} from "@angular/core";
22
import {ActivatedRoute, Router} from "@angular/router";
33
import {WindupService} from "../services/windup.service";
44
import {WindupExecution, RegisteredApplication} from "../generated/windup-services";
@@ -12,6 +12,7 @@ import {RuleProviderExecutionsService} from "./rule-provider-executions/rule-pro
1212
import {ExecutionPhaseModel} from "../generated/tsModels/ExecutionPhaseModel";
1313
import {RoutedComponent} from "../shared/routed.component";
1414
import {RouteFlattenerService} from "../core/routing/route-flattener.service";
15+
import {SchedulerService} from "../shared/scheduler.service";
1516

1617
@Component({
1718
templateUrl: './execution-detail.component.html',
@@ -33,7 +34,9 @@ export class ExecutionDetailComponent extends RoutedComponent implements OnInit,
3334
_routeFlattener: RouteFlattenerService,
3435
private _eventBus: EventBusService,
3536
private _windupService: WindupService,
36-
private _ruleProviderExecutionsService: RuleProviderExecutionsService
37+
private _ruleProviderExecutionsService: RuleProviderExecutionsService,
38+
private _schedulerService: SchedulerService,
39+
private _zone: NgZone
3740
) {
3841
super(_router, _activatedRoute, _routeFlattener);
3942
}
@@ -60,16 +63,17 @@ export class ExecutionDetailComponent extends RoutedComponent implements OnInit,
6063
});
6164
}));
6265

63-
this.currentTimeTimer = <any> setInterval(() => {
66+
this.currentTimeTimer = this._schedulerService.setInterval(this._zone.run(() => {
6467
this.currentTime = new Date().getTime();
6568
console.log("Updating the current time field");
66-
}, 5000);
69+
}), 5000);
6770
}
6871

6972
ngOnDestroy(): void {
7073
super.ngOnDestroy();
71-
if (this.currentTimeTimer != null)
72-
clearInterval(this.currentTimeTimer);
74+
if (this.currentTimeTimer != null) {
75+
this._schedulerService.clearInterval(this.currentTimeTimer);
76+
}
7377
}
7478

7579
get loglines(): Observable<string[]> {

ui/src/main/webapp/src/app/executions/executions-list.component.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild} from "@angular/core";
1+
import {Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild} from "@angular/core";
22
import {WindupService} from "../services/windup.service";
33
import {MigrationProject, WindupExecution} from "../generated/windup-services";
44
import {NotificationService} from "../core/notification/notification.service";
@@ -8,6 +8,7 @@ import {MigrationProjectService} from "../project/migration-project.service";
88
import {WindupExecutionService} from "../services/windup-execution.service";
99
import {ConfirmationModalComponent} from "../shared/dialog/confirmation-modal.component";
1010
import {AbstractComponent} from "../shared/AbstractComponent";
11+
import {SchedulerService} from "../shared/scheduler.service";
1112

1213
@Component({
1314
selector: 'wu-executions-list',
@@ -53,7 +54,9 @@ export class ExecutionsListComponent extends AbstractComponent implements OnInit
5354
private _windupService: WindupService,
5455
private _notificationService: NotificationService,
5556
private _sortingService: SortingService<WindupExecution>,
56-
private _projectService: MigrationProjectService
57+
private _projectService: MigrationProjectService,
58+
private _schedulerService: SchedulerService,
59+
private _zone: NgZone
5760
) {
5861
super();
5962
this.element = _elementRef.nativeElement;
@@ -72,14 +75,17 @@ export class ExecutionsListComponent extends AbstractComponent implements OnInit
7275
this.doDeleteExecution(execution);
7376
});
7477

75-
this.currentTimeTimer = <any> setInterval(() => {
76-
this.currentTime = new Date().getTime();
78+
this.currentTimeTimer = this._schedulerService.setInterval(() => {
79+
this._zone.run(() => {
80+
this.currentTime = new Date().getTime();
81+
});
7782
}, 5000);
7883
}
7984

8085
ngOnDestroy(): void {
81-
if (this.currentTimeTimer)
82-
clearInterval(this.currentTimeTimer);
86+
if (this.currentTimeTimer) {
87+
this._schedulerService.clearInterval(this.currentTimeTimer);
88+
}
8389
}
8490

8591
@Input()

ui/src/main/webapp/src/app/executions/project-executions.component.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, OnInit} from "@angular/core";
1+
import {Component, NgZone, OnDestroy, OnInit} from "@angular/core";
22
import {AnalysisContext, MigrationProject, WindupExecution} from "../generated/windup-services";
33
import {RegisteredApplication} from "../generated/windup-services";
44
import {ActivatedRoute} from "@angular/router";
@@ -10,24 +10,28 @@ import {ExecutionEvent} from "../core/events/windup-event";
1010
import {AnalysisContextService} from "../analysis-context/analysis-context.service";
1111
import {NotificationService} from "../core/notification/notification.service";
1212
import {utils} from "../shared/utils";
13+
import {SchedulerService} from "../shared/scheduler.service";
1314

1415
@Component({
1516
templateUrl: './project-executions.component.html'
1617
})
17-
export class ProjectExecutionsComponent extends ExecutionsMonitoringComponent implements OnInit {
18+
export class ProjectExecutionsComponent extends ExecutionsMonitoringComponent implements OnInit, OnDestroy {
1819
executions: WindupExecution[];
1920
private doNotRefreshList: boolean;
2021
private analysisContext: AnalysisContext;
2122

2223
protected showRunAnalysisButton: boolean;
24+
protected refreshTimeout: any;
2325

2426
constructor(
2527
private _activatedRoute: ActivatedRoute,
2628
_windupExecutionService: WindupExecutionService,
2729
private _eventBus: EventBusService,
2830
private _windupService: WindupService,
2931
private _analysisContextService: AnalysisContextService,
30-
private _notificationService: NotificationService
32+
private _notificationService: NotificationService,
33+
private _schedulerService: SchedulerService,
34+
private _ngZone: NgZone
3135
) {
3236
super(_windupExecutionService);
3337
}
@@ -56,13 +60,23 @@ export class ProjectExecutionsComponent extends ExecutionsMonitoringComponent im
5660
this.doNotRefreshList = false;
5761
}
5862

63+
ngOnDestroy(): void {
64+
super.ngOnDestroy();
65+
66+
if (this.refreshTimeout) {
67+
this._schedulerService.clearTimeout(this.refreshTimeout);
68+
this.refreshTimeout = null;
69+
}
70+
}
71+
5972
refreshExecutionList() {
6073
this._windupService.getProjectExecutions(this.project.id).subscribe(executions => {
6174
this.executions = executions;
6275

6376
// If there are cancelled jobs that have not yet had a cancelled date added, then refresh the list
64-
if (this.executions.find(execution => execution.state == "CANCELLED" && execution.timeCompleted == null) != null)
65-
setTimeout(() => this.refreshExecutionList(), 1000);
77+
if (this.executions.find(execution => execution.state == "CANCELLED" && execution.timeCompleted == null) != null) {
78+
this.refreshTimeout = this._schedulerService.setTimeout(this._ngZone.run(() => this.refreshExecutionList()), 1000);
79+
}
6680

6781
this.loadActiveExecutions(this.executions);
6882
});

ui/src/main/webapp/src/app/reports/migration-issues/migration-issues-table.component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ChangeDetectionStrategy, Component, Input, OnInit} from "@angular/core";
1+
import {ChangeDetectionStrategy, Component, Input, NgZone, OnInit} from "@angular/core";
22
import {Router, ActivatedRoute} from "@angular/router";
33
import "../source/prism";
44

@@ -7,6 +7,7 @@ import {NotificationService} from "../../core/notification/notification.service"
77
import {SortingService, OrderDirection} from "../../shared/sort/sorting.service";
88
import {RouteFlattenerService} from "../../core/routing/route-flattener.service";
99
import {FilterableReportComponent} from "../filterable-report.component";
10+
import {SchedulerService} from "../../shared/scheduler.service";
1011

1112
@Component({
1213
selector: 'wu-migration-issues-table',
@@ -32,7 +33,8 @@ export class MigrationIssuesTableComponent extends FilterableReportComponent imp
3233
_activatedRoute: ActivatedRoute,
3334
private _migrationIssuesService: MigrationIssuesService,
3435
private _notificationService: NotificationService,
35-
private _sortingService: SortingService<ProblemSummary>
36+
private _sortingService: SortingService<ProblemSummary>,
37+
private _schedulerService: SchedulerService
3638
) {
3739
super(_router, _activatedRoute, _routeFlattener);
3840
}
@@ -76,7 +78,7 @@ export class MigrationIssuesTableComponent extends FilterableReportComponent imp
7678

7779
private delayedPrismRender() {
7880
// Colorize the included code snippets on the first displaying.
79-
setTimeout(() => Prism.highlightAll(false), 1000);
81+
this._schedulerService.setTimeout(() => Prism.highlightAll(false), 1000);
8082
}
8183

8284
toggleFiles(summary: ProblemSummary) {

ui/src/main/webapp/src/app/reports/migration-issues/problem-summary-files.component.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import {Component, Input, OnInit} from "@angular/core";
1+
import {Component, Input, NgZone, OnDestroy, OnInit} from "@angular/core";
22
import {FileModel} from "../../generated/tsModels/FileModel";
33
import {ActivatedRoute, Router} from "@angular/router";
44
import {GraphJSONToModelService} from "../../services/graph/graph-json-to-model.service";
55
import {PaginationService} from "../../shared/pagination.service";
66

77
import * as showdown from "showdown";
8+
import {SchedulerService} from "../../shared/scheduler.service";
89

910
@Component({
1011
selector: 'wu-problem-summary-files',
1112
templateUrl: './problem-summary-files.component.html',
1213
styleUrls: ['./problem-summary-files.component.scss']
1314
})
14-
export class ProblemSummaryFilesComponent implements OnInit {
15+
export class ProblemSummaryFilesComponent implements OnInit, OnDestroy {
1516
_problemSummaryFiles: any[];
1617

1718
@Input()
@@ -29,11 +30,14 @@ export class ProblemSummaryFilesComponent implements OnInit {
2930

3031
private markdownCache: Map<string, string> = new Map<string, string>();
3132

33+
private renderTimeout;
34+
3235
public constructor(
3336
private _router: Router,
3437
private _activatedRoute: ActivatedRoute,
3538
private _graphJsonToModelService: GraphJSONToModelService<any>,
36-
private _paginationService: PaginationService
39+
private _paginationService: PaginationService,
40+
private _schedulerService: SchedulerService
3741
) {
3842
}
3943

@@ -52,6 +56,13 @@ export class ProblemSummaryFilesComponent implements OnInit {
5256
this.parseExecutedRulesPath();
5357
}
5458

59+
ngOnDestroy(): void {
60+
if (this.renderTimeout) {
61+
this._schedulerService.clearTimeout(this.renderTimeout);
62+
this.renderTimeout = null;
63+
}
64+
}
65+
5566
protected parseExecutedRulesPath() {
5667
let currentUrl = this._activatedRoute.snapshot.pathFromRoot.reduce<string>((accumulator, item) => {
5768
let currentPart = item.url.reduce((acc, itm) => {
@@ -86,7 +97,7 @@ export class ProblemSummaryFilesComponent implements OnInit {
8697
private delayedPrismRender() {
8798
const timeout = 60 * 1000;
8899
// Colorize the included code snippets on the first displaying.
89-
setTimeout(() => Prism.highlightAll(false), timeout);
100+
this.renderTimeout = this._schedulerService.setTimeout(() => Prism.highlightAll(false), timeout);
90101
}
91102

92103
renderMarkdownToHtml(markdownCode: string): string {

ui/src/main/webapp/src/app/shared/js-tree-angular-wrapper.component.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import {
22
Component, OnInit, Input, ElementRef, SimpleChange, Output, EventEmitter, NgZone,
3-
OnChanges
3+
OnChanges, OnDestroy
44
} from "@angular/core";
55
import {Package} from "../generated/windup-services";
66
import * as $ from "jquery";
77
import 'jstree';
8+
import {SchedulerService} from "./scheduler.service";
89

910
/**
1011
* Wrapper for jstree from: https://www.jstree.com/
@@ -14,7 +15,7 @@ import 'jstree';
1415
selector: 'wu-js-tree-wrapper',
1516
host: { 'style': 'display: block; overflow: auto;' }
1617
})
17-
export class JsTreeAngularWrapperComponent implements OnInit, OnChanges {
18+
export class JsTreeAngularWrapperComponent implements OnInit, OnChanges, OnDestroy {
1819
@Input()
1920
treeNodes: TreeData[];
2021

@@ -39,7 +40,9 @@ export class JsTreeAngularWrapperComponent implements OnInit, OnChanges {
3940
protected updateSelectionCallback: Function = () => {};
4041
protected static EMPTY_CALLBACK = () => {};
4142

42-
public constructor(element: ElementRef, private _zone: NgZone) {
43+
protected treeRedrawTimeout: any;
44+
45+
public constructor(element: ElementRef, private _zone: NgZone, private _schedulerService: SchedulerService) {
4346
this.element = element.nativeElement;
4447
}
4548

@@ -60,7 +63,7 @@ export class JsTreeAngularWrapperComponent implements OnInit, OnChanges {
6063

6164
if (changes.hasOwnProperty('selectedNodes')) {
6265
// Another ugly workaround, now to give enough time to initialize jsTree first
63-
setTimeout(() => this.redrawSelection(), 100);
66+
this._schedulerService.setTimeout(this._zone.run(() => this.redrawSelection()), 100);
6467
}
6568
}
6669

@@ -121,6 +124,13 @@ export class JsTreeAngularWrapperComponent implements OnInit, OnChanges {
121124
$(this.element).on('changed.jstree loaded.jstree', (event, data) => this.redrawSelection());
122125
}
123126

127+
ngOnDestroy(): void {
128+
if (this.treeRedrawTimeout) {
129+
this._schedulerService.clearTimeout(this.treeRedrawTimeout);
130+
this.treeRedrawTimeout = null;
131+
}
132+
}
133+
124134
fireNodeClicked(event, data) {
125135
this.nodeClicked.emit(this.treeNodesMap[data.node.id]);
126136
}

ui/src/main/webapp/src/app/shared/notification.component.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {Component, OnDestroy, OnInit, Input} from "@angular/core";
1+
import {Component, OnDestroy, OnInit, Input, NgZone} from "@angular/core";
22
import {NotificationService} from "../core/notification/notification.service";
33
import {Subscription} from "rxjs/Subscription";
44
import {Notification, NotificationLevel} from "../core/notification/notification";
5+
import {SchedulerService} from "./scheduler.service";
56

67
@Component({
78
selector: 'wu-notification',
@@ -21,7 +22,13 @@ export class NotificationComponent implements OnInit, OnDestroy {
2122

2223
notificationsStack: Notification[] = [];
2324

24-
constructor(private _notificationService: NotificationService) {
25+
protected closeTimeoutHandle: any;
26+
27+
constructor(
28+
private _notificationService: NotificationService,
29+
private _schedulerService: SchedulerService,
30+
private _zone: NgZone
31+
) {
2532

2633
}
2734

@@ -37,7 +44,10 @@ export class NotificationComponent implements OnInit, OnDestroy {
3744
this.notificationsStack.push(notification);
3845

3946
if (this.autoCloseNotifications) {
40-
setTimeout(() => this.closeNotification(notification), this.closeTimeout * 1000);
47+
this.closeTimeoutHandle = this._schedulerService.setTimeout(
48+
this._zone.run(() => this.closeNotification(notification)),
49+
this.closeTimeout * 1000
50+
);
4151
}
4252
}
4353

@@ -92,5 +102,10 @@ export class NotificationComponent implements OnInit, OnDestroy {
92102

93103
ngOnDestroy(): any {
94104
this.subscription.unsubscribe();
105+
106+
if (this.closeTimeoutHandle) {
107+
this._schedulerService.clearTimeout(this.closeTimeoutHandle);
108+
this.closeTimeoutHandle = null;
109+
}
95110
}
96111
}

0 commit comments

Comments
 (0)