diff --git a/project-manager/e2e/access/access.e2e-spec.ts b/project-manager/e2e/access/access.e2e-spec.ts new file mode 100644 index 0000000..b3aa34b --- /dev/null +++ b/project-manager/e2e/access/access.e2e-spec.ts @@ -0,0 +1,53 @@ +import {browser, element, by} from 'protractor'; +import {AbstractHeaderPage} from "../page-object/abstract-header.po"; +import {TaskOverviewPage} from "../page-object/task/task-overview.po"; + +describe('Access Projectmanager Homepage', function () { + + beforeEach(() => { + browser.get('/') + }); + + it('should start at Dashboard page and brand link should redirect there again', () => { + const startingPage = 'Dashboard'; + + function validatePage() { + const dashboardNavEntryCssClass = element(by.linkText(startingPage)).element(by.xpath('..')).getAttribute('class'); + expect(dashboardNavEntryCssClass).toContain('active'); + + const heading = element(by.css('h1')); + const headingText = heading.getText(); + expect(headingText).toBe(startingPage); + } + + validatePage(); + const navBarBrand = element(by.className('navbar-brand')); + expect(navBarBrand.getAttribute('href')).toBe(browser.baseUrl + '/#'); + navBarBrand.click(); + validatePage(); + }); + + it('should provide navigation to "Backlog" via Schnellzugriff button - PageObject', () => { + let headerPage = new AbstractHeaderPage; + headerPage.clickSchnellzugriffFollowedBy('Backlog'); + headerPage.validateCurrentUrl('tasks?query=BACKLOG'); + headerPage.validatePageHeadline('Aufgaben durchsuchen'); + }); + + it('should provide navigation to "Backlog" via Schnellzugriff button - one PageObject per page', () => { + let headerPage = new AbstractHeaderPage; + headerPage.clickSchnellzugriffFollowedBy('Backlog'); + const taskOverviewPage = new TaskOverviewPage; + taskOverviewPage.validateCurrentUrl('tasks?query=BACKLOG'); + taskOverviewPage.validateSearchFieldValue('BACKLOG'); + }); + + it('should provide navigation to "In Bearbeitung" via Schnellzugriff button', () => { + let headerPage = new AbstractHeaderPage; + headerPage.clickSchnellzugriffFollowedBy('In Bearbeitung'); + const taskOverviewPage = new TaskOverviewPage; + taskOverviewPage.validateCurrentUrl('tasks?query=IN_PROGRESS'); + taskOverviewPage.validateSearchFieldValue('IN_PROGRESS'); + }); + +}); diff --git a/project-manager/e2e/example/example.e2e-spec.ts b/project-manager/e2e/example/example.e2e-spec.ts deleted file mode 100644 index 09f2496..0000000 --- a/project-manager/e2e/example/example.e2e-spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {browser, element, by} from 'protractor' - -describe('angularjs homepage todo list', function() { - it('should add a todo', function() { - browser.get('https://angularjs.org'); - - element(by.model('todoList.todoText')).sendKeys('write first protractor test'); - element(by.css('[value="add"]')).click(); - - let todoList = element.all(by.repeater('todo in todoList.todos')); - expect(todoList.count()).toEqual(3); - expect(todoList.get(2).getText()).toEqual('write first protractor test'); - - // You wrote your first test, cross it off the list - todoList.get(2).element(by.css('input')).click(); - let completedAmount = element.all(by.css('.done-true')); - expect(completedAmount.count()).toEqual(2); - }); -}); diff --git a/project-manager/e2e/login/login.e2e-spec.ts b/project-manager/e2e/login/login.e2e-spec.ts deleted file mode 100644 index 0e69e28..0000000 --- a/project-manager/e2e/login/login.e2e-spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {browser, element, by} from 'protractor' - -describe('Login', function() { - - it('should be redirected after login', function() { - browser.get('/'); - }); - -}); diff --git a/project-manager/e2e/page-object/abstract-header.po.ts b/project-manager/e2e/page-object/abstract-header.po.ts new file mode 100644 index 0000000..d809747 --- /dev/null +++ b/project-manager/e2e/page-object/abstract-header.po.ts @@ -0,0 +1,23 @@ +import {element, by} from "protractor"; +import {AbstractPage} from "./abstract-page.po"; + +export class AbstractHeaderPage extends AbstractPage { + + validatePageHeadline(headline: string) { + const head = element(by.css('h1')); + expect(head.getText()).toBe(headline); + } + + clickBrandLink(linkText: string) { + const headerEntry = element(by.linkText(linkText)); + headerEntry.click() + } + + clickSchnellzugriffFollowedBy(linkText: string) { + // starts with css Selector: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors + const schnellzugriffBtn = element(by.className('dropdown')); + schnellzugriffBtn.click(); + schnellzugriffBtn.element(by.linkText(linkText)).click(); + } + +} diff --git a/project-manager/e2e/page-object/abstract-page.po.ts b/project-manager/e2e/page-object/abstract-page.po.ts new file mode 100644 index 0000000..d4125d3 --- /dev/null +++ b/project-manager/e2e/page-object/abstract-page.po.ts @@ -0,0 +1,11 @@ +import {browser} from "protractor"; + +export class AbstractPage { + + baseUrl: string = browser.baseUrl + '/'; + + validateCurrentUrl(urlEnding: string) { + expect(browser.getCurrentUrl()).toBe(this.baseUrl + urlEnding); + } + +} diff --git a/project-manager/e2e/page-object/task/task-edit.po.ts b/project-manager/e2e/page-object/task/task-edit.po.ts new file mode 100644 index 0000000..54bf95d --- /dev/null +++ b/project-manager/e2e/page-object/task/task-edit.po.ts @@ -0,0 +1,71 @@ +import {AbstractHeaderPage} from "../abstract-header.po"; +import {by, element} from "protractor"; +import {TaskOverviewPage} from "./task-overview.po"; + +export class TaskEditPage extends AbstractHeaderPage { + + errorMessages = {title: 'Titel muss mindestens 5 Zeichen enthalten', + email : 'Bitte geben Sie eine gültige E-Mail Adresse an'}; + + titleInput = element(by.id('title')); + descriptionInput = element(by.id('description')); + statusDropdown = element(by.id('state')); + assigneeName = element(by.id('assignee_name')); + assigneeEmail = element(by.id('assignee_email')); + saveButton = element(by.id('save')); + + constructor(newTask: boolean) { + super(); + let headline; + if (newTask) { + headline = 'Neue Aufgabe anlegen'; + } else { + headline = 'Aufgabe bearbeiten'; + } + super.validatePageHeadline(headline) + } + + clearEnterTitle(title: string) { + + this.titleInput.clear(); + this.titleInput.sendKeys(title); + } + + clearEnterDescription(description: string) { + this.descriptionInput.clear(); + this.descriptionInput.sendKeys(description); + } + + setStatus(state: string) { + this.statusDropdown.element(by.css('[value="${state}"]')) + .click()// -> Höller, Christoph - Angular, S. 562 - Listing 14.12 + } + + clearInsertName(name: string) { + this.assigneeName.clear(); + this.assigneeName.sendKeys(name); + } + + clearInsertEmail(email: string) { + this.assigneeEmail.clear(); + this.assigneeEmail.sendKeys(email); + } + + submitTaskForm() { + this.saveButton.click(); + return new TaskOverviewPage; + } + + validateInput(elementId: string, value: string) { + const input = element(by.id(elementId)); + expect(input.getAttribute('value')).toBe(value); + } + + validateError(input: string) { + expect(element(by.className('alert')).getText()) + .toBe(this.errorMessages[input]); + } + + + +} diff --git a/project-manager/e2e/page-object/task/task-overview.po.ts b/project-manager/e2e/page-object/task/task-overview.po.ts new file mode 100644 index 0000000..13a99d6 --- /dev/null +++ b/project-manager/e2e/page-object/task/task-overview.po.ts @@ -0,0 +1,37 @@ +import {AbstractHeaderPage} from "../abstract-header.po"; +import {browser, by, element, ElementArrayFinder, ElementFinder, ExpectedConditions} from "protractor"; +import {TaskEditPage} from "./task-edit.po"; + +export class TaskOverviewPage extends AbstractHeaderPage { + + neueAufgabeBtn: ElementFinder; + taskEntries: ElementArrayFinder; + + constructor() { + super(); + super.validatePageHeadline('Aufgaben durchsuchen'); + this.neueAufgabeBtn = element(by.linkText('Neue Aufgabe anlegen')); + this.taskEntries = element.all(by.className('task-list-entry')); + } + + validateSearchFieldValue(value: string) { + const inputField = element(by.id('search-tasks')); + expect(inputField.getAttribute('value')).toEqual(value); + } + + clickNeueAufgabe(): TaskEditPage { + this.neueAufgabeBtn.click(); + return new TaskEditPage(true); + } + + verifyNewTask(title: string) { + const newEntry = element(by.linkText(title)); + browser.wait(ExpectedConditions.presenceOf(newEntry)); + } + + clickTask(title: string) { + const entry = element(by.linkText(title)); + entry.click(); + return new TaskEditPage(false); + } +} diff --git a/project-manager/e2e/task/create-new-task.e2e-spec.ts b/project-manager/e2e/task/create-new-task.e2e-spec.ts new file mode 100644 index 0000000..2c792e5 --- /dev/null +++ b/project-manager/e2e/task/create-new-task.e2e-spec.ts @@ -0,0 +1,40 @@ +import {browser} from 'protractor'; +import {TaskOverviewPage} from "../page-object/task/task-overview.po"; +import {TaskEditPage} from "../page-object/task/task-edit.po"; + +describe('Create New Task Form', function () { + + it('should redirect to "New Task Form" and contain new entry in list after creation with valid title and assignee-email', () => { + const testTitle = 'valid title'; + const testEmail = 'testuser@test.com'; + + browser.get('/tasks'); + const taskOverviewPage = new TaskOverviewPage; + taskOverviewPage.validateSearchFieldValue(''); + const taskEditPage = taskOverviewPage.clickNeueAufgabe(); + taskEditPage.clearEnterTitle(testTitle); + taskEditPage.clearInsertEmail(testEmail); + taskEditPage.submitTaskForm().verifyNewTask(testTitle); + }); + + it('should show error-message on title entered being too short', () => { + const testTitle = 'xyz'; + + browser.get('/tasks/new'); + const taskEditPage = new TaskEditPage(true); + taskEditPage.clearEnterTitle(testTitle); + taskEditPage.clearEnterDescription(''); + taskEditPage.validateError('title'); + }); + + it('should show error-message on email entered not matching pattern', () => { + const testEmail = 'wrong@mail.'; + + browser.get('/tasks/new'); + const taskEditPage = new TaskEditPage(true); + taskEditPage.clearInsertEmail(testEmail); + taskEditPage.clearEnterDescription(''); + taskEditPage.validateError('email'); + }) + +}); diff --git a/project-manager/e2e/task/edit-task.e2e-spec.ts b/project-manager/e2e/task/edit-task.e2e-spec.ts new file mode 100644 index 0000000..303b3ad --- /dev/null +++ b/project-manager/e2e/task/edit-task.e2e-spec.ts @@ -0,0 +1,45 @@ +import {browser} from 'protractor'; +import {TaskOverviewPage} from "../page-object/task/task-overview.po"; +import {TaskEditPage} from "../page-object/task/task-edit.po"; + +describe('Edit Task Form', function () { + + const dbTask = { + id: 3, + title: "Ersten Prototyp mit Angular 2.0 entwickeln", + description: "Der Prototyp soll zeigen, wie Routing und HTTP-Anbindung umgesetzt werden können.", + tags: [], + state: "IN_PROGRESS", + assignee: { + name: "Christoph Höller", + email: "" + } + }; + + it('should load task with its values in edit form', () => { + browser.get('/tasks'); + const taskOverviewPage = new TaskOverviewPage; + taskOverviewPage.validateSearchFieldValue(''); + const taskEditPage = taskOverviewPage.clickTask(dbTask.title); + // check for correct url - test input field values + taskEditPage.validateCurrentUrl('tasks/edit/' + dbTask.id); + taskEditPage.validateInput('title', dbTask.title); + taskEditPage.validateInput('description', dbTask.description); + taskEditPage.validateInput('state', dbTask.state); + taskEditPage.validateInput('assignee_name', dbTask.assignee['name']) + taskEditPage.validateInput('assignee_email', dbTask.assignee['email']) + }); + + it('should have updated title in overview after edit, but same task id as before', () => { + browser.get('/tasks/edit/' + dbTask.id); + const newTitle = 'Ersten Prototyp mit Angular 4.0 entwickelt'; + const taskEditPage = new TaskEditPage(false); + taskEditPage.clearEnterTitle(newTitle); + let taskOverviewPage = taskEditPage.submitTaskForm(); + // validate that ID is kept after change + taskOverviewPage.verifyNewTask(newTitle); + taskOverviewPage.clickTask(newTitle).validateCurrentUrl('tasks/edit/' + dbTask.id); + }); + +}); + diff --git a/project-manager/e2e/tsconfig.e2e.json b/project-manager/e2e/tsconfig.e2e.json index ac7a373..5e6ee11 100644 --- a/project-manager/e2e/tsconfig.e2e.json +++ b/project-manager/e2e/tsconfig.e2e.json @@ -3,8 +3,8 @@ "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", - "target": "es5", - "types":[ + "target": "es6", + "types": [ "jasmine", "node" ] diff --git a/project-manager/package.json b/project-manager/package.json index 8f01959..ce42c78 100644 --- a/project-manager/package.json +++ b/project-manager/package.json @@ -9,8 +9,8 @@ "starte2e": "concurrently \"node projects-server/server.js db=projects-server/test.json\" \"ng serve --env=e2e\" ", "lint": "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check", "test": "ng test", - "pree2e": "webdriver-manager update --standalone false --gecko false", - "e2e": "protractor", + "pree2e": "cp ./projects-server/safe/test.json ./projects-server/test.json && webdriver-manager update --standalone false --gecko false", + "e2e": "concurrently \"node projects-server/server.js db=projects-server/test.json\" \"ng e2e --env=e2e\" ", "install-server": "npm install --prefix ./projects-server ./projects-server", "e2e-screenshots": "protractor ./protractor-html-reporter.conf.js", "postinstall": "npm run install-server" diff --git a/project-manager/projects-server/safe/test.json b/project-manager/projects-server/safe/test.json new file mode 100644 index 0000000..7506597 --- /dev/null +++ b/project-manager/projects-server/safe/test.json @@ -0,0 +1,62 @@ +{ + "tasks": [ + { + "id": 3, + "title": "Ersten Prototyp mit Angular 2.0 entwickeln", + "description": "Der Prototyp soll zeigen, wie Routing und HTTP-Anbindung umgesetzt werden können.", + "tags": [], + "state": "IN_PROGRESS", + "assignee": { + "name": "Christoph Höller", + "email": "" + } + }, + { + "id": 4, + "title": "Präsentation vorbereiten...", + "description": "Nicht länger als 15 Minuten... ", + "tags": [], + "assignee": { + "name": "John Doe", + "email": "john@doe.com" + }, + "state": "BACKLOG" + }, + { + "title": "Test der Websocket-Funktionalität", + "description": "Mit Hilfe der Websocket-Funktionalität soll es möglich sein, die Task-Daten über alle Browserfenster hinweg synchron zu halten.", + "tags": [], + "assignee": {}, + "id": 5, + "state": "BACKLOG" + }, + { + "assignee": {}, + "tags": [], + "state": "IN_PROGRESS", + "title": "New Task 1484898385971", + "id": 6 + }, + { + "assignee": {}, + "tags": [], + "state": "IN_PROGRESS", + "title": "New Task", + "id": 7 + }, + { + "assignee": {}, + "tags": [], + "state": "IN_PROGRESS", + "title": "New Task 1487974284967", + "id": 8 + }, + { + "assignee": {}, + "tags": [], + "state": "IN_PROGRESS", + "title": "New Task", + "id": 9 + } + ] +} \ No newline at end of file diff --git a/project-manager/projects-server/test.json b/project-manager/projects-server/test.json index 7506597..568c627 100644 --- a/project-manager/projects-server/test.json +++ b/project-manager/projects-server/test.json @@ -2,7 +2,7 @@ "tasks": [ { "id": 3, - "title": "Ersten Prototyp mit Angular 2.0 entwickeln", + "title": "Ersten Prototyp mit Angular 4.0 entwickelt", "description": "Der Prototyp soll zeigen, wie Routing und HTTP-Anbindung umgesetzt werden können.", "tags": [], "state": "IN_PROGRESS", @@ -57,6 +57,15 @@ "state": "IN_PROGRESS", "title": "New Task", "id": 9 + }, + { + "assignee": { + "email": "testuser@test.com" + }, + "tags": [], + "state": "BACKLOG", + "title": "valid title", + "id": 10 } ] } \ No newline at end of file diff --git a/project-manager/protractor.conf.js b/project-manager/protractor.conf.js index 1c5e1e5..d46e32a 100644 --- a/project-manager/protractor.conf.js +++ b/project-manager/protractor.conf.js @@ -3,7 +3,7 @@ const { SpecReporter } = require('jasmine-spec-reporter'); -exports.config = { +exports.config= { allScriptsTimeout: 11000, specs: [ './e2e/**/*.e2e-spec.ts' @@ -12,7 +12,7 @@ exports.config = { 'browserName': 'chrome' }, directConnect: true, - baseUrl: 'http://localhost:4200/', + baseUrl: 'http://localhost:4200', framework: 'jasmine', jasmineNodeOpts: { showColors: true, diff --git a/project-manager/src/app/tasks/edit-task/edit-task.component.html b/project-manager/src/app/tasks/edit-task/edit-task.component.html index 413826f..dd273d8 100644 --- a/project-manager/src/app/tasks/edit-task/edit-task.component.html +++ b/project-manager/src/app/tasks/edit-task/edit-task.component.html @@ -1,5 +1,5 @@ -

Neue Aufgabe anlegen

-

Aufgabe bearbeiten

+

Neue Aufgabe anlegen

+

Aufgabe bearbeiten