-
Notifications
You must be signed in to change notification settings - Fork 499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial version of a e2e test suite created with Cypress and Cucumber #145
Open
Holger-Mayer
wants to merge
3
commits into
spring-petclinic:master
Choose a base branch
from
Holger-Mayer:Introduce-cypress-cucumber-testing-for-form-validation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,5 +151,8 @@ | |
"@angular-eslint/schematics:library": { | ||
"setParserOptionsProject": true | ||
} | ||
}, | ||
"cli": { | ||
"analytics": false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { defineConfig } from "cypress"; | ||
import * as createBundler from "@bahmutov/cypress-esbuild-preprocessor"; | ||
import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor"; | ||
import createEsbuildPlugin from "@badeball/cypress-cucumber-preprocessor/esbuild"; | ||
export default defineConfig({ | ||
e2e: { | ||
specPattern: "e2e/features/**/*.feature", | ||
supportFile: 'e2e/cypress/support/**/e2e.{js,jsx,ts,tsx}', // Adjust this if you have a support file | ||
fixturesFolder: 'e2e/cypress/fixtures', | ||
screenshotsFolder: 'e2e/cypress/screenshots', | ||
videosFolder: 'e2e/cypress/videos', | ||
downloadsFolder: 'e2e/cypress/downloads', | ||
video: false, | ||
async setupNodeEvents( | ||
on: Cypress.PluginEvents, | ||
config: Cypress.PluginConfigOptions | ||
): Promise<Cypress.PluginConfigOptions> { | ||
// This is required for the preprocessor to be able to generate JSON reports after each run, and more, | ||
await addCucumberPreprocessorPlugin(on, config); | ||
on( | ||
"file:preprocessor", | ||
createBundler({ | ||
plugins: [createEsbuildPlugin(config)], | ||
}) | ||
); | ||
// Make sure to return the config object as it might have been modified by the plugin. | ||
return config; | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# E2E testing with Cypress and Cucumber | ||
|
||
## Overview | ||
|
||
The implemented e2e test suite uses [Gherkin](https://cucumber.io/docs/gherkin/reference/) feature files for test descriptions. The test system reads these feature files and links each scenario test step to a step definition in a JavaScript file. In the step definition, we implement the corresponding test actions by calling page object functions. These page objects realize specific test functions of the application under test using [Cypress](http://www.cypress.io/). | ||
|
||
|
||
## Test suite structure | ||
|
||
|
||
### cypress.config.ts | ||
|
||
The file cypress.config.ts is stored under the project root. | ||
This file contains the basic configuration for the test suite. The most crucial part includes the definitions of the locations, where the different test files (input or results) are stored. | ||
|
||
### Feature files | ||
|
||
We store the feature files under ***e2e/features***. | ||
The [Gherkin](https://cucumber.io/docs/gherkin/reference/) language is used to write test system agnostic descriptions of test cases. | ||
#### Special Keywords | ||
In this test suite, we use the following special keywords in test steps or example tables. | ||
|
||
EMPTY | ||
SPACES | ||
|
||
EMPTY is automatically replaced during testing by an empty string: "". | ||
SPACES is automatically replaced by a string comprising only space characters: " ". | ||
|
||
|
||
#### Tags | ||
|
||
There are several tags defined in the test suite. | ||
|
||
Important is the tag `@Bug`. This tag shows scenarios that will fail because of a known bug. With `npm confirmation-test` one can retest these exclusively during fixing bugs. | ||
|
||
### Step definitions | ||
You can find the step definition files under ***e2e/cypress/support/step_definitions***. | ||
Developers write step definition files in JavaScript or (in the future) TypeScript. | ||
Besides the definition of the keywords (Given, When, Then, And) these files are test suite agnostic. The test suite needs to be written in Javascript or TypeScript. | ||
|
||
It is important to make sure that the string value of a step definition header fits to the scenario steps in the feature files. | ||
|
||
### Page objects | ||
|
||
You can find the page object files under ***e2e/cypress/support/page_objects***. | ||
|
||
They contain page classes that implement the test tool specific automation code. Each function implements one input or verification sequence of a single page of the application under test. | ||
|
||
### Tools | ||
|
||
You can find the tool files under ***e2e/cypress/support/tools***. | ||
Currently, the only tool is the implementation for a rest api access to delete a specific pet owner. The reason for this is that there is no UI function in the Angular application under test to delete a pet owner. | ||
|
||
## Running the test suite | ||
|
||
There are different ways to call the test suite. | ||
|
||
### Interactive with the Cypress app | ||
|
||
In the terminal enter `npx cypress open`. | ||
|
||
Then the cypress app will open. Select E2E testing and electron browser to test. You now will see a list of feature files to run interactively. | ||
|
||
|
||
### Headless with NPM | ||
|
||
There are two test execution variants defined in the package.json | ||
|
||
`npm full-regression-test`- this will run all feature files | ||
`npm confirmation-test`- this will run only scenarios or features tagged with @Bug. | ||
|
||
## Reports | ||
|
||
After a test run there is an [HTML report](../reports/html/cucumber-report.html) available under ***e2e/cypress/reports/cucumber-report.html***. | ||
|
||
You can find the screen shot files under ***e2e/cypress/screenshots***. | ||
|
||
## FAQ | ||
|
||
### I want to run the rest server backend or the Angular front end from a different base URL than in the example defined. | ||
|
||
The test suite uses the file ***e2e/cypress/fixtures/config.json*** to define the base URLs of the rest server app and the Angular front end app. If these URLs are not the standard values, these need to be changed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"baseURL":"http://localhost:4200/petclinic/", | ||
"restURL":"http://localhost:9966/petclinic/api/" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/// <reference types="cypress" /> | ||
// *********************************************** | ||
// This example commands.ts shows you how to | ||
// create various custom commands and overwrite | ||
// existing commands. | ||
// | ||
// For more comprehensive examples of custom | ||
// commands please read more here: | ||
// https://on.cypress.io/custom-commands | ||
// *********************************************** | ||
// | ||
// | ||
// -- This is a parent command -- | ||
// Cypress.Commands.add('login', (email, password) => { ... }) | ||
// | ||
// | ||
// -- This is a child command -- | ||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) | ||
// | ||
// | ||
// -- This is a dual command -- | ||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) | ||
// | ||
// | ||
// -- This will overwrite an existing command -- | ||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) | ||
// | ||
// declare global { | ||
// namespace Cypress { | ||
// interface Chainable { | ||
// login(email: string, password: string): Chainable<void> | ||
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element> | ||
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element> | ||
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element> | ||
// } | ||
// } | ||
// } | ||
|
||
Cypress.Commands.add('typeAndVerify',(path,value) => { | ||
|
||
if (value.trim() == "EMPTY") { | ||
cy.get(path).type('{selectall}{backspace}{selectall}{backspace}'); | ||
} else if (value.trim() == "SPACES") { | ||
cy.get(path).type('{selectall}{backspace}{selectall}{backspace}').type(" "); | ||
}else { | ||
cy.get(path).clear(); | ||
cy.get(path).type('{selectall}{backspace}{selectall}{backspace}').type(value).should('have.value', value); | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// *********************************************************** | ||
// This example support/e2e.ts is processed and | ||
// loaded automatically before your test files. | ||
// | ||
// This is a great place to put global configuration and | ||
// behavior that modifies Cypress. | ||
// | ||
// You can change the location of this file or turn off | ||
// automatically serving support files with the | ||
// 'supportFile' configuration option. | ||
// | ||
// You can read more here: | ||
// https://on.cypress.io/configuration | ||
// *********************************************************** | ||
|
||
// Import commands.js using ES2015 syntax: | ||
import './commands' | ||
|
||
// Alternatively you can use CommonJS syntax: | ||
// require('./commands') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/// <reference types="cypress" /> | ||
|
||
class BaseClass_PO { | ||
|
||
constructor() { | ||
this.selectors = new Map(); // Initialize the Map, but content will be added in child classes | ||
} | ||
|
||
addSelector(name,value){ | ||
this.selectors.set(name,value); | ||
} | ||
selector(name){ | ||
return this.selectors.get(name); | ||
} | ||
|
||
|
||
} | ||
|
||
export default BaseClass_PO; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
///<reference types="cypress" /> | ||
|
||
class BaseInputList_PO { | ||
|
||
|
||
navigateHome() { | ||
cy.get('button.btn').contains('Home').click(); | ||
} | ||
|
||
validateNotInList(name){ | ||
cy.get('table tbody tr').each(($row, index, $rows) => { | ||
cy.wrap($row).find('input').then($input => { | ||
cy.wrap($input).should('not.have.value', name); | ||
}); | ||
}); | ||
} | ||
|
||
validateInList(name){ | ||
|
||
let textFound = false; | ||
|
||
cy.get('table input').each(($input) => { | ||
if ($input.val() === name) { | ||
textFound = true; | ||
} | ||
}).then(() => { | ||
// Assert that the text was found in at least one input field | ||
expect(textFound).to.be.true; | ||
}); | ||
} | ||
|
||
validateInitialArray(dataArray) { | ||
|
||
cy.get('table tbody tr').each(($row, index, $rows) => { | ||
cy.wrap($row).find('input').then($input => { | ||
|
||
cy.wrap($input).should('have.value', dataArray[index]); | ||
}); | ||
}); | ||
} | ||
|
||
|
||
deleteListElement(name) { | ||
|
||
cy.get('table tbody tr').each(($row, index, $rows) => { | ||
|
||
cy.wrap($row).as('currentRow'); | ||
|
||
cy.get('@currentRow').find('input').invoke('val').then((inputValue) => { | ||
if (inputValue == name) { | ||
|
||
cy.wrap($row).contains('Delete') .should('exist').as('deleteButton'); | ||
|
||
} | ||
}); | ||
}); | ||
|
||
cy.get('@deleteButton').click(); | ||
|
||
} | ||
|
||
|
||
|
||
beginAdd(){ | ||
cy.get('button.btn').contains('Add').click(); | ||
} | ||
|
||
clearElement() { | ||
cy.get('#name').clear(); | ||
// cy.get("#name").type('{selectall}{backspace}{selectall}{backspace}'); | ||
} | ||
|
||
setName(name){ | ||
cy.typeAndVerify("#name", name); | ||
} | ||
} | ||
|
||
|
||
export default BaseInputList_PO; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to remove
Protactor
and keep thee2e
goal that is common.