diff --git a/src/app/items-api.service.ts b/src/app/items-api.service.ts index 5fdacf2f..a194c35b 100644 --- a/src/app/items-api.service.ts +++ b/src/app/items-api.service.ts @@ -74,4 +74,8 @@ export class ItemsApiService { setData(data) { this.response.next(data); } + + fetchProcessingItems(projectName, scenarioName): Observable<[]> { + return this.http.get<[]>(`projects/${projectName}/scenarios/${scenarioName}/processing-items`); + } } diff --git a/src/app/items.service.ts b/src/app/items.service.ts index d4d68c84..d33e0a3b 100644 --- a/src/app/items.service.ts +++ b/src/app/items.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, interval } from 'rxjs'; import { Items } from './items.service.model'; import { ItemsApiService } from './items-api.service'; @@ -8,6 +8,10 @@ import { ItemsApiService } from './items-api.service'; }) export class ItemsService { + public interval; + + private processingItems = new BehaviorSubject<[]>([]); + public processingItems$ = this.processingItems.asObservable(); private items = new BehaviorSubject({ name, data: [], total: 0 }); public items$ = this.items.asObservable(); @@ -21,4 +25,15 @@ export class ItemsService { .subscribe(_ => this.items.next(_)); } + fetchProcessingItems(projectName, scenarioName) { + return this.itemsApiService.fetchProcessingItems(projectName, scenarioName).subscribe((_) => this.processingItems.next(_)); + } + + processingItemsInterval(projectName, scenarioName) { + this.fetchProcessingItems(projectName, scenarioName); + this.interval = interval(5000).subscribe(() => { + return this.fetchProcessingItems(projectName, scenarioName); + }); + } + } diff --git a/src/app/project.service.ts b/src/app/project.service.ts index 2d40cf5c..12a3ab46 100644 --- a/src/app/project.service.ts +++ b/src/app/project.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@angular/core'; import { ProjectApiService } from './project-api.service'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, interval } from 'rxjs'; import { ProjectsListing } from './project-api.service.model'; import { IScenarios, Items } from './items.service.model'; -import { scenarioHistoryGraphs } from './graphs/scenario-trends'; import { ScenarioApiService } from './scenario-api.service'; @Injectable({ @@ -11,6 +10,7 @@ import { ScenarioApiService } from './scenario-api.service'; }) export class ProjectService { + public processingItemsInterval; private state = new BehaviorSubject([]); public state$ = this.state.asObservable(); @@ -21,9 +21,6 @@ export class ProjectService { private scenarios = new BehaviorSubject([]); public scenarios$ = this.scenarios.asObservable(); - private trends = new BehaviorSubject<{}>({}); - public trends$ = this.trends.asObservable(); - constructor( private projectApiService: ProjectApiService, private scenarioApiService: ScenarioApiService @@ -53,9 +50,4 @@ export class ProjectService { .subscribe(_ => this.scenarios.next(_)); } - fetchScenarioTrends(projectName, scenarioName) { - this.scenarioApiService.fetchScenarioTrend(projectName, scenarioName) - .subscribe(_ => this.trends.next(scenarioHistoryGraphs(_, projectName, scenarioName))); - } - } diff --git a/src/app/scenario.service.spec.ts b/src/app/scenario.service.spec.ts new file mode 100644 index 00000000..2c57337a --- /dev/null +++ b/src/app/scenario.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; + +import { ScenarioService } from './scenario.service'; +import { HttpClientModule } from '@angular/common/http'; + +describe('ScenarioService', () => { + beforeEach(() => TestBed.configureTestingModule({ + imports: [ + HttpClientModule + ], + })); + + it('should be created', () => { + const service: ScenarioService = TestBed.get(ScenarioService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/scenario.service.ts b/src/app/scenario.service.ts new file mode 100644 index 00000000..5ddeb6e1 --- /dev/null +++ b/src/app/scenario.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, interval } from 'rxjs'; +import { scenarioHistoryGraphs } from './graphs/scenario-trends'; +import { ScenarioApiService } from './scenario-api.service'; + +@Injectable({ + providedIn: 'root' +}) + +export class ScenarioService { + + private trends = new BehaviorSubject<{}>({}); + public trends$ = this.trends.asObservable(); + + constructor( + private scenarioApiService: ScenarioApiService + ) { } + + + fetchScenarioTrends(projectName, scenarioName) { + this.scenarioApiService.fetchScenarioTrend(projectName, scenarioName) + .subscribe(_ => this.trends.next(scenarioHistoryGraphs(_, projectName, scenarioName))); + } + +} diff --git a/src/app/scenario/add-new-item/add-new-item.component.ts b/src/app/scenario/add-new-item/add-new-item.component.ts index efb1f344..05fb910b 100644 --- a/src/app/scenario/add-new-item/add-new-item.component.ts +++ b/src/app/scenario/add-new-item/add-new-item.component.ts @@ -11,6 +11,7 @@ import { ItemsService } from 'src/app/items.service'; import { ProjectService } from 'src/app/project.service'; import { ItemStatus } from './add-new-item.model'; import { ItemStatusValue } from 'src/app/item-detail/item-detail.model'; +import { ScenarioService } from 'src/app/scenario.service'; @Component({ selector: 'app-add-new-item-modal', @@ -35,9 +36,9 @@ export class AddNewItemComponent implements OnInit { constructor( private route: ActivatedRoute, private modalService: NgbModal, - private itemsService: ItemsService, private itemsApiService: ItemsApiService, - private projectService: ProjectService, + private itemService: ItemsService, + private scenarioService: ScenarioService, private notification: NotificationMessage, private spinner: NgxSpinnerService ) { } @@ -103,8 +104,8 @@ export class AddNewItemComponent implements OnInit { .pipe(catchError(r => of(r))) .subscribe(_ => { const message = this.notification.newTestItemNotificationMessage(_); - this.itemsService.fetchItems(this.routeParams.projectName, this.routeParams.scenarioName); - this.projectService.fetchScenarioTrends(this.routeParams.projectName, this.routeParams.scenarioName); + this.itemService.fetchProcessingItems(this.routeParams.projectName, this.routeParams.scenarioName); + this.scenarioService.fetchScenarioTrends(this.routeParams.projectName, this.routeParams.scenarioName); this.spinner.hide(); return this.itemsApiService.setData(message); }); diff --git a/src/app/scenario/scenario.component.html b/src/app/scenario/scenario.component.html index 35c3ca79..b363bafb 100644 --- a/src/app/scenario/scenario.component.html +++ b/src/app/scenario/scenario.component.html @@ -52,7 +52,8 @@
Error rate
Test Runs - {{items.total}} tests + {{items.total}} + {{processingItems.inprogress?.length}}
diff --git a/src/app/scenario/scenario.component.scss b/src/app/scenario/scenario.component.scss index 3f5d9ea8..40237a4b 100644 --- a/src/app/scenario/scenario.component.scss +++ b/src/app/scenario/scenario.component.scss @@ -140,7 +140,15 @@ tbody tr:hover { .total { font-size: 15px; float: right; -} + color: #28a746 !important; +} + +.in-progress { + font-size: 15px; + float: right; + margin-right: 15px; + color: #f8cd0bea !important; +} .base-icon { color: #0066ffd1 @@ -149,4 +157,3 @@ tbody tr:hover { .status-icon { font-size: 19px; } - \ No newline at end of file diff --git a/src/app/scenario/scenario.component.ts b/src/app/scenario/scenario.component.ts index 8716ae1f..f88675c7 100644 --- a/src/app/scenario/scenario.component.ts +++ b/src/app/scenario/scenario.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { of, Observable } from 'rxjs'; -import { switchMap, catchError, withLatestFrom } from 'rxjs/operators'; +import { of, Observable, Subscription } from 'rxjs'; +import { switchMap, catchError } from 'rxjs/operators'; import { ProjectOverview, Items } from '../items.service.model'; import { ProjectService } from '../project.service'; import { ItemsService } from '../items.service'; import { SharedMainBarService } from '../shared-main-bar.service'; +import { ScenarioService } from '../scenario.service'; const LIMIT = 15; const OFFSET = 15; @@ -15,24 +16,33 @@ const OFFSET = 15; templateUrl: './scenario.component.html', styleUrls: ['./scenario.component.scss', '../shared-styles.css'], }) -export class ScenarioComponent implements OnInit { +export class ScenarioComponent implements OnInit, OnDestroy { projectName: string; overview$: Observable; items$: Observable; trends$: Observable<{}>; + processingItems$: Observable<{}>; params; page = 1; pageSize = LIMIT; + currentProcessingItems = []; + processingItems; + subscription: Subscription; constructor( private route: ActivatedRoute, - private projectService: ProjectService, + private scenarioService: ScenarioService, private itemsService: ItemsService, private router: Router, - private sharedMainBarService: SharedMainBarService + private sharedMainBarService: SharedMainBarService, ) { this.items$ = itemsService.items$; - this.trends$ = projectService.trends$; + this.trends$ = scenarioService.trends$; + } + + ngOnDestroy() { + this.itemsService.interval.unsubscribe(); + this.subscription.unsubscribe(); } ngOnInit() { @@ -41,18 +51,31 @@ export class ScenarioComponent implements OnInit { this.params = routeParams; this.projectName = routeParams.projectName; this.sharedMainBarService.setProjectName(this.projectName); - this.itemsService.fetchItems(this.projectName, this.params.scenarioName, { limit: LIMIT, offset: 0}); - this.projectService.fetchScenarioTrends(this.projectName, this.params.scenarioName); + this.itemsService.fetchItems(this.projectName, this.params.scenarioName, { limit: LIMIT, offset: 0 }); + this.scenarioService.fetchScenarioTrends(this.projectName, this.params.scenarioName); + this.itemsService.processingItemsInterval(this.projectName, this.params.scenarioName); return new Observable().pipe(catchError(err => of([]))); }) - ).subscribe(_ => { + ).subscribe(_ => { + }); + this.subscription = this.itemsService.processingItems$.subscribe((_) => { + this.processingItems = _; + const { inprogress } = _ as any; + if (Array.isArray(inprogress)) { + const processingItems = inprogress.map((item) => item.id); + const reloadItems = !this.currentProcessingItems.every((id) => processingItems.includes(id)); + if (reloadItems) { + this.itemsService.fetchItems(this.projectName, this.params.scenarioName, { limit: LIMIT, offset: 0 }); + } + return this.currentProcessingItems = inprogress.map((item) => item.id); + } }); } loadMore() { const offset = (this.page - 1) * OFFSET; - this.itemsService.fetchItems(this.projectName, this.params.scenarioName, { limit: LIMIT, offset }); + this.itemsService.fetchItems(this.projectName, this.params.scenarioName, { limit: LIMIT, offset }); } open(itemId) {