From 7642938f61707904436636319637738af40f3396 Mon Sep 17 00:00:00 2001 From: Matthijs Smets <93487259+MatthijsSmets@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:36:10 +0200 Subject: [PATCH] Refactor: refactor compare tab (#512) * refactor: removed the jqx-widgets library and refactored the compare tab because of it * refactor: process pr comments * Test new CICD job --- .github/workflows/testing.js.yml | 12 + angular.json | 9 +- package.json | 4 +- pnpm-lock.yaml | 25 +- src/app/compare/compare-data.ts | 3 - .../compare-tree/compare-tree.component.css | 17 +- .../compare-tree/compare-tree.component.html | 22 +- .../compare-tree.component.spec.ts | 4 +- .../compare-tree/compare-tree.component.ts | 293 +++++++----------- src/app/compare/compare.component.css | 11 + src/app/compare/compare.component.html | 43 ++- src/app/compare/compare.component.ts | 103 +++--- .../debug/debug-tree/debug-tree.component.ts | 2 +- src/app/debug/table/table.component.html | 20 +- src/app/debug/table/table.component.ts | 16 +- src/app/shared/enums/compare-method.ts | 5 - src/app/shared/enums/node-link-strategy.ts | 2 + src/app/shared/pipes/str-replace.pipe.ts | 11 + src/app/shared/services/helper.service.ts | 139 --------- src/app/shared/util/report-util.ts | 11 +- src/app/test/test.component.ts | 2 - src/main.ts | 9 +- src/styles.css | 4 + 23 files changed, 292 insertions(+), 475 deletions(-) delete mode 100644 src/app/shared/enums/compare-method.ts create mode 100644 src/app/shared/enums/node-link-strategy.ts create mode 100644 src/app/shared/pipes/str-replace.pipe.ts diff --git a/.github/workflows/testing.js.yml b/.github/workflows/testing.js.yml index ef669242..4cfed3a2 100644 --- a/.github/workflows/testing.js.yml +++ b/.github/workflows/testing.js.yml @@ -69,6 +69,18 @@ jobs: run: pnpm install --frozen-lockfile working-directory: ladybug-frontend + - name: Install Cypress + run: npx cypress install + working-directory: ladybug-frontend + + - uses: actions/cache@v4 + name: Cache Cypress + with: + path: ~/.cache/Cypress + key: ${{ runner.os }}-cypress-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-cypress- + - name: Set Cypress environment variables run: cp cypress.env.json.cicd cypress.env.json working-directory: ladybug-frontend diff --git a/angular.json b/angular.json index 29ad0cb3..b2bab741 100644 --- a/angular.json +++ b/angular.json @@ -55,8 +55,7 @@ "node_modules/bootstrap/dist/css/bootstrap.min.css", "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.css", - "node_modules/font-awesome/css/font-awesome.css", - "./node_modules/jqwidgets-ng/jqwidgets/styles/jqx.base.css" + "node_modules/font-awesome/css/font-awesome.css" ], "scripts": [] }, @@ -129,11 +128,7 @@ "styles": [ "node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "node_modules/bootstrap/dist/css/bootstrap.min.css", - "src/styles.css", - "node_modules/jqwidgets-ng/jqwidgets/styles/jqx.base.css" - ], - "scripts": [ - "node_modules/jquery/dist/jquery.slim.min.js" + "src/styles.css" ] } }, diff --git a/package.json b/package.json index 920f85cb..fe5deebc 100644 --- a/package.json +++ b/package.json @@ -47,10 +47,8 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^54.0.0", "font-awesome": "^4.7.0", - "jquery": "^3.7.1", - "jqwidgets-ng": "^14.1.17", "monaco-editor": "0.44.0", - "ng-simple-file-tree": "^0.1.13", + "ng-simple-file-tree": "^0.1.19", "ngx-monaco-editor-v2": "17.0.1", "path": "^0.12.7", "popper.js": "^1.16.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a95eea00..c23f5902 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,18 +86,12 @@ importers: font-awesome: specifier: ^4.7.0 version: 4.7.0 - jquery: - specifier: ^3.7.1 - version: 3.7.1 - jqwidgets-ng: - specifier: ^14.1.17 - version: 14.1.20 monaco-editor: specifier: 0.44.0 version: 0.44.0 ng-simple-file-tree: - specifier: ^0.1.13 - version: 0.1.14(bootstrap-icons@1.11.3) + specifier: ^0.1.19 + version: 0.1.19(@angular/common@17.0.8(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(rxjs@7.4.0))(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(bootstrap-icons@1.11.3) ngx-monaco-editor-v2: specifier: 17.0.1 version: 17.0.1(@angular/common@17.0.8(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(rxjs@7.4.0))(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(monaco-editor@0.44.0) @@ -3717,9 +3711,6 @@ packages: jquery@3.7.1: resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==} - jqwidgets-ng@14.1.20: - resolution: {integrity: sha512-rApVo4/k1M4aj3AI5bn2nG/5pKmeCGSuCFTynLS0F0/iXYV6iegPC8bNzwui0qkQJ19a7I5IW3yyi1LaxJpttA==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4143,9 +4134,11 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - ng-simple-file-tree@0.1.14: - resolution: {integrity: sha512-Hg85jSqF2YNoFsjIIiKyMFjjJpOZ5Vg3lUe8G0fE0OwFkIZGh+ub/lfRd5DMXnBd5mzQ9UEal+ygDCCrC096pw==} + ng-simple-file-tree@0.1.19: + resolution: {integrity: sha512-WtoMPalo6oLAqouaH57OJSU0csIpqDbkR7fl4udTgZYpGS9/80WJV9W4wlwXD3qGpaRw13z/HepItlrHggK7Cg==} peerDependencies: + '@angular/common': ^17.2.3 + '@angular/core': ^17.3.2 bootstrap-icons: ^1.11.3 ngx-monaco-editor-v2@17.0.1: @@ -10027,8 +10020,6 @@ snapshots: jquery@3.7.1: {} - jqwidgets-ng@14.1.20: {} - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -10457,8 +10448,10 @@ snapshots: neo-async@2.6.2: {} - ng-simple-file-tree@0.1.14(bootstrap-icons@1.11.3): + ng-simple-file-tree@0.1.19(@angular/common@17.0.8(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(rxjs@7.4.0))(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(bootstrap-icons@1.11.3): dependencies: + '@angular/common': 17.0.8(@angular/core@17.0.8(rxjs@7.4.0)(zone.js@0.14.3))(rxjs@7.4.0) + '@angular/core': 17.0.8(rxjs@7.4.0)(zone.js@0.14.3) bootstrap-icons: 1.11.3 tslib: 2.3.1 diff --git a/src/app/compare/compare-data.ts b/src/app/compare/compare-data.ts index f25c53e0..30ae72a4 100644 --- a/src/app/compare/compare-data.ts +++ b/src/app/compare/compare-data.ts @@ -1,9 +1,6 @@ -import { NodeLinkStrategy } from '../shared/enums/compare-method'; - export class CompareData { id!: string; originalReport: any; runResultReport: any; viewName: string = ''; - nodeLinkStrategy: NodeLinkStrategy = NodeLinkStrategy.NONE; } diff --git a/src/app/compare/compare-tree/compare-tree.component.css b/src/app/compare/compare-tree/compare-tree.component.css index 40172c0b..edf6df32 100644 --- a/src/app/compare/compare-tree/compare-tree.component.css +++ b/src/app/compare/compare-tree/compare-tree.component.css @@ -1,18 +1,5 @@ -/*#tree-container {*/ -/* background: linear-gradient(#e9ecef, #e9ecef) no-repeat center/2px 100%;*/ -/*}*/ - -.compare-header { - display: flex; - align-items: center; - height: 50px; - border-bottom: 1px solid #dee2e6; -} - .trees-container { + display: flex; + flex-direction: row; height: 46vh; } - -label { - margin: 0; -} diff --git a/src/app/compare/compare-tree/compare-tree.component.html b/src/app/compare/compare-tree/compare-tree.component.html index 069bffdf..f8e8d5da 100644 --- a/src/app/compare/compare-tree/compare-tree.component.html +++ b/src/app/compare/compare-tree/compare-tree.component.html @@ -1,20 +1,4 @@ -
-
- - -
-
-
- -
-
- -
-
+
+ +
diff --git a/src/app/compare/compare-tree/compare-tree.component.spec.ts b/src/app/compare/compare-tree/compare-tree.component.spec.ts index bdb1ab0a..aea8b069 100644 --- a/src/app/compare/compare-tree/compare-tree.component.spec.ts +++ b/src/app/compare/compare-tree/compare-tree.component.spec.ts @@ -1,8 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CompareTreeComponent } from './compare-tree.component'; -import { jqxTreeModule } from 'jqwidgets-ng/jqxtree'; import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { NgSimpleFileTreeModule } from 'ng-simple-file-tree'; describe('CompareTreeComponent', () => { let component: CompareTreeComponent; @@ -10,7 +10,7 @@ describe('CompareTreeComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [jqxTreeModule, CompareTreeComponent, HttpClientTestingModule], + imports: [NgSimpleFileTreeModule, CompareTreeComponent, HttpClientTestingModule], }).compileComponents(); }); diff --git a/src/app/compare/compare-tree/compare-tree.component.ts b/src/app/compare/compare-tree/compare-tree.component.ts index 3fc66fbf..11024438 100644 --- a/src/app/compare/compare-tree/compare-tree.component.ts +++ b/src/app/compare/compare-tree/compare-tree.component.ts @@ -1,209 +1,156 @@ -import { Component, EventEmitter, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { Report } from '../../shared/interfaces/report'; -import { HelperService } from '../../shared/services/helper.service'; -import { jqxTreeComponent, jqxTreeModule } from 'jqwidgets-ng/jqxtree'; -import { NodeLinkStrategy } from '../../shared/enums/compare-method'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TitleCasePipe } from '@angular/common'; + +import { ReportHierarchyTransformer } from '../../shared/classes/report-hierarchy-transformer'; +import { ReportUtil } from '../../shared/util/report-util'; +import { CompareData } from '../compare-data'; +import { Checkpoint } from '../../shared/interfaces/checkpoint'; +import { NodeLinkStrategy } from '../../shared/enums/node-link-strategy'; +import { + CreateTreeItem, + FileTreeItem, + FileTreeOptions, + NgSimpleFileTree, + NgSimpleFileTreeModule, + OptionalParameters, +} from 'ng-simple-file-tree'; + +export const treeSideConst = ['left', 'right'] as const; +export type TreeSide = (typeof treeSideConst)[number]; @Component({ selector: 'app-compare-tree', templateUrl: './compare-tree.component.html', styleUrls: ['./compare-tree.component.css'], standalone: true, - imports: [ReactiveFormsModule, jqxTreeModule], + imports: [ReactiveFormsModule, TitleCasePipe, FormsModule, NgSimpleFileTreeModule], }) export class CompareTreeComponent { - constructor(private helperService: HelperService) {} - @Output() compareEvent = new EventEmitter(); - @Output() changeNodeLinkStrategyEvent = new EventEmitter(); - @ViewChild('leftTreeReference') leftTreeReference!: jqxTreeComponent; - @ViewChild('rightTreeReference') rightTreeReference!: jqxTreeComponent; - nodeLinkStrategy: NodeLinkStrategy = NodeLinkStrategy.NONE; - viewName: string = ''; - leftReport: any; - rightReport: any; - - public get NodeLinkStrategy(): typeof NodeLinkStrategy { - return NodeLinkStrategy; - } - - nodeSelected(data: any, left: boolean) { - this.selectOtherNode(data, left); - this.compareEvent.emit({ - leftReport: this.leftReport, - rightReport: this.rightReport, - }); + @Output() compareEvent: EventEmitter = new EventEmitter(); + @ViewChild('leftTree') leftTree!: NgSimpleFileTree; + @ViewChild('rightTree') rightTree!: NgSimpleFileTree; + @Input({ required: true }) nodeLinkStrategy!: NodeLinkStrategy; + @Input({ required: true }) compareData!: CompareData; + leftReport?: Report; + rightReport?: Report; + left?: Report; + leftTreeOptions: FileTreeOptions = { + highlightOpenFolders: false, + folderBehaviourOnClick: 'select', + expandAllFolders: true, + }; + + rightTreeOptions: FileTreeOptions = { + highlightOpenFolders: false, + folderBehaviourOnClick: 'select', + expandAllFolders: true, + determineFontColor: (item: CreateTreeItem) => this.setRedLabels(item), + }; + + createTrees(leftReport: Report, rightReport: Report): void { + this.leftReport = new ReportHierarchyTransformer().transform(leftReport); + this.rightReport = new ReportHierarchyTransformer().transform(rightReport); + const optional: OptionalParameters = { childrenKey: 'checkpoints' }; + this.leftTree.addItem(this.leftReport, optional); + this.rightTree.addItem(this.rightReport, optional); + this.selectFirstItem(); } - selectOtherNode(data: any, left: boolean) { - if (this.nodeLinkStrategy === 'NONE') { - this.compareOnNothing(data, left); - } else { - this.compareOnSomething(this.nodeLinkStrategy, data, left); + setRedLabels(item: CreateTreeItem): string { + if (ReportUtil.isReport(item)) { + return this.namesMatch(item.name, this.leftReport?.name); } - } - compareOnSomething(method: string, data: any, left: boolean) { - let treeReference: jqxTreeComponent = left ? this.rightTreeReference : this.leftTreeReference; - let selectedReport = data.owner.selectedItem; - - let otherReport = - method === 'PATH' - ? this.getOtherReportBasedOnPath(selectedReport, treeReference) - : this.getOtherReportBasedOnCheckpointNumber(selectedReport, treeReference); - - if (otherReport) { - this.unfoldTree(treeReference, otherReport, otherReport.parentId); - treeReference.selectItem(otherReport); - } else { - treeReference.selectItem(null); + if (ReportUtil.isCheckPoint(item)) { + const checkpoint: Checkpoint | null = this.findCorrespondingCheckpoint(item, this.leftReport); + return this.namesMatch(item.name, checkpoint?.name); } - - this.leftReport = left ? selectedReport : otherReport; - this.rightReport = left ? otherReport : selectedReport; - } - - getOtherReportBasedOnCheckpointNumber(selectedReport: any, treeReference: jqxTreeComponent) { - let checkpointNumber = selectedReport.value.index; - return treeReference.getItems().find((item: any) => checkpointNumber == item.value.index); - } - - getOtherReportBasedOnPath(selectedReport: any, treeReference: jqxTreeComponent) { - let path = this.getFullPath(selectedReport, [selectedReport.value.name]); - return this.matchFullPath(treeReference.getItems()[0], path); - } - compareOnNothing(data: any, left: boolean) { - left ? (this.leftReport = data.owner.selectedItem) : (this.rightReport = data.owner.selectedItem); - } - - changeNodeLinkStrategy(event: any) { - this.nodeLinkStrategy = event.target.value; - this.changeNodeLinkStrategyEvent.next(this.nodeLinkStrategy); - localStorage.setItem(this.viewName + '.NodeLinkStrategy', this.nodeLinkStrategy); - } - getParent(checkpoint: any, parentId: string): any { - let items = checkpoint.treeInstance.items; - return items.find((item: any) => item.id == parentId); + return ''; } - unfoldTree(treeReference: any, checkpoint: any, parentId: string) { - while (parentId.toString() !== '0') { - treeReference.expandItem(checkpoint); - parentId = checkpoint.parentId; - checkpoint = this.getParent(checkpoint, checkpoint.parentId); + selectFirstItem(): void { + if (ReportUtil.isReport(this.leftReport!)) { + this.leftTree.selectItem(this.leftReport.path); + } + if (ReportUtil.isReport(this.rightReport)) { + this.rightTree.selectItem(this.rightReport.path); } + this.compareEvent.emit(); } - getFullPath(checkpoint: any, pathSoFar: string[]): string[] { - let parent = this.getParent(checkpoint, checkpoint.parentId); - if (parent) { - pathSoFar.push(parent.value.name); - return this.getFullPath(parent, pathSoFar); + syncTrees(treeSide: TreeSide): void { + if (treeSide === 'left') { + this.selectItem(this.rightTree, this.leftTree.getSelected()); + } else if (treeSide === 'right') { + this.selectItem(this.leftTree, this.rightTree.getSelected()); } - - return pathSoFar; } - matchFullPath(checkpoint: any, path: string[]): any { - if (path.length === 0) return checkpoint; - - let toBeSelected = null; - let firstPart = path.shift(); - checkpoint.treeInstance.items.every((item: any) => { - if (item.value.name === firstPart) { - let result = this.checkIfSameParents(item, item.parentId, path); - if (result) { - toBeSelected = item; - return false; //Breaking since we found him; - } + selectItem(otherSide: NgSimpleFileTree, treeItem: FileTreeItem): void { + switch (this.nodeLinkStrategy) { + case 'PATH': { + otherSide.selectItem(treeItem.path); + break; + } + case 'CHECKPOINT_NUMBER': { + this.selectByCheckPointNumber(otherSide, treeItem.originalValue as Report | Checkpoint); } - return true; - }); - - return toBeSelected; - } - - checkIfSameParents(checkpoint: string, parentId: string, path: string[]): boolean { - if (path.length === 0) return true; - let parent = this.getParent(checkpoint, parentId); - if (parent && parent.value.name == path[0]) { - path.shift(); - return this.checkIfSameParents(parent, parent.parentId, path); } - return false; + this.compareEvent.emit(); } - createTrees(viewName: string, nodeLinkStrategy: NodeLinkStrategy, leftReport: Report, rightReport: Report) { - this.viewName = viewName; - this.nodeLinkStrategy = nodeLinkStrategy; - let nls = localStorage.getItem(this.viewName + '.NodeLinkStrategy'); - if (nls) { - this.nodeLinkStrategy = NodeLinkStrategy[nls as keyof typeof NodeLinkStrategy]; + selectByCheckPointNumber(tree: NgSimpleFileTree, treeItem: Report | Checkpoint): void { + if (ReportUtil.isReport(treeItem)) { + tree.selectItem(tree.getItems()[0].path); + } else if (ReportUtil.isCheckPoint(treeItem)) { + this.selectCheckpoint(tree, tree.getItems()[0], treeItem); } - const leftTree = this.helperService.convertReportToJqxTree(leftReport); - const rightTree = this.helperService.convertReportToJqxTree(rightReport); - const both = this.iterateToMakeLabelsRed(leftTree, rightTree); - - this.leftTreeReference.createComponent({ - height: '100%', - source: [both.left], - }); - this.rightTreeReference.createComponent({ - height: '100%', - source: [both.right], - }); - - this.selectFirstItem(); - } - - selectFirstItem() { - this.leftReport = this.leftTreeReference.getItems()[0]; - this.rightReport = this.rightTreeReference.getItems()[0]; - this.leftTreeReference.selectItem(this.leftReport); - this.rightTreeReference.selectItem(this.rightReport); - this.compareEvent.emit({ - leftReport: this.leftReport, - rightReport: this.rightReport, - }); - } - - makeLabelsRed(left: any, right: any) { - this.redLabel(left); - this.redLabel(right); - } - - redLabel(item: any) { - item.label = `${item.label}`; } - iterateToMakeLabelsRed(leftItem: any, rightItem: any) { - let result = this.checkIfLabelsDifferent(leftItem, rightItem); - const shortestTreeLength = Math.min(leftItem.items.length, rightItem.items.length); - this.makeRestOfTreesRed(shortestTreeLength, rightItem.items, leftItem.items); - - for (let i = 0; i < shortestTreeLength; i++) { - let both = this.iterateToMakeLabelsRed(leftItem.items[i], rightItem.items[i]); - leftItem.items[i] = both.left; - rightItem.items[i] = both.right; + selectCheckpoint(tree: NgSimpleFileTree, item: FileTreeItem, checkpointToMatch: Checkpoint): void { + if (item.children) { + for (const child of item.children) { + const checkpoint = child.originalValue as Checkpoint; + if (this.getCheckpointId(checkpoint.uid) === this.getCheckpointId(checkpointToMatch.uid)) { + tree.selectItem(child.path); + return; + } else { + this.selectCheckpoint(tree, child, checkpointToMatch); + } + } } - - return result; } - makeRestOfTreesRed(startPoint: number, leftItems: any[], rightItems: any[]) { - let items = leftItems; - if (rightItems.length > leftItems.length) items = rightItems; - for (let i = startPoint; i < items.length; i++) { - this.redLabel(items[i]); + namesMatch(name1: string | undefined, name2?: string | undefined): string { + if (name1 && name2 && name1 === name2) { + return ''; } - } - - checkIfLabelsDifferent(left: any, right: any): { left: any; right: any } { - if (left.level > -1) { - if (left.value.message !== right.value.message) this.makeLabelsRed(left, right); - } else { - if (left.value.xml !== right.value.xml) this.makeLabelsRed(left, right); + return 'red'; + } + + findCorrespondingCheckpoint( + itemToMatch: Checkpoint | Report, + report: Report | Checkpoint | undefined, + ): Checkpoint | null { + if (report) { + const checkpoints: Checkpoint[] = report.checkpoints ?? []; + for (let checkpoint of checkpoints) { + if ( + ReportUtil.isCheckPoint(itemToMatch) && + this.getCheckpointId(checkpoint.uid) === this.getCheckpointId(itemToMatch.uid) + ) { + return checkpoint; + } + if (checkpoint.checkpoints) { + return this.findCorrespondingCheckpoint(itemToMatch, checkpoint); + } + } } + return null; + } - return { left: left, right: right }; + getCheckpointId(uid: string): string { + return uid.split('#')[1]; } } diff --git a/src/app/compare/compare.component.css b/src/app/compare/compare.component.css index 2d2ecfd0..f5bd78c9 100644 --- a/src/app/compare/compare.component.css +++ b/src/app/compare/compare.component.css @@ -5,6 +5,17 @@ height: 94vh; } +.compare-header { + display: flex; + align-items: center; + height: 50px; + border-bottom: 1px solid #dee2e6; + + & > label { + margin: 0; + } +} + .trees { resize: vertical; overflow: auto; diff --git a/src/app/compare/compare.component.html b/src/app/compare/compare.component.html index 4c699d07..249258a2 100644 --- a/src/app/compare/compare.component.html +++ b/src/app/compare/compare.component.html @@ -1,21 +1,36 @@
+
+ + +
+ @if (compareData) { - -
+ [compareData]="compareData" + [nodeLinkStrategy]="nodeLinkStrategy" + (compareEvent)="showDifference()" + /> + } +
+ @if (compareData) {
- - + +
- -
- } + } + +
+
diff --git a/src/app/compare/compare.component.ts b/src/app/compare/compare.component.ts index 3903ca06..24028be9 100644 --- a/src/app/compare/compare.component.ts +++ b/src/app/compare/compare.component.ts @@ -1,37 +1,49 @@ import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { CompareTreeComponent } from './compare-tree/compare-tree.component'; -import { NodeLinkStrategy } from '../shared/enums/compare-method'; import { CompareData } from './compare-data'; import { DiffEditorModel, MonacoEditorModule } from 'ngx-monaco-editor-v2'; import { TabService } from '../shared/services/tab.service'; import { ActivatedRoute, Router } from '@angular/router'; import { DisplayTableComponent } from '../shared/components/display-table/display-table.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TitleCasePipe } from '@angular/common'; +import { NodeLinkStrategy, nodeLinkStrategyConst } from '../shared/enums/node-link-strategy'; +import { Report } from '../shared/interfaces/report'; +import { Checkpoint } from '../shared/interfaces/checkpoint'; +import { ReportUtil } from '../shared/util/report-util'; +import { StrReplacePipe } from '../shared/pipes/str-replace.pipe'; @Component({ selector: 'app-compare', templateUrl: './compare.component.html', styleUrls: ['./compare.component.css'], standalone: true, - imports: [CompareTreeComponent, DisplayTableComponent, MonacoEditorModule], + imports: [ + CompareTreeComponent, + DisplayTableComponent, + MonacoEditorModule, + ReactiveFormsModule, + TitleCasePipe, + FormsModule, + StrReplacePipe, + ], }) -export class CompareComponent implements OnInit, AfterViewInit { - static readonly leftReportKey: string = 'leftId'; - static readonly rightReportKey: string = 'rightId'; +export class CompareComponent implements AfterViewInit, OnInit { static readonly ROUTER_PATH: string = 'compare'; - @ViewChild('trees') compareTreeComponent!: CompareTreeComponent; - diffOptions = { theme: 'vs', language: 'xml', readOnly: true, renderSideBySide: true, automaticLayout: true }; - originalModel: DiffEditorModel = { - code: '', + @ViewChild(CompareTreeComponent) compareTreeComponent!: CompareTreeComponent; + protected readonly nodeLinkStrategyConst = nodeLinkStrategyConst; + protected nodeLinkStrategy!: NodeLinkStrategy; + protected diffOptions = { + theme: 'vs', language: 'xml', + readOnly: true, + renderSideBySide: true, + automaticLayout: true, }; + protected originalModel: DiffEditorModel = { code: '', language: 'xml' }; + protected modifiedModel: DiffEditorModel = { code: '', language: 'xml' }; - modifiedModel: DiffEditorModel = { - code: '', - language: 'xml', - }; - id?: string; - - compareData: CompareData | undefined; + protected compareData?: CompareData; constructor( public tabService: TabService, @@ -41,60 +53,55 @@ export class CompareComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.compareData = this.getData(this.getIdsFromPath()); + this.getStrategyFromLocalStorage(); + } + + ngAfterViewInit(): void { if (this.compareData) { this.renderDiffs(this.compareData.originalReport.xml, this.compareData.runResultReport.xml); + this.showReports(); } else { this.router.navigate(['debug']); } } - ngAfterViewInit(): void { - setTimeout(() => { - this.showReports(); - }, 100); + private getData(id: string): CompareData | undefined { + return this.tabService.activeCompareTabs.get(id); } - getIdsFromPath(): string { + private getIdsFromPath(): string { return this.route.snapshot.paramMap.get('id') as string; } - getData(id: string): CompareData | undefined { - return this.tabService.activeCompareTabs.get(id); + private renderDiffs(leftSide: string, rightSide: string): void { + this.originalModel = { ...this.originalModel, code: leftSide }; + this.modifiedModel = { ...this.originalModel, code: rightSide }; } - showReports(): void { - if (this.compareData && this.compareData.originalReport && this.compareData.runResultReport) { - this.compareTreeComponent?.createTrees( - this.compareData.viewName, - this.compareData.nodeLinkStrategy, - this.compareData.originalReport, - this.compareData.runResultReport, - ); + private getStrategyFromLocalStorage(): void { + if (this.compareData) { + const strategy: string | null = localStorage.getItem(this.compareData.viewName + '.NodeLinkStrategy'); + this.nodeLinkStrategy = strategy ? (strategy as NodeLinkStrategy) : 'NONE'; } } - showDifference(data: any): void { - const leftSide = data.leftReport ? this.extractMessage(data.leftReport) : ''; - const rightSide = data.rightReport ? this.extractMessage(data.rightReport) : ''; - this.renderDiffs(leftSide, rightSide); + private showReports(): void { + this.compareTreeComponent.createTrees(this.compareData!.originalReport, this.compareData!.runResultReport); } - changeNodeLinkStrategy(nodeLinkStrategy: NodeLinkStrategy): void { - if (this.compareData) { - this.compareData.nodeLinkStrategy = nodeLinkStrategy; - } + protected showDifference(): void { + const leftSide = this.extractMessage(this.compareTreeComponent.leftTree.getSelected().originalValue); + const rightSide = this.extractMessage(this.compareTreeComponent.rightTree.getSelected().originalValue); + this.renderDiffs(leftSide, rightSide); } - extractMessage(report: any): string { - return report.parentElement ? report.value.message ?? '' : report.value.xml ?? ''; + private extractMessage(selectedNode: Report | Checkpoint): string { + return ReportUtil.isReport(selectedNode) ? selectedNode.xml : selectedNode.message; } - renderDiffs(leftSide: string, rightSide: string): void { - this.originalModel = Object.assign({}, this.originalModel, { - code: leftSide, - }); - this.modifiedModel = Object.assign({}, this.originalModel, { - code: rightSide, - }); + protected changeNodeLinkStrategy(): void { + if (this.compareData && this.nodeLinkStrategy) { + localStorage.setItem(this.compareData.viewName + '.NodeLinkStrategy', this.nodeLinkStrategy); + } } } diff --git a/src/app/debug/debug-tree/debug-tree.component.ts b/src/app/debug/debug-tree/debug-tree.component.ts index 83ddfd6e..bbf4a7ec 100644 --- a/src/app/debug/debug-tree/debug-tree.component.ts +++ b/src/app/debug/debug-tree/debug-tree.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { Report } from '../../shared/interfaces/report'; import { HelperService } from '../../shared/services/helper.service'; -import { Observable, Subscription, catchError, of } from 'rxjs'; +import { catchError, Observable, Subscription } from 'rxjs'; import { HttpService } from '../../shared/services/http.service'; import { SettingsService } from '../../shared/services/settings.service'; import { diff --git a/src/app/debug/table/table.component.html b/src/app/debug/table/table.component.html index 19b469ec..0ae61795 100644 --- a/src/app/debug/table/table.component.html +++ b/src/app/debug/table/table.component.html @@ -75,7 +75,7 @@
+ [value]="tableSettings.numberOfReportsInProgress" [max]="tableSettings.numberOfReportsInProgress"/>
+ [value]="tableSettings.numberOfReportsInProgress" [max]="tableSettings.numberOfReportsInProgress"/>
@@ -106,7 +106,7 @@

[One or more reports are in progress for more } @case (2) { - } @@ -117,8 +117,9 @@

[One or more reports are in progress for more - + *matCellDef="let row" + [style.padding]="getTableSpacing()" + [title]="header"> + {{ getMetadata(row, getMetadataNameFromHeader(header))|tableCellShortener }} + } diff --git a/src/app/debug/table/table.component.ts b/src/app/debug/table/table.component.ts index f82e4744..32525bfd 100644 --- a/src/app/debug/table/table.component.ts +++ b/src/app/debug/table/table.component.ts @@ -31,6 +31,7 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { View } from '../../shared/interfaces/view'; import { OptionsSettings } from '../../shared/interfaces/options-settings'; import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; +import { CompareReport } from '../../shared/interfaces/compare-reports'; @Component({ selector: 'app-table', @@ -123,6 +124,7 @@ export class TableComponent implements OnInit, OnDestroy { protected selectedReportStorageId?: number; tableDataSource: MatTableDataSource = new MatTableDataSource(); tableDataSort?: MatSort; + @ViewChild(MatSort) set matSort(sort: MatSort) { this.tableDataSort = sort; this.tableDataSource.sort = this.tableDataSort; @@ -372,12 +374,12 @@ export class TableComponent implements OnInit, OnDestroy { } compareTwoReports(): void { - let compareReports: any = {}; const selectedReports: number[] = this.tableSettings.reportMetadata .filter((report) => report.checked) .map((report) => report.storageId); + this.httpService.getReports(selectedReports, this.currentView.storageName).subscribe({ - next: (data) => { + next: (data: Record) => { const leftObject = data[selectedReports[0]]; const originalReport = leftObject.report; originalReport.xml = leftObject.xml; @@ -387,19 +389,15 @@ export class TableComponent implements OnInit, OnDestroy { runResultReport.xml = rightObject.xml; const id = this.helperService.createCompareTabId(originalReport, runResultReport); - compareReports = { + + this.tabService.openNewCompareTab({ id: id, originalReport: originalReport, runResultReport: runResultReport, viewName: this.currentView.name, - nodeLinkStrategy: this.currentView.nodeLinkStrategy, - }; + }); }, error: () => catchError(this.errorHandler.handleError()), - - complete: () => { - this.tabService.openNewCompareTab(compareReports); - }, }); } diff --git a/src/app/shared/enums/compare-method.ts b/src/app/shared/enums/compare-method.ts deleted file mode 100644 index 280f4d3b..00000000 --- a/src/app/shared/enums/compare-method.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum NodeLinkStrategy { - PATH = 'PATH', - CHECKPOINT_NUMBER = 'CHECKPOINT_NUMBER', - NONE = 'NONE', -} diff --git a/src/app/shared/enums/node-link-strategy.ts b/src/app/shared/enums/node-link-strategy.ts new file mode 100644 index 00000000..cfed8d0f --- /dev/null +++ b/src/app/shared/enums/node-link-strategy.ts @@ -0,0 +1,2 @@ +export const nodeLinkStrategyConst = ['PATH', 'CHECKPOINT_NUMBER', 'NONE'] as const; +export type NodeLinkStrategy = (typeof nodeLinkStrategyConst)[number]; diff --git a/src/app/shared/pipes/str-replace.pipe.ts b/src/app/shared/pipes/str-replace.pipe.ts new file mode 100644 index 00000000..a45cc292 --- /dev/null +++ b/src/app/shared/pipes/str-replace.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'strReplace', + standalone: true, +}) +export class StrReplacePipe implements PipeTransform { + transform(value: string, pattern: string, replace: string): string { + return value.replace(pattern, replace); + } +} diff --git a/src/app/shared/services/helper.service.ts b/src/app/shared/services/helper.service.ts index 524370fe..c38bfb96 100644 --- a/src/app/shared/services/helper.service.ts +++ b/src/app/shared/services/helper.service.ts @@ -6,61 +6,6 @@ import { Report } from '../interfaces/report'; providedIn: 'root', }) export class HelperService { - THROWABLE_ENCODER: string = 'printStackTrace()'; - - constructor() {} - - getImage(type: number, encoding: string, level: number): string { - const even = this.determineEvenCheckpoint(level); - let img = `assets/tree-icons/${this.getCheckpointType(type)}`; - if (encoding === this.THROWABLE_ENCODER) { - img += '-error'; - } - if (even) { - return `${img}-even.gif`; - } - return `${img}-odd.gif`; - } - - private determineEvenCheckpoint(level: number) { - return level % 2 == 0; - } - - getCheckpointType(type: number): string { - switch (type) { - case 1: { - return 'startpoint'; - } - case 2: { - return 'endpoint'; - } - case 3: { - return 'abortpoint'; - } - case 4: { - return 'inputpoint'; - } - case 5: { - return 'outputpoint'; - } - case 6: { - return 'infopoint'; - } - case 7: { - return 'threadStartpoint-error'; - } // Doesn't exist? - case 8: { - return 'threadStartpoint'; - } - case 9: { - return 'threadEndpoint'; - } - default: { - return ''; - } - } - } - download(queryString: string, storage: string, exportBinary: boolean, exportXML: boolean): void { window.open( 'api/report/download/' + storage + '/' + exportBinary + '/' + exportXML + '?' + queryString.slice(0, -1), @@ -96,90 +41,6 @@ export class HelperService { button.target.innerHTML = type; } - convertReportToJqxTree(report: Report) { - let index: number = 0; - let parentMap: any[] = []; - - const showingId: string = this.getCheckpointOrStorageId(report, true); - let rootNode = this.createNode(report, showingId, '', index++, -1); - this.createChildNodes(rootNode, index, parentMap); - return rootNode; - } - - createNode(report: Report, showingId: string, icon: string, index: number, level: number) { - const expanded: boolean = level <= 0; - return { - label: showingId + report.name, - icon: icon, - value: report, - expanded: expanded, - id: Math.random(), - index: index, - items: [] as any[], - level: level, - }; - } - - getCheckpointOrStorageId(checkpoint: any, root: boolean): string { - if (root && localStorage.getItem('showReportStorageIds')) { - return localStorage.getItem('showReportStorageIds') === 'true' ? `${[checkpoint.storageId]}` : ''; - } else if (localStorage.getItem('showCheckpointIds')) { - return localStorage.getItem('showCheckpointIds') === 'true' ? `${checkpoint.index}. ` : ''; - } else { - return ''; - } - } - - createChildNodes(rootNode: any, index: number, parentMap: any[]): void { - let previousNode = rootNode; - let checkpoints: any[] = previousNode.value.checkpoints; - - if (checkpoints && checkpoints.length > 0) { - for (let checkpoint of checkpoints) { - const img: string = this.getImage(checkpoint.type, checkpoint.encoding, checkpoint.level); - const showingId: string = this.getCheckpointOrStorageId(checkpoint, false); - const currentNode: any = this.createNode(checkpoint, showingId, img, index++, checkpoint.level); - this.createHierarchy(previousNode, currentNode, parentMap); - previousNode = currentNode; - } - } - } - - createHierarchy(previousNode: any, node: any, parentMap: any[]): void { - // If the level is higher, then the previous node was its parent - if (node.level > previousNode.level) { - this.addChild(previousNode, node, parentMap); - - // If the level is lower, then the previous node is a (grand)child of this node's sibling - } else if (node.level < previousNode.level) { - this.findParent(node, previousNode, parentMap); - - // Else the level is equal, meaning the previous node is its sibling - } else { - const newParent: any = parentMap.find((x): boolean => x.id == previousNode.id).parent; - this.addChild(newParent, node, parentMap); - } - } - - findParent(currentNode: any, potentialParent: any, parentMap: any[]): any { - if (currentNode.level < 0) { - currentNode.value.path = `[INVALID LEVEL ${currentNode.value.level}] ${currentNode.value.name}`; - currentNode.level = 0; - } else if (currentNode.level - 1 == potentialParent.level) { - // If the level difference is only 1, then the potential parent is the actual parent - this.addChild(potentialParent, currentNode, parentMap); - return currentNode; - } - - const newPotentialParent: any = parentMap.find((node): boolean => node.id == potentialParent.id).parent; - return this.findParent(currentNode, newPotentialParent, parentMap); - } - - addChild(parent: any, node: any, parentMap: any[]): void { - parentMap.push({ id: node.id, parent: parent }); - parent.items.push(node); - } - getSelectedIds(reports: any[]): number[] { let copiedIds: number[] = []; for (const report of this.getSelectedReports(reports)) { diff --git a/src/app/shared/util/report-util.ts b/src/app/shared/util/report-util.ts index eef2e83f..e56dab25 100644 --- a/src/app/shared/util/report-util.ts +++ b/src/app/shared/util/report-util.ts @@ -1,12 +1,15 @@ import { Report } from '../interfaces/report'; import { Checkpoint } from '../interfaces/checkpoint'; +import { CreateTreeItem, FileTreeItem } from 'ng-simple-file-tree'; + +type ReportOrCheckpoint = Report | Checkpoint | CreateTreeItem | FileTreeItem | undefined; export const ReportUtil = { - isReport(node: Report | Checkpoint): node is Report { - return !!(node as Report).xml; + isReport(node: ReportOrCheckpoint): node is Report { + return !!node && !!(node as Report).xml; }, - isCheckPoint(node: Report | Checkpoint): node is Checkpoint { - return !!(node as Checkpoint).uid; + isCheckPoint(node: ReportOrCheckpoint): node is Checkpoint { + return !!node && !!(node as Checkpoint).uid; }, }; diff --git a/src/app/test/test.component.ts b/src/app/test/test.component.ts index a500a757..f4c498dc 100644 --- a/src/app/test/test.component.ts +++ b/src/app/test/test.component.ts @@ -12,7 +12,6 @@ import { ToastService } from '../shared/services/toast.service'; import { TabService } from '../shared/services/tab.service'; import { UpdatePathSettings } from '../shared/interfaces/update-path-settings'; import { ReportData } from '../shared/interfaces/report-data'; -import { NodeLinkStrategy } from '../shared/enums/compare-method'; import { TestFolderTreeComponent } from './test-folder-tree/test-folder-tree.component'; import { ToastComponent } from '../shared/components/toast/toast.component'; import { FormsModule, NgModel, ReactiveFormsModule } from '@angular/forms'; @@ -272,7 +271,6 @@ export class TestComponent implements OnInit, AfterViewInit { ); this.tabService.openNewCompareTab({ id: tabId, - nodeLinkStrategy: NodeLinkStrategy.NONE, viewName: 'compare', originalReport: reranReport.originalReport, runResultReport: reranReport.runResultReport, diff --git a/src/main.ts b/src/main.ts index 689e46dc..ce88ae29 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,4 @@ import { enableProdMode, importProvidersFrom } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { environment } from './environments/environment'; import { AppComponent } from './app/app.component'; @@ -9,14 +8,13 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MonacoEditorModule } from 'ngx-monaco-editor-v2'; import { AngularSplitModule } from 'angular-split'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { jqxTreeModule } from 'jqwidgets-ng/jqxtree'; -import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatSortModule } from '@angular/material/sort'; -import { withInterceptorsFromDi, provideHttpClient } from '@angular/common/http'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { provideAnimations } from '@angular/platform-browser/animations'; import { AppRoutingModule } from './app/app-routing.module'; -import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; +import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; if (environment.production) { enableProdMode(); @@ -32,7 +30,6 @@ function main() { MatSortModule, ReactiveFormsModule, FormsModule, - jqxTreeModule, MatProgressSpinnerModule, AngularSplitModule, MonacoEditorModule.forRoot(), diff --git a/src/styles.css b/src/styles.css index 71a32d20..d9bb7acc 100644 --- a/src/styles.css +++ b/src/styles.css @@ -138,3 +138,7 @@ html, body { .cursor-pointer { cursor: pointer; } + +.red { + color: red; +}
- + + @@ -136,10 +137,11 @@

[One or more reports are in progress for more {{ getShortenedTableHeaderNames(header) }}

- {{ getMetadata(row, getMetadataNameFromHeader(header))|tableCellShortener }}