@@ -175,10 +174,8 @@
Testrun
*ngIf="
vm.hasConnectionSettings === true &&
vm.hasDevices &&
- (!vm.systemStatus?.status ||
- vm.systemStatus?.status === StatusOfTestrun.Idle) &&
- vm.isStatusLoaded === true &&
- vm.isTestrunStarted === false
+ (!vm.systemStatus || vm.systemStatus === StatusOfTestrun.Idle) &&
+ vm.isStatusLoaded === true
">
Step 3: Once device is created, you are able to
Testrun
class="settings-drawer">
diff --git a/modules/ui/src/app/app.component.spec.ts b/modules/ui/src/app/app.component.spec.ts
index 643806234..bb6141c33 100644
--- a/modules/ui/src/app/app.component.spec.ts
+++ b/modules/ui/src/app/app.component.spec.ts
@@ -56,13 +56,21 @@ import {
selectHasDevices,
selectInterfaces,
selectIsOpenStartTestrun,
- selectIsTestrunStarted,
+ selectIsOpenWaitSnackBar,
selectMenuOpened,
+ selectStatus,
selectSystemStatus,
} from './store/selectors';
import { MatIconTestingModule } from '@angular/material/icon/testing';
import { CertificatesComponent } from './pages/certificates/certificates.component';
import { of } from 'rxjs';
+import { WINDOW } from './providers/window.provider';
+
+const windowMock = {
+ location: {
+ href: '',
+ },
+};
describe('AppComponent', () => {
let component: AppComponent;
@@ -140,12 +148,13 @@ describe('AppComponent', () => {
{ selector: selectError, value: null },
{ selector: selectMenuOpened, value: false },
{ selector: selectHasDevices, value: false },
- { selector: selectIsTestrunStarted, value: false },
- { selector: selectSystemStatus, value: null },
+ { selector: selectStatus, value: null },
{ selector: selectIsOpenStartTestrun, value: false },
+ { selector: selectIsOpenWaitSnackBar, value: false },
],
}),
{ provide: FocusManagerService, useValue: mockFocusManagerService },
+ { provide: WINDOW, useValue: windowMock },
],
declarations: [
AppComponent,
@@ -443,7 +452,6 @@ describe('AppComponent', () => {
store.overrideSelector(selectHasConnectionSettings, true);
store.overrideSelector(selectHasDevices, true);
store.overrideSelector(selectSystemStatus, MOCK_PROGRESS_DATA_IDLE);
- store.overrideSelector(selectIsTestrunStarted, false);
fixture.detectChanges();
});
@@ -518,7 +526,6 @@ describe('AppComponent', () => {
describe('with devices setted but without systemStatus data', () => {
beforeEach(() => {
store.overrideSelector(selectHasDevices, true);
- store.overrideSelector(selectIsTestrunStarted, false);
component.appStore.updateIsStatusLoaded(true);
store.overrideSelector(selectHasConnectionSettings, true);
store.overrideSelector(selectSystemStatus, null);
@@ -562,7 +569,6 @@ describe('AppComponent', () => {
describe('with devices setted, without systemStatus data, but run the tests ', () => {
beforeEach(() => {
store.overrideSelector(selectHasDevices, true);
- store.overrideSelector(selectIsTestrunStarted, true);
fixture.detectChanges();
});
diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts
index 2cf2869e1..39e4789c6 100644
--- a/modules/ui/src/app/app.component.ts
+++ b/modules/ui/src/app/app.component.ts
@@ -167,7 +167,7 @@ export class AppComponent {
this.appStore.setContent();
}
- isTestrunInProgress(status?: string) {
+ isTestrunInProgress(status?: string | null) {
return this.testRunService.testrunInProgress(status);
}
}
diff --git a/modules/ui/src/app/app.module.ts b/modules/ui/src/app/app.module.ts
index c182fa3ea..26c16c693 100644
--- a/modules/ui/src/app/app.module.ts
+++ b/modules/ui/src/app/app.module.ts
@@ -47,6 +47,7 @@ import { SettingsDropdownComponent } from './pages/settings/components/settings-
import { ShutdownAppComponent } from './components/shutdown-app/shutdown-app.component';
import { WindowProvider } from './providers/window.provider';
import { CertificatesComponent } from './pages/certificates/certificates.component';
+import { LOADER_TIMEOUT_CONFIG_TOKEN } from './services/loaderConfig';
@NgModule({
declarations: [AppComponent, GeneralSettingsComponent],
@@ -89,6 +90,7 @@ import { CertificatesComponent } from './pages/certificates/certificates.compone
useClass: LoadingInterceptor,
multi: true,
},
+ { provide: LOADER_TIMEOUT_CONFIG_TOKEN, useValue: 1000 },
],
bootstrap: [AppComponent],
})
diff --git a/modules/ui/src/app/app.store.spec.ts b/modules/ui/src/app/app.store.spec.ts
index d53840850..bbec6bc7c 100644
--- a/modules/ui/src/app/app.store.spec.ts
+++ b/modules/ui/src/app/app.store.spec.ts
@@ -23,15 +23,24 @@ import {
selectHasConnectionSettings,
selectHasDevices,
selectInterfaces,
- selectIsTestrunStarted,
selectMenuOpened,
- selectSystemStatus,
+ selectStatus,
} from './store/selectors';
import { TestRunService } from './services/test-run.service';
import SpyObj = jasmine.SpyObj;
import { device } from './mocks/device.mock';
-import { setDevices, setTestrunStatus } from './store/actions';
+import { fetchSystemStatus, setDevices } from './store/actions';
import { MOCK_PROGRESS_DATA_IN_PROGRESS } from './mocks/progress.mock';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NotificationService } from './services/notification.service';
+import { WINDOW } from './providers/window.provider';
+import { Routes } from './model/routes';
+
+const windowMock = {
+ location: {
+ href: '',
+ },
+};
const mock = (() => {
let store: { [key: string]: string } = {};
@@ -55,16 +64,25 @@ describe('AppStore', () => {
let appStore: AppStore;
let store: MockStore;
let mockService: SpyObj;
+ let mockNotificationService: SpyObj;
beforeEach(() => {
- mockService = jasmine.createSpyObj(['fetchDevices', 'fetchSystemStatus']);
+ mockService = jasmine.createSpyObj('mockService', ['fetchDevices']);
+ mockNotificationService = jasmine.createSpyObj('mockNotificationService', [
+ 'notify',
+ ]);
TestBed.configureTestingModule({
providers: [
AppStore,
- provideMockStore({}),
+ provideMockStore({
+ selectors: [{ selector: selectStatus, value: null }],
+ }),
{ provide: TestRunService, useValue: mockService },
+ { provide: NotificationService, useValue: mockNotificationService },
+ { provide: WINDOW, useValue: windowMock },
],
+ imports: [BrowserAnimationsModule],
});
store = TestBed.inject(MockStore);
@@ -75,8 +93,6 @@ describe('AppStore', () => {
store.overrideSelector(selectMenuOpened, true);
store.overrideSelector(selectInterfaces, {});
store.overrideSelector(selectError, null);
- store.overrideSelector(selectSystemStatus, MOCK_PROGRESS_DATA_IN_PROGRESS);
- store.overrideSelector(selectIsTestrunStarted, false);
spyOn(store, 'dispatch').and.callFake(() => {});
});
@@ -115,9 +131,8 @@ describe('AppStore', () => {
expect(store).toEqual({
consentShown: false,
hasDevices: true,
- isTestrunStarted: false,
isStatusLoaded: false,
- systemStatus: MOCK_PROGRESS_DATA_IN_PROGRESS,
+ systemStatus: null,
hasConnectionSettings: true,
isMenuOpen: true,
interfaces: {},
@@ -161,27 +176,47 @@ describe('AppStore', () => {
});
describe('getSystemStatus', () => {
- const status = MOCK_PROGRESS_DATA_IN_PROGRESS;
-
- beforeEach(() => {
- mockService.fetchSystemStatus.and.returnValue(of(status));
- });
-
- it('should dispatch action setTestrunStatus', () => {
+ it('should dispatch fetchSystemStatus', () => {
appStore.getSystemStatus();
- expect(store.dispatch).toHaveBeenCalledWith(
- setTestrunStatus({ systemStatus: status })
- );
+ expect(store.dispatch).toHaveBeenCalledWith(fetchSystemStatus());
});
+ });
+ describe('statusLoaded', () => {
it('should update store', done => {
appStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
- expect(store.systemStatus).toEqual(status);
+ expect(store.isStatusLoaded).toEqual(true);
done();
});
- appStore.getSystemStatus();
+ store.overrideSelector(
+ selectStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS.status
+ );
+ store.refreshState();
+ });
+
+ it('should notify when url is not "/testing"', () => {
+ windowMock.location.href = 'localhost:8080';
+ store.overrideSelector(
+ selectStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS.status
+ );
+ store.refreshState();
+
+ expect(mockNotificationService.notify).toHaveBeenCalled();
+ });
+
+ it('should not notify when url is "/testing"', () => {
+ windowMock.location.href = 'localhost:8080/' + Routes.Testing;
+ store.overrideSelector(
+ selectStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS.status
+ );
+ store.refreshState();
+
+ expect(mockNotificationService.notify).toHaveBeenCalledTimes(0);
});
});
});
diff --git a/modules/ui/src/app/app.store.ts b/modules/ui/src/app/app.store.ts
index d214d4848..5c0600033 100644
--- a/modules/ui/src/app/app.store.ts
+++ b/modules/ui/src/app/app.store.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
+import { Inject, Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { tap } from 'rxjs/operators';
import {
@@ -22,28 +22,29 @@ import {
selectHasConnectionSettings,
selectHasDevices,
selectInterfaces,
- selectIsTestrunStarted,
selectMenuOpened,
- selectSystemStatus,
+ selectStatus,
} from './store/selectors';
import { Store } from '@ngrx/store';
import { AppState } from './store/state';
import { TestRunService } from './services/test-run.service';
-import { exhaustMap, Observable } from 'rxjs';
+import { exhaustMap, Observable, skip } from 'rxjs';
import { Device } from './model/device';
import {
setDevices,
- setTestrunStatus,
setIsOpenStartTestrun,
+ fetchSystemStatus,
} from './store/actions';
import { TestrunStatus } from './model/testrun-status';
import { SettingMissedError, SystemInterfaces } from './model/setting';
+import { NotificationService } from './services/notification.service';
+import { Routes } from './model/routes';
+import { WINDOW } from './providers/window.provider';
export const CONSENT_SHOWN_KEY = 'CONSENT_SHOWN';
export interface AppComponentState {
consentShown: boolean;
isStatusLoaded: boolean;
- isTestrunStarted: boolean;
systemStatus: TestrunStatus | null;
}
@Injectable()
@@ -59,13 +60,11 @@ export class AppStore extends ComponentStore {
this.store.select(selectInterfaces);
private settingMissedError$: Observable =
this.store.select(selectError);
- private systemStatus$ = this.store.select(selectSystemStatus);
- private isTestrunStarted$ = this.store.select(selectIsTestrunStarted);
+ systemStatus$: Observable = this.store.select(selectStatus);
viewModel$ = this.select({
consentShown: this.consentShown$,
hasDevices: this.hasDevices$,
- isTestrunStarted: this.isTestrunStarted$,
isStatusLoaded: this.isStatusLoaded$,
systemStatus: this.systemStatus$,
hasConnectionSettings: this.hasConnectionSetting$,
@@ -105,15 +104,22 @@ export class AppStore extends ComponentStore {
);
});
+ statusLoaded = this.effect(() => {
+ return this.systemStatus$.pipe(
+ skip(1),
+ tap(status => {
+ this.updateIsStatusLoaded(true);
+ if (!this.window.location.href.includes(Routes.Testing)) {
+ this.notification.notify(`Test run is ${status}`);
+ }
+ })
+ );
+ });
+
getSystemStatus = this.effect(trigger$ => {
return trigger$.pipe(
- exhaustMap(() => {
- return this.testRunService.fetchSystemStatus().pipe(
- tap((res: TestrunStatus) => {
- this.updateIsStatusLoaded(true);
- this.store.dispatch(setTestrunStatus({ systemStatus: res }));
- })
- );
+ tap(() => {
+ this.store.dispatch(fetchSystemStatus());
})
);
});
@@ -130,12 +136,13 @@ export class AppStore extends ComponentStore {
constructor(
private store: Store,
- private testRunService: TestRunService
+ private testRunService: TestRunService,
+ private notification: NotificationService,
+ @Inject(WINDOW) private window: Window
) {
super({
consentShown: sessionStorage.getItem(CONSENT_SHOWN_KEY) !== null,
isStatusLoaded: false,
- isTestrunStarted: false,
systemStatus: null,
});
}
diff --git a/modules/ui/src/app/interceptors/loading.interceptor.ts b/modules/ui/src/app/interceptors/loading.interceptor.ts
index 6a00847d4..c49ea4bc6 100644
--- a/modules/ui/src/app/interceptors/loading.interceptor.ts
+++ b/modules/ui/src/app/interceptors/loading.interceptor.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
+import { Injectable, NgZone } from '@angular/core';
import {
HttpEvent,
HttpHandler,
@@ -28,19 +28,26 @@ import { LoaderService } from '../services/loader.service';
export class LoadingInterceptor implements HttpInterceptor {
private totalRequests = 0;
- constructor(private loadingService: LoaderService) {}
+ constructor(
+ private loadingService: LoaderService,
+ private zone: NgZone
+ ) {}
intercept(
request: HttpRequest,
next: HttpHandler
): Observable> {
this.totalRequests++;
- this.loadingService.setLoading(true);
+ this.zone.run(() => {
+ this.loadingService.setLoading(true);
+ });
return next.handle(request).pipe(
finalize(() => {
this.totalRequests--;
if (this.totalRequests === 0) {
- this.loadingService.setLoading(false);
+ this.zone.run(() => {
+ this.loadingService.setLoading(false);
+ });
}
})
);
diff --git a/modules/ui/src/app/pages/settings/general-settings.component.ts b/modules/ui/src/app/pages/settings/general-settings.component.ts
index 7c903207c..3698d3f34 100644
--- a/modules/ui/src/app/pages/settings/general-settings.component.ts
+++ b/modules/ui/src/app/pages/settings/general-settings.component.ts
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import {
+ ChangeDetectionStrategy,
Component,
ElementRef,
EventEmitter,
@@ -39,6 +40,7 @@ import { LoaderService } from '../../services/loader.service';
styleUrls: ['./general-settings.component.scss'],
hostDirectives: [CdkTrapFocus],
providers: [SettingsStore],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GeneralSettingsComponent implements OnInit, OnDestroy {
@ViewChild('reloadSettingLink') public reloadSettingLink!: ElementRef;
diff --git a/modules/ui/src/app/pages/testrun/progress.component.spec.ts b/modules/ui/src/app/pages/testrun/progress.component.spec.ts
index 4c22e3cfd..f7ee51aa9 100644
--- a/modules/ui/src/app/pages/testrun/progress.component.spec.ts
+++ b/modules/ui/src/app/pages/testrun/progress.component.spec.ts
@@ -51,7 +51,6 @@ import {
selectIsOpenStartTestrun,
selectIsOpenWaitSnackBar,
selectIsStopTestrun,
- selectIsTestrunStarted,
selectSystemStatus,
} from '../../store/selectors';
import { TestrunStore } from './testrun.store';
@@ -116,7 +115,6 @@ describe('ProgressComponent', () => {
selectors: [
{ selector: selectHasDevices, value: false },
{ selector: selectIsOpenStartTestrun, value: false },
- { selector: selectIsTestrunStarted, value: false },
{ selector: selectIsOpenWaitSnackBar, value: false },
{ selector: selectIsStopTestrun, value: false },
{
@@ -208,15 +206,6 @@ describe('ProgressComponent', () => {
expect(stopTestrunSpy).toHaveBeenCalled();
});
-
- describe('#ngOnInit', () => {
- it('should get systemStatus value', () => {
- const spyOpenSetting = spyOn(component.testrunStore, 'getStatus');
- component.ngOnInit();
-
- expect(spyOpenSetting).toHaveBeenCalled();
- });
- });
});
describe('DOM tests', () => {
diff --git a/modules/ui/src/app/pages/testrun/progress.component.ts b/modules/ui/src/app/pages/testrun/progress.component.ts
index a7a17d425..781715137 100644
--- a/modules/ui/src/app/pages/testrun/progress.component.ts
+++ b/modules/ui/src/app/pages/testrun/progress.component.ts
@@ -31,7 +31,6 @@ import { DeleteFormComponent } from '../../components/delete-form/delete-form.co
import { LoaderService } from '../../services/loader.service';
import { LOADER_TIMEOUT_CONFIG_TOKEN } from '../../services/loaderConfig';
import { FocusManagerService } from '../../services/focus-manager.service';
-import { combineLatest } from 'rxjs/internal/observable/combineLatest';
import { TestrunStore } from './testrun.store';
import { TestRunService } from '../../services/test-run.service';
import { NotificationService } from '../../services/notification.service';
@@ -61,14 +60,10 @@ export class ProgressComponent implements OnInit, OnDestroy {
) {}
ngOnInit(): void {
- this.testrunStore.getStatus();
- combineLatest([
- this.testrunStore.isOpenStartTestrun$,
- this.testrunStore.isTestrunStarted$,
- ])
+ this.testrunStore.isOpenStartTestrun$
.pipe(takeUntil(this.destroy$))
- .subscribe(([isOpenStartTestrun, isTestrunStarted]) => {
- if (isOpenStartTestrun && !isTestrunStarted) {
+ .subscribe(isOpenStartTestrun => {
+ if (isOpenStartTestrun) {
this.openTestRunModal();
}
});
@@ -139,7 +134,6 @@ export class ProgressComponent implements OnInit, OnDestroy {
this.notificationService.dismissSnackBar();
this.destroy$.next(true);
this.destroy$.unsubscribe();
- this.testrunStore.destroyInterval();
}
openTestRunModal(): void {
@@ -160,8 +154,7 @@ export class ProgressComponent implements OnInit, OnDestroy {
window.dataLayer.push({
event: 'successful_testrun_initiation',
});
- this.testrunStore.setIsTestrunStarted(true);
- this.testrunStore.getStatus();
+ this.testrunStore.getSystemStatus();
}
this.testrunStore.setIsOpenStartTestrun(false);
timer(10)
diff --git a/modules/ui/src/app/pages/testrun/testrun.store.spec.ts b/modules/ui/src/app/pages/testrun/testrun.store.spec.ts
index 7cd5de718..1c80a5259 100644
--- a/modules/ui/src/app/pages/testrun/testrun.store.spec.ts
+++ b/modules/ui/src/app/pages/testrun/testrun.store.spec.ts
@@ -15,12 +15,7 @@
*/
import { TestRunService } from '../../services/test-run.service';
import SpyObj = jasmine.SpyObj;
-import {
- discardPeriodicTasks,
- fakeAsync,
- TestBed,
- tick,
-} from '@angular/core/testing';
+import { TestBed } from '@angular/core/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { AppState } from '../../store/state';
import { skip, take, of } from 'rxjs';
@@ -30,12 +25,11 @@ import {
selectIsOpenStartTestrun,
selectIsOpenWaitSnackBar,
selectIsStopTestrun,
- selectIsTestrunStarted,
selectSystemStatus,
} from '../../store/selectors';
import {
+ fetchSystemStatus,
setIsOpenStartTestrun,
- setIsTestrunStarted,
setTestrunStatus,
} from '../../store/actions';
import { TestrunStore } from './testrun.store';
@@ -69,10 +63,7 @@ describe('TestrunStore', () => {
]);
beforeEach(() => {
- mockService = jasmine.createSpyObj('mockService', [
- 'stopTestrun',
- 'fetchSystemStatus',
- ]);
+ mockService = jasmine.createSpyObj('mockService', ['stopTestrun']);
TestBed.configureTestingModule({
providers: [
@@ -84,7 +75,6 @@ describe('TestrunStore', () => {
selectors: [
{ selector: selectHasDevices, value: false },
{ selector: selectSystemStatus, value: null },
- { selector: selectIsTestrunStarted, value: true },
{ selector: selectHasConnectionSettings, value: true },
{ selector: selectIsOpenStartTestrun, value: false },
{ selector: selectIsOpenWaitSnackBar, value: false },
@@ -109,10 +99,8 @@ describe('TestrunStore', () => {
expect(store).toEqual({
hasDevices: false,
systemStatus: null,
- dataSource: undefined,
+ dataSource: [],
stepsToResolveCount: 0,
- isCancelling: false,
- startInterval: false,
});
done();
});
@@ -131,88 +119,27 @@ describe('TestrunStore', () => {
testrunStore.setDataSource(dataSource);
});
-
- it('should update isCancelling', (done: DoneFn) => {
- testrunStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
- expect(store.isCancelling).toEqual(true);
- done();
- });
-
- testrunStore.updateCancelling(true);
- });
-
- it('should update startInterval', (done: DoneFn) => {
- testrunStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
- expect(store.startInterval).toEqual(true);
- done();
- });
-
- testrunStore.updateStartInterval(true);
- });
});
describe('effects', () => {
- describe('getStatus', () => {
- beforeEach(() => {
- testrunStore.updateStartInterval(true);
- mockService.fetchSystemStatus.and.returnValue(
- of({ ...MOCK_PROGRESS_DATA_MONITORING })
- );
- });
+ describe('getSystemStatus', () => {
+ it('should dispatch fetchSystemStatus', () => {
+ testrunStore.getSystemStatus();
- it('should dispatch action setTestrunStatus', () => {
- testrunStore.getStatus();
-
- expect(store.dispatch).toHaveBeenCalledWith(
- setTestrunStatus({
- systemStatus: { ...MOCK_PROGRESS_DATA_MONITORING },
- })
- );
- });
-
- it('should change status to Cancelling if cancelling', () => {
- testrunStore.updateCancelling(true);
- testrunStore.getStatus();
-
- expect(store.dispatch).toHaveBeenCalledWith(
- setTestrunStatus({ systemStatus: MOCK_PROGRESS_DATA_CANCELLING })
- );
- });
-
- describe('pullingSystemStatusData with available status "In Progress"', () => {
- beforeEach(() => {
- mockService.fetchSystemStatus.and.returnValue(
- of({ ...MOCK_PROGRESS_DATA_IN_PROGRESS })
- );
- mockService.fetchSystemStatus.calls.reset();
- });
-
- it('should call again getSystemStatus', fakeAsync(() => {
- testrunStore.updateStartInterval(false);
- testrunStore.updateCancelling(false);
- store.overrideSelector(
- selectSystemStatus,
- MOCK_PROGRESS_DATA_IN_PROGRESS
- );
-
- testrunStore.getStatus();
- expect(mockService.fetchSystemStatus).toHaveBeenCalled();
-
- tick(5000);
-
- expect(mockService.fetchSystemStatus).toHaveBeenCalledTimes(2);
- discardPeriodicTasks();
- }));
+ expect(store.dispatch).toHaveBeenCalledWith(fetchSystemStatus());
});
+ });
+ describe('getStatus', () => {
describe('dataSource', () => {
it('should set value with empty values if result length < total for status "In Progress"', done => {
const expectedResult = TEST_DATA_TABLE_RESULT;
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_IN_PROGRESS)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS
);
- testrunStore.getStatus();
+ store.refreshState();
testrunStore.viewModel$.pipe(take(1)).subscribe(store => {
expect(store.dataSource).toEqual(expectedResult);
@@ -223,10 +150,11 @@ describe('TestrunStore', () => {
it('should set value with empty values for status "Monitoring"', done => {
const expectedResult = EMPTY_RESULT;
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_MONITORING)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_MONITORING
);
- testrunStore.getStatus();
+ store.refreshState();
testrunStore.viewModel$.pipe(take(1)).subscribe(store => {
expect(store.dataSource).toEqual(expectedResult);
@@ -237,10 +165,11 @@ describe('TestrunStore', () => {
it('should set value with empty values for status "Waiting for Device"', done => {
const expectedResult = EMPTY_RESULT;
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_WAITING_FOR_DEVICE)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_WAITING_FOR_DEVICE
);
- testrunStore.getStatus();
+ store.refreshState();
testrunStore.viewModel$.pipe(take(1)).subscribe(store => {
expect(store.dataSource).toEqual(expectedResult);
@@ -251,10 +180,11 @@ describe('TestrunStore', () => {
it('should set value with empty values for status "Cancelled" and empty result', done => {
const expectedResult = EMPTY_RESULT;
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_CANCELLED_EMPTY)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_CANCELLED_EMPTY
);
- testrunStore.getStatus();
+ store.refreshState();
testrunStore.viewModel$.pipe(take(1)).subscribe(store => {
expect(store.dataSource).toEqual(expectedResult);
@@ -264,19 +194,21 @@ describe('TestrunStore', () => {
describe('hideLoading', () => {
it('should called if testrun is finished', () => {
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_COMPLIANT)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_COMPLIANT
);
- testrunStore.getStatus();
+ store.refreshState();
expect(loaderServiceMock.setLoading).toHaveBeenCalledWith(false);
});
it('should called if testrun is in progress and have some test finished', () => {
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_IN_PROGRESS)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS
);
- testrunStore.getStatus();
+ store.refreshState();
expect(loaderServiceMock.setLoading).toHaveBeenCalledWith(false);
});
@@ -284,19 +216,21 @@ describe('TestrunStore', () => {
describe('showLoading', () => {
it('should be called if testrun is monitoring', () => {
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_MONITORING)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_MONITORING
);
- testrunStore.getStatus();
+ store.refreshState();
expect(loaderServiceMock.setLoading).toHaveBeenCalledWith(true);
});
it('should be called if testrun is in progress and have some test finished', () => {
- mockService.fetchSystemStatus.and.returnValue(
- of(MOCK_PROGRESS_DATA_IN_PROGRESS_EMPTY)
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS_EMPTY
);
- testrunStore.getStatus();
+ store.refreshState();
expect(loaderServiceMock.setLoading).toHaveBeenCalledWith(true);
});
@@ -313,6 +247,7 @@ describe('TestrunStore', () => {
testrunStore.stopTestrun();
expect(mockService.stopTestrun).toHaveBeenCalled();
+ expect(store.dispatch).toHaveBeenCalledWith(fetchSystemStatus());
});
});
@@ -326,23 +261,21 @@ describe('TestrunStore', () => {
});
});
- describe('setIsTestrunStarted', () => {
- it('should dispatch action setIsTestrunStarted', () => {
- testrunStore.setIsTestrunStarted(true);
-
- expect(store.dispatch).toHaveBeenCalledWith(
- setIsTestrunStarted({ isTestrunStarted: true })
+ describe('setCancellingStatus', () => {
+ it('should dispatch setTestrunStatus', () => {
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS
);
- });
- });
+ store.refreshState();
- describe('setCancellingStatus', () => {
- it('should update state', done => {
- testrunStore.viewModel$.pipe(skip(1), take(1)).subscribe(store => {
- expect(store.isCancelling).toEqual(true);
- done();
- });
testrunStore.setCancellingStatus();
+
+ expect(store.dispatch).toHaveBeenCalledWith(
+ setTestrunStatus({
+ systemStatus: { ...MOCK_PROGRESS_DATA_CANCELLING },
+ })
+ );
});
});
});
diff --git a/modules/ui/src/app/pages/testrun/testrun.store.ts b/modules/ui/src/app/pages/testrun/testrun.store.ts
index 6b5392355..1e253738f 100644
--- a/modules/ui/src/app/pages/testrun/testrun.store.ts
+++ b/modules/ui/src/app/pages/testrun/testrun.store.ts
@@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { TestRunService } from '../../services/test-run.service';
-import { exhaustMap, interval, Subject, take, timer } from 'rxjs';
+import { exhaustMap, Subject, take, timer } from 'rxjs';
import { tap, withLatestFrom } from 'rxjs/operators';
import { AppState } from '../../store/state';
import { Store } from '@ngrx/store';
@@ -26,13 +26,13 @@ import {
selectIsOpenStartTestrun,
selectIsOpenWaitSnackBar,
selectIsStopTestrun,
- selectIsTestrunStarted,
selectSystemStatus,
} from '../../store/selectors';
import {
+ fetchSystemStatus,
setIsOpenStartTestrun,
- setIsTestrunStarted,
setTestrunStatus,
+ stopInterval,
} from '../../store/actions';
import {
IResult,
@@ -51,24 +51,18 @@ const WAIT_TO_OPEN_SNACKBAR_MS = 60 * 1000;
export interface TestrunComponentState {
dataSource: IResult[] | undefined;
- isCancelling: boolean;
- startInterval: boolean;
stepsToResolveCount: number;
}
@Injectable()
export class TestrunStore extends ComponentStore {
- private destroyInterval$: Subject = new Subject();
private destroyWaitDeviceInterval$: Subject = new Subject();
private dataSource$ = this.select(state => state.dataSource);
- private isCancelling$ = this.select(state => state.isCancelling);
- private startInterval$ = this.select(state => state.startInterval);
private stepsToResolveCount$ = this.select(
state => state.stepsToResolveCount
);
private hasDevices$ = this.store.select(selectHasDevices);
private systemStatus$ = this.store.select(selectSystemStatus);
- isTestrunStarted$ = this.store.select(selectIsTestrunStarted);
isStopTestrun$ = this.store.select(selectIsStopTestrun);
isOpenWaitSnackBar$ = this.store.select(selectIsOpenWaitSnackBar);
isOpenStartTestrun$ = this.store.select(selectIsOpenStartTestrun);
@@ -77,8 +71,6 @@ export class TestrunStore extends ComponentStore {
systemStatus: this.systemStatus$,
dataSource: this.dataSource$,
stepsToResolveCount: this.stepsToResolveCount$,
- isCancelling: this.isCancelling$,
- startInterval: this.startInterval$,
});
setDataSource = this.updater((state, dataSource: IResult[] | undefined) => {
@@ -91,114 +83,91 @@ export class TestrunStore extends ComponentStore {
};
});
- updateCancelling = this.updater((state, isCancelling: boolean) => {
- return {
- ...state,
- isCancelling,
- };
- });
-
- updateStartInterval = this.updater((state, startInterval: boolean) => {
- return {
- ...state,
- startInterval,
- };
- });
-
- getStatus = this.effect(trigger$ => {
+ getSystemStatus = this.effect(trigger$ => {
return trigger$.pipe(
- exhaustMap(() => {
- return this.testRunService.fetchSystemStatus().pipe(
- withLatestFrom(
- this.isCancelling$,
- this.startInterval$,
- this.isOpenWaitSnackBar$
- ),
- // change status if cancelling in process
- tap(([res, isCancelling]) => {
- if (isCancelling && res.status !== StatusOfTestrun.Cancelled) {
- res.status = StatusOfTestrun.Cancelling;
- }
- }),
- // perform some additional actions
- tap(([res, , startInterval, isOpenWaitSnackBar]) => {
- this.store.dispatch(setTestrunStatus({ systemStatus: res }));
+ tap(() => {
+ this.store.dispatch(fetchSystemStatus());
+ })
+ );
+ });
- if (this.testrunInProgress(res.status) && !startInterval) {
- this.pullingSystemStatusData();
- }
- if (
- res.status === StatusOfTestrun.WaitingForDevice &&
- !isOpenWaitSnackBar
- ) {
- this.showSnackBar();
- }
- if (
- res.status !== StatusOfTestrun.WaitingForDevice &&
- isOpenWaitSnackBar
- ) {
- this.notificationService.dismissWithTimout();
- }
- if (
- res.status === StatusOfTestrun.WaitingForDevice ||
- res.status === StatusOfTestrun.Monitoring ||
- (res.status === StatusOfTestrun.InProgress &&
- this.resultIsEmpty(res.tests))
- ) {
- this.showLoading();
- }
- if (
- res.status === StatusOfTestrun.InProgress &&
- !this.resultIsEmpty(res.tests)
- ) {
- this.hideLoading();
- }
- if (
- !this.testrunInProgress(res.status) &&
- res.status !== StatusOfTestrun.Cancelling
- ) {
- this.updateCancelling(false);
- this.destroyInterval$.next(true);
- this.updateStartInterval(false);
- this.hideLoading();
- }
- }),
- // update data source
- tap(([res]) => {
- const results = (res.tests as TestsData)?.results || [];
- if (
- res.status === StatusOfTestrun.Monitoring ||
- res.status === StatusOfTestrun.WaitingForDevice ||
- (res.status === StatusOfTestrun.Cancelled && !results.length)
- ) {
- this.setDataSource(EMPTY_RESULT);
- return;
- }
+ getStatus = this.effect(() => {
+ return this.systemStatus$.pipe(
+ withLatestFrom(this.isOpenWaitSnackBar$),
+ tap(([res, isOpenWaitSnackBar]) => {
+ if (
+ res?.status === StatusOfTestrun.WaitingForDevice &&
+ !isOpenWaitSnackBar
+ ) {
+ this.showSnackBar();
+ }
+ if (
+ res?.status !== StatusOfTestrun.WaitingForDevice &&
+ isOpenWaitSnackBar
+ ) {
+ this.notificationService.dismissWithTimout();
+ }
+ }),
+ // perform some additional actions
+ tap(([res]) => {
+ if (
+ res?.status === StatusOfTestrun.WaitingForDevice ||
+ res?.status === StatusOfTestrun.Monitoring ||
+ (res?.status === StatusOfTestrun.InProgress &&
+ this.resultIsEmpty(res.tests))
+ ) {
+ this.showLoading();
+ }
+ if (
+ (res?.status === StatusOfTestrun.InProgress &&
+ !this.resultIsEmpty(res.tests)) ||
+ (!this.testrunInProgress(res?.status) &&
+ res?.status !== StatusOfTestrun.Cancelling)
+ ) {
+ this.hideLoading();
+ }
+ }),
+ // update data source
+ tap(([res]) => {
+ const results = (res?.tests as TestsData)?.results || [];
+ if (
+ res?.status === StatusOfTestrun.Monitoring ||
+ res?.status === StatusOfTestrun.WaitingForDevice ||
+ (res?.status === StatusOfTestrun.Cancelled && !results.length)
+ ) {
+ this.setDataSource(EMPTY_RESULT);
+ return;
+ }
- const total = (res.tests as TestsData)?.total || 100;
- if (
- res.status === StatusOfTestrun.InProgress &&
- results.length < total
- ) {
- this.setDataSource([
- ...results,
- ...new Array(total - results.length)
- .fill(null)
- .map(() => ({}) as IResult),
- ]);
- return;
- }
+ const total = (res?.tests as TestsData)?.total || 100;
+ if (
+ res?.status === StatusOfTestrun.InProgress &&
+ results.length < total
+ ) {
+ this.setDataSource([
+ ...results,
+ ...new Array(total - results.length)
+ .fill(null)
+ .map(() => ({}) as IResult),
+ ]);
+ return;
+ }
- this.setDataSource(results);
- })
- );
+ this.setDataSource(results);
})
);
});
stopTestrun = this.effect(trigger$ => {
return trigger$.pipe(
exhaustMap(() => {
- return this.testRunService.stopTestrun();
+ this.store.dispatch(stopInterval());
+ return this.testRunService.stopTestrun().pipe(
+ tap(stopped => {
+ if (stopped) {
+ this.getSystemStatus();
+ }
+ })
+ );
})
);
});
@@ -211,28 +180,10 @@ export class TestrunStore extends ComponentStore {
);
});
- setIsTestrunStarted = this.effect(trigger$ => {
- return trigger$.pipe(
- tap(isTestrunStarted => {
- this.store.dispatch(setIsTestrunStarted({ isTestrunStarted }));
- })
- );
- });
-
- destroyInterval = this.effect(trigger$ => {
- return trigger$.pipe(
- tap(() => {
- this.destroyInterval$.next(true);
- this.destroyInterval$.unsubscribe();
- })
- );
- });
-
setCancellingStatus = this.effect(trigger$ => {
return trigger$.pipe(
withLatestFrom(this.systemStatus$),
tap(([, systemStatus]) => {
- this.updateCancelling(true);
if (systemStatus) {
this.store.dispatch(
setTestrunStatus({
@@ -271,16 +222,6 @@ export class TestrunStore extends ComponentStore {
.subscribe();
}
- private pullingSystemStatusData(): void {
- this.updateStartInterval(true);
- interval(5000)
- .pipe(
- takeUntil(this.destroyInterval$),
- tap(() => this.getStatus())
- )
- .subscribe();
- }
-
private getCancellingStatus(systemStatus: TestrunStatus): TestrunStatus {
const status = Object.assign({}, systemStatus);
status.status = StatusOfTestrun.Cancelling;
@@ -307,8 +248,6 @@ export class TestrunStore extends ComponentStore {
private readonly loaderService: LoaderService
) {
super({
- isCancelling: false,
- startInterval: false,
dataSource: undefined,
stepsToResolveCount: 0,
});
diff --git a/modules/ui/src/app/services/test-run.service.ts b/modules/ui/src/app/services/test-run.service.ts
index 375677e82..884881eba 100644
--- a/modules/ui/src/app/services/test-run.service.ts
+++ b/modules/ui/src/app/services/test-run.service.ts
@@ -106,7 +106,10 @@ export class TestRunService {
stopTestrun(): Observable {
return this.http
.post<{ success: string }>(`${API_URL}${SYSTEM_STOP}`, {})
- .pipe(map(() => true));
+ .pipe(
+ catchError(() => of(false)),
+ map(res => !!res)
+ );
}
shutdownTestrun(): Observable {
@@ -170,7 +173,7 @@ export class TestRunService {
};
}
- testrunInProgress(status?: string): boolean {
+ testrunInProgress(status?: string | null): boolean {
return (
status === StatusOfTestrun.InProgress ||
status === StatusOfTestrun.WaitingForDevice ||
diff --git a/modules/ui/src/app/store/actions.ts b/modules/ui/src/app/store/actions.ts
index 4a6081760..a84577709 100644
--- a/modules/ui/src/app/store/actions.ts
+++ b/modules/ui/src/app/store/actions.ts
@@ -96,12 +96,21 @@ export const setIsOpenStartTestrun = createAction(
props<{ isOpenStartTestrun: boolean }>()
);
-export const setIsTestrunStarted = createAction(
- '[Shared] Set Testrun Started',
- props<{ isTestrunStarted: boolean }>()
-);
-
export const setDeviceInProgress = createAction(
'[Shared] Set Device In Progress',
props<{ device: Device | null }>()
);
+
+export const fetchSystemStatus = createAction('[Shared] Fetch system status');
+
+export const fetchSystemStatusSuccess = createAction(
+ '[Shared] Fetch system status success',
+ props<{ systemStatus: TestrunStatus }>()
+);
+
+export const setStatus = createAction(
+ '[Shared] Set Status',
+ props<{ status: string }>()
+);
+
+export const stopInterval = createAction('[Shared] Stop Interval');
diff --git a/modules/ui/src/app/store/effects.spec.ts b/modules/ui/src/app/store/effects.spec.ts
index 2067cd4e6..82f5ce1cf 100644
--- a/modules/ui/src/app/store/effects.spec.ts
+++ b/modules/ui/src/app/store/effects.spec.ts
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { TestBed } from '@angular/core/testing';
+import {
+ discardPeriodicTasks,
+ fakeAsync,
+ TestBed,
+ tick,
+} from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Observable, of } from 'rxjs';
import { AppEffects } from './effects';
@@ -23,24 +28,30 @@ import { Action } from '@ngrx/store';
import * as actions from './actions';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { AppState } from './state';
-import { selectMenuOpened } from './selectors';
+import { selectMenuOpened, selectSystemStatus } from './selectors';
import { device } from '../mocks/device.mock';
import { MOCK_PROGRESS_DATA_IN_PROGRESS } from '../mocks/progress.mock';
+import { fetchSystemStatus, setStatus, setTestrunStatus } from './actions';
describe('Effects', () => {
let actions$ = new Observable();
let effects: AppEffects;
let testRunServiceMock: SpyObj;
let store: MockStore;
-
+ let dispatchSpy: jasmine.Spy;
beforeEach(() => {
- testRunServiceMock = jasmine.createSpyObj([
+ testRunServiceMock = jasmine.createSpyObj('testRunServiceMock', [
'getSystemInterfaces',
'getSystemConfig',
'createSystemConfig',
+ 'fetchSystemStatus',
+ 'testrunInProgress',
]);
testRunServiceMock.getSystemInterfaces.and.returnValue(of({}));
testRunServiceMock.getSystemConfig.and.returnValue(of({}));
testRunServiceMock.createSystemConfig.and.returnValue(of({}));
+ testRunServiceMock.fetchSystemStatus.and.returnValue(
+ of(MOCK_PROGRESS_DATA_IN_PROGRESS)
+ );
TestBed.configureTestingModule({
providers: [
AppEffects,
@@ -53,10 +64,16 @@ describe('Effects', () => {
store = TestBed.inject(MockStore);
effects = TestBed.inject(AppEffects);
+ dispatchSpy = spyOn(store, 'dispatch');
store.refreshState();
});
+ afterEach(() => {
+ dispatchSpy.calls.reset();
+ });
+
it('onSetDevices$ should call setDeviceInProgress when testrun in progress', done => {
+ testRunServiceMock.testrunInProgress.and.returnValue(true);
const status = MOCK_PROGRESS_DATA_IN_PROGRESS;
actions$ = of(actions.setTestrunStatus({ systemStatus: status }));
@@ -206,4 +223,58 @@ describe('Effects', () => {
});
});
});
+
+ it('onFetchSystemStatus$ should call onFetchSystemStatusSuccess on success', done => {
+ actions$ = of(actions.fetchSystemStatus());
+
+ effects.onFetchSystemStatus$.subscribe(action => {
+ expect(action).toEqual(
+ actions.fetchSystemStatusSuccess({
+ systemStatus: MOCK_PROGRESS_DATA_IN_PROGRESS,
+ })
+ );
+ done();
+ });
+ });
+
+ describe('onFetchSystemStatusSuccess$', () => {
+ beforeEach(() => {
+ store.overrideSelector(selectSystemStatus, null);
+ });
+
+ describe('with status "in progress"', () => {
+ beforeEach(() => {
+ store.overrideSelector(selectSystemStatus, null);
+ testRunServiceMock.testrunInProgress.and.returnValue(true);
+ actions$ = of(
+ actions.fetchSystemStatusSuccess({
+ systemStatus: MOCK_PROGRESS_DATA_IN_PROGRESS,
+ })
+ );
+ });
+
+ it('should call fetchSystemStatus for status "in progress"', fakeAsync(() => {
+ effects.onFetchSystemStatusSuccess$.subscribe(() => {
+ tick(5000);
+
+ expect(dispatchSpy).toHaveBeenCalledWith(fetchSystemStatus());
+ discardPeriodicTasks();
+ });
+ }));
+
+ it('should dispatch status and systemStatus', done => {
+ effects.onFetchSystemStatusSuccess$.subscribe(() => {
+ expect(dispatchSpy).toHaveBeenCalledWith(
+ setStatus({ status: MOCK_PROGRESS_DATA_IN_PROGRESS.status })
+ );
+
+ expect(dispatchSpy).toHaveBeenCalledWith(
+ setTestrunStatus({ systemStatus: MOCK_PROGRESS_DATA_IN_PROGRESS })
+ );
+ dispatchSpy.calls.reset();
+ done();
+ });
+ });
+ });
+ });
});
diff --git a/modules/ui/src/app/store/effects.ts b/modules/ui/src/app/store/effects.ts
index c6a5ff499..372b1f683 100644
--- a/modules/ui/src/app/store/effects.ts
+++ b/modules/ui/src/app/store/effects.ts
@@ -14,20 +14,30 @@
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
+import { Injectable, NgZone } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
-import { map, withLatestFrom } from 'rxjs/operators';
+import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import * as AppActions from './actions';
import { AppState } from './state';
import { TestRunService } from '../services/test-run.service';
-import { filter, combineLatest } from 'rxjs';
-import { selectMenuOpened } from './selectors';
-import { StatusOfTestrun } from '../model/testrun-status';
+import { filter, combineLatest, interval, Subject } from 'rxjs';
+import { selectMenuOpened, selectSystemStatus } from './selectors';
+import { IResult, TestsData } from '../model/testrun-status';
+import {
+ fetchSystemStatus,
+ setStatus,
+ setTestrunStatus,
+ stopInterval,
+} from './actions';
+import { takeUntil } from 'rxjs/internal/operators/takeUntil';
@Injectable()
export class AppEffects {
+ private startInterval = false;
+ private destroyInterval$: Subject = new Subject();
+
checkInterfacesInConfig$ = createEffect(() =>
combineLatest([
this.actions$.pipe(ofType(AppActions.fetchInterfacesSuccess)),
@@ -99,22 +109,103 @@ export class AppEffects {
onSetTestrunStatus$ = createEffect(() => {
return this.actions$.pipe(
ofType(AppActions.setTestrunStatus),
- map(({ systemStatus }) =>
- AppActions.setDeviceInProgress({
- device:
- systemStatus.status === StatusOfTestrun.Monitoring ||
- systemStatus.status === StatusOfTestrun.InProgress ||
- systemStatus.status === StatusOfTestrun.WaitingForDevice
- ? systemStatus.device
- : null,
- })
+ map(({ systemStatus }) => {
+ return AppActions.setDeviceInProgress({
+ device: this.testrunService.testrunInProgress(systemStatus?.status)
+ ? systemStatus.device
+ : null,
+ });
+ })
+ );
+ });
+
+ onFetchSystemStatus$ = createEffect(() => {
+ return this.actions$.pipe(
+ ofType(AppActions.fetchSystemStatus),
+ switchMap(() =>
+ this.testrunService.fetchSystemStatus().pipe(
+ map(systemStatus => {
+ return AppActions.fetchSystemStatusSuccess({ systemStatus });
+ })
+ )
)
);
});
+ onStopInterval$ = createEffect(
+ () => {
+ return this.actions$.pipe(
+ ofType(AppActions.stopInterval),
+ tap(() => {
+ this.startInterval = false;
+ this.destroyInterval$.next(true);
+ })
+ );
+ },
+ { dispatch: false }
+ );
+
+ onFetchSystemStatusSuccess$ = createEffect(
+ () => {
+ return this.actions$.pipe(
+ ofType(AppActions.fetchSystemStatusSuccess),
+ tap(({ systemStatus }) => {
+ if (
+ this.testrunService.testrunInProgress(systemStatus.status) &&
+ !this.startInterval
+ ) {
+ this.pullingSystemStatusData();
+ } else if (
+ !this.testrunService.testrunInProgress(systemStatus.status)
+ ) {
+ this.store.dispatch(stopInterval());
+ }
+ }),
+ withLatestFrom(this.store.select(selectSystemStatus)),
+ tap(([{ systemStatus }, status]) => {
+ // for app - requires only status
+ if (systemStatus.status !== status?.status) {
+ this.ngZone.run(() => {
+ this.store.dispatch(setStatus({ status: systemStatus.status }));
+ this.store.dispatch(
+ setTestrunStatus({ systemStatus: systemStatus })
+ );
+ });
+ } else if (
+ systemStatus.finished !== status?.finished ||
+ (systemStatus.tests as TestsData)?.results?.length !==
+ (status?.tests as TestsData)?.results?.length ||
+ (systemStatus.tests as IResult[])?.length !==
+ (status?.tests as IResult[])?.length
+ ) {
+ this.ngZone.run(() => {
+ this.store.dispatch(
+ setTestrunStatus({ systemStatus: systemStatus })
+ );
+ });
+ }
+ })
+ );
+ },
+ { dispatch: false }
+ );
+
+ private pullingSystemStatusData(): void {
+ this.ngZone.runOutsideAngular(() => {
+ this.startInterval = true;
+ interval(5000)
+ .pipe(
+ takeUntil(this.destroyInterval$),
+ tap(() => this.store.dispatch(fetchSystemStatus()))
+ )
+ .subscribe();
+ });
+ }
+
constructor(
private actions$: Actions,
private testrunService: TestRunService,
- private store: Store
+ private store: Store,
+ private ngZone: NgZone
) {}
}
diff --git a/modules/ui/src/app/store/reducers.spec.ts b/modules/ui/src/app/store/reducers.spec.ts
index bea3b69f7..389725fab 100644
--- a/modules/ui/src/app/store/reducers.spec.ts
+++ b/modules/ui/src/app/store/reducers.spec.ts
@@ -25,7 +25,7 @@ import {
setIsOpenStartTestrun,
setIsOpenWaitSnackBar,
setIsStopTestrun,
- setIsTestrunStarted,
+ setStatus,
setTestrunStatus,
toggleMenu,
updateError,
@@ -227,12 +227,17 @@ describe('Reducer', () => {
});
});
- describe('setIsTestrunStarted action', () => {
+ describe('setStatus action', () => {
it('should update state', () => {
const initialState = initialSharedState;
- const action = setIsTestrunStarted({ isTestrunStarted: true });
+ const action = setStatus({
+ status: MOCK_PROGRESS_DATA_CANCELLING.status,
+ });
const state = fromReducer.sharedReducer(initialState, action);
- const newState = { ...initialState, ...{ isTestrunStarted: true } };
+ const newState = {
+ ...initialState,
+ ...{ status: MOCK_PROGRESS_DATA_CANCELLING.status },
+ };
expect(state).toEqual(newState);
expect(state).not.toBe(initialState);
diff --git a/modules/ui/src/app/store/reducers.ts b/modules/ui/src/app/store/reducers.ts
index c7966eb85..1a5a552b3 100644
--- a/modules/ui/src/app/store/reducers.ts
+++ b/modules/ui/src/app/store/reducers.ts
@@ -89,16 +89,16 @@ export const sharedReducer = createReducer(
isOpenStartTestrun,
};
}),
- on(Actions.setIsTestrunStarted, (state, { isTestrunStarted }) => {
+ on(Actions.setDeviceInProgress, (state, { device }) => {
return {
...state,
- isTestrunStarted,
+ deviceInProgress: device,
};
}),
- on(Actions.setDeviceInProgress, (state, { device }) => {
+ on(Actions.setStatus, (state, { status }) => {
return {
...state,
- deviceInProgress: device,
+ status,
};
})
);
diff --git a/modules/ui/src/app/store/selectors.spec.ts b/modules/ui/src/app/store/selectors.spec.ts
index 4b5a0f9eb..3638c987b 100644
--- a/modules/ui/src/app/store/selectors.spec.ts
+++ b/modules/ui/src/app/store/selectors.spec.ts
@@ -26,8 +26,8 @@ import {
selectIsOpenStartTestrun,
selectIsOpenWaitSnackBar,
selectIsStopTestrun,
- selectIsTestrunStarted,
selectMenuOpened,
+ selectStatus,
selectSystemStatus,
} from './selectors';
@@ -50,8 +50,8 @@ describe('Selectors', () => {
isOpenWaitSnackBar: false,
isOpenStartTestrun: false,
systemStatus: null,
- isTestrunStarted: false,
deviceInProgress: null,
+ status: null,
},
};
@@ -95,11 +95,6 @@ describe('Selectors', () => {
expect(result).toEqual(null);
});
- it('should select isTestrunStarted', () => {
- const result = selectIsTestrunStarted.projector(initialState);
- expect(result).toEqual(false);
- });
-
it('should select isOpenStartTestrun', () => {
const result = selectIsOpenStartTestrun.projector(initialState);
expect(result).toEqual(false);
@@ -119,4 +114,9 @@ describe('Selectors', () => {
const result = selectDeviceInProgress.projector(initialState);
expect(result).toEqual(null);
});
+
+ it('should select status', () => {
+ const result = selectStatus.projector(initialState);
+ expect(result).toEqual(null);
+ });
});
diff --git a/modules/ui/src/app/store/selectors.ts b/modules/ui/src/app/store/selectors.ts
index cac0e572e..d1c710db9 100644
--- a/modules/ui/src/app/store/selectors.ts
+++ b/modules/ui/src/app/store/selectors.ts
@@ -64,12 +64,9 @@ export const selectError = createSelector(
export const selectSystemStatus = createSelector(
selectAppState,
- (state: AppState) => state.shared.systemStatus
-);
-
-export const selectIsTestrunStarted = createSelector(
- selectAppState,
- (state: AppState) => state.shared.isTestrunStarted
+ (state: AppState) => {
+ return state.shared.systemStatus;
+ }
);
export const selectIsStopTestrun = createSelector(
@@ -86,3 +83,8 @@ export const selectIsOpenStartTestrun = createSelector(
selectAppState,
(state: AppState) => state.shared.isOpenStartTestrun
);
+
+export const selectStatus = createSelector(
+ selectAppState,
+ (state: AppState) => state.shared.status
+);
diff --git a/modules/ui/src/app/store/state.ts b/modules/ui/src/app/store/state.ts
index 341ee3720..11f52af2b 100644
--- a/modules/ui/src/app/store/state.ts
+++ b/modules/ui/src/app/store/state.ts
@@ -39,9 +39,8 @@ export interface SharedState {
//used in app, devices, testrun
hasDevices: boolean;
//app, testrun
+ status: string | null;
systemStatus: TestrunStatus | null;
- //app, testrun
- isTestrunStarted: boolean;
//app, settings
hasConnectionSettings: boolean | null;
// app, devices
@@ -72,5 +71,5 @@ export const initialSharedState: SharedState = {
deviceInProgress: null,
isOpenStartTestrun: false,
systemStatus: null,
- isTestrunStarted: false,
+ status: null,
};