diff --git a/make/DEBIAN/control b/make/DEBIAN/control index ddd07d4da..439f0c1bc 100644 --- a/make/DEBIAN/control +++ b/make/DEBIAN/control @@ -1,5 +1,5 @@ Package: Testrun -Version: 2.4.0-beta.3 +Version: 2.4.0-beta.5 Architecture: amd64 Maintainer: Google Homepage: https://github.com/google/testrun diff --git a/modules/ui/src/app/app.component.html b/modules/ui/src/app/app.component.html index 362a1d50f..d962cc67c 100644 --- a/modules/ui/src/app/app.component.html +++ b/modules/ui/src/app/app.component.html @@ -72,7 +72,8 @@ + (consentShownEvent)="consentShown()" + (navigateToRouteEvent)="navigateToRoute($event)"> diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts index 7116aaa51..71278c911 100644 --- a/modules/ui/src/app/app.component.ts +++ b/modules/ui/src/app/app.component.ts @@ -324,6 +324,10 @@ export class AppComponent implements AfterViewInit { } } + navigateToRoute(route: Routes): void { + this.route.navigate([route]); + } + private subscribeToNavigation() { this.route.events .pipe( diff --git a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.html b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.html index becde20cf..0a6c606a4 100644 --- a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.html +++ b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.html @@ -50,7 +50,6 @@

Welcome to Testrun!

> to share you thoughts -
  • Risk Profile was lastly updated in 2026 V2.4.0
  • @@ -82,6 +81,32 @@

    Welcome to Testrun!

    } +
    + + Risk assessment questionnaire was lastly updated in version 2.4.0. Visit + the + + Devices + + and + + Risk Profiles + + pages to update. + +
    Testrun uses Google Analytics to learn about how our users use the diff --git a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.spec.ts b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.spec.ts index 9a12c04e1..032ec9a98 100644 --- a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.spec.ts +++ b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.spec.ts @@ -30,6 +30,7 @@ import { MatButtonModule } from '@angular/material/button'; import { of } from 'rxjs'; import { NEW_VERSION, VERSION } from '../../../mocks/version.mock'; import { MatIconTestingModule } from '@angular/material/icon/testing'; +import { Routes } from '../../../model/routes'; describe('ConsentDialogComponent', () => { let component: ConsentDialogComponent; @@ -72,7 +73,7 @@ describe('ConsentDialogComponent', () => { const confirmButton = compiled.querySelector( '.confirm-button' ) as HTMLButtonElement; - const dialogRes = { grant: true }; + const dialogRes = { grant: true, route: undefined }; confirmButton?.click(); @@ -88,7 +89,7 @@ describe('ConsentDialogComponent', () => { const confirmButton = compiled.querySelector( '.confirm-button' ) as HTMLButtonElement; - const dialogRes = { grant: false }; + const dialogRes = { grant: false, route: undefined }; confirmButton?.click(); @@ -97,6 +98,38 @@ describe('ConsentDialogComponent', () => { closeSpy.calls.reset(); }); + it('should close dialog with devices route on device link click', () => { + component.optOut = true; + fixture.detectChanges(); + const closeSpy = spyOn(component.dialogRef, 'close'); + const devicesLink = compiled.querySelector( + '#devices-link' + ) as HTMLButtonElement; + const dialogRes = { grant: false, route: Routes.Devices }; + + devicesLink?.click(); + + expect(closeSpy).toHaveBeenCalledWith(dialogRes); + + closeSpy.calls.reset(); + }); + + it('should close dialog with risk profiles route on risk profiles link click', () => { + component.optOut = true; + fixture.detectChanges(); + const closeSpy = spyOn(component.dialogRef, 'close'); + const devicesLink = compiled.querySelector( + '#risk-profiles-link' + ) as HTMLButtonElement; + const dialogRes = { grant: false, route: Routes.RiskAssessment }; + + devicesLink?.click(); + + expect(closeSpy).toHaveBeenCalledWith(dialogRes); + + closeSpy.calls.reset(); + }); + it('should not close dialog on download link click', () => { const closeSpy = spyOn(component.dialogRef, 'close'); const confirmButton = compiled.querySelector( diff --git a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.ts b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.ts index ef6aff1d4..05a8a865b 100644 --- a/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.ts +++ b/modules/ui/src/app/components/version/consent-dialog/consent-dialog.component.ts @@ -27,6 +27,7 @@ import { CalloutType } from '../../../model/callout-type'; import { MatCheckbox } from '@angular/material/checkbox'; import { FormsModule } from '@angular/forms'; import { timer } from 'rxjs'; +import { Routes } from '../../../model/routes'; type DialogData = { version: Version; @@ -51,11 +52,13 @@ export class ConsentDialogComponent { public readonly CalloutType = CalloutType; optOut = this.data.optOut; + public readonly Routes = Routes; - confirm(optOut: boolean) { + confirm(optOut: boolean, route?: Routes) { // dialog should be closed with opposite value to grant or deny access to GA const dialogResult: ConsentDialogResult = { grant: !optOut, + route, }; this.dialogRef.close(dialogResult); timer(100).subscribe(() => { diff --git a/modules/ui/src/app/components/version/version.component.spec.ts b/modules/ui/src/app/components/version/version.component.spec.ts index 381b6dbba..9a3cdefb4 100644 --- a/modules/ui/src/app/components/version/version.component.spec.ts +++ b/modules/ui/src/app/components/version/version.component.spec.ts @@ -33,6 +33,7 @@ import { of } from 'rxjs'; import { MatDialogRef } from '@angular/material/dialog'; import { ConsentDialogComponent } from './consent-dialog/consent-dialog.component'; import { MatIconTestingModule } from '@angular/material/icon/testing'; +import { Routes } from '../../model/routes'; describe('VersionComponent', () => { let component: VersionComponent; @@ -43,6 +44,8 @@ describe('VersionComponent', () => { const versionBehaviorSubject$ = new BehaviorSubject(null); beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (window).gtag = jasmine.createSpy('gtag'); // @ts-expect-error data layer should be defined window.dataLayer = window.dataLayer || []; mockService = jasmine.createSpyObj(['getVersion', 'fetchVersion']); @@ -103,6 +106,25 @@ describe('VersionComponent', () => { expect(openSpy).toHaveBeenCalled(); }); + it('should emit navigateToRouteEvent when dialogResult contains a route', () => { + const navigateToRouteEventSpy = spyOn( + component.navigateToRouteEvent, + 'emit' + ); + const mockDialogResult = { + grant: true, + route: Routes.Devices, + }; + spyOn(component.dialog, 'open').and.returnValue({ + afterClosed: () => of(mockDialogResult), + } as MatDialogRef); + + component.openConsentDialog(VERSION); + + expect(navigateToRouteEventSpy).toHaveBeenCalledTimes(1); + expect(navigateToRouteEventSpy).toHaveBeenCalledWith(Routes.Devices); + }); + describe('update is not available', () => { beforeEach(() => { versionBehaviorSubject$.next(VERSION); diff --git a/modules/ui/src/app/components/version/version.component.ts b/modules/ui/src/app/components/version/version.component.ts index 2d80a46c0..c999eb9fa 100644 --- a/modules/ui/src/app/components/version/version.component.ts +++ b/modules/ui/src/app/components/version/version.component.ts @@ -38,6 +38,7 @@ import { takeUntil } from 'rxjs/internal/operators/takeUntil'; import { filter, timer } from 'rxjs'; import { ConsentDialogComponent } from './consent-dialog/consent-dialog.component'; import { LocalStorageService } from '../../services/local-storage.service'; +import { Routes } from '../../model/routes'; export const INSTALLED_VERSION = 'INSTALLED_VERSION'; @@ -56,6 +57,7 @@ export class VersionComponent implements OnInit, OnDestroy { @Input() consentShown!: boolean; @Output() consentShownEvent = new EventEmitter(); + @Output() navigateToRouteEvent = new EventEmitter(); version$!: Observable; private destroy$: Subject = new Subject(); @@ -122,7 +124,10 @@ export class VersionComponent implements OnInit, OnDestroy { gtag('consent', 'update', { analytics_storage: dialogResult.grant ? 'granted' : 'denied', }); - this.localStorageService.setGAConsent(dialogResult.grant); + + if (dialogResult.route) { + this.navigateToRouteEvent.emit(dialogResult.route); + } }); } diff --git a/modules/ui/src/app/model/version.ts b/modules/ui/src/app/model/version.ts index 5eb946e42..31fd9ded8 100644 --- a/modules/ui/src/app/model/version.ts +++ b/modules/ui/src/app/model/version.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { Routes } from './routes'; + export interface Version { installed_version: string; update_available: boolean; @@ -23,4 +25,5 @@ export interface Version { export interface ConsentDialogResult { grant: boolean; + route?: Routes; }