@@ -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, };