Fix findings view toggle reactivity
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { signal } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ActivatedRoute, convertToParamMap, provideRouter } from '@angular/router';
|
||||
import { BehaviorSubject, of } from 'rxjs';
|
||||
|
||||
import { FindingsContainerComponent } from '../../features/findings/container/findings-container.component';
|
||||
import { CompareService } from '../../features/compare/services/compare.service';
|
||||
import { ViewPreferenceService, FindingsViewMode } from '../services/view-preference.service';
|
||||
import { MockScoringApi, SCORING_API } from '../services/scoring.service';
|
||||
|
||||
describe('FindingsContainerComponent', () => {
|
||||
let fixture: ComponentFixture<FindingsContainerComponent>;
|
||||
let queryParamMap$: BehaviorSubject<ReturnType<typeof convertToParamMap>>;
|
||||
let preferenceSignal: ReturnType<typeof signal<FindingsViewMode>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
queryParamMap$ = new BehaviorSubject(convertToParamMap({}));
|
||||
preferenceSignal = signal<FindingsViewMode>('diff');
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [FindingsContainerComponent, NoopAnimationsModule],
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{
|
||||
provide: ViewPreferenceService,
|
||||
useValue: {
|
||||
viewMode: preferenceSignal.asReadonly(),
|
||||
getViewMode: () => preferenceSignal(),
|
||||
setViewMode: (mode: FindingsViewMode) => preferenceSignal.set(mode),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: CompareService,
|
||||
useValue: {
|
||||
getBaselineRecommendations: () =>
|
||||
of({
|
||||
selectedDigest: 'baseline-123',
|
||||
selectionReason: 'Last Green Build',
|
||||
alternatives: [],
|
||||
autoSelectEnabled: true,
|
||||
}),
|
||||
computeDelta: () =>
|
||||
of({
|
||||
categories: [
|
||||
{ id: 'added', name: 'Added', icon: 'add', added: 1, removed: 0, changed: 0 },
|
||||
],
|
||||
items: [],
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: SCORING_API,
|
||||
useClass: MockScoringApi,
|
||||
},
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {
|
||||
paramMap: of(convertToParamMap({ scanId: 'test-scan-123' })),
|
||||
queryParamMap: queryParamMap$,
|
||||
snapshot: {
|
||||
paramMap: convertToParamMap({ scanId: 'test-scan-123' }),
|
||||
queryParamMap: convertToParamMap({}),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(FindingsContainerComponent);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('switches from diff to detail when the persisted preference changes without a URL override', async () => {
|
||||
expect(fixture.nativeElement.textContent).toContain('Comparing:');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('backend-api');
|
||||
|
||||
preferenceSignal.set('detail');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.textContent).toContain('backend-api');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Comparing:');
|
||||
});
|
||||
|
||||
it('keeps the explicit URL override authoritative over the persisted preference', async () => {
|
||||
queryParamMap$.next(convertToParamMap({ view: 'diff' }));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
preferenceSignal.set('detail');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.viewMode()).toBe('diff');
|
||||
expect(fixture.nativeElement.textContent).toContain('Comparing:');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('backend-api');
|
||||
});
|
||||
});
|
||||
@@ -197,8 +197,8 @@ export class FindingsContainerComponent implements OnInit {
|
||||
private readonly viewPref = inject(ViewPreferenceService);
|
||||
private readonly compareService = inject(CompareService);
|
||||
|
||||
// View mode: URL param > user preference > default (diff)
|
||||
readonly viewMode = signal<FindingsViewMode>('diff');
|
||||
// View mode: explicit URL override > reactive user preference
|
||||
readonly viewMode = computed<FindingsViewMode>(() => this.urlViewMode() ?? this.viewPref.viewMode());
|
||||
|
||||
// Loading state
|
||||
readonly loading = signal(false);
|
||||
@@ -209,6 +209,11 @@ export class FindingsContainerComponent implements OnInit {
|
||||
// Delta summary for diff view
|
||||
readonly deltaSummary = signal<{ added: number; removed: number; changed: number } | null>(null);
|
||||
|
||||
private readonly urlViewMode = toSignal(
|
||||
this.route.queryParamMap.pipe(map((params) => this.readViewMode(params.get('view')))),
|
||||
{ initialValue: this.readViewMode(this.route.snapshot.queryParamMap.get('view')) }
|
||||
);
|
||||
|
||||
// Current scan ID resolved from route param, query param, or deterministic fallback.
|
||||
private readonly scanId = toSignal(
|
||||
this.route.paramMap.pipe(
|
||||
@@ -227,25 +232,9 @@ export class FindingsContainerComponent implements OnInit {
|
||||
);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initializeViewMode();
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
private initializeViewMode(): void {
|
||||
// Check URL override first
|
||||
const urlView = this.route.snapshot.queryParamMap.get('view');
|
||||
if (urlView === 'diff' || urlView === 'detail') {
|
||||
this.viewMode.set(urlView);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to user preference
|
||||
this.viewMode.set(this.viewPref.getViewMode());
|
||||
|
||||
// Subscribe to preference changes
|
||||
// Note: Using effect would be cleaner but keeping it simple here
|
||||
}
|
||||
|
||||
private loadData(): void {
|
||||
const scanId = this.scanId();
|
||||
if (!scanId) return;
|
||||
@@ -278,4 +267,8 @@ export class FindingsContainerComponent implements OnInit {
|
||||
// Navigate to finding detail or open drawer
|
||||
console.log('Selected finding:', finding.id);
|
||||
}
|
||||
|
||||
private readViewMode(raw: string | null): FindingsViewMode | null {
|
||||
return raw === 'diff' || raw === 'detail' ? raw : null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user