diff --git a/project-manager/karma.conf.js b/project-manager/karma.conf.js
index 9e88999..9f0e343 100644
--- a/project-manager/karma.conf.js
+++ b/project-manager/karma.conf.js
@@ -1,5 +1,5 @@
// Karma configuration
-// Generated on Sat Mar 25 2017 06:31:51 GMT+0100 (CET)
+// Generated on Thu Mar 09 2017 06:31:51 GMT+0100 (CET)
module.exports = function(config) {
config.set({
@@ -12,6 +12,9 @@ module.exports = function(config) {
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine', '@angular/cli'],
+
+ // list of plugins to load. A plugin can be a string (in which case it will
+ // be required by Karma) or an inlined plugin - Object
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
diff --git a/project-manager/src/app/blog/blog-entry/blog-entry.component.html b/project-manager/src/app/blog/blog-entry/blog-entry.component.html
index 22a4e2f..e7c99b9 100644
--- a/project-manager/src/app/blog/blog-entry/blog-entry.component.html
+++ b/project-manager/src/app/blog/blog-entry/blog-entry.component.html
@@ -4,9 +4,14 @@
{{entry.title}}
-
{{entry.text}}
+
{{entry.text}}
+
+
+ created at:
+ {{entry.createdAt | date: 'yyyy-MM-dd'}}
+
diff --git a/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts b/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts
new file mode 100644
index 0000000..86a7a58
--- /dev/null
+++ b/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts
@@ -0,0 +1,60 @@
+import {TestBed} from "@angular/core/testing";
+import {BlogEntryComponent} from "./blog-entry.component";
+import {BlogEntry} from "./blog-entry";
+
+describe('Blog Entry Isolated Test', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BlogEntryComponent],
+ })
+ });
+
+ it('should render DOM correct according to Input', () => {
+ // Umgebung initialisieren
+ const fixture = TestBed.createComponent(BlogEntryComponent);
+ const blogEntryComponent : BlogEntryComponent = fixture.componentInstance;
+ const element = fixture.nativeElement;
+ const blogEntry : BlogEntry = new BlogEntry;
+
+ // Testdaten
+ const testId = 101;
+ const testTitle = "test title";
+ const testText = "test text";
+ const testImage = "https://www.google.de/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png";
+
+ // Input-Daten
+ blogEntry.id = testId;
+ blogEntry.title = testTitle;
+ blogEntry.text = testText;
+ blogEntry.image = testImage;
+ blogEntry.createdAt = new Date();
+ ( blogEntryComponent).entry = blogEntry;
+
+ // DOM update
+ fixture.detectChanges();
+
+ // DOM auslesen und Daten abgleichen
+ const imageSrc = element.querySelector('div /deep/ .blog-image > img').getAttribute("src");
+ expect(imageSrc).toBe(testImage);
+
+ // textContent statt innerHtml
+ // siehe http://stackoverflow.com/questions/40227533/angular-2-and-jasmine-unit-testing-cannot-get-the-innerhtml
+ const blogTitle = element.querySelector('div /deep/ .blog-summary > span');
+ expect(blogTitle.textContent).toBe(testTitle);
+
+ const blogText = element.querySelector('div /deep/ .blog-summary > p').textContent;
+ expect(blogText).toBe(testText);
+
+ const blogTs = element.querySelector('div /deep/ .blog-timestamp > .timestamp').textContent;
+ expect(new Date(blogTs).getDate()).toBe(new Date().getDate());
+
+ const blogDelete = element.querySelector('div /deep/ .blog-delete > button').textContent;
+ expect(blogDelete).toBe("Entfernen")
+
+ // keine Überprüfung der id, die lediglich im EventListener des Delete Button hinterlegt ist, möglich!
+ // nur durch click-Event auslösen, was jedoch BlogComponent vorraussetzt
+
+ });
+
+});
diff --git a/project-manager/src/app/blog/blog-entry/blog-entry.component.ts b/project-manager/src/app/blog/blog-entry/blog-entry.component.ts
index e348332..933e4aa 100644
--- a/project-manager/src/app/blog/blog-entry/blog-entry.component.ts
+++ b/project-manager/src/app/blog/blog-entry/blog-entry.component.ts
@@ -1,4 +1,4 @@
-import {Component, Input } from '@angular/core';
+import {Component, Input} from '@angular/core';
import {BlogEntry} from './blog-entry';
import {BlogComponent} from "../blog.component";
diff --git a/project-manager/src/app/blog/blog-entry/blog-entry.ts b/project-manager/src/app/blog/blog-entry/blog-entry.ts
index 087336f..e37f920 100644
--- a/project-manager/src/app/blog/blog-entry/blog-entry.ts
+++ b/project-manager/src/app/blog/blog-entry/blog-entry.ts
@@ -1,6 +1,7 @@
export class BlogEntry {
- id :number;
+ id : number;
title: string;
text: string;
image: string;
+ createdAt: Date;
}
diff --git a/project-manager/src/app/blog/blog.component.spec.ts b/project-manager/src/app/blog/blog.component.spec.ts
index c562ebf..ab216bb 100644
--- a/project-manager/src/app/blog/blog.component.spec.ts
+++ b/project-manager/src/app/blog/blog.component.spec.ts
@@ -1,9 +1,48 @@
import {BlogComponent} from './blog.component'
+import {Title} from "@angular/platform-browser";
-describe('', () => {
+describe('Blog Component Isolated Test', () => {
- it('', () => {
+ let blogComponent: BlogComponent;
+ beforeEach(() => {
+ blogComponent = new BlogComponent(null, null, new Title('Project Blog'));
});
+ it('should have initial entries', () => {
+ expect(blogComponent.entries.length).toBe(2);
+ blogComponent.entries.forEach((entry) => {
+ expect(entry.id).toBeLessThanOrEqual(blogComponent.id);
+ expect(entry.createdAt.getDate()).toBe(new Date().getDate());
+ })
+ });
+
+ it('should create new list entry and increment id', () => {
+ let preCreationId = blogComponent.id;
+ let entryTitle = "some fancy title";
+ let entryImage = "https://avatars1.githubusercontent.com/u/3284117";
+ let entryText = "some important text";
+ blogComponent.createBlogEntry(entryTitle, entryImage, entryText);
+
+ let newEntry = blogComponent.entries[blogComponent.entries.length - 1];
+ expect(newEntry.id - 1).toBe(preCreationId)
+ expect(newEntry.image).toBe(entryImage);
+ expect(newEntry.text).toBe(entryText);
+ expect(newEntry.createdAt.getDate()).toBe(new Date().getDate());
+ });
+
+ it('should delete entry by given id - and not change global max-id', () => {
+ let preDeletionId = blogComponent.id;
+ let latestId = blogComponent.entries[blogComponent.entries.length - 1].id;
+ blogComponent.deleteBlogEntry(latestId);
+
+ expect(blogComponent.id).toBe(preDeletionId);
+ expect(() => {
+ if (blogComponent.entries.length > 0) {
+ return blogComponent.entries[blogComponent.entries.length - 1];
+ } else {
+ return 0;
+ }
+ }).not.toBe(latestId);
+ });
});
diff --git a/project-manager/src/app/blog/blog.component.ts b/project-manager/src/app/blog/blog.component.ts
index 920742a..acabd6f 100644
--- a/project-manager/src/app/blog/blog.component.ts
+++ b/project-manager/src/app/blog/blog.component.ts
@@ -18,9 +18,12 @@ export class BlogComponent {
constructor(r: ActivatedRoute, private router: Router, private titleService: Title) {
this.entries = initialEntries;
- if (this.entries.length != 0) {
- this.id = this.entries[this.entries.length-1].id;
- }
+ this.entries.forEach((entry)=> {
+ entry.createdAt = new Date()
+ if (this.id < entry.id) {
+ this.id = entry.id;
+ }
+ });
}
createBlogEntry(title:string, image:string, text:string) {
@@ -31,6 +34,8 @@ export class BlogComponent {
entry.title = title;
entry.image = image;
entry.text = text;
+ entry.createdAt = new Date();
+ console.log(this.entries);
this.entries.push(entry);
}
diff --git a/project-manager/src/app/blog/initialEntries.ts b/project-manager/src/app/blog/initialEntries.ts
index 07dcb67..41700b9 100644
--- a/project-manager/src/app/blog/initialEntries.ts
+++ b/project-manager/src/app/blog/initialEntries.ts
@@ -3,12 +3,14 @@ export var initialEntries = [
id: 1,
title: "Angular 2 erschienen",
image: "data:image/png;base64,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",
- text: "Endlich ist die finale Version von Angular 2 erschienen. Nach langer Beta-Phase ..."
+ text: "Endlich ist die finale Version von Angular 2 erschienen. Nach langer Beta-Phase ...",
+ createdAt: null
},
{
id: 2,
title: "Neue Vorschläge für die kommende Module-Loader-Syntax",
image: "https://cdn-images-1.medium.com/max/1000/1*RQFLsbQumE-iNrXzs_Oz8g.jpeg",
- text: "Nachdem im ES2015-Standard bereits die Module-API verabschiedet wurde, hat das zuständige Konsortium nun neue Vorschläge für die Module-Loader-Syntax bekannt gegeben..."
+ text: "Nachdem im ES2015-Standard bereits die Module-API verabschiedet wurde, hat das zuständige Konsortium nun neue Vorschläge für die Module-Loader-Syntax bekannt gegeben...",
+ createdAt : null
}
-]
\ No newline at end of file
+];