From 145f7d2d1ad1dc7a476e7529a6d77c6ee91f0e03 Mon Sep 17 00:00:00 2001 From: Rudreshuar Galangco Date: Thu, 30 Jan 2020 10:49:45 +0800 Subject: [PATCH 1/5] Initial commit --- e2e/protractor.conf.js | 56 +++++--- e2e/src/feature/employee/employee.feature | 32 +++++ e2e/src/hooks/generic.hooks.ts | 19 +++ e2e/src/model/employee.class.ts | 115 +++++++++++++++ e2e/src/po/dashboard.po.ts | 70 ++++++++++ e2e/src/po/form.po.ts | 45 ++++++ e2e/src/po/message.po.ts | 11 ++ e2e/src/po/security.po.ts | 23 +++ .../step_definition/employee.definitions.ts | 132 ++++++++++++++++++ .../step_definition/security.definitions.ts | 0 protractor.conf.js | 45 ++++++ reports/mryumul.json | 71 ++++++++++ 12 files changed, 598 insertions(+), 21 deletions(-) create mode 100644 e2e/src/feature/employee/employee.feature create mode 100644 e2e/src/hooks/generic.hooks.ts create mode 100644 e2e/src/model/employee.class.ts create mode 100644 e2e/src/po/dashboard.po.ts create mode 100644 e2e/src/po/form.po.ts create mode 100644 e2e/src/po/message.po.ts create mode 100644 e2e/src/po/security.po.ts create mode 100644 e2e/src/step_definition/employee.definitions.ts create mode 100644 e2e/src/step_definition/security.definitions.ts create mode 100644 protractor.conf.js create mode 100644 reports/mryumul.json diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index 73e4e68..e270671 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -1,32 +1,46 @@ -// @ts-check // Protractor configuration file, see link for more information // https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter } = require('jasmine-spec-reporter'); - -/** - * @type { import("protractor").Config } - */ exports.config = { allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], + getPageTimeout: 60000, capabilities: { - 'browserName': 'chrome' + 'browserName': 'chrome', + 'chromeOptions': { + 'args': [ + '--start-maximized', // Uncomment to launch tests using the browser and comment the argument `properties` below + // '--headless', + // '--disable-gpu', + // '--window-size=1920,1080', + // '--no-sandbox', + // '--disable-dev-shm-usage' + ] + } }, directConnect: true, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} + baseUrl: 'localhost:4300', + specs: [ + 'src/feature/employee/employee.feature', + ], + framework: 'custom', + frameworkPath: require.resolve('protractor-cucumber-framework'), + cucumberOpts: { + require: [ + 'src/step_definition/*.definitions.ts', + 'src/hooks/generic.hooks.ts' + ], + tags: '@Employee', + strict: true, + format: [ + `json:reports/mryumul.json`, + ], + dryRun: false, + compiler: [] }, + params: {}, onPrepare() { require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.json') + project: 'e2e/tsconfig.e2e.json' }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } -}; \ No newline at end of file + }, + // ignoreUncaughtExceptions: true, +}; diff --git a/e2e/src/feature/employee/employee.feature b/e2e/src/feature/employee/employee.feature new file mode 100644 index 0000000..0bc0ab6 --- /dev/null +++ b/e2e/src/feature/employee/employee.feature @@ -0,0 +1,32 @@ +@Employee +Feature: Employee + As a user + I should be able to create, read, update, and delete an Employee record + + @Create + Scenario: Create an Employee record + Given I am on the dashboard page + When I "create" an employee record + Then I should see the success message "Success: Data has been added!" + And I should "see" the "created" employee record in the list + + @Read + Scenario: Read Employee records + Given I am on the dashboard page + When I "read" an employee record + Then I should "see" the employee records in the list + + @Update + Scenario: Update an Employee record + Given I am on the dashboard page + And There are existing employee records + When I "update" an employee record + Then I should see the success message "Success: Data has been updated!" + And I should "see" the "updated" employee record in the list + + @Delete + Scenario: Delete an Employee record + Given I am on the dashboard page + And There are existing employee records + When I "delete" an employee record + Then I should "not see" the "deleted" employee record in the list diff --git a/e2e/src/hooks/generic.hooks.ts b/e2e/src/hooks/generic.hooks.ts new file mode 100644 index 0000000..f0386e0 --- /dev/null +++ b/e2e/src/hooks/generic.hooks.ts @@ -0,0 +1,19 @@ +import { BeforeAll, Before, setDefaultTimeout } from 'cucumber'; +import { browser, protractor } from 'protractor'; +import { SecurityPage } from '../po/security.po'; + +const EC = protractor.ExpectedConditions; + +BeforeAll(() => { + browser.waitForAngularEnabled(false); + setDefaultTimeout(60 *10000); +}); + +Before(async () => { + // Check if logged out, else login + if (await browser.getCurrentUrl() === 'data:,' || await browser.getCurrentUrl() === 'http://localhost:4300/login') { + browser.get(`http://localhost:4300/login`); + await browser.wait(EC.urlIs(`http://localhost:4300/login`), browser.params.defaultTimer); + await SecurityPage.login(); + } +}); diff --git a/e2e/src/model/employee.class.ts b/e2e/src/model/employee.class.ts new file mode 100644 index 0000000..3139340 --- /dev/null +++ b/e2e/src/model/employee.class.ts @@ -0,0 +1,115 @@ +export interface EmployeeDetails { + id?: number; + firstName: string; + lastName: string; + country?: string; + nationality?: string; + company?: string; + designation?: string; + workExp?: string; + dataSource?: string; +} + +export class Employee implements EmployeeDetails { + id: number; + firstName: string; + lastName: string; + country: string; + nationality: string; + company: string; + designation: string; + workExp: string; + dataSource: string; + + constructor( + employeeDetails: EmployeeDetails + ) { + this.id = employeeDetails.id; + this.firstName = employeeDetails.firstName; + this.lastName = employeeDetails.lastName; + this.company = employeeDetails.company; + this.country = employeeDetails.country; + this.nationality = employeeDetails.nationality; + this.designation = employeeDetails.designation; + this.workExp = employeeDetails.workExp; + this.dataSource = employeeDetails.dataSource; + } + + get getFirstName() { + return this.firstName; + } + + get getLastName() { + return this.lastName; + } + + get getCountry() { + return this.country; + } + + get getNationality() { + return this.nationality; + } + + get getCompany() { + return this.company; + } + + get getDesignation() { + return this.designation; + } + + get getWorkExp() { + return this.workExp; + } + + get getDataSource() { + return this.dataSource; + } + + set updateFirstName(newName: string) { + this.firstName = newName; + } + + set updateLastName(newLastName: string) { + this.lastName = newLastName; + } + + set setCountry(newCountry: string) { + this.country = newCountry; + } + + set setNationality(newNationality: string) { + this.nationality = newNationality; + } + + set setCompany(newCompany: string) { + this.company = newCompany; + } + + set setDesignation(newDesignation: string) { + this.designation = newDesignation; + } + + set setWorkExp(newWorkExp: string) { + this.workExp = newWorkExp; + } + + set setDataSource(newDataSource: string) { + this.dataSource = newDataSource; + } + + get details(): EmployeeDetails { + return { + id: this.id, + firstName: this.firstName, + lastName: this.lastName, + country: this.country, + nationality: this.nationality, + company: this.company, + designation: this.designation, + workExp: this.workExp, + dataSource: this.dataSource, + }; + } +} diff --git a/e2e/src/po/dashboard.po.ts b/e2e/src/po/dashboard.po.ts new file mode 100644 index 0000000..662098e --- /dev/null +++ b/e2e/src/po/dashboard.po.ts @@ -0,0 +1,70 @@ +import { element, by, browser } from 'protractor'; +import { FormPage } from './form.po'; +import { Employee, EmployeeDetails } from '../model/employee.class'; + +export class DashboardPage { + static get table() { + return element(by.tagName('p-table')); + } +​ + static get tableRows() { + return this.table.all(by.tagName('tr')); + } + + static get tableBody() { + return this.table.all(by.tagName('tbody')).get(0); + } + + static get tableBodyRows() { + return this.tableBody.all(by.tagName('tr')); + } +​ + static get tableNextPageButton() { + return element(by.css('span[class="ui-paginator-icon pi pi-caret-right"]')); + } + + static async checkTableRowForData(data: string) { + let foundData = null; + for (const trData of (await this.tableRows.getText() as any)) { + if (trData.includes(data)) { + foundData = trData; + break; + } + } +​ + return foundData; + } + + static get newEmployeeButton() { + return element(by.buttonText('New Employee')); + } + + static deleteEmployeeButton(index: number) { + return element.all(by.buttonText('Delete')).get(index); + } + + static async createEmployee(createData: EmployeeDetails) { + await this.newEmployeeButton.click(); + await FormPage.fillForm(createData); + await FormPage.formButton('Add').click(); + } + + static async updateEmployee(updateData: EmployeeDetails): Promise { + const employeeRecordElement = this.tableBodyRows.get(4); + const employeeRecordID = JSON.parse(await employeeRecordElement.all(by.tagName('td')).get(0).getText()); + + await employeeRecordElement.click(); + await FormPage.fillForm(updateData); + await FormPage.formButton('Update').click(); + + updateData.id = employeeRecordID; + return updateData; + } + + static async deleteEmployee(): Promise { + await this.deleteEmployeeButton(3).click(); + await (await browser.switchTo().alert()).accept(); + + return new Employee({id: 3, firstName: 'Doris', lastName: 'Wilder'}); + } +} diff --git a/e2e/src/po/form.po.ts b/e2e/src/po/form.po.ts new file mode 100644 index 0000000..198e144 --- /dev/null +++ b/e2e/src/po/form.po.ts @@ -0,0 +1,45 @@ +import { element, by, ElementFinder } from 'protractor'; +import { EmployeeDetails, Employee } from '../model/employee.class'; + + + +export class FormPage { + + static get updateButton() { + return element(by.buttonText('Update')); + } + + static get newEmployeeButton(): ElementFinder{ + return element(by.id('new-employee')); + } + + static getFormControlElement(formControlName: string) { + return element(by.css(`[formcontrolname=${formControlName}]`)); + } + + static formButton(buttonText: string) { + return element(by.buttonText(buttonText)); + } + + static async fillForm(employeeData: EmployeeDetails) { + // tslint:disable-next-line: forin + for (const key in employeeData) { + if (key === 'id') { + continue; + } + + const formElement = this.getFormControlElement(key); + + if (key !== 'dataSource') { + await formElement.clear(); + await formElement.sendKeys(employeeData[key]); + } else { + await formElement.click(); + const dropdownItemsElements = element.all(by.tagName('p-dropdownitem')); + const dropdownItems = await dropdownItemsElements.getText(); + + await dropdownItemsElements.get(dropdownItems.indexOf(employeeData[key])).click(); + } + } + } +} diff --git a/e2e/src/po/message.po.ts b/e2e/src/po/message.po.ts new file mode 100644 index 0000000..09e57af --- /dev/null +++ b/e2e/src/po/message.po.ts @@ -0,0 +1,11 @@ +import { element, by } from 'protractor'; +​ +export class MessagePage { + static get successMessage() { + return element(by.id('success-message')); + } +​ +​ static get updatedMessage() { + return element(by.css('span[class="ui-messages-detail ng-tns-c3-2 ng-star-inserted"]')); + } +} diff --git a/e2e/src/po/security.po.ts b/e2e/src/po/security.po.ts new file mode 100644 index 0000000..4279b00 --- /dev/null +++ b/e2e/src/po/security.po.ts @@ -0,0 +1,23 @@ +import { element, by, ElementFinder, logging, browser } from 'protractor'; + +export class SecurityPage { + + static get usernameField(): ElementFinder{ + return element(by.id('username-field')); + } + + static get passwordField(): ElementFinder{ + return element(by.id('password-field')); + } + + static get loginButton(): ElementFinder{ + return element(by.css('button[label="Login"]')); + } + + + static async login(){ + await this.usernameField.sendKeys('asd'); + await this.passwordField.sendKeys('asd'); + await this.loginButton.click(); + } +} diff --git a/e2e/src/step_definition/employee.definitions.ts b/e2e/src/step_definition/employee.definitions.ts new file mode 100644 index 0000000..eac14c9 --- /dev/null +++ b/e2e/src/step_definition/employee.definitions.ts @@ -0,0 +1,132 @@ +import { Given, When, Then } from 'cucumber'; +import { browser, element, by } from 'protractor'; +import { DashboardPage } from '../po/dashboard.po'; +import { MessagePage } from '../po/message.po'; +import { Employee, EmployeeDetails } from '../model/employee.class'; + +const chai = require('chai').use(require('chai-as-promised')); +const expect = chai.expect; + +let data: EmployeeDetails; + +Given('I am on the dashboard page', async function() { + const currentUrl = await browser.getCurrentUrl(); + const dashboardUrl = 'http://localhost:4300/dashboard'; + + if (currentUrl !== dashboardUrl) { + await browser.get(dashboardUrl); + } + + const dashboardElement = element(by.tagName('app-dashboard')); + if (!(await dashboardElement.isPresent())) { + throw Error('The user is not on the dashboard page'); + } +}); + +Given('There are existing employee records', async function() { + if (!(await DashboardPage.tableBodyRows.get(0).isPresent())) { + throw Error('There are no existing employee records'); + } +}) + +When('I {string} an employee record', async function(action: string) { + switch (action) { + case 'create': + data = new Employee({ + firstName: 'Arden', + lastName: 'Mercado', + country: 'Japan', + nationality: 'Nihonjin', + company: 'TitusUniversal', + designation: 'CEO', + workExp: '69 years', + dataSource: 'Monster Gulf' + }).details; + await DashboardPage.createEmployee(data); + break; + case 'read': + // user should read with his/her eyes + break; + case 'update': + data = await DashboardPage.updateEmployee(new Employee( + { + firstName: 'Ivan', + lastName: 'Mercado', + country: 'Japan', + nationality: 'Nihonjin', + company: 'TitusUniversal', + designation: 'CEO', + workExp: '69 years', + dataSource: 'Monster Gulf' + } + ).details); + break; + case 'delete': + data = await DashboardPage.deleteEmployee(); + break; + default: + throw Error(`Action "${action}" for the employee record is not yet implemented!`) + } + + await browser.sleep(1000); +}); + +Then('I should see the success message {string}', async function(expectedMessage: string) { + const messageObject = MessagePage.successMessage; + expect(true, 'The success message was not displayed').to.equal(await messageObject.isPresent()); + expect(await messageObject.getText(), 'The success message did not match the expected message').to.equal(expectedMessage); +}); + +Then('I should {string} the {string} employee record in the list', async function(view: string, action: string) { + const { firstName, lastName } = data; + const fullName = `${firstName} ${lastName}`; + + if (view === 'see') { + switch(action) { + case 'created': + case 'updated': + + let rowData; + + if (!(await DashboardPage.checkTableRowForData(fullName))) { + await DashboardPage.tableNextPageButton.click(); + await browser.sleep(1000); + } + + rowData = (await DashboardPage.checkTableRowForData(fullName) as string); + rowData = rowData ? (rowData.replace('Delete', '')).trim() : null; + + let stringifiedData = ''; + + Object.keys(data).forEach(key => { + stringifiedData = `${stringifiedData} ${data[key]}`; + }); + + expect(stringifiedData.trim()).to.equal(rowData); + break; + default: + throw Error(`Action "${action}" for the employee record is not yet implemented!`) + } + } else { + const rowData = (await DashboardPage.checkTableRowForData(fullName) as string); + expect(null, 'The data is still in the table').to.equal(rowData); + } +}); + +Then('I should see the employee record(s) in the list', async function() { + let dataWasNotFound = false; + const expectedEmployeeList = [ + '0 Brenden Wagner United States of America, California American Facebook Software Engineer 8 Facebook', + '1 Cara Steves United States of America, New York American Walmart Sales Assistant 5 Google', + '4 Jenny Chang Singapore, Singapore Chinese Singapore Airlines Regional Director 15 Twitter', + ]; + + for (const employeeData of expectedEmployeeList) { + if (!await DashboardPage.checkTableRowForData(employeeData)) { + dataWasNotFound = true; + break; + } + } + + expect(false, 'The expected data was not found in the table').to.equal(dataWasNotFound) +}); diff --git a/e2e/src/step_definition/security.definitions.ts b/e2e/src/step_definition/security.definitions.ts new file mode 100644 index 0000000..e69de29 diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100644 index 0000000..44904a7 --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,45 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts +exports.config = { + allScriptsTimeout: 11000, + getPageTimeout: 60000, + capabilities: { + 'browserName': 'chrome', + 'chromeOptions': { + 'args': [ + '--start-maximized', // Uncomment to launch tests using the browser and comment the argument `properties` below + // '--headless', + // '--disable-gpu', + // '--window-size=1920,1080', + // '--no-sandbox', + // '--disable-dev-shm-usage' + ] + } + }, + directConnect: true, + baseUrl: 'localhost:4300', + specs: [ + 'e2e/src/feature/security/security.feature' + ], + framework: 'custom', + frameworkPath: require.resolve('protractor-cucumber-framework'), + cucumberOpts: { + require: [ + './e2e/src/step_definition/security.definitions.ts' + ], + tags: [], + strict: true, + format: [ + `json:reports/mryumul.json`, + ], + dryRun: false, + compiler: [] + }, + params: {}, + onPrepare() { + require('ts-node').register({ + project: 'e2e/tsconfig.e2e.json' + }); + }, + // ignoreUncaughtExceptions: true, +}; diff --git a/reports/mryumul.json b/reports/mryumul.json new file mode 100644 index 0000000..ff99e67 --- /dev/null +++ b/reports/mryumul.json @@ -0,0 +1,71 @@ +[ + { + "description": " As a user\n I should be able to access the system with proper credentials", + "keyword": "Feature", + "name": "Security", + "line": 2, + "id": "security", + "tags": [ + { + "name": "@Security", + "line": 1 + } + ], + "uri": "e2e\\src\\feature\\security\\security.feature", + "elements": [ + { + "id": "security;login", + "keyword": "Scenario", + "line": 6, + "name": "Login", + "tags": [ + { + "name": "@Security", + "line": 1 + } + ], + "type": "scenario", + "steps": [ + { + "arguments": [], + "keyword": "Given ", + "line": 7, + "name": "I am on the login page", + "result": { + "status": "undefined" + } + }, + { + "arguments": [], + "keyword": "When ", + "line": 8, + "name": "I log in", + "result": { + "status": "undefined" + } + }, + { + "arguments": [], + "keyword": "Then ", + "line": 9, + "name": "I should be redirected to the dashboard", + "result": { + "status": "undefined" + } + }, + { + "keyword": "After", + "hidden": true, + "match": { + "location": "node_modules\\protractor-cucumber-framework\\lib\\resultsCapturer.js:27" + }, + "result": { + "status": "passed", + "duration": 2000000 + } + } + ] + } + ] + } +] \ No newline at end of file From b4de83afc4cca8f8c7486a4035e55dfaf50a4ed3 Mon Sep 17 00:00:00 2001 From: Rudreshuar Galangco Date: Thu, 30 Jan 2020 13:19:33 +0800 Subject: [PATCH 2/5] Added wait before clicking submit button --- e2e/src/po/dashboard.po.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/e2e/src/po/dashboard.po.ts b/e2e/src/po/dashboard.po.ts index 662098e..59a8667 100644 --- a/e2e/src/po/dashboard.po.ts +++ b/e2e/src/po/dashboard.po.ts @@ -46,6 +46,7 @@ export class DashboardPage { static async createEmployee(createData: EmployeeDetails) { await this.newEmployeeButton.click(); await FormPage.fillForm(createData); + await browser.sleep(800); await FormPage.formButton('Add').click(); } @@ -55,6 +56,7 @@ export class DashboardPage { await employeeRecordElement.click(); await FormPage.fillForm(updateData); + await browser.sleep(800); await FormPage.formButton('Update').click(); updateData.id = employeeRecordID; From eaf83fd1b87f88c0cd2c7fd911e9f4d449dad139 Mon Sep 17 00:00:00 2001 From: Rudreshuar Galangco Date: Thu, 30 Jan 2020 13:19:52 +0800 Subject: [PATCH 3/5] Fixed parameter" --- e2e/src/feature/employee/employee.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/src/feature/employee/employee.feature b/e2e/src/feature/employee/employee.feature index 0bc0ab6..4ef6622 100644 --- a/e2e/src/feature/employee/employee.feature +++ b/e2e/src/feature/employee/employee.feature @@ -14,7 +14,7 @@ Feature: Employee Scenario: Read Employee records Given I am on the dashboard page When I "read" an employee record - Then I should "see" the employee records in the list + Then I should see the employee records in the list @Update Scenario: Update an Employee record From 87d0d4fe84cd117189259ad2f32af28ce9ea0af6 Mon Sep 17 00:00:00 2001 From: Rudreshuar Galangco Date: Mon, 3 Feb 2020 11:38:57 +0800 Subject: [PATCH 4/5] Added utils and fixed read data --- e2e/src/po/dashboard.po.ts | 2 + .../step_definition/employee.definitions.ts | 67 +++++++++++++++---- e2e/src/utils/test.utils.ts | 13 ++++ 3 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 e2e/src/utils/test.utils.ts diff --git a/e2e/src/po/dashboard.po.ts b/e2e/src/po/dashboard.po.ts index 59a8667..bfa86f5 100644 --- a/e2e/src/po/dashboard.po.ts +++ b/e2e/src/po/dashboard.po.ts @@ -26,6 +26,8 @@ export class DashboardPage { static async checkTableRowForData(data: string) { let foundData = null; for (const trData of (await this.tableRows.getText() as any)) { + console.log('tr ->', trData) + console.log('data ->', data) if (trData.includes(data)) { foundData = trData; break; diff --git a/e2e/src/step_definition/employee.definitions.ts b/e2e/src/step_definition/employee.definitions.ts index eac14c9..0ba7c32 100644 --- a/e2e/src/step_definition/employee.definitions.ts +++ b/e2e/src/step_definition/employee.definitions.ts @@ -3,6 +3,7 @@ import { browser, element, by } from 'protractor'; import { DashboardPage } from '../po/dashboard.po'; import { MessagePage } from '../po/message.po'; import { Employee, EmployeeDetails } from '../model/employee.class'; +import { TestUtils } from '../utils/test.utils'; const chai = require('chai').use(require('chai-as-promised')); const expect = chai.expect; @@ -96,13 +97,7 @@ Then('I should {string} the {string} employee record in the list', async functio rowData = (await DashboardPage.checkTableRowForData(fullName) as string); rowData = rowData ? (rowData.replace('Delete', '')).trim() : null; - let stringifiedData = ''; - - Object.keys(data).forEach(key => { - stringifiedData = `${stringifiedData} ${data[key]}`; - }); - - expect(stringifiedData.trim()).to.equal(rowData); + expect(TestUtils.stringifyData(data).trim()).to.equal(rowData); break; default: throw Error(`Action "${action}" for the employee record is not yet implemented!`) @@ -115,18 +110,62 @@ Then('I should {string} the {string} employee record in the list', async functio Then('I should see the employee record(s) in the list', async function() { let dataWasNotFound = false; - const expectedEmployeeList = [ - '0 Brenden Wagner United States of America, California American Facebook Software Engineer 8 Facebook', - '1 Cara Steves United States of America, New York American Walmart Sales Assistant 5 Google', - '4 Jenny Chang Singapore, Singapore Chinese Singapore Airlines Regional Director 15 Twitter', - ]; + + const brender = new Employee( + { + id: 0, + firstName: 'Brenden', + lastName: 'Wagner', + country: 'United States of America, California', + nationality: 'American', + company: 'Facebook', + designation: 'Software Engineer', + workExp: '8', + dataSource: 'Facebook' + } + ); + + const cara = new Employee( + { + id: 1, + firstName: 'Cara', + lastName: 'Steves', + country: 'United States of America, New York', + nationality: 'American', + company: 'Walmart', + designation: 'Sales Assistant', + workExp: '5', + dataSource: 'Google' + } + ); + + const jenny = new Employee( + { + id: 4, + firstName: 'Jenny', + lastName: 'Chang', + country: 'Singapore, Singapore', + nationality: 'Chinese', + company: 'Singapore Airlines', + designation: 'Regional Director', + workExp: '15', + dataSource: 'Twitter' + } + ); + + const expectedEmployeeList = []; + const expectedEmployeeDetails: EmployeeDetails[] = [brender.details , cara.details, jenny.details]; + + for (const details of expectedEmployeeDetails) { + expectedEmployeeList.push(TestUtils.stringifyData(details)); + } for (const employeeData of expectedEmployeeList) { - if (!await DashboardPage.checkTableRowForData(employeeData)) { + if (!await DashboardPage.checkTableRowForData(employeeData.trim() + '\nDelete')) { dataWasNotFound = true; break; } } - expect(false, 'The expected data was not found in the table').to.equal(dataWasNotFound) + expect(false, 'The expected data was not found in the table').to.equal(dataWasNotFound); }); diff --git a/e2e/src/utils/test.utils.ts b/e2e/src/utils/test.utils.ts new file mode 100644 index 0000000..f8312a8 --- /dev/null +++ b/e2e/src/utils/test.utils.ts @@ -0,0 +1,13 @@ +export class TestUtils { + private constructor() {} + + static stringifyData(data: object) { + let stringifiedData = ''; + + Object.keys(data).forEach(key => { + stringifiedData = `${stringifiedData} ${data[key]}`; + }); + + return stringifiedData; + } +} From 3e36c76259b1f6f872f7ccbd242302188c26581a Mon Sep 17 00:00:00 2001 From: Rudreshuar Galangco Date: Thu, 13 Feb 2020 13:44:58 +0800 Subject: [PATCH 5/5] Semi blank state for QA training --- .gitignore | 1 + e2e/protractor.conf.js | 2 +- e2e/src/hooks/generic.hooks.ts | 6 +++--- e2e/src/po/security.po.ts | 10 ++++------ package.json | 2 ++ 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 86d943a..a330e61 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # dependencies /node_modules +/e2e/node_modules # profiling files chrome-profiler-events*.json diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index e270671..a982b5d 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -17,7 +17,7 @@ exports.config = { } }, directConnect: true, - baseUrl: 'localhost:4300', + baseUrl: 'localhost:4200', specs: [ 'src/feature/employee/employee.feature', ], diff --git a/e2e/src/hooks/generic.hooks.ts b/e2e/src/hooks/generic.hooks.ts index f0386e0..d00adfa 100644 --- a/e2e/src/hooks/generic.hooks.ts +++ b/e2e/src/hooks/generic.hooks.ts @@ -11,9 +11,9 @@ BeforeAll(() => { Before(async () => { // Check if logged out, else login - if (await browser.getCurrentUrl() === 'data:,' || await browser.getCurrentUrl() === 'http://localhost:4300/login') { - browser.get(`http://localhost:4300/login`); - await browser.wait(EC.urlIs(`http://localhost:4300/login`), browser.params.defaultTimer); + if (await browser.getCurrentUrl() === 'data:,' || await browser.getCurrentUrl() === 'http://localhost:4200/login') { + browser.get(`http://localhost:4200/login`); + await browser.wait(EC.urlIs(`http://localhost:4200/login`), browser.params.defaultTimer); await SecurityPage.login(); } }); diff --git a/e2e/src/po/security.po.ts b/e2e/src/po/security.po.ts index 4279b00..386e5b8 100644 --- a/e2e/src/po/security.po.ts +++ b/e2e/src/po/security.po.ts @@ -1,23 +1,21 @@ import { element, by, ElementFinder, logging, browser } from 'protractor'; +import { protractor } from 'protractor'; export class SecurityPage { static get usernameField(): ElementFinder{ - return element(by.id('username-field')); + return; } static get passwordField(): ElementFinder{ - return element(by.id('password-field')); + return; } static get loginButton(): ElementFinder{ - return element(by.css('button[label="Login"]')); + return; } static async login(){ - await this.usernameField.sendKeys('asd'); - await this.passwordField.sendKeys('asd'); - await this.loginButton.click(); } } diff --git a/package.json b/package.json index 3c90325..bed3c2d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@types/jasminewd2": "~2.0.3", "@types/node": "~8.9.4", "codelyzer": "^5.0.0", + "cucumber": "^6.0.5", "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", "karma": "~4.1.0", @@ -45,6 +46,7 @@ "karma-jasmine-html-reporter": "^1.4.0", "lodash": "^4.17.15", "protractor": "~5.4.0", + "protractor-cucumber-framework": "^6.2.0", "ts-node": "~7.0.0", "tslint": "~5.15.0", "typescript": "~3.5.3"