Skip to content

Commit

Permalink
feat(latest-changes): display changelog based on GitHub Release infor…
Browse files Browse the repository at this point in the history
…mation

and use version from environment.ts

closes #238
  • Loading branch information
sleidig committed Jun 23, 2020
1 parent 2a84025 commit 7226f08
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 384 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,31 @@
import { async, ComponentFixture, TestBed } from "@angular/core/testing";

import { AppVersionComponent } from "./app-version.component";
import { MockSessionService } from "../../session/session-service/mock-session.service";
import { EntityMapperService } from "../../entity/entity-mapper.service";
import { LoginState } from "../../session/session-states/login-state.enum";
import { SessionService } from "app/core/session/session-service/session.service";
import { MatDialogModule } from "@angular/material/dialog";
import { LatestChangesService } from "../latest-changes.service";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { ChangelogComponent } from "../changelog/changelog.component";
import { of } from "rxjs";
import { EntitySchemaService } from "app/core/entity/schema/entity-schema.service";
import { LatestChangesDialogService } from "../latest-changes-dialog.service";

describe("AppVersionComponent", () => {
let component: AppVersionComponent;
let fixture: ComponentFixture<AppVersionComponent>;

let latestChangesService: LatestChangesService;
let sessionService: MockSessionService;
let entitySchemaService: EntitySchemaService;
let entityMapper: EntityMapperService;
let latestChangesDialogService: jasmine.SpyObj<LatestChangesDialogService>;

beforeEach(async(() => {
entitySchemaService = new EntitySchemaService();
sessionService = new MockSessionService(entitySchemaService);
latestChangesService = new LatestChangesService(null, null, null, null);
entityMapper = new EntityMapperService(null, null);

spyOn(latestChangesService, "getChangelogs").and.returnValue(
of([
{
name: "test",
tag_name: "v1.0",
body: "latest test",
published_at: "2018-01-01",
},
])
);
spyOn(sessionService.getLoginState(), "getState").and.returnValue(
LoginState.LOGGED_IN
);
latestChangesDialogService = jasmine.createSpyObj([
"getCurrentVersion",
"showLatestChanges",
]);

TestBed.configureTestingModule({
declarations: [AppVersionComponent, ChangelogComponent],
imports: [MatDialogModule, NoopAnimationsModule],
providers: [
{ provide: SessionService, useValue: sessionService },
{ provide: EntityMapperService, useValue: entityMapper },
{ provide: LatestChangesService, useValue: latestChangesService },
{
provide: LatestChangesDialogService,
useValue: latestChangesDialogService,
},
],
});

Expand All @@ -81,10 +59,17 @@ describe("AppVersionComponent", () => {
expect(component).toBeTruthy();
});

it("should open dialog", () => {
const spy = spyOn(latestChangesService, "showLatestChanges");
it("should load currentVersion", () => {
const testVersion = "1.9.9";
latestChangesDialogService.getCurrentVersion.and.returnValue(testVersion);

component.ngOnInit();

expect(component.currentVersion).toEqual(testVersion);
});

it("should open dialog", () => {
component.showLatestChanges();
expect(spy.calls.count()).toBe(1, "dialog not opened");
expect(latestChangesDialogService.showLatestChanges).toHaveBeenCalled();
});
});
14 changes: 4 additions & 10 deletions src/app/core/latest-changes/app-version/app-version.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
*/

import { Component, OnInit } from "@angular/core";
import { EntityMapperService } from "../../entity/entity-mapper.service";
import { LatestChangesService } from "../latest-changes.service";
import { LatestChangesDialogService } from "../latest-changes-dialog.service";

/**
* Simple component displaying the current app version
Expand All @@ -32,21 +31,16 @@ export class AppVersionComponent implements OnInit {
/** the current app version */
currentVersion: string;

constructor(
private _entityMapperService: EntityMapperService,
private changelog: LatestChangesService
) {}
constructor(private changelogDialog: LatestChangesDialogService) {}

ngOnInit(): void {
this.changelog
.getCurrentVersion()
.subscribe((version) => (this.currentVersion = version));
this.currentVersion = this.changelogDialog.getCurrentVersion();
}

/**
* Open dialog box to display changelog information about the latest version to the user.
*/
public showLatestChanges(): void {
this.changelog.showLatestChanges();
this.changelogDialog.showLatestChanges();
}
}
37 changes: 25 additions & 12 deletions src/app/core/latest-changes/changelog/changelog.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,29 @@
-->


<div>
<h3>System updated!</h3>
<h4>
{{ currentChangelog ? currentChangelog.tag_name : "No Changelog Available" }}
<span> {{ (currentChangelog && currentChangelog.name) ? (": " + currentChangelog.name) : ""}}</span>
</h4>
<p class="version-changes-date" style="font-style:italic;">
{{ currentChangelog?.published_at | date }}
</p>
<p style="white-space: pre-wrap; text-align: left" [innerHTML]="currentChangelog?.body"></p>
<h1 mat-dialog-title>Latest Changes</h1>

<button mat-button (click)="onCloseClick()">Close</button>
</div>
<mat-dialog-content #changelogContainer>
<mat-card *ngFor="let changelog of changelogs" class="mat-elevation-z3">
<mat-card-header>
<mat-card-title>{{ changelog ? changelog.name : "No Changelog Available" }}</mat-card-title>
<mat-card-subtitle>
<span class="meta-info"><mat-icon fontIcon="fa-calendar-o"></mat-icon>{{ changelog?.published_at | date }}</span>
<span class="meta-info"><mat-icon fontIcon="fa-tag"></mat-icon>{{ changelog?.tag_name }}</span>
</mat-card-subtitle>
</mat-card-header>

<mat-card-content>
<markdown [data]="changelog?.body"></markdown>
</mat-card-content>

<mat-card-actions *ngIf="showAdvancedDetails">
<button mat-stroked-button><a [href]="'https://www.github.com'" target="_blank">More Information</a></button>
</mat-card-actions>
</mat-card>
</mat-dialog-content>

<mat-dialog-actions class="general-actions">
<button mat-raised-button (click)="loadPreviousRelease()">Show previous changes</button>
<button mat-raised-button color="accent" [mat-dialog-close]="true">Close</button>
</mat-dialog-actions>
20 changes: 20 additions & 0 deletions src/app/core/latest-changes/changelog/changelog.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,23 @@
* along with ndb-core. If not, see <http://www.gnu.org/licenses/>.
*/

.meta-info {
padding: 4px;
margin-right: 10px;
}

.mat-card {
margin: 4px 4px 10px 4px;
}
.mat-card-subtitle {
margin-bottom: 0;
}

a {
color: inherit;
text-decoration: inherit;
}

.general-actions {
justify-content: flex-end;
}
24 changes: 19 additions & 5 deletions src/app/core/latest-changes/changelog/changelog.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,26 @@ describe("ChangelogComponent", () => {
let component: ChangelogComponent;
let fixture: ComponentFixture<ChangelogComponent>;

let mockLatestChangesService: jasmine.SpyObj<LatestChangesService>;

const testChangelog = new Changelog();
testChangelog.tag_name = "1.0.0";
testChangelog.name = "test name";
testChangelog.body = "test changes body";
testChangelog.published_at = "2018-01-01";

beforeEach(async(() => {
mockLatestChangesService = jasmine.createSpyObj([
"getChangelogsBeforeVersion",
]);

TestBed.configureTestingModule({
declarations: [ChangelogComponent],
imports: [MatDialogModule],
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: {} },
{
provide: LatestChangesService,
useValue: { getChangelogs: () => of([testChangelog]) },
},
{ provide: MAT_DIALOG_DATA, useValue: of([testChangelog]) },
{ provide: LatestChangesService, useValue: mockLatestChangesService },
],
}).compileComponents();
}));
Expand All @@ -59,5 +63,15 @@ describe("ChangelogComponent", () => {

it("should be created", () => {
expect(component).toBeTruthy();
expect(component.changelogs).toEqual([testChangelog]);
});

it("should add release info to end of array on 'show previous'", () => {
mockLatestChangesService.getChangelogsBeforeVersion.and.returnValue(
of([testChangelog])
);
component.loadPreviousRelease();

expect(component.changelogs).toEqual([testChangelog, testChangelog]);
});
});
46 changes: 35 additions & 11 deletions src/app/core/latest-changes/changelog/changelog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@
* along with ndb-core. If not, see <http://www.gnu.org/licenses/>.
*/

import { Component, Inject, OnInit } from "@angular/core";
import {
Component,
ElementRef,
Inject,
OnInit,
ViewChild,
} from "@angular/core";
import { Changelog } from "../changelog";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import { isObservable, Observable } from "rxjs";
import { LatestChangesService } from "../latest-changes.service";

/**
* Display information from the changelog for the latest version.
Expand All @@ -30,8 +37,13 @@ import { isObservable, Observable } from "rxjs";
styleUrls: ["./changelog.component.scss"],
})
export class ChangelogComponent implements OnInit {
/** The changelog entry of the version to be displayed */
currentChangelog: Changelog;
/** The array of relevant changelog entries to be displayed */
changelogs: Changelog[];

/** Display advanced information that may not be useful to normal users */
showAdvancedDetails = false;

@ViewChild("changelogContainer") contentContainer: ElementRef;

/**
* This component is to be created through a MatDialog that should pass in the relevant data.
Expand All @@ -40,25 +52,37 @@ export class ChangelogComponent implements OnInit {
* dialog.open(ChangelogComponent, { data: { changelogData: latestChangesService.getChangelogs() } });
*
* @param dialogRef Reference to the parent dialog.
* @param data Changelog data
* @param data Changelog data to be display initially
* @param latestChangesService
*/
constructor(
public dialogRef: MatDialogRef<ChangelogComponent>,
@Inject(MAT_DIALOG_DATA) public data: Observable<Changelog[]>
@Inject(MAT_DIALOG_DATA) public data: Observable<Changelog[]>,
private latestChangesService: LatestChangesService
) {}

ngOnInit(): void {
if (this.data && isObservable(this.data)) {
this.data.subscribe(
(changelog) => (this.currentChangelog = changelog[0])
);
this.data.subscribe((changelog) => (this.changelogs = changelog));
}
}

/**
* Close the parent dialog box.
* Add one more previous release card to the end of the currently displayed list of changelogs.
*/
onCloseClick(): void {
this.dialogRef.close();
loadPreviousRelease() {
const lastDisplayedVersion = this.changelogs[this.changelogs.length - 1]
.tag_name;
this.latestChangesService
.getChangelogsBeforeVersion(lastDisplayedVersion, 1)
.subscribe((additionalChangelog) => {
this.changelogs.push(...additionalChangelog);

setTimeout(() => this.scrollToBottomOfReleases());
});
}

private scrollToBottomOfReleases() {
this.contentContainer.nativeElement.scrollTop = this.contentContainer.nativeElement.scrollHeight;
}
}
Loading

0 comments on commit 7226f08

Please sign in to comment.