Skip to content

Commit

Permalink
Fix: show front and backend versions (#726)
Browse files Browse the repository at this point in the history
* feat: show frontend and backend versions, frontend version in browser tab is now correct

* refactor: remove unused service

* test: add unit tests for setting versions

* refactor: remove comments
  • Loading branch information
MatthijsSmets authored Oct 15, 2024
1 parent 9bae99b commit 23646a2
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</li>
}
<li class="ml-auto my-auto mr-1" id="version">
<a>v{{ appVersion }}</a>
<a>v{{ frontendVersion }}</a>
<ng-template></ng-template>
</li>
</ul>
Expand Down
43 changes: 31 additions & 12 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { Title } from '@angular/platform-browser';
import { VersionService } from './shared/services/version.service';
import { provideRouter } from '@angular/router';

class MockVersionService {
getFrontendVersion = jasmine.createSpy('getFrontendVersion').and.returnValue(Promise.resolve('1.0-TEST'));
}

describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let titleService: Title;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterTestingModule, NgbNavModule, AppComponent],
providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
provideRouter([]),
{ provide: VersionService, useClass: MockVersionService },
Title,
],
}).compileComponents();

fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
titleService = TestBed.inject(Title);
});

it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
expect(component).toBeTruthy();
});

it(`should have as title 'ladybug'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('ladybug');
it('should set the title correctly based on frontend version from version service', async () => {
spyOn(titleService, 'setTitle');

await component.fetchAndSetFrontendVersion();

expect(titleService.setTitle).toHaveBeenCalledWith('Ladybug - v1.0-TEST');
expect(component.frontendVersion).toEqual('1.0-TEST');
});
});
27 changes: 8 additions & 19 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { CloseTab } from './shared/interfaces/close-tab';
import { HttpService } from './shared/services/http.service';
import { StubStrategy } from './shared/enums/stub-strategy';
import { ErrorHandling } from './shared/classes/error-handling.service';
import { VersionService } from './shared/services/version.service';

@Component({
selector: 'app-root',
Expand All @@ -27,7 +28,7 @@ import { ErrorHandling } from './shared/classes/error-handling.service';
imports: [RouterLinkActive, RouterLink, RouterOutlet, ToastComponent],
})
export class AppComponent implements OnInit, OnDestroy {
appVersion: string = '0.0.0';
frontendVersion?: string;
@ViewChild(CompareComponent) compareComponent!: CompareComponent;
@ViewChild(TestComponent) testComponent!: TestComponent;

Expand All @@ -51,12 +52,11 @@ export class AppComponent implements OnInit, OnDestroy {
private location: Location,
private httpService: HttpService,
private errorHandler: ErrorHandling,
) {
this.titleService.setTitle(`Ladybug - v${this.appVersion}`);
}
private versionService: VersionService,
) {}

ngOnInit(): void {
this.fetchAndSetAppVersion();
this.fetchAndSetFrontendVersion();
this.subscribeToServices();
this.getStubStrategies();
}
Expand All @@ -65,20 +65,9 @@ export class AppComponent implements OnInit, OnDestroy {
this.unsubscribeAll();
}

fetchAndSetAppVersion() {
fetch('assets/package.json')
.then((response: Response): void => {
if (response.ok) {
response.json().then((packageJson: { version: string }): void => {
this.appVersion = packageJson.version;
});
} else {
console.error('package.json could not be found in assets', response);
}
})
.catch((error): void => {
console.error('package.json could not be found in assets', error);
});
async fetchAndSetFrontendVersion() {
this.frontendVersion = await this.versionService.getFrontendVersion();
this.titleService.setTitle(`Ladybug - v${this.frontendVersion}`);
}

subscribeToServices(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ <h5 class="modal-title" id="settingsModal">Options</h5>
<form [formGroup]="settingsForm">

<div class="modal-body container">
<div>
<h5>Info</h5>
<div class="d-flex flex-column gap-2">
<div class="d-flex flex-row gap-1">
<p class="m-0">Backend Version: {{ backendVersion }}</p>
<i class="bi bi-copy cursor-pointer" [appCopyTooltip]="backendVersion"></i>
</div>
<div class="d-flex flex-row gap-1">
<p class="m-0">Frontend Version: {{ frontendVersion }}</p>
<i class="bi bi-copy cursor-pointer" [appCopyTooltip]="frontendVersion"></i>
</div>
</div>
</div>
<hr>
<div class="form-group">
<h5>Debug tree</h5>
<div class="checkbox " title="When checked, file tree shows multiple files">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ import { ReactiveFormsModule, UntypedFormControl, UntypedFormGroup } from '@angu
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { HttpService } from '../../../shared/services/http.service';
import { SettingsService } from '../../../shared/services/settings.service';
import { catchError, Subscription, tap } from 'rxjs';
import { catchError, Subscription } from 'rxjs';
import { ToastService } from '../../../shared/services/toast.service';
import { UploadParams } from 'src/app/shared/interfaces/upload-params';
import { ToastComponent } from '../../../shared/components/toast/toast.component';
import { ErrorHandling } from 'src/app/shared/classes/error-handling.service';
import { OptionsSettings } from '../../../shared/interfaces/options-settings';
import { Transformation } from '../../../shared/interfaces/transformation';
import { VersionService } from '../../../shared/services/version.service';
import { CopyTooltipDirective } from '../../../shared/directives/copy-tooltip.directive';

@Component({
selector: 'app-table-settings-modal',
templateUrl: './table-settings-modal.component.html',
styleUrls: ['./table-settings-modal.component.css'],
standalone: true,
imports: [ReactiveFormsModule, ToastComponent],
imports: [ReactiveFormsModule, ToastComponent, CopyTooltipDirective],
})
export class TableSettingsModalComponent implements OnDestroy {
@ViewChild('modal') modal!: ElementRef;
Expand All @@ -38,21 +40,34 @@ export class TableSettingsModalComponent implements OnDestroy {
});
@Output() openLatestReportsEvent = new EventEmitter<any>();
saving: boolean = false;
backendVersion?: string;
frontendVersion?: string;

constructor(
private modalService: NgbModal,
private httpService: HttpService,
private settingsService: SettingsService,
private toastService: ToastService,
private errorHandler: ErrorHandling,
private versionService: VersionService,
) {
this.getApplicationVersions();
this.subscribeToSettingsServiceObservables();
}

ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}

getApplicationVersions() {
this.versionService.getFrontendVersion().then((frontendVersion) => {
this.frontendVersion = frontendVersion;
});
this.versionService.getBackendVersion().then((backendVersion) => {
this.backendVersion = backendVersion;
});
}

subscribeToSettingsServiceObservables(): void {
const showMultipleSubscription: Subscription = this.settingsService.showMultipleAtATimeObservable.subscribe({
next: (value: boolean): void => {
Expand Down
6 changes: 6 additions & 0 deletions src/app/shared/services/http.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,10 @@ export class HttpService {
headers: this.headers,
});
}

getBackendVersion(): Observable<string> {
return this.http.get('api/testtool/version', {
responseType: 'text',
});
}
}
16 changes: 0 additions & 16 deletions src/app/shared/services/node-link-strategy.service.spec.ts

This file was deleted.

10 changes: 0 additions & 10 deletions src/app/shared/services/node-link-strategy.service.ts

This file was deleted.

41 changes: 41 additions & 0 deletions src/app/shared/services/version.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { TestBed } from '@angular/core/testing';

import { VersionService } from './version.service';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';

describe('VersionService', () => {
let service: VersionService;

let httpTestingController: HttpTestingController;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()],
});
service = TestBed.inject(VersionService);
httpTestingController = TestBed.inject(HttpTestingController);
});

it('should be created', () => {
expect(service).toBeTruthy();
});

it('should set version from package.json', async () => {
const frontendVersionPromise = service.getFrontendVersion();
const backendVersionPromise = service.getBackendVersion();
const mockPackageJson = { version: '1.0-TEST' };

const frontendVersionReq = httpTestingController.expectOne(service.packageJsonPath);

const backendVersionReg = httpTestingController.expectOne('api/testtool/version');
frontendVersionReq.flush(mockPackageJson);
backendVersionReg.flush('3.0-TEST');

const frontendVersion = await frontendVersionPromise;
const backendVersion = await backendVersionPromise;

expect(frontendVersion).toEqual('1.0-TEST');
expect(backendVersion).toEqual('3.0-TEST');
});
});
39 changes: 39 additions & 0 deletions src/app/shared/services/version.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import { HttpService } from './http.service';
import { firstValueFrom } from 'rxjs';
import { HttpClient } from '@angular/common/http';

@Injectable({
providedIn: 'root',
})
export class VersionService {
packageJsonPath = 'assets/package.json';
frontendVersion?: string;
backendVersion?: string;

constructor(
private httpService: HttpService,
private httpClient: HttpClient,
) {}

async getFrontendVersion() {
if (!this.frontendVersion) {
try {
const packageJson = await firstValueFrom(this.httpClient.get<{ version: string }>(this.packageJsonPath));
if (packageJson) {
this.frontendVersion = packageJson.version;
}
} catch (error) {
console.error('package.json could not be found in assets', error);
}
}
return this.frontendVersion!;
}

async getBackendVersion() {
if (!this.backendVersion) {
this.backendVersion = await firstValueFrom(this.httpService.getBackendVersion());
}
return this.backendVersion;
}
}
12 changes: 12 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,15 @@ html, body {
.red {
color: red;
}

.gap-1 {
gap: 0.5rem;
}

.gap-2 {
gap: 0.75rem;
}

.cdk-overlay-container {
z-index: 1100 !important; /* Ensure it is higher than the modal's z-index */
}

0 comments on commit 23646a2

Please sign in to comment.