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": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types":[
|
||||
"target": "es6",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<h2 *ngIf="!task.id">Neue Aufgabe anlegen</h2>
|
||||
<h2 *ngIf="task.id">Aufgabe bearbeiten</h2>
|
||||
<h1 *ngIf="!task.id">Neue Aufgabe anlegen</h1>
|
||||
<h1 *ngIf="task.id">Aufgabe bearbeiten</h1>
|
||||
|
||||
<form (submit)="saveTask()" #form="ngForm">
|
||||
<div class="form-group">
|
||||
|
|
Loading…
Reference in New Issue