diff --git a/report/report-core/src/test/java/com/mastercard/test/flow/report/detail/AbstractFlowSequenceTest.java b/report/report-core/src/test/java/com/mastercard/test/flow/report/detail/AbstractFlowSequenceTest.java index 1801b2d07d..24ee38c110 100644 --- a/report/report-core/src/test/java/com/mastercard/test/flow/report/detail/AbstractFlowSequenceTest.java +++ b/report/report-core/src/test/java/com/mastercard/test/flow/report/detail/AbstractFlowSequenceTest.java @@ -45,8 +45,8 @@ void messages() { dseq.detail( "display=Diff", "msg=2" ) .flow() .hasMessage( - "1 1 No, I'm worried about her dairy consumption.", - "2 2 I'm cutting you both off" ); + "1 1 No, I'm worried about her dairy consumption.", + "2 2 I'm cutting you both off" ); } /** diff --git a/report/report-core/src/test/java/com/mastercard/test/flow/report/diff/ModelDiffTest.java b/report/report-core/src/test/java/com/mastercard/test/flow/report/diff/ModelDiffTest.java index e6e4879852..13ac71b076 100644 --- a/report/report-core/src/test/java/com/mastercard/test/flow/report/diff/ModelDiffTest.java +++ b/report/report-core/src/test/java/com/mastercard/test/flow/report/diff/ModelDiffTest.java @@ -181,62 +181,58 @@ void naturalChanges() { dseq.change( "removed a b c" ) .hasDiff( - " 1 - Identity:", - " 2 - removed", - " 3 - a", - " 4 - b", - " 5 - c", - " 6 - Motivation:", - " 7 - ", - " 8 - Context:", - " 9 - {}", - "10 - Interactions:", - "11 - ┌REQUEST AVA => BEN []", - "12 - │Hi Ben!", - "13 - └", - "14 - ┌RESPONSE AVA <= BEN []", - "15 - │Hello Ava!", - "16 - └" ); + " 1 - Identity: ", + " 2 - removed ", + " 3 - a ", + " 4 - b ", + " 5 - c ", + " 6 - Motivation: ", + " 7 - ", + " 8 - Context: ", + " 9 - {} ", + "10 - Interactions: ", + "11 - ┌REQUEST AVA => BEN [] ", + "12 - │Hi Ben! ", + "13 - └ ", + "14 - ┌RESPONSE AVA <= BEN []", + "15 - │Hello Ava! ", + "16 - └ " ); dseq.change( "added d e f" ) .hasDiff( - " 1 + Identity:", - " 2 + added", - " 3 + d", - " 4 + e", - " 5 + f", - " 6 + Motivation:", - " 7 + ", - " 8 + Context:", - " 9 + {}", - "10 + Interactions:", - "11 + ┌REQUEST AVA => BEN []", - "12 + │Hi Ben!", - "13 + └", - "14 + ┌RESPONSE AVA <= BEN []", - "15 + │Hello Ava!", - "16 + └" ); + " 1 + Identity: ", + " 2 + added ", + " 3 + d ", + " 4 + e ", + " 5 + f ", + " 6 + Motivation: ", + " 7 + ", + " 8 + Context: ", + " 9 + {} ", + " 10 + Interactions: ", + " 11 + ┌REQUEST AVA => BEN [] ", + " 12 + │Hi Ben! ", + " 13 + └ ", + " 14 + ┌RESPONSE AVA <= BEN []", + " 15 + │Hello Ava! ", + " 16 + └ " ); dseq.change( "updated c d e" ) .hasDiff( - " 1 1 Identity:", - " 2 2 updated", - " 3 3 c", - " 4 4 d", - " 5 5 e", - " 6 6 Motivation:", - " 7 7 ", - " 8 8 Context:", - " 9 9 {}", - "10 10 Interactions:", - "11 11 ┌REQUEST AVA => BEN []", - "12 - │Hi Ben!", - "12 + │Hiii Beeen!", - "13 13 └", - "14 14 ┌RESPONSE AVA <= BEN []", - "15 - │Hello Ava!", - "15 + │Heeellooo Avaaa!", - "16 16 └" ); + " 1 1 Identity: ", + " 2 2 updated ", + " 3 3 c ", + " 5 unchanged lines ", + " 9 9 {} ", + "10 10 Interactions: ", + "11 11 ┌REQUEST AVA => BEN [] ", + "12 - │Hi Ben! ", + " 12 + │Hiii Beeen! ", + "13 13 └ ", + "14 14 ┌RESPONSE AVA <= BEN []", + "15 - │Hello Ava! ", + " 15 + │Heeellooo Avaaa! ", + "16 16 └ " ); dseq.hasUrlArgs( "ff=C38030BF0DCCF31DB770B2EAFA779DFC", @@ -253,24 +249,20 @@ void naturalChanges() { "tf=C38030BF0DCCF31DB770B2EAFA779DFC", "to=http%253A%252F%252Flocalhost%253A" + to.port() + "%252Fto%252F" ) .hasDiff( - " 1 1 Identity:", - " 2 2 updated", - " 3 3 c", - " 4 4 d", - " 5 5 e", - " 6 6 Motivation:", - " 7 7 ", - " 8 8 Context:", - " 9 9 {}", - "10 10 Interactions:", - "11 11 ┌REQUEST AVA => BEN []", - "12 - │Hi Ben!", - "12 + │Hiii Beeen!", - "13 13 └", - "14 14 ┌RESPONSE AVA <= BEN []", - "15 - │Hello Ava!", - "15 + │Heeellooo Avaaa!", - "16 16 └" ); + " 1 1 Identity: ", + " 2 2 updated ", + " 3 3 c ", + " 5 unchanged lines ", + " 9 9 {} ", + "10 10 Interactions: ", + "11 11 ┌REQUEST AVA => BEN [] ", + "12 - │Hi Ben! ", + " 12 + │Hiii Beeen! ", + "13 13 └ ", + "14 14 ┌RESPONSE AVA <= BEN []", + "15 - │Hello Ava! ", + " 15 + │Heeellooo Avaaa! ", + "16 16 └ " ); } /** @@ -293,22 +285,22 @@ void naturalAnalysis() { dseq.clickDiff( 0 ) .hasDiff( - " 1 - Identity:", - " 2 - removed", - " 3 - a", - " 4 - b", - " 5 - c", - " 6 - Motivation:", - " 7 - ", - " 8 - Context:", - " 9 - {}", - "10 - Interactions:", - "11 - ┌REQUEST AVA => BEN []", - "12 - │Hi Ben!", - "13 - └", - "14 - ┌RESPONSE AVA <= BEN []", - "15 - │Hello Ava!", - "16 - └" ); + " 1 - Identity: ", + " 2 - removed ", + " 3 - a ", + " 4 - b ", + " 5 - c ", + " 6 - Motivation: ", + " 7 - ", + " 8 - Context: ", + " 9 - {} ", + "10 - Interactions: ", + "11 - ┌REQUEST AVA => BEN [] ", + "12 - │Hi Ben! ", + "13 - └ ", + "14 - ┌RESPONSE AVA <= BEN []", + "15 - │Hello Ava! ", + "16 - └ " ); } /** @@ -341,26 +333,22 @@ void pairing() { "removed ↦ added a b c d e f" ) .change( "removed ↦ added a b c d e f" ) .hasDiff( - " 1 1 Identity:", - " 2 - removed", - " 2 + added", - " 3 - a", - " 3 + d", - " 4 - b", - " 4 + e", - " 5 - c", - " 5 + f", - " 6 6 Motivation:", - " 7 7 ", - " 8 8 Context:", - " 9 9 {}", - "10 10 Interactions:", - "11 11 ┌REQUEST AVA => BEN []", - "12 12 │Hi Ben!", - "13 13 └", - "14 14 ┌RESPONSE AVA <= BEN []", - "15 15 │Hello Ava!", - "16 16 └" ); + " 1 1 Identity: ", + " 2 - removed ", + " 3 - a ", + " 4 - b ", + " 5 - c ", + " 2 + added ", + " 3 + d ", + " 4 + e ", + " 5 + f ", + " 6 6 Motivation: ", + " 7 7 ", + " 8 8 Context: ", + " 5 unchanged lines ", + "14 14 ┌RESPONSE AVA <= BEN []", + "15 15 │Hello Ava! ", + "16 16 └ " ); } diff --git a/report/report-core/src/test/java/com/mastercard/test/flow/report/seq/DiffSequence.java b/report/report-core/src/test/java/com/mastercard/test/flow/report/seq/DiffSequence.java index 4bf61ae649..15290944f7 100644 --- a/report/report-core/src/test/java/com/mastercard/test/flow/report/seq/DiffSequence.java +++ b/report/report-core/src/test/java/com/mastercard/test/flow/report/seq/DiffSequence.java @@ -7,11 +7,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.openqa.selenium.support.ui.ExpectedConditions.elementToBeClickable; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; @@ -370,8 +371,6 @@ public DiffSequence change( String name ) { return this; } - private static final Pattern DIFF_LINE = Pattern.compile( "^([0-9]+) ([-+0-9]+) (.*)" ); - /** * Asserts on the displayed diff * @@ -381,25 +380,46 @@ public DiffSequence change( String name ) { public DiffSequence hasDiff( String... expected ) { trace( "hasDiff", (Object[]) expected ); - // format the line indices so our ascii art comes out nicely - String raw = driver.findElement( By.tagName( "td-ngx-text-diff" ) ) - .getText(); - int liw = Stream.of( raw.split( "\n" ) ) - .map( DIFF_LINE::matcher ) - .filter( Matcher::matches ) - .flatMap( m -> Stream.of( m.group( 1 ), m.group( 2 ) ) ) - .mapToInt( String::length ) + List> tableCells = new ArrayList<>(); + WebElement diffElement = driver.findElement( By.tagName( "app-text-diff" ) ); + diffElement.findElements( By.tagName( "tr" ) ) + .forEach( row -> tableCells.add( row.findElements( By.tagName( "td" ) ).stream() + .map( WebElement::getText ) + .collect( toList() ) ) ); + + int[] widths = new int[tableCells.stream() + .mapToInt( List::size ) .max() - .orElse( 1 ); - String fmt = "%" + liw + "s %" + liw + "s %s"; - - String formatted = Stream.of( raw.split( "\n" ) ) - .map( line -> { - Matcher m = DIFF_LINE.matcher( line ); - if( m.matches() ) { - return String.format( fmt, m.group( 1 ), m.group( 2 ), m.group( 3 ) ); + .orElse( 0 )]; + Arrays.fill( widths, 1 ); + tableCells.forEach( row -> { + if( row.size() == widths.length ) { + for( int i = 0; i < row.size(); i++ ) { + widths[i] = Math.max( widths[i], row.get( i ).length() ); + } + } + } ); + + int totalWidth = IntStream.of( widths ).sum() + widths.length - 1; + String fmt = IntStream.of( widths ) + .limit( widths.length - 1 ) + .mapToObj( i -> "%" + i + "s" ) + .collect( joining( " " ) ) + + " %-" + widths[widths.length - 1] + "s"; + + String formatted = tableCells.stream() + .map( row -> { + if( row.size() == widths.length ) { + return String.format( fmt, row.toArray( new Object[row.size()] ) ); + } + String s = row.stream().collect( joining( " " ) ); + while( s.length() < totalWidth ) { + s = " " + s; + if( s.length() < totalWidth ) { + s = s + " "; + } } - return line; + return s; } ) .collect( joining( "\n" ) ); diff --git a/report/report-ng/README.md b/report/report-ng/README.md index 1de5fedf80..bdcfdbba81 100644 --- a/report/report-ng/README.md +++ b/report/report-ng/README.md @@ -86,8 +86,6 @@ As noted above, these tests will be skipped if system property `node` is set to ## Acknowledgements -Credit for the sequence diagram on the flow detail pages belongs to [geraintluff/sequence-diagram-html](https://github.com/geraintluff/sequence-diagram-html). - -The text diff component is sourced from [ABenassi87/ngx-text-diff](https://github.com/ABenassi87/ngx-text-diff). +Credit for the sequence diagram on the flow detail pages belongs to [geraintluff/sequence-diagram-html](https://github.com/geraintluff/sequence-diagram-html). A pale shadow of that work has been angularised here. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.1.2 and has since been upgraded to 14.1.2. diff --git a/report/report-ng/angular.json b/report/report-ng/angular.json index f0ed3c23f9..06a95ecf84 100644 --- a/report/report-ng/angular.json +++ b/report/report-ng/angular.json @@ -100,7 +100,8 @@ ], "scripts": [ "projects/report/src/app/test-data.js", - ] + ], + "codeCoverage": true } } } diff --git a/report/report-ng/package.json b/report/report-ng/package.json index 2fe84b5b4b..71908f06b1 100644 --- a/report/report-ng/package.json +++ b/report/report-ng/package.json @@ -25,7 +25,6 @@ "@types/diff-match-patch": "^1.0.32", "diff-match-patch": "^1.0.5", "ngx-markdown": "^14.0.1", - "ngx-text-diff": "^0.6.0", "rxjs": "~7.8.1", "tslib": "^2.5.3", "zone.js": "~0.11.4" diff --git a/report/report-ng/projects/report/src/app/app.module.ts b/report/report-ng/projects/report/src/app/app.module.ts index 04a417194d..bf285fe0b6 100644 --- a/report/report-ng/projects/report/src/app/app.module.ts +++ b/report/report-ng/projects/report/src/app/app.module.ts @@ -32,7 +32,6 @@ import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { NgxTextDiffModule } from 'ngx-text-diff'; import { ChangeViewComponent } from './change-view/change-view.component'; import { FlowFilterComponent } from './flow-filter/flow-filter.component'; import { FlowNavItemComponent } from './flow-nav-item/flow-nav-item.component'; @@ -58,6 +57,7 @@ import { ResidueViewComponent } from './residue-view/residue-view.component'; import { MsgSearchInputComponent } from './msg-search-input/msg-search-input.component'; import { MatRipple, MatRippleModule } from '@angular/material/core'; import { HighlightedTextComponent } from './highlighted-text/highlighted-text.component'; +import { TextDiffComponent } from './text-diff/text-diff.component'; const routes: Routes = [ { path: "diff", component: ModelDiffComponent }, @@ -99,6 +99,7 @@ const routes: Routes = [ ResidueViewComponent, MsgSearchInputComponent, HighlightedTextComponent, + TextDiffComponent, ], imports: [ BrowserAnimationsModule, @@ -125,7 +126,6 @@ const routes: Routes = [ MatTabsModule, MatToolbarModule, MatTooltipModule, - NgxTextDiffModule, ReactiveFormsModule, RouterModule.forRoot(routes, { useHash: true }), ], diff --git a/report/report-ng/projects/report/src/app/change-view/change-view.component.css b/report/report-ng/projects/report/src/app/change-view/change-view.component.css index ff2ad44fcf..4e1692d3de 100644 --- a/report/report-ng/projects/report/src/app/change-view/change-view.component.css +++ b/report/report-ng/projects/report/src/app/change-view/change-view.component.css @@ -57,19 +57,3 @@ mat-drawer-content { /* ensures that all of the controls are visible without scrolling */ min-height: calc(100vh - 100px); } - -.td-wrapper .line-number-col { - user-select: none; -} - -.td-wrapper .line-number-col-left { - user-select: none; -} - -.td-wrapper .prefix-col { - user-select: none; -} - -.td-wrapper { - font-family: monospace; -} \ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/change-view/change-view.component.html b/report/report-ng/projects/report/src/app/change-view/change-view.component.html index 1e36716959..3457e7a0c3 100644 --- a/report/report-ng/projects/report/src/app/change-view/change-view.component.html +++ b/report/report-ng/projects/report/src/app/change-view/change-view.component.html @@ -35,10 +35,10 @@
- + vertical_split - + horizontal_split @@ -60,9 +60,8 @@
- - + +
diff --git a/report/report-ng/projects/report/src/app/change-view/change-view.component.ts b/report/report-ng/projects/report/src/app/change-view/change-view.component.ts index e0cf437228..c5df9d57e6 100644 --- a/report/report-ng/projects/report/src/app/change-view/change-view.component.ts +++ b/report/report-ng/projects/report/src/app/change-view/change-view.component.ts @@ -3,26 +3,24 @@ import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatSelectionListChange } from '@angular/material/list'; import { ActivatedRoute } from '@angular/router'; -import { DiffContent, DiffTableFormat } from 'ngx-text-diff/lib/ngx-text-diff.model'; -import { Observable, Subject } from 'rxjs'; import { DiffPair, FlowDiffService } from '../flow-diff.service'; import { FlowFilterService } from '../flow-filter.service'; import { ModelDiffDataService } from '../model-diff-data.service'; import { ListPair } from '../pair-select-item/pair-select-item.component'; +import { DiffDisplay } from '../text-diff/text-diff.component'; @Component({ selector: 'app-change-view', templateUrl: './change-view.component.html', styleUrls: ['./change-view.component.css'], - encapsulation: ViewEncapsulation.None }) export class ChangeViewComponent implements OnInit { treeOpen: boolean = true; contextLines: number = 1; - diffFormat: DiffTableFormat = 'LineByLine'; + diffFormat: DiffDisplay = 'unified'; selected: ListPair | null = null; private selectionListeners: (() => void)[] = []; @@ -32,8 +30,6 @@ export class ChangeViewComponent implements OnInit { diffLeft: string = ""; diffRight: string = ""; - diffObservable: Subject = new Subject(); - diffObservable$: Observable = this.diffObservable.asObservable(); constructor( private fds: FlowDiffService, @@ -133,12 +129,6 @@ export class ChangeViewComponent implements OnInit { if (this.selected != null && this.selected.pair.right != null) { this.diffRight = this.selected.pair.right.flat; } - - const diff: DiffContent = { - leftContent: this.diffLeft, - rightContent: this.diffRight, - } - this.diffObservable.next(diff); } hasDiffData(): boolean { diff --git a/report/report-ng/projects/report/src/app/detail/detail.component.spec.ts b/report/report-ng/projects/report/src/app/detail/detail.component.spec.ts index a64a033095..08dd47127f 100644 --- a/report/report-ng/projects/report/src/app/detail/detail.component.spec.ts +++ b/report/report-ng/projects/report/src/app/detail/detail.component.spec.ts @@ -10,8 +10,8 @@ import { MatTabsModule } from '@angular/material/tabs'; import { MatIconModule } from '@angular/material/icon'; import { MatToolbarModule } from '@angular/material/toolbar'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { DiffTableFormat } from 'ngx-text-diff/lib/ngx-text-diff.model'; import { SequenceData } from '../flow-sequence/flow-sequence.component'; +import { DiffDisplay } from '../text-diff/text-diff.component'; describe('DetailComponent', () => { let component: DetailComponent; @@ -89,7 +89,7 @@ class StubTransmission { @Input() options: Options = new Options(); @Input() display: Display = Display.Actual; @Input() diffType: DiffType = DiffType.Asserted; - @Input() diffFormat: DiffTableFormat = 'LineByLine'; + @Input() diffFormat: DiffDisplay = 'unified'; } @Component({ diff --git a/report/report-ng/projects/report/src/app/detail/detail.component.ts b/report/report-ng/projects/report/src/app/detail/detail.component.ts index 0332406888..e39ce754cd 100644 --- a/report/report-ng/projects/report/src/app/detail/detail.component.ts +++ b/report/report-ng/projects/report/src/app/detail/detail.component.ts @@ -50,7 +50,7 @@ export class DetailComponent implements OnInit { this.options.display = Display[query.get("display", "Actual") as keyof typeof Display]; this.options.dataDisplay = DataDisplay[query.get("facet", "Human") as keyof typeof DataDisplay]; this.options.diffType = DiffType[query.get("diff", "Asserted") as keyof typeof DiffType]; - const dtf: string = query.get("dtf", "LineByLine"); + const dtf: string = query.get("dtf", "unified"); if (isDiffFormat(dtf)) { this.options.diffFormat = dtf; } diff --git a/report/report-ng/projects/report/src/app/residue-view/residue-view.component.html b/report/report-ng/projects/report/src/app/residue-view/residue-view.component.html index b8638fc5ec..e792bce59e 100644 --- a/report/report-ng/projects/report/src/app/residue-view/residue-view.component.html +++ b/report/report-ng/projects/report/src/app/residue-view/residue-view.component.html @@ -19,17 +19,17 @@ Full - - + + Masked - - + + diff --git a/report/report-ng/projects/report/src/app/residue-view/residue-view.component.ts b/report/report-ng/projects/report/src/app/residue-view/residue-view.component.ts index 9aaf0db5e4..19326917e5 100644 --- a/report/report-ng/projects/report/src/app/residue-view/residue-view.component.ts +++ b/report/report-ng/projects/report/src/app/residue-view/residue-view.component.ts @@ -1,7 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MatSelectionListChange } from '@angular/material/list'; -import { DiffTableFormat } from 'ngx-text-diff/lib/ngx-text-diff.model'; import { Residue, residueAsserted, residueAssertionPassed } from '../types'; +import { DiffDisplay } from '../text-diff/text-diff.component'; @Component({ selector: 'app-residue-view', @@ -10,7 +9,7 @@ import { Residue, residueAsserted, residueAssertionPassed } from '../types'; }) export class ResidueViewComponent implements OnInit { @Input() residues: Residue[] = []; - diffFormat: DiffTableFormat = 'LineByLine'; + diffFormat: DiffDisplay = 'unified'; constructor() { } diff --git a/report/report-ng/projects/report/src/app/text-diff/text-diff.component.css b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.css new file mode 100644 index 0000000000..b9a2283e16 --- /dev/null +++ b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.css @@ -0,0 +1,61 @@ +.linenumber { + user-select: none; + font-family: monospace; + filter: opacity(60%); + border-right: 1px solid gray; + text-align: right; + padding-right: 0.25em; + padding-left: 0.25em; +} + +.difftype { + padding-left: 0.25em; + white-space: pre; +} + +.removed{ + background-color: #FF000033; + font-family: monospace; +} + +.unchanged { + font-family: monospace; +} + +.added{ + background-color: #00FF0033; + font-family: monospace; +} + +.content { + white-space: pre; + padding-right: 0.25em; + padding-left: 0.25em; + border-right: 1px solid gray; +} + +.content.unified { + width: 99%; +} +.content.split { + width: 49%; +} + +.collapsed { + user-select: none; + font-style: italic; + filter: opacity(60%); + text-align: center; + border: 1px dotted gray; +} + +.collapsed:hover{ + filter: opacity(90%); + background-color: #0000FF33; + cursor: zoom-in; +} + +table { + border-collapse: collapse; + border: 1px solid gray; +} \ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/text-diff/text-diff.component.html b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.html new file mode 100644 index 0000000000..493fc920a7 --- /dev/null +++ b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.html @@ -0,0 +1,97 @@ + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
{{line.leftLineNumber}}- + + {{chunk.content}} + +
{{line.leftLineNumber}}{{line.rightLineNumber}}  + {{line.chunks[0].content}} +
{{line.rightLineNumber}}+ + + {{chunk.content}} + +
+ {{line.type !== 'added' ? line.leftLineNumber : ''}} + + {{line.type === 'removed' || line.type === 'changed' ? '-' : ' '}} + + + {{chunk.content}} + + + {{line.type !== 'removed' ? line.rightLineNumber : ''}} + {{line.type === 'added' || line.type === 'changed' ? '+' : ' '}} + + {{chunk.content}} + +
\ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/text-diff/text-diff.component.spec.ts b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.spec.ts new file mode 100644 index 0000000000..b3c3f7078d --- /dev/null +++ b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.spec.ts @@ -0,0 +1,449 @@ +import { ComponentFixture, TestBed, tick } from '@angular/core/testing'; + +import { DiffDisplay, TextDiffComponent } from './text-diff.component'; + +describe('TextDiffComponent', () => { + let component: TextDiffComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TextDiffComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TextDiffComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display unified diffs', () => { + expect(test(component, fixture, { + left: `unchanged +removed +unchanged +changed foo +unchanged`, + right: `unchanged +unchanged +changed bar +unchanged +added`, + })) + .withContext("display 'unified' (default value)") + .toEqual([ + ['1', '1', ' ', 'unchanged '], + ['2', ' ', '-', '{removed} '], + ['3', '2', ' ', 'unchanged '], + ['4', ' ', '-', 'changed {foo}'], + [' ', '3', '+', 'changed [bar]'], + ['5', '4', ' ', 'unchanged '], + [' ', '5', '+', '[added] '], + ]); + }); + + it('should display split diffs', () => { + expect(test(component, fixture, { + left: `unchanged +removed +unchanged +changed foo +unchanged`, + right: `unchanged +unchanged +changed bar +unchanged +added`, + display: 'split' + })) + .withContext("display 'split'") + .toEqual([ + ['1', ' ', 'unchanged ', '1', ' ', 'unchanged '], + ['2', '-', '{removed} ', ' ', ' ', ' '], + ['3', ' ', 'unchanged ', '2', ' ', 'unchanged '], + ['4', '-', 'changed {foo}', '3', '+', 'changed [bar]'], + ['5', ' ', 'unchanged ', '4', ' ', 'unchanged '], + [' ', ' ', ' ', '5', '+', '[added] '], + ]); + }); + + it('should display grouped diffs', () => { + expect(test(component, fixture, { + left: `unchanged +changed foo +changed foo +unchanged`, + right: `unchanged +changed bar +changed bar +unchanged`, + })) + .withContext("blocksize 0 (default value)") + .toEqual([ + ['1', '1', ' ', 'unchanged '], + ['2', ' ', '-', 'changed {foo}'], + ['3', ' ', '-', 'changed {foo}'], + [' ', '2', '+', 'changed [bar]'], + [' ', '3', '+', 'changed [bar]'], + ['4', '4', ' ', 'unchanged '], + ]); + }); + + it('should display ungrouped diffs', () => { + expect(test(component, fixture, { + left: `unchanged +changed foo +changed foo +unchanged`, + right: `unchanged +changed bar +changed bar +unchanged`, + blockSize: 1 + })) + .withContext("blocksize 1") + .toEqual([ + ['1', '1', ' ', 'unchanged '], + ['2', ' ', '-', 'changed {foo}'], + [' ', '2', '+', 'changed [bar]'], + ['3', ' ', '-', 'changed {foo}'], + [' ', '3', '+', 'changed [bar]'], + ['4', '4', ' ', 'unchanged '], + ]); + }); + + it('should preserve empty lines', () => { + expect(test(component, fixture, { + left: "start\n\n\n\n\nend", + right: "start\n\n\n\n\nend", + })) + .withContext("empty lines") + .toEqual([ + ['1', '1', '', 'start'], + ['2', '2', '', ' '], + ['3', '3', '', ' '], + ['4', '4', '', ' '], + ['5', '5', '', ' '], + ['6', '6', '', 'end '], + ]); + }); + + it('should diff empty lines', () => { + expect(test(component, fixture, { + left: "start\n\n\n\nmid\n\n\nend", + right: "start\n\n\nmid\n\n\n\nend", + })) + .withContext("empty lines") + .toEqual([ + ['1', '1', ' ', 'start'], + ['2', '2', ' ', ' '], + ['3', '3', ' ', ' '], + ['4', ' ', '-', '{} '], + ['5', '4', ' ', 'mid '], + [' ', '5', '+', '[] '], + ['6', '6', ' ', ' '], + ['7', '7', ' ', ' '], + ['8', '8', ' ', 'end '], + ]); + }); + + it('should display unified collapsed unchanged blocks', () => { + let left = "head unchanged\n".repeat(5) + + "changed foo\n" + + "mid unchanged\n".repeat(5) + + "changed foo\n" + + "tail unchanged\n".repeat(5); + let right = left.replace(/foo/g, "bar"); + + expect(test(component, fixture, { + left: left, + right: right, + context: 1, + })) + .withContext("collapsed unchanged blocks") + .toEqual([ + ['1 ', '1 ', ' ', 'head unchanged'], + [' 3 unchanged lines '], + ['5 ', '5 ', ' ', 'head unchanged'], + ['6 ', ' ', '-', 'changed {foo} '], + [' ', '6 ', '+', 'changed [bar] '], + ['7 ', '7 ', ' ', 'mid unchanged '], + [' 3 unchanged lines '], + ['11', '11', ' ', 'mid unchanged '], + ['12', ' ', '-', 'changed {foo} '], + [' ', '12', '+', 'changed [bar] '], + ['13', '13', ' ', 'tail unchanged'], + [' 3 unchanged lines '], + ['17', '17', ' ', 'tail unchanged'], + ]); + + fixture.nativeElement.querySelectorAll(".collapsed")[1].click(); + + expect(dumpTable(fixture.nativeElement.querySelector("table"))) + .withContext("expanded the middle block") + .toEqual([ + ['1 ', '1 ', ' ', 'head unchanged'], + [' 3 unchanged lines '], + ['5 ', '5 ', ' ', 'head unchanged'], + ['6 ', ' ', '-', 'changed {foo} '], + [' ', '6 ', '+', 'changed [bar] '], + ['7 ', '7 ', ' ', 'mid unchanged '], + ['8 ', '8 ', ' ', 'mid unchanged '], + ['9 ', '9 ', ' ', 'mid unchanged '], + ['10', '10', ' ', 'mid unchanged '], + ['11', '11', ' ', 'mid unchanged '], + ['12', ' ', '-', 'changed {foo} '], + [' ', '12', '+', 'changed [bar] '], + ['13', '13', ' ', 'tail unchanged'], + [' 3 unchanged lines '], + ['17', '17', ' ', 'tail unchanged'], + ]); + }); + + it('should display split collapsed unchanged blocks', () => { + let left = "head unchanged\n".repeat(5) + + "changed foo\n" + + "mid unchanged\n".repeat(5) + + "changed foo\n" + + "tail unchanged\n".repeat(5); + let right = left.replace(/foo/g, "bar"); + + expect(test(component, fixture, { + left: left, + right: right, + context: 1, + display: 'split', + })) + .withContext("collapsed unchanged blocks") + .toEqual([ + ['1 ', ' ', 'head unchanged', '1 ', ' ', 'head unchanged'], + [' 3 unchanged lines '], + ['5 ', ' ', 'head unchanged', '5 ', ' ', 'head unchanged'], + ['6 ', '-', 'changed {foo} ', '6 ', '+', 'changed [bar] '], + ['7 ', ' ', 'mid unchanged ', '7 ', ' ', 'mid unchanged '], + [' 3 unchanged lines '], + ['11', ' ', 'mid unchanged ', '11', ' ', 'mid unchanged '], + ['12', '-', 'changed {foo} ', '12', '+', 'changed [bar] '], + ['13', ' ', 'tail unchanged', '13', ' ', 'tail unchanged'], + [' 3 unchanged lines '], + ['17', ' ', 'tail unchanged', '17', ' ', 'tail unchanged'], + ]); + + fixture.nativeElement.querySelectorAll(".collapsed")[1].click(); + + expect(dumpTable(fixture.nativeElement.querySelector("table"))) + .withContext("expanded the middle block") + .toEqual([ + ['1 ', ' ', 'head unchanged', '1 ', ' ', 'head unchanged'], + [' 3 unchanged lines '], + ['5 ', ' ', 'head unchanged', '5 ', ' ', 'head unchanged'], + ['6 ', '-', 'changed {foo} ', '6 ', '+', 'changed [bar] '], + ['7 ', ' ', 'mid unchanged ', '7 ', ' ', 'mid unchanged '], + ['8 ', ' ', 'mid unchanged ', '8 ', ' ', 'mid unchanged '], + ['9 ', ' ', 'mid unchanged ', '9 ', ' ', 'mid unchanged '], + ['10', ' ', 'mid unchanged ', '10', ' ', 'mid unchanged '], + ['11', ' ', 'mid unchanged ', '11', ' ', 'mid unchanged '], + ['12', '-', 'changed {foo} ', '12', '+', 'changed [bar] '], + ['13', ' ', 'tail unchanged', '13', ' ', 'tail unchanged'], + [' 3 unchanged lines '], + ['17', ' ', 'tail unchanged', '17', ' ', 'tail unchanged'], + ]); + }); + + it('can be configured to not collapse', () => { + let left = "unchanged\n".repeat(5); + let right = left; + + expect(test(component, fixture, { + left: left, + right: right, + context: 0, + })) + .withContext("max collapse") + .toEqual([ + ['5 unchanged lines'] + ]); + + expect(test(component, fixture, { + left: left, + right: right, + context: -1, + })) + .withContext("no collapse") + .toEqual([ + ['1', '1', '', 'unchanged'], + ['2', '2', '', 'unchanged'], + ['3', '3', '', 'unchanged'], + ['4', '4', '', 'unchanged'], + ['5', '5', '', 'unchanged'], + ]); + }); + + it('should handle a typical json rest response', () => { + expect(test(component, fixture, { + left: `HTTP/1.1 200 OK +content-type: application/json +date: Sun, 06 Jun 2021 19:50:29 GMT +server: Jetty(9.4.48.v20220622) +transfer-encoding: chunked + +{ + " " : 1, + "!" : 1, + "H" : 1, + "d" : 1, + "e" : 1, + "l" : 3, + "o" : 2, + "r" : 1, + "w" : 1 +}`, + right: `HTTP/1.1 200 OK +content-type: application/json +date: Sun, 06 Jun 2021 19:50:29 GMT +server: Jetty(9.4.48.v20220622) +transfer-encoding: chunked + +{ + "e" : 1, + "o" : 2 +}`, + blockSize: 1 + })) + .withContext("display 'unified' (default value)") + .toEqual([ + ['1 ', '1 ', ' ', 'HTTP/1.1 200 OK '], + ['2 ', '2 ', ' ', 'content-type: application/json '], + ['3 ', '3 ', ' ', 'date: Sun, 06 Jun 2021 19:50:29 GMT'], + ['4 ', '4 ', ' ', 'server: Jetty(9.4.48.v20220622) '], + ['5 ', '5 ', ' ', 'transfer-encoding: chunked '], + ['6 ', '6 ', ' ', ' '], + ['7 ', '7 ', ' ', '{ '], + ['8 ', ' ', '-', '{ " " : 1,} '], + ['9 ', ' ', '-', '{ "!" : 1,} '], + ['10', ' ', '-', '{ "H" : 1,} '], + ['11', ' ', '-', '{ "d" : 1,} '], + ['12', '8 ', ' ', ' "e" : 1, '], + ['13', ' ', '-', '{ "l" : 3,} '], + ['14', ' ', '-', ' "o" : 2{,} '], + [' ', '9 ', '+', ' "o" : 2 '], + ['15', ' ', '-', '{ "r" : 1,} '], + ['16', ' ', '-', '{ "w" : 1} '], + ['17', '10', ' ', '} '], + ]); + }); + + it('should handle a different json', () => { + expect(test(component, fixture, { + left: `{ }`, + right: `{ + "e" : 1, + "o" : 2 +}`, + blockSize: 1 + })) + .withContext("display 'unified' (default value)") + .toEqual([ + ['1', ' ', '-', '{{ }} '], + [' ', '1', '+', '[{] '], + [' ', '2', '+', '[ "e" : 1,]'], + [' ', '3', '+', '[ "o" : 2] '], + [' ', '4', '+', '[}] '], + ]); + }); +}); + + +function test( + component: TextDiffComponent, + fixture: ComponentFixture, + args: any): string[][] { + + if (args.left != undefined) { + component.left = args.left; + } + if (args.right != undefined) { + component.right = args.right; + } + if (args.display !== undefined) { + component.display = args.display; + } + if (args.blockSize !== undefined) { + component.blockSize = args.blockSize; + } + if (args.context !== undefined) { + component.context = args.context; + } + + component.ngOnChanges(); + fixture.detectChanges(); + return dumpTable(fixture.nativeElement.querySelector("table")); +} + +function dumpTable(table: HTMLElement): string[][] { + let rows: string[][] = []; + + // extract + table.querySelectorAll("tr").forEach(row => { + let rowData: string[] = []; + row.querySelectorAll("td").forEach(cell => { + let content = ''; + cell.querySelectorAll("span").forEach(span => { + let isAdd = span.classList.contains("added"); + let isRem = span.classList.contains("removed"); + if (isAdd) { + content += "["; + } + if (isRem) { + content += "{"; + } + content += span.textContent; + if (isAdd) { + content += "]"; + } + if (isRem) { + content += "}"; + } + }); + if (content.length === 0) { + content = cell.textContent?.trim() || ''; + } + rowData.push(content); + }); + rows.push(rowData); + }); + + if (rows.length > 1) { + let columns = rows + .map(row => row.length) + .reduce((p, c) => Math.max(p, c)); + + // pad + let widths: number[] = new Array(columns); + widths.fill(0); + + rows.filter(row => row.length > 1) + .forEach(row => row + .forEach((cell, index) => widths[index] = Math.max(widths[index], cell.length))); + let totalWidth = widths.reduce((p, c) => p + c) + 4 * (widths.length - 1); + + rows.forEach(row => { + if (row.length === 1) { + row[0] = row[0] + .padStart(totalWidth / 2 + row[0].length / 2) + .padEnd(totalWidth); + } + else { + row.forEach((cell, index) => { + row[index] = cell.padEnd(widths[index]); + }); + } + }); + } + + return rows; +} \ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/text-diff/text-diff.component.ts b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.ts new file mode 100644 index 0000000000..525c111650 --- /dev/null +++ b/report/report-ng/projects/report/src/app/text-diff/text-diff.component.ts @@ -0,0 +1,276 @@ +import { Component, Input, OnChanges, OnInit, SimpleChanges, ChangeDetectorRef } from '@angular/core'; +import { DIFF_EQUAL } from 'diff-match-patch'; +import { DIFF_DELETE, DIFF_INSERT, Diff, diff_match_patch } from 'diff-match-patch'; + +@Component({ + selector: 'app-text-diff', + templateUrl: './text-diff.component.html', + styleUrls: ['./text-diff.component.css'] +}) +export class TextDiffComponent implements OnInit, OnChanges { + + /** + * One half of the diff - the "before" + */ + @Input() left: string = ""; + /** + * The other half of the diff - the "after" + */ + @Input() right: string = ""; + /** + * Controls display type - line-by-line or side-by-side + */ + @Input() display: DiffDisplay = "unified"; + /** + * Controls the grouping of diff lines in line-by-line display mode. + * Set 0 or less for blocks of unlimited size + */ + @Input() blockSize: number = 0; + /** + * Controls the number of context lines to display around + * diffs. Set a negative number to display all lines + */ + @Input() context: number = 3; + + blocks: Block[] = []; + + constructor( + private cdRef: ChangeDetectorRef + ) { + } + + ngOnInit(): void { + this.ngOnChanges(); + } + + ngOnChanges(changes?: SimpleChanges): void { + let diffs = diff(this.left, this.right); + let lines = diffToLines(diffs); + // TODO: don't bother recomputing lines if it's just the blocksize that has changed + let rawBlocks = linesToBlocks(lines, this.blockSize); + // TODO: don't bother recomputing blocks if it's just the context that has changed + this.blocks = collapseBlocks(rawBlocks, this.context); + } + + expand(block: Block) { + block.collapsed = false; + // provoke our template to be rerendered + this.cdRef.detectChanges(); + } +} + +// line-by-line or side-by-side diff display +export type DiffDisplay = 'unified' | 'split'; + +// Defines the diff type for a single chunk of text. These values are also used as css class names +type DiffType = 'added' | 'removed' | 'unchanged'; + +// A single chunk of content, either added, removed or unchanged +interface Chunk { + type: DiffType; + content: string; +} + +// Defines the diff type for a single line of text (that can contain multiple chunks). +// 'changed' indictes that the line contains chunks with distinct diff types +type LineType = DiffType | 'changed'; + +// A single line of content, consisting of one or more chunks +interface Line { + leftLineNumber: number; + rightLineNumber: number; + type: LineType; // this is aguably redundant, but it makes the template much more succinct + chunks: Chunk[]; +} + +// A group of line diffs with the same LineType +interface Block { + lines: Line[]; + collapsed: boolean; +} + +// compares two strings +function diff(left: string, right: string) { + let diffs: Diff[] = []; + let lineDiffs: Diff[] = diffRough(left, right); + + while (lineDiffs.length > 0) { + let lineDiff = lineDiffs.shift()!; + + if (lineDiff[0] === DIFF_DELETE + && lineDiffs.length > 0 + && lineDiffs[0][0] === DIFF_INSERT + && lineDiff[1].includes("\n") == lineDiffs[0][1].includes("\n") + ) { + // we've got a deletion followed by an addition, and they've got + // matching single/multiline status + // let's diff deeper into those + let next = lineDiffs.shift()!; + diffFine(lineDiff[1], next[1]) + .forEach(d => diffs.push(d)); + } + // It looks like dif-match-patch will always present changes in + // DELETE,INSERT order, so we don't have to handle inverse case + else { + // it's an unchanged line, or an add/delete without a counterpart + diffs.push(lineDiff); + } + } + // console.log("diffs", Object.assign({}, diffs)); + return diffs; +} + +// First-stage diff: find changed lines +function diffRough(left: string, right: string) { + let dmp = new diff_match_patch(); + let a = dmp.diff_linesToChars_(left, right); + let lineLeft = a.chars1; + let lineRight = a.chars2; + let diffs = dmp.diff_main(lineLeft, lineRight, false); + dmp.diff_charsToLines_(diffs, a.lineArray); + // console.log("diffRough", Object.assign({}, diffs)); + return diffs; +} + +// Second-stage diff: find changed chunks in lines +function diffFine(left: string, right: string) { + let dmp = new diff_match_patch(); + let diffs = dmp.diff_main(left, right); + // console.log("diffFine", Object.assign({}, diffs)); + return diffs; +} + +// Splits a list of diffs onto lines +function diffToLines(diffs: Diff[]): Line[] { + let lines: Line[] = []; + let lln = 1; + let rln = 1; + + let line: Line | null = null; + + while (diffs.length !== 0) { + let diff = diffs.shift(); + let dt: DiffType = mapDiffType(diff![0]); + let content = diff![1]; + const nli = content.indexOf("\n"); + + if (nli != -1) { + // this diff spans a line break, so split it up + let nextLine = content.substring(nli + 1); + // note we're not including the \n in our content + content = content.substring(0, nli); + + if (nextLine.length > 0) { + // deal with the remnants in the next iteration + diffs.unshift([diff![0], nextLine]); + } + } + + if (line === null) { + // start a new line + line = { + leftLineNumber: lln, + rightLineNumber: rln, + type: dt, + chunks: [], + }; + lines.push(line); + } + + if (line.chunks.length === 0 || content.length > 0) { + line.chunks.push({ type: dt, content: content }); + } + + if (content.length > 0 && line.type !== dt) { + line.type = 'changed'; + } + + if (nli !== -1) { + if (line.type !== 'added') { lln++; } + if (line.type !== 'removed') { rln++; } + line = null; + } + } + + // console.log("lines", lines); + return lines; +} + +// Converts from the diff type flag used in dff-match-patch to the one used here +function mapDiffType(dmpt: number): DiffType { + switch (dmpt) { + case DIFF_DELETE: + return 'removed'; + case DIFF_INSERT: + return 'added'; + } + return 'unchanged'; +} + +// groups line diffs into similar blocks +function linesToBlocks(lines: Line[], blockSize: number): Block[] { + let blocks: Block[] = []; + + let block: Block | null = null; + + lines.forEach(line => { + if (block === null) { + block = { lines: [line], collapsed: false }; + blocks.push(block); + } + else { + if (block.lines[0].type === 'changed' + && line.type === 'changed' + && (blockSize <= 0 || block.lines.length < blockSize)) { + // match! both lines are changes and our blocksize hasn't been reached yet + block.lines.push(line); + } + else if (block.lines[0].chunks.length === 1 + && line.chunks.length === 1 + && block.lines[0].type === line.type) { + // match! both lines are atomic and of the same type + block.lines.push(line); + } + else { + // line types are different (or blockSize has been reached), start a new block + block = { lines: [line], collapsed: false }; + blocks.push(block); + } + } + }); + + return blocks; +} + +// Finds long-enough blocks of unchanged lines and splits +// them into 3, with the middle one collapsed +function collapseBlocks(raw: Block[], context: number): Block[] { + let collapsed: Block[] = []; + raw.forEach(block => { + // if we're configured to collapse, and the block is: + // * comprised of atomic lines + // * that are unchanged + if (context >= 0 && block.lines[0].type === 'unchanged' + ) { + if (context === 0) { + // no context, so no need to split, just collapse it + collapsed.push({ lines: block.lines, collapsed: true }); + } + else if (block.lines.length > context * 2 + 1) { + // split into 3 + collapsed.push({ lines: block.lines.slice(0, context), collapsed: false }); + collapsed.push({ lines: block.lines.slice(context, -context), collapsed: true }); + collapsed.push({ lines: block.lines.slice(-context), collapsed: false }); + } + else { + // the context encompasses all of the block, no point collapsing + collapsed.push(block); + } + } + else { + // either we're not configured to collapse, or the block is unsuitable for it + collapsed.push(block); + } + }); + return collapsed; +} \ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/transmission/transmission.component.css b/report/report-ng/projects/report/src/app/transmission/transmission.component.css index a255b848f2..bc2fa0f0b5 100644 --- a/report/report-ng/projects/report/src/app/transmission/transmission.component.css +++ b/report/report-ng/projects/report/src/app/transmission/transmission.component.css @@ -6,17 +6,4 @@ .no_data_warning mat-icon { vertical-align: middle; -} - -.td-wrapper .line-number-col { - user-select: none; -} -.td-wrapper .line-number-col-left { - user-select: none; -} -.td-wrapper .prefix-col { - user-select: none; -} -.td-wrapper { - font-family: monospace; } \ No newline at end of file diff --git a/report/report-ng/projects/report/src/app/transmission/transmission.component.html b/report/report-ng/projects/report/src/app/transmission/transmission.component.html index 4a49981186..c29baeef71 100644 --- a/report/report-ng/projects/report/src/app/transmission/transmission.component.html +++ b/report/report-ng/projects/report/src/app/transmission/transmission.component.html @@ -6,9 +6,9 @@ [human]="action?.transmission?.full?.expect" [base64]="action?.transmission?.full?.expectBytes">
- - + +
visibility_off diff --git a/report/report-ng/projects/report/src/app/transmission/transmission.component.ts b/report/report-ng/projects/report/src/app/transmission/transmission.component.ts index 9d48805864..4b15b9c563 100644 --- a/report/report-ng/projects/report/src/app/transmission/transmission.component.ts +++ b/report/report-ng/projects/report/src/app/transmission/transmission.component.ts @@ -1,38 +1,32 @@ import { Component, OnInit, OnChanges, Input, ViewEncapsulation } from '@angular/core'; -import { DiffContent, DiffTableFormat } from 'ngx-text-diff/lib/ngx-text-diff.model'; -import { Observable, Subject } from 'rxjs'; import { DiffType, Display, Options } from '../types'; import { TxSelectionService } from '../tx-selection.service'; -import { Transmission } from '../types'; import { QueryService } from '../query.service'; import { BasisFetchService } from '../basis-fetch.service'; import { Action, empty_action } from '../seq-action/seq-action.component'; +import { DiffDisplay } from '../text-diff/text-diff.component'; @Component({ selector: 'app-transmission', templateUrl: './transmission.component.html', styleUrls: ['./transmission.component.css'], - encapsulation: ViewEncapsulation.None }) export class TransmissionComponent implements OnInit, OnChanges { action?: Action; @Input() options: Options = new Options(); - // We don't stictly need these inputs, as we already have the options + // We don't strictly need these inputs, as we already have the options // input that contains them, but we do need to listen to changes on // these values so that ngOnChanges gets called @Input() display: Display = Display.Actual; @Input() diffType: DiffType = DiffType.Asserted; - @Input() diffFormat: DiffTableFormat = 'LineByLine'; + @Input() diffFormat: DiffDisplay = 'unified'; // If the user want to show actual or diff data but that isn't // available, we'll show the expected data with a warning instead effectiveDisplay: Display = this.display; substitutionWarning: boolean = false; - diffObservable: Subject = new Subject(); - diffObservable$: Observable = this.diffObservable.asObservable(); - constructor( private txSelect: TxSelectionService, private query: QueryService, @@ -41,7 +35,6 @@ export class TransmissionComponent implements OnInit, OnChanges { this.action = action; this.ngOnChanges(); }); - basis.onLoad(() => { this.refreshDiff(); }); } ngOnInit(): void { @@ -49,7 +42,6 @@ export class TransmissionComponent implements OnInit, OnChanges { } ngOnChanges(): void { - this.refreshDiff(); if (this.display !== Display.Actual) { this.query.set("display", this.display.toString()); } @@ -62,7 +54,7 @@ export class TransmissionComponent implements OnInit, OnChanges { else { this.query.delete("diff"); } - if (this.diffFormat !== 'LineByLine') { + if (this.diffFormat !== 'unified') { this.query.set("dtf", this.diffFormat); } else { @@ -82,18 +74,6 @@ export class TransmissionComponent implements OnInit, OnChanges { } } - /** - * The diff component needs a wee kick to recalculate - * itself - you can't just use the left and right inputs - */ - refreshDiff(): void { - const diff: DiffContent = { - leftContent: this.diffLeft(), - rightContent: this.diffRight(), - } - this.diffObservable.next(diff); - } - diffLeft(): string { if (this.options.display === Display.Basis) { return this.basis.message(this.action || empty_action) ?? ''; diff --git a/report/report-ng/projects/report/src/app/types.ts b/report/report-ng/projects/report/src/app/types.ts index 86e880edbd..085b74a4d3 100644 --- a/report/report-ng/projects/report/src/app/types.ts +++ b/report/report-ng/projects/report/src/app/types.ts @@ -93,14 +93,13 @@ export const empty_index: Index = { entries: [] } -import { FooterRowOutlet } from "@angular/cdk/table"; -import { DiffTableFormat } from "ngx-text-diff/lib/ngx-text-diff.model"; +import { DiffDisplay } from "./text-diff/text-diff.component"; export class Options { display: Display = Display.Actual; dataDisplay: DataDisplay = DataDisplay.Human; diffType: DiffType = DiffType.Asserted; - diffFormat: DiffTableFormat = 'LineByLine'; + diffFormat: DiffDisplay = 'unified'; } export enum Display { @@ -126,9 +125,9 @@ export enum DataDisplay { Hex = "Hex" } -export function isDiffFormat(data: any): data is DiffTableFormat { +export function isDiffFormat(data: any): data is DiffDisplay { return typeof data === 'string' - && (data === 'SideBySide' || data === 'LineByLine'); + && (data === 'unified' || data === 'split'); } /** diff --git a/report/report-ng/projects/report/src/app/view-options/view-options.component.html b/report/report-ng/projects/report/src/app/view-options/view-options.component.html index 324a361299..679e8706d6 100644 --- a/report/report-ng/projects/report/src/app/view-options/view-options.component.html +++ b/report/report-ng/projects/report/src/app/view-options/view-options.component.html @@ -26,12 +26,10 @@
- + vertical_split - + horizontal_split diff --git a/report/report-ng/yarn.lock b/report/report-ng/yarn.lock index 071cd370eb..131d63aa26 100644 --- a/report/report-ng/yarn.lock +++ b/report/report-ng/yarn.lock @@ -3432,7 +3432,7 @@ di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== -diff-match-patch@^1.0.4, diff-match-patch@^1.0.5: +diff-match-patch@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== @@ -5232,13 +5232,6 @@ ngx-markdown@^14.0.1: prismjs "^1.28.0" tslib "^2.3.0" -ngx-text-diff@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/ngx-text-diff/-/ngx-text-diff-0.6.0.tgz#f369966fc7943bc515191acf518ee478e4e000ba" - integrity sha512-61njY/kRmbNLJr/a9FxhfouJ8LpUtejEGqcethZemqlE9lWqu0mxIuoqZYFB2gHkMzhvvBqfgWV190uRIxAClA== - dependencies: - diff-match-patch "^1.0.4" - nice-napi@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b"