Add working e2e-test setup incl. tests
parent
ad73633ae8
commit
a964b4b492
|
@ -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');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
import {browser, element, by} from 'protractor'
|
|
||||||
|
|
||||||
describe('Login', function() {
|
|
||||||
|
|
||||||
it('should be redirected after login', function() {
|
|
||||||
browser.get('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../out-tsc/e2e",
|
"outDir": "../out-tsc/e2e",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"types":[
|
"types": [
|
||||||
"jasmine",
|
"jasmine",
|
||||||
"node"
|
"node"
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
"starte2e": "concurrently \"node projects-server/server.js db=projects-server/test.json\" \"ng serve --env=e2e\" ",
|
"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",
|
"lint": "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
"pree2e": "cp ./projects-server/safe/test.json ./projects-server/test.json && webdriver-manager update --standalone false --gecko false",
|
||||||
"e2e": "protractor",
|
"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",
|
"install-server": "npm install --prefix ./projects-server ./projects-server",
|
||||||
"e2e-screenshots": "protractor ./protractor-html-reporter.conf.js",
|
"e2e-screenshots": "protractor ./protractor-html-reporter.conf.js",
|
||||||
"postinstall": "npm run install-server"
|
"postinstall": "npm run install-server"
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"id": 3,
|
"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.",
|
"description": "Der Prototyp soll zeigen, wie Routing und HTTP-Anbindung umgesetzt werden können.",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"state": "IN_PROGRESS",
|
"state": "IN_PROGRESS",
|
||||||
|
@ -57,6 +57,15 @@
|
||||||
"state": "IN_PROGRESS",
|
"state": "IN_PROGRESS",
|
||||||
"title": "New Task",
|
"title": "New Task",
|
||||||
"id": 9
|
"id": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assignee": {
|
||||||
|
"email": "testuser@test.com"
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"state": "BACKLOG",
|
||||||
|
"title": "valid title",
|
||||||
|
"id": 10
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
exports.config = {
|
exports.config= {
|
||||||
allScriptsTimeout: 11000,
|
allScriptsTimeout: 11000,
|
||||||
specs: [
|
specs: [
|
||||||
'./e2e/**/*.e2e-spec.ts'
|
'./e2e/**/*.e2e-spec.ts'
|
||||||
|
@ -12,7 +12,7 @@ exports.config = {
|
||||||
'browserName': 'chrome'
|
'browserName': 'chrome'
|
||||||
},
|
},
|
||||||
directConnect: true,
|
directConnect: true,
|
||||||
baseUrl: 'http://localhost:4200/',
|
baseUrl: 'http://localhost:4200',
|
||||||
framework: 'jasmine',
|
framework: 'jasmine',
|
||||||
jasmineNodeOpts: {
|
jasmineNodeOpts: {
|
||||||
showColors: true,
|
showColors: true,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<h2 *ngIf="!task.id">Neue Aufgabe anlegen</h2>
|
<h1 *ngIf="!task.id">Neue Aufgabe anlegen</h1>
|
||||||
<h2 *ngIf="task.id">Aufgabe bearbeiten</h2>
|
<h1 *ngIf="task.id">Aufgabe bearbeiten</h1>
|
||||||
|
|
||||||
<form (submit)="saveTask()" #form="ngForm">
|
<form (submit)="saveTask()" #form="ngForm">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
Loading…
Reference in New Issue