├── README.md ├── cypress.json ├── cypress ├── fixtures │ └── example.json ├── integration │ ├── search-tests.js │ └── visual.js ├── page-objects │ └── search-page.js ├── plugins │ └── index.js ├── support │ ├── commands.js │ ├── eyes-index.d.ts │ └── index.js └── utils │ └── search-validation-utils.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Cypress 2 | Test Automation Using Cypress 3 | 4 | This code base tests https://automationbookstore.dev/ 5 | 6 | It demonstrates how to use Cypress for web UI automation, how to implement Page Object Model using Cypress, and also includes examples of visual testing with Applitools 7 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "watchForFileChanges": false 3 | } 4 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /cypress/integration/search-tests.js: -------------------------------------------------------------------------------- 1 | import { SearchPage } from '../page-objects/search-page' 2 | import { ValidationUtils } from '../utils/search-validation-utils' 3 | 4 | describe('Search for books', () => { 5 | const page = new SearchPage() 6 | const validator = new ValidationUtils(page) 7 | 8 | beforeEach( () => { 9 | page.navigate() 10 | }) 11 | 12 | it('should return one book with title Agile Testing', () => { 13 | const title = 'Agile Testing' 14 | page.search(title) 15 | validator.validateNumberOfVisibleBooks(1) 16 | validator.validateTitleIsVisible(title) 17 | }) 18 | 19 | it('should return multiple books with title Test', () => { 20 | const query = 'Test' 21 | const expectedBooks = [ 22 | 'Test Automation in the Real World', 23 | 'Experiences of Test Automation', 24 | 'Agile Testing', 25 | 'How Google Tests Software', 26 | 'Java For Testers'] 27 | page.search(query) 28 | validator.validateNumberOfVisibleBooks(expectedBooks.length) 29 | expectedBooks.forEach(b => validator.validateTitleIsVisible(b)) 30 | }) 31 | }) -------------------------------------------------------------------------------- /cypress/integration/visual.js: -------------------------------------------------------------------------------- 1 | import { SearchPage } from '../page-objects/search-page' 2 | import { ValidationUtils } from '../utils/search-validation-utils' 3 | 4 | describe('Search for books', () => { 5 | const page = new SearchPage() 6 | const validator = new ValidationUtils(page) 7 | 8 | beforeEach( () => { 9 | page.navigate() 10 | cy.eyesOpen({ 11 | appName: 'Automation Bookstore', 12 | batchName: 'Search', 13 | browser: [ {name: 'chrome', width: 1024, height: 768} ] 14 | }) 15 | }) 16 | 17 | afterEach(() => cy.eyesClose()) 18 | 19 | it('should return one book with title Agile Testing', () => { 20 | page.search('Agile Testing', true) 21 | cy.eyesCheckWindow() 22 | }) 23 | 24 | it('should return multiple books with title Test', () => { 25 | page.search('Test', true) 26 | cy.eyesCheckWindow() 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /cypress/page-objects/search-page.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export class SearchPage { 4 | 5 | constructor() { 6 | this.url = 'https://automationbookstore.dev/' 7 | this.searchBar = '#searchBar' 8 | this.visibleBooks = 'li:not(.ui-screen-hidden)' 9 | this.hiddenBooks = 'li.ui-screen-hidden' 10 | } 11 | 12 | navigate() { 13 | cy.visit(this.url) 14 | } 15 | 16 | search(query) { 17 | this.search(false) 18 | } 19 | 20 | search(query, expectBooksToHide){ 21 | cy.get(this.searchBar).type(query) 22 | if(expectBooksToHide) { 23 | expect(cy.get(this.hiddenBooks).its('length')).to.not.equal(0) 24 | } 25 | } 26 | 27 | getVisibleBooks() { 28 | return cy.get(this.visibleBooks) 29 | } 30 | } -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | } 18 | 19 | 20 | require('@applitools/eyes-cypress')(module); 21 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | 27 | /// -------------------------------------------------------------------------------- /cypress/support/eyes-index.d.ts: -------------------------------------------------------------------------------- 1 | import "@applitools/eyes-cypress/eyes-index" -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | 18 | import '@applitools/eyes-cypress/commands' 19 | 20 | import './commands' 21 | 22 | // Alternatively you can use CommonJS syntax: 23 | // require('./commands') 24 | -------------------------------------------------------------------------------- /cypress/utils/search-validation-utils.js: -------------------------------------------------------------------------------- 1 | export class ValidationUtils { 2 | 3 | constructor(page) { 4 | this.page = page 5 | } 6 | 7 | validateNumberOfVisibleBooks(expectedCount) { 8 | this.page.getVisibleBooks().should('have.length', expectedCount, `There should be exactly ${expectedCount} book(s) visible`) 9 | } 10 | 11 | validateTitleIsVisible(title) { 12 | this.page.getVisibleBooks().within( ()=>{ 13 | cy.get('h2').should('contain.text', title, `${title} should be visible`) 14 | }) 15 | } 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookstore-tests", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "cypress run" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@applitools/eyes-cypress": "^3.9.2" 14 | }, 15 | "devDependencies": { 16 | "cypress": "3.8.1" 17 | } 18 | } 19 | --------------------------------------------------------------------------------