Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: keep state in tabs #779

Merged
merged 9 commits into from
Nov 19, 2024
2 changes: 1 addition & 1 deletion cypress/e2e/debug/runningReports.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('Test Reports in progress warning', () => {

it('If threshold time has been met then show warning', () => {
cy.get('[data-cy-debug="refresh"]').click();
cy.contains(`[One or more reports are in progress for more than ${1 / 1000 / 60} minutes]`);
cy.contains(`[One or more reports are in progress for more than 1 minutes]`);
});
});

65 changes: 65 additions & 0 deletions cypress/e2e/keep_state.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
describe('Tests for keeping state in tabs when switching tabs', () => {

beforeEach(() => {
cy.createReport();
cy.createOtherReport();
cy.initializeApp();
});

afterEach(() => {
cy.clearDebugStore()
cy.clearTestReports()
});

it('should reopen the last opened report in debug tab when switching tabs', () => {
const metadataCellIdentifier = '[data-cy-metadata="storageId"]'
cy.clickRowInTable(0);
cy.clickRootNodeInFileTree();
cy.get('[data-cy-open-metadata-table]').click()
cy.get(metadataCellIdentifier).then((element) => {
const openedReportUid = element.text()
cy.navigateToTestTab();
cy.navigateToDebugTab();
cy.get(metadataCellIdentifier).should('contain.text', openedReportUid);
})
});

it('should keep the same reports selected in debug table', () => {
cy.selectRowInDebugTable(0)
cy.navigateToTestTab();
cy.navigateToDebugTab()
cy.get('[data-cy-debug="selectOne"]').eq(0).should('be.checked')
});

it('should keep the same reports selected in test table', () => {
copyToTestTab(0)
copyToTestTab(1)
cy.intercept({
method: 'GET',
hostname: 'localhost',
url: /\/metadata\/Test\/*?/g,
}).as('test-reports');
cy.navigateToTestTab()
cy.wait('@test-reports').then((result) => {
cy.selectAllRowsInTestTable();
cy.get('[data-cy-test="selectOne"]').eq(1).click()
cy.navigateToDebugTab()
cy.navigateToTestTab();
cy.get('[data-cy-test="selectOne"]').eq(0).should('not.be.checked')
cy.get('[data-cy-test="selectOne"]').eq(1).should('be.checked')
})
});

});

function copyToTestTab(index: number) {
const alias = `copy-report-${index}`
cy.intercept({
method: 'PUT',
hostname: 'localhost',
url: /\/report\/store\/*?/g,
}).as(alias);
cy.clickRowInTable(index)
cy.get('[data-cy-debug-editor="copy"]').click()
cy.wait(`@${alias}`)
}
57 changes: 0 additions & 57 deletions cypress/e2e/settings-component.cy.ts

This file was deleted.

2 changes: 1 addition & 1 deletion cypress/e2e/test/testtab.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('Test the Test tab', () => {
cy.createOtherReport();
cy.initializeApp();
copyTheReportsToTestTab();
cy.navigateToTestTabAndWait();
cy.navigateToTestTabAndInterceptApiCall();
});

afterEach(() => cy.resetApp());
Expand Down
48 changes: 40 additions & 8 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ declare global {

clearTestReports(): Chainable;

navigateToTestTabAndWait(): Chainable;
navigateToTestTabAndInterceptApiCall(): Chainable;

navigateToDebugTabAndWait(): Chainable;
navigateToDebugTabAndInterceptApiCall(): Chainable;

navigateToTestTab(): Chainable;

navigateToDebugTab(): Chainable;

createReport(): Chainable;

Expand Down Expand Up @@ -67,6 +71,12 @@ declare global {
getTestTableRows(): Chainable

assertDebugTableLength(length: number): Chainable;

selectRowInDebugTable(index: number): Chainable;

selectRowInTestTable(index: number): Chainable;

selectAllRowsInTestTable(): Chainable;
}
}
}
Expand All @@ -91,14 +101,21 @@ Cypress.Commands.add('clearTestReports' as keyof Chainable, (): void => {
});
});

Cypress.Commands.add('navigateToTestTabAndWait' as keyof Chainable, (): void => {
navigateToTabAndWait('test');
Cypress.Commands.add('navigateToTestTabAndInterceptApiCall' as keyof Chainable, (): void => {
navigateToTabAndInterceptApiCall('test');
});

Cypress.Commands.add('navigateToDebugTabAndWait' as keyof Chainable, (): void => {
navigateToTabAndWait('debug');
Cypress.Commands.add('navigateToDebugTabAndInterceptApiCall' as keyof Chainable, (): void => {
navigateToTabAndInterceptApiCall('debug');
});

Cypress.Commands.add('navigateToTestTab' as keyof Chainable, () => {
navigateToTab('test');
})
Cypress.Commands.add('navigateToDebugTab' as keyof Chainable, () => {
navigateToTab('debug');
})

Cypress.Commands.add('createReport' as keyof Chainable, (): void => {
// No cy.visit because then the API call can happen multiple times.
cy.request(`${Cypress.env('backendServer')}/index.jsp?createReport=Simple%20report`).then((resp: Cypress.Response<ApiResponse>) => {
Expand Down Expand Up @@ -242,16 +259,27 @@ Cypress.Commands.add('assertDebugTableLength' as keyof Chainable, (length: numbe
: cy.getDebugTableRows().should('have.length', length);
});

Cypress.Commands.add('selectRowInDebugTable' as keyof Chainable, (index: number): Chainable => {
cy.get('[data-cy-debug="selectOne"]').eq(index).click();
})

Cypress.Commands.add('selectRowInTestTable' as keyof Chainable, (index: number): Chainable => {
cy.get('[data-cy-test="selectOne"]').eq(index).click();
})
Cypress.Commands.add('selectAllRowsInTestTable' as keyof Chainable, (): Chainable => {
cy.get('[data-cy-test="toggleSelectAll"]').click()
})

function interceptGetApiCall(alias: string): void {
cy.intercept({
method: 'GET',
hostname: 'localhost',
url: /\/api\/*?/g,
url: /\/metadata\/Debug\/*?/g,
}).as(alias);
}

//More string values can be added for each tab that can be opened
function navigateToTabAndWait(tab: 'debug' | 'test'): void {
function navigateToTabAndInterceptApiCall(tab: 'debug' | 'test'): void {
const apiCallAlias: string = `apiCall${tab}Tab`;
interceptGetApiCall(apiCallAlias);
cy.visit('');
Expand All @@ -261,6 +289,10 @@ function navigateToTabAndWait(tab: 'debug' | 'test'): void {
});
}

function navigateToTab(tab: 'debug' | 'test'): void {
cy.get(`[data-cy-nav-tab="${tab}Tab"]`).click();
}

interface ApiResponse {
status: boolean;
}
29 changes: 28 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy, RouterModule, Routes } from '@angular/router';
import { DebugComponent } from './debug/debug.component';
import { TestComponent } from './test/test.component';
import { CompareComponent } from './compare/compare.component';
Expand Down Expand Up @@ -35,3 +35,30 @@ export const routes: Routes = [
exports: [RouterModule],
})
export class AppRoutingModule {}

export class AppRouteReuseStrategy implements RouteReuseStrategy {
storedRoutes: { [key: string]: DetachedRouteHandle } = {};

shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}

store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
if (route.routeConfig && handle) {
this.storedRoutes[route.routeConfig.path || ''] = handle;
}
}

shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!route.routeConfig && !!this.storedRoutes[route.routeConfig.path || ''];
}

retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (!route.routeConfig) return null;
return this.storedRoutes[route.routeConfig.path || ''];
}

shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
30 changes: 14 additions & 16 deletions src/app/debug/debug.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { Report } from '../shared/interfaces/report';
import { AngularSplitModule } from 'angular-split';
import { TableComponent } from './table/table.component';
import { ReportComponent } from '../report/report.component';
import { ToastService } from '../shared/services/toast.service';
Expand All @@ -14,11 +13,10 @@ import { ErrorHandling } from '../shared/classes/error-handling.service';
templateUrl: './debug.component.html',
styleUrls: ['./debug.component.css'],
standalone: true,
imports: [TableComponent, AngularSplitModule, ReportComponent],
imports: [TableComponent, ReportComponent],
})
export class DebugComponent implements OnInit {
static readonly ROUTER_PATH: string = 'debug';
@Output() openSelectedCompareReportsEvent = new EventEmitter<any>();
@ViewChild('reportComponent') customReportComponent!: ReportComponent;
currentView?: View;
views?: View[];
Expand All @@ -34,7 +32,16 @@ export class DebugComponent implements OnInit {
this.retrieveErrorsAndWarnings();
}

retrieveViews(): void {
protected addReportToTree(report: Report): void {
this.customReportComponent.addReportToTree(report);
}

protected onViewChange(view: View): void {
this.currentView = view;
this.retrieveErrorsAndWarnings();
}

private retrieveViews(): void {
this.httpService
.getViews()
.pipe(catchError(this.errorHandler.handleError()))
Expand All @@ -48,16 +55,7 @@ export class DebugComponent implements OnInit {
});
}

addReportToTree(report: Report): void {
this.customReportComponent.addReportToTree(report);
}

onViewChange(view: View): void {
this.currentView = view;
this.retrieveErrorsAndWarnings();
}

retrieveErrorsAndWarnings(): void {
private retrieveErrorsAndWarnings(): void {
if (this.currentView) {
this.httpService
.getWarningsAndErrors(this.currentView.storageName)
Expand All @@ -72,7 +70,7 @@ export class DebugComponent implements OnInit {
}
}

showErrorsAndWarnings(value: string): void {
private showErrorsAndWarnings(value: string): void {
if (value.length > this.toastService.TOASTER_LINE_LENGTH) {
const errorSnippet: string = value.slice(0, Math.max(0, this.toastService.TOASTER_LINE_LENGTH)).trim();
this.toastService.showDanger(`${errorSnippet}...`, value);
Expand Down
2 changes: 1 addition & 1 deletion src/app/debug/table/table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
</div>
@if (hasTimedOut) {
<h4 class="progress-report-timeout">[One or more reports are in progress for more
than {{ this.reportsInProgressThreshold / 1000 / 60 }} minutes]</h4>
than {{ this.reportsInProgressThreshold ?? 1 / 1000 / 60 }} minutes]</h4>
}
<div data-cy-debug="table" class="table-responsive table-container">
<table mat-table class="table mb-0" matSort [dataSource]="tableDataSource">
Expand Down
Loading
Loading