cypress-page-object-model

1.0.26 • Public • Published

Cypress-POM-Ready-To-Use Cypress Page Object Basic Model ( Ready To Use ) - UI Test Automation Design Pattern for Cypress.io Contact me! if you have more ideas - padmaraj.nidagundi@gmail.com

How To Setup and Run ==> How To Use Cypress.io Page Object Design Pattern Basic Model ( Ready To Use )

Step 1

  • Install NodeJs https://nodejs.org/en/download/
  • Make Folder called "POM" in C:\POM drive
  • Open Folder C:\POM\ Using the VSCode editor or any IDE
  • Clone the repo from NPMJS ( npm i cypress-page-object-model ) or Github.com
  • Go to folder C:\POM\node_modules\cypress-page-object-model>
  • Run comman - npx cypress open ( Its take some time, keep patience for 2-5 min max First time run )
  • Now you can run test - Test1.spec.js

Step 2 How To Add Page Code ==> Create your individual pages objects with pageName.js in the folder => pageObjects Page code pattern in the folder => "pageObjects"

class homePageLinks {

   homepage() {
      return cy.visit('https://www.cypress.io/')
   }
   howitworkspagelink() {
      return cy.get('.styled__MenuWrapper-sc-16oj5lj-1 > .styled__NavList-sc-16oj5lj-3:nth-child(1) > .styled__NavItem-sc-16oj5lj-4:nth-child(2) > .Link-sc-5cc5in-0')
   }
   pricingpagelink() {
      return cy.get('.styled__NavList-sc-16oj5lj-3:nth-child(1) > .styled__NavItem-sc-16oj5lj-4:nth-child(4) > .Link-sc-5cc5in-0')
   }

}

export default homePageLinks

Step 3 How To Add UI Test Case ==> Create your individual test with name contain spec in it: UITestX.Spec.js in the folder => integration Test case code pattern in the folder => integration

import homePageLinks from '../pageObjects/homePageLinks'
import pricingPageLinks from '../pageObjects/pricingPageLinks'
describe('User visit diffrent pages on cypress.io', () => {
    const l = new homePageLinks()
    it('Test 1.1 - User visit "Home Page" and visit the "How it works"', () => {
        l.homepage()
        cy.title().should('eq', 'JavaScript End to End Testing Framework | cypress.io')  //Verify Page Title with exact & full text
        cy.title().should('include', 'JavaScript')     //Verify Page Title with partial text
        l.howitworkspagelink().click()
        cy.title().should('eq', 'End to End Testing Framework | cypress.io')  //Verify Page Title with exact & full text
        cy.title().should('include', 'End to End')     //Verify Page Title with partial text
    })

    it('Test 1.2 - User visit "Pricing" page and check for the price of cypress.io and click on Team and Business', () => {
        l.homepage()
        l.pricingpagelink().click()
        cy.title().should('eq', 'Pricing | cypress.io')  //Verify Page Title with exact & full text
        cy.title().should('include', 'Pricing')     //Verify Page Title with partial text
        const p = new pricingPageLinks()
        p.teamtab().click()
        p.businesstab().click()
    })
})


Step 4 How To Add API Test Case ==> Create your individual test with name contain spec in it: API-GETTestX.spec.js in the folder => integration Test case code pattern in the folder => integration

describe('API Test on cypress.io', () => {
    it("API Test 1.1 - GET request to that endpoint should return server status", () => {
    cy.request("GET", "https://www.cypress.io/", {
    }).then((response) => {
      // response.body is automatically serialized into JSON
      cy.log(response.body);
      expect(response.status).to.eq(200)
      expect(response).to.have.property('headers')
      expect(response).to.have.property('duration')
    });
  });
});

Default used chaijs for assertion https://www.chaijs.com/ https://docs.cypress.io/guides/references/assertions/

Test data is isolated from the test case in the folder => fixtures using JSON file.

{
  "name": "Using fixtures to represent data",
  "email": "hello@cypress.io",
  "body": "Fixtures are a great way to mock data for responses to routes"
}

Keywords

Cypress, Cypress.io, Chai, Cypress POM, Cypress framework, Software testing, Test automation, Browser Testing, Qualiy assurance, Test automation framework, Test automation framework architecture, Test automation framework design, Page object model, web ui testing, E2E testing, Angular testing, AngularJS testing, Automation, UI testing framework JavaScript, UI testing framework React, Cypress testing, Cypress-testing-library, Cypress-testing-library npm, Cypress testing React, Cypress react testing library, Cypress npm, CypressAPI, Cypress-API-Test

maintaining codebase tips Kudos (Help) for you

  • npm install --save-dev
  • npm install mochawesome ( for test report )

How to run test in diffrent modes Kudos (Help) for you

  • npx cypress open
  • npx cypress run
  • npx cypress run --browser chrome
  • npx cypress run --browser chrome --headless
  • npx cypress run --spec cypress/integration/UITest1.spec.js

Cypress API

Selecting a single element - querying DOM

<h1>Numbers:</h1>
<div class="one"></div>
<div id="two"></div>
<div shape="three"></div>

.get('one') // select by tag
.get('.square') // select by class
.get('#two') // select by id
.get('[shape="three"]'); // select by attribute

debugging tests in Cypress

.pause()
console.log()

Overall cypress helping notes

Setup the challenges and run through GUI

cd challenges
npm i
npx cypress open

Create a new Cypress project

mkdir my-new-project
cd my-new-project
npm config set registry http://npm.company.com/
npm init
npm install cypress --save-dev
npm install mochawesome --save-dev
npm install cypress-real-events --save-dev
npm install cypress-wait-until --save-dev
npm install @company/cypress-service-client
npm install eslint-plugin-cypress

Create a .gitignore file

node_modules/
npm-debug.log
debug.log
results/
videos/
screenshots/

When npm install thinks cypress is installed but it isn't

npx cypress install

Start Cypress / initialise

npx cypress open
node_modules/.bin/cypress open

Run headlessly using electron

npx cypress run

Run one spec file

npx cypress run --spec cypress/integration/challenge_02.js

Run through various browsers headfully

npx cypress run --browser chrome
npx cypress run --browser chromium
npx cypress run --browser firefox
npx cypress run --browser edge
npx cypress run --browser electron --headed

Show currently installed cypress version

npx cypress info
.
Proxy Settings: none detected
Environment Variables: none detected

Application Data: C:\Users\user\AppData\Roaming\cypress\cy\development
Browser Profiles: C:\Users\user\AppData\Roaming\cypress\cy\development\browsers
Binary Caches: C:\Users\user\AppData\Local\Cypress\Cache

Cypress Version: 6.9.1
System Platform: win32 (10.0.17763)
System Memory: 17.1 GB free 7.31 GB

Remove clear windows app data in case of corruption

npx cypress open

File -> View App Data

Delete everything in the cy folder (typically found at C:\Users\<user>\AppData\Roaming\cypress\cy\)

Show installed versions of cypress

npx cypress cache list

Remove all but current installed versions of cypress

npx cypress cache prune

Remove all installed versions of cypress

npx cypress cache clear

Troubleshooting Error: ENOENT: no such file or directory, stat '/initrd.img'

The following error was thrown by a plugin. We stopped running your tests because a plugin crashed. Please check your plugins file.

Error: The following error was thrown by a plugin. We stopped running your tests because a plugin crashed. Please check your plugins file (`/home/tim/git/cypress-server/cypress/plugins/index.js`)
    at Object.get (/home/tim/.cache/Cypress/6.5.0/Cypress/resources/app/packages/server/lib/errors.js:966:15)
    at EventEmitter.handleError (/home/tim/.cache/Cypress/6.5.0/Cypress/resources/app/packages/server/lib/plugins/index.js:168:20)
    at EventEmitter.emit (events.js:315:20)
    at ChildProcess.<anonymous> (/home/tim/.cache/Cypress/6.5.0/Cypress/resources/app/packages/server/lib/plugins/util.js:19:22)
    at ChildProcess.emit (events.js:315:20)
    at emit (internal/child_process.js:876:12)
    at processTicksAndRejections (internal/process/task_queues.js:85:21)

but it turned out Visual Studio Code automatically added this line to the top of commands.js

const { expect } = require('chai');

cypress.json

{
    "browser": "electron",
    "headless": true,
    "video": true,
    "viewportWidth": 375,
    "viewportHeight": 1000,
    "defaultCommandTimeout": 30000,
    "requestTimeout": 30000,
    "env": {
        "brandHost": "mybrand.com",
        "name": "live",
        "blockHosts": ["*tealiumiq.com", "*tiqcdn.com"]
    },
    "retries": {
        "runMode": 2,
        "openMode": 0
    }
}

test structure

describe('Login workflow', () => {
    beforeEach(() => {
        cy.setCookie('CONSENTMGR', 'consent:true'); // stop cookie banner
    });

    it(
        'Should login as existing user',
        {
            retries: {
                runMode: 4,
                openMode: 0,
            },
        },
        () => {
            cy.visit('/login', { retryOnStatusCodeFailure: true });
        },
    );
});

checking links

it('Should have href attribute in the header arrow linking to MyColours', () => {
    cy.visit('/widgets');
    cy.get('[data-testid=title-arrow]').should('have.attr', 'href').and('include', 'MyColours.aspx');
    cy.get('[data-testid=title-arrow]').should('have.attr', 'target', '_blank');
});

cy.contains

Get the element whose text exactly matches Upload

cy.contains(/^Upload$/).click();

cy.get

cy.get('[data=timefield]')
    .children('time')
    .then((date) => {
        expect(parseInt(date[0].dateTime)).to.be.greaterThan(parseInt(date[1].dateTime));
    });

cy.get('[data="logo"]')
    .children('img')
    .each((logo) => {
        expect(logo.get(0).src).not.to.be.empty;
    });

cy.get('[class=btn-close]').first().click({ force: true });
cy.contains('Log in').click({ force: true });

cy.get('[type=file][name=file]').attachFile('SmallCV.rtf');

partial class name match

cy.get('*[class^="convai-widget-button"]').as('convaiButton');

class starts with send and contains svg

cy.get("button[class^='send'] > svg").should('have.css', 'fill', desiredColourRGB);

cy.get then find to drill down into DOM

In this example, we get the recommender widget then find the job within that widget, then the unsaved job within that ignoring other widgets

cy.get('[data-component="component-RecommendedJobs"]')
    .find('[id="job-item-55667788"]')
    .find('[data-testid="unsavedjob-icon-star"]')
    .click({ scrollBehavior: 'center' });

cy.request

cy.request({
    url: '/search',
    method: 'GET',
    failOnStatusCode: false,
    headers: { Cookie: '' },
}).then((res) => {
    expect(res.status).to.eq(200);
    expect(res.status).to.match(/(400|401)/);
    expect(res.body).to.have.property('results');
});

cookies

Cypress.Cookies.debug(true);
cy.getCookies().then((cookies) => {
    cookies.forEach((element) => cy.log(element.name));
});

cy.getCookie('auth').then((cookie) => {
    const token = cookie.value;
    cy.clearCookie('auth');
});
cy.clearCookies({ domain: Cypress.env('host') });

cy.get('[data="title"]').each((item) => {
    expect(item.get(0).innerText).not.to.be.empty;
    expect(item.get(0).getAttribute('href')).not.to.be.empty;
    expect(item.get(0).getAttribute('target')).to.eq('_blank');
});

See commandsState.js and usages/state.js for saving and restoring all browser session state.

cy.restoreState('myLogin');
cy.saveState('myLogin');

See commandsState.js and usages/state.js for saving and restoring just the persistent cookies.

cy.restorePersistentCookies('myLogin');
cy.savePersistentCookies('myLogin');

conditional testing

Assert that some text is present or perform an action if other text is present

  • see command assertContainsOrActionIfContains in commandsConditional.js and usages/conditional.js

Click something if present, after verifying it is present for a period of time (maybe DOM rewriting causing issues)

  • see command clickLocatorIfConsistentlyVisible in commandsConditional.js and usages/conditional.js

element is getting detached from the DOM

When a React page is rendered the elements tend to get updated moments after being first created. Cypress tends to run too quickly, or the page too slowly, especially under CI.

Cypress.io advises that you should figure out what condition to wait for, and assert that condition before trying to do what you really want to do. I fundamentaly disagree that you should need to understand the inner workings of a page to automate it robustly. Do you expect actual users should know the fine details of your page to use it robustly?

Here we get the element several times with a short pause in between, then use the element.

cy.getElementConsistently('[data-testid="apply-job-button"]').first().click();

See commandsRobust.js and usages/robust.js for interacting with an element getting detached from the dom.

expect assertions

<i class="material-icons">message</i>
cy.get('[class=material-icons]').then((item) => {
    expect(item.get(0).innerText).to.contain('message');
});
expect(res.status).to.eq(200);
expect(res.status).to.match(/(400|401)/);
expect(res.body).to.have.property('results');
expect(res.body).to.have.property('results').and.to.have.length.greaterThan(0);
expect(title).not.be.null.and.not.to.be.an('undefined');
expect(newAuth).not.to.be.undefined;
expect(response.body.results).to.not.be.empty;
expect(JSON.stringify(res.body)).not.contain('12344321');
expect(newAuth).not.to.contains(originalToken);
const locationResults = res.body.results.filter((result) => result.type === 'location');
expect(locationResults.length).to.equal(0);
expect(locationResults.length).to.be.greaterThan(0);
locationResults.map((res) => {
    expect(res.text).to.match(/(TOP DEAL|BEST OFFER)/);
});
expect(item.get(0).innerText).match(/Click here/g);

cy.get('html:root').then((html) => {
    expect(html).not.contain('We use cookies');
});

cy.visit('/my/feature/')
    .window()
    .should(function (win) {
        expect(win.localStorage.getItem('widgetDisplayed')).to.be.ok;
        expect(win.localStorage.getItem('widgetDisplayed')).to.eq('false');
    });

When the code decides not to show a widget we could have it write a value to localStorage, sessionStorage or the DOM so we know the decision has been taken. Otherwise we are forced to wait an abitary amount of time and assert negative which is a very flaky and slow practice.

headers

const resHeaders = res.headers;
const newAuth = resHeaders['set-cookie'].find((header) => {
    if (header.startsWith('auth=')) {
        return true;
    }
});
expect(newAuth).not.to.be.undefined;
expect(newAuth).not.to.contains(originalToken);
expect(response.headers).not.to.have.property('x-powered-by');
expect(response.headers).to.include({
    'cache-control': 'no-cache, no-store, must-revalidate',
});

http convenience commands

See commandsHttp.js and usages/http.js for http convenience commands.

cy.httpGet(`/test/path`, 200, 'Your account was created', 'Unexpected error');
cy.httpGetRetry(`/job/0`, 200, 'Lorem ipsum dolor', 5, 500);

ignoring JavaScript errors

Cypress.Commands.add('uncaughtException', () => {
    cy.on('uncaught:exception', (err, runnable) => {
        console.log(err);
        return false;
    });
});

intercept and get response

cy.intercept('POST', `${postPath}/*`).as('save');
cy.intercept('DELETE', `${deletePath}/*`).as('remove');
cy.wait('@save').its('response.statusCode').should('be.oneOf', [200, 201]);

intercept and replace response

data = { my: 'data' };

cy.intercept('GET', '/results', data).as('results');

cy.intercept('/path', {
    statusCode: 500,
});

intercept and block unwanted requests

We want to stop calls to third party resources that we don't need for our tests. Helps the tests to run faster.

The blockHosts cypress.json config feature does not seem to work. So we need to use intercept.

See commandsIntercept.js and usages/intercept.js for blocking unwanted requests.

cy.blockUnwantedRequests();

local storage

Cypress.Commands.add('getLocalStorage', (key) => {
    let value = localStorage.getItem(key);
    return cy.wrap(value);
});

move cookies across domains

Grab the cookies from one domain and create them on another domain.

See commandsCookies.js and usages/cookies.js for moving cookies across domains.

cy.stashCookies('myStash').then((cookies) => {
    cy.setCookiesOnDomain(cookies, 'www.cypress.io');
});

stash cookies to local storage and unstash later

See commandsCookies.js and usages/cookies.js for stashing and unstashing cookies.

cy.stashCookies('ourStash'); // stash the cookies and clear them
cy.unstashCookies('ourStash');

Compare current cookies with stash to see which new cookies have been added

See commandsCookies.js and usages/cookies.js for finding newly added cookies.

cy.unstashCookies('thisStash');
cy.setCookie('CONSENTMGR', 'consent:true');
cy.compareCookiesWithStash('thisStash').then((newCookies) => {});

Save all localstorage to file using handle and restore it on a subsequent run (if handle exists)

See commandsLocal.js and usages/local.js for saving and restoring local storage.

cy.restoreLocalStorage('totaljobs'); // will do nothing if handle does not exist - safe first run!
cy.saveLocalStorage('totaljobs');

mochawesome

Add the request url, response headers and response body to mochawesome.

const addContext = require('mochawesome/addContext');
Cypress.Commands.add('requestAndReport', (request) => {
    let url;
    let duration;
    let responseBody;
    let responseHeaders;
    let requestHeaders;

    Cypress.on('test:after:run', (test, runnable) => {
        if (url) {
            addContext({ test }, { title: 'Request url', value: url });
            addContext({ test }, { title: 'Duration', value: duration });
            addContext({ test }, { title: 'Request headers', value: requestHeaders });
            addContext({ test }, { title: 'Response headers', value: responseHeaders });
            addContext({ test }, { title: 'Response body', value: responseBody });
        }

        // To stop spurious reporting for other tests in the same file
        url = '';
        duration = '';
        requestHeaders = {};
        responseHeaders = {};
        responseBody = {};
    });

    let requestOptions = request;
    if (typeof request === 'string') {
        requestOptions = { url: request };
    }
    url = requestOptions.url;

    cy.request(requestOptions).then(function (response) {
        duration = response.duration;
        responseBody = response.body;
        responseHeaders = response.headers;
        requestHeaders = response.requestHeaders;
        return response;
    });
});
cy.requestAndReport('/path').then((response) => {
    expect(response.headers).to.have.property('x-custom-header');
});

reportScreenshot - can be used multiple times in a single test

Cypress.Commands.add('reportScreenshot', (text = 'No description') => {
    let screenshotDescription;
    let base64Image;

    Cypress.on('test:after:run', (test, runnable) => {
        if (screenshotDescription) {
            addContext(
                { test },
                {
                    title: screenshotDescription,
                    value: 'data:image/png;base64,' + base64Image,
                },
            );
        }

        screenshotDescription = ''; // To stop spurious reporting for other tests in the same file
        base64Image = '';
    });

    screenshotDescription = text;
    const key = util.key();
    const screenshotPath = `${Cypress.config('screenshotsFolder')}/${Cypress.spec.name}/reportScreenshot_${key}.png`;
    cy.log(`Taking screenshot: ${screenshotDescription}`);
    cy.screenshot(`reportScreenshot_${key}`);
    cy.determineRealPath(screenshotPath).then((realPath) => {
        // Cypress might add something like ' (attempt 2)'
        cy.readFile(realPath, 'base64').then((file) => {
            base64Image = file;
        });
    });
});

Cypress.Commands.add('determineRealPath', (supposedPath) => {
    const supposedPathNoExt = supposedPath.slice(0, -4);

    function testPath(attempt) {
        if (attempt < 0) {
            cy.log('All attempts to find the file failed.');
            return cy.wrap(supposedPath);
        }

        let attemptSuffix = ` (attempt ${attempt}).png`;
        if (attempt === 0) attemptSuffix = '.png';
        const tryPath = supposedPathNoExt + attemptSuffix;
        cy.task('isFile', tryPath).then((exists) => {
            if (exists) {
                cy.log(`Found path ${tryPath}`);
                return cy.wrap(tryPath);
            }
            return testPath(attempt - 1);
        });
    }

    const maxPossibleAttempts = 4; // Cypress will retry up to a max of 4 times
    testPath(maxPossibleAttempts);
});

plugins/index.js

const fs = require('fs-extra');

module.exports = (on, config) => {
    on('task', {
        isFile(filename) {
            if (fs.existsSync(filename)) {
                return true;
            }

            return false;
        },
    });
};

util.key()

import { v4 as uuidv4 } from 'uuid';
function key() {
    return uuidv4().substring(0, 8);
}

module.exports = {
    key,
};
cy.reportScreenshot('Before submitting login form');

reportScreenshotOnFailure

util.js

function reportScreenshotOnFailure(message = 'Screenshot on failure') {
    let screenshotFailureMessage;
    let base64ImageFailure;
    const addContext = require('mochawesome/addContext');

    afterEach(function () {
        if (this.currentTest.state === 'failed') {
            let titlePathArray = this.currentTest.titlePath();

            const spec = titlePathArray[0].trim();
            const test = titlePathArray[1].trim();
            const screenshotFilenName = `${spec} -- ${test} \(failed\).png`.replace(/[/"]/g, '');
            const screenshotPath = `${Cypress.config('screenshotsFolder')}/${Cypress.spec.name}/${screenshotFilenName}`;

            cy.readFile(screenshotPath, 'base64').then((file) => {
                base64ImageFailure = file;
            });
            screenshotFailureMessage = message;
        }
    });
    Cypress.on('test:after:run', (test, runnable) => {
        if (screenshotFailureMessage) {
            addContext(
                { test },
                {
                    title: screenshotFailureMessage,
                    value: 'data:image/png;base64,' + base64ImageFailure,
                },
            );
        }

        screenshotFailureMessage = ''; // To stop spurious reporting for other tests in the same file
        base64ImageFailure = '';
    });
}

module.exports = {
    reportScreenshotOnFailure,
};

cypress.json

  "screenshotOnRunFailure": true,

spec.js - at the very top outside of any describe or context

const util = require('../../util/util');
util.reportScreenshotOnFailure();

multipart forms - new method

See usages/multipart.js for posting to a multipart form using the new method. Do not use arrow syntax!

multipart forms - old method

See commandsMultipart.js and usages/multipart.js for posting to a multipart form using the old method.

parse text, parse html source, parseresponse

Cypress.Commands.add('parsetext', (regexString) => {
    cy.get('html').then(($html) => {
        const text = $html.text();
        const regex = new RegExp(regexString);
        if (regex.test(text)) {
            const match = text.match(regex);
            console.log(`Match: ${match[1]}`);
            return match[1];
        } else {
            console.log('No matches could be found.');
        }
        return '';
    });
});
cy.get('html').should('contain', 'Enter your first name'); // make sure text is present first
cy.parsetext('Enter your ([a-z]+) name').then((result) => {
    cy.log(`Result: ${result}`);
});
Cypress.Commands.add('parsesource', (regexString) => {
    cy.get('html:root')
        .eq(0)
        .invoke('prop', 'innerHTML')
        .then((doc) => {
            const regex = new RegExp(regexString);
            if (regex.test(doc)) {
                const match = doc.match(regex);
                console.log(`Match: ${match[1]}`);
                return match[1];
            } else {
                console.log('No matches could be found.');
            }
            return '';
        });
});

regular expressions

Return an array of matching first capture groups

Cypress.Commands.add('getJobIdsFromSearch', (schemeHost = '', keyword = 'manager') => {
    cy.request({
        url: `${schemeHost}/jobs/${keyword}`,
        failOnStatusCode: true,
        retryOnStatusCodeFailure: true,
        method: 'GET',
    }).then((response) => {
        expect(response.status).to.match(/(200|201)/);
        const regex = new RegExp(/"id":([\d]{7,10}),"title"/g);
        let jobIds = [];
        let result;
        while ((result = regex.exec(response.body)) !== null) {
            jobIds.push(result[1]); // 0 is full match, 1 is capture group 1
        }
        expect(jobIds.length).to.be.greaterThan(0);
        return cy.wrap(jobIds);
    });
});
Cypress.Commands.add('getJobId', (text) => {
    const regex = new RegExp(/([\d]{7,10})/);
    if (regex.test(text)) {
        const match = text.match(regex);
        return cy.wrap(match[1]);
    } else {
        cy.log('No job ids could be found.');
    }
    return cy.wrap('');
});

Cypress.Commands.add('getAllJobIds', (text, leftDelim = '', rightDelim = '') => {
    const regex = new RegExp(`${leftDelim}([\\d]{7,10})${rightDelim}`, 'g');
    let jobIds = [];
    let result;
    while ((result = regex.exec(text)) !== null) {
        jobIds.push(result[1]); // 0 is full match, 1 is capture group 1
    }
    return cy.wrap(jobIds);
});

const leftJobIdDelim = 'Expired job<.p>[^>]+JobId=';
const rightJobIdDelim = '"';
cy.getAllJobIds(res.body, leftJobIdDelim, rightJobIdDelim).then((ids) => {
    return cy.wrap(ids);
});

should assertions

<i class="material-icons">message</i>
cy.get('[class=material-icons]').should('contain', 'message');
cy.get('[data="info"]').should('not.exist');
cy.get('[data=item]').should('have.length.at.most', 12);
cy.get('[data=item]').should('have.length.greaterThan', 0);
cy.get('[data=item]').should('have.length.lessThan', 7);
cy.get('[data=item]').should('not.have.length', 0);
cy.get('[data=item]').first().should('be.visible');
cy.get('[data=item]').should('be.visible').should('contain', 'Please click here');
cy.get('[data=item]').first().should('have.css', 'max-width', '55%');
cy.get('[data=item]').first().should('have.attr', 'href').and('include', 'my-tab');
cy.getCookie('lang').should('have.property', 'value', 'fr');
cy.wait('@saved').its('response.statusCode').should('be.oneOf', [200, 201]);
cy.get('body').should('contain', 'MY_EXPECTED_TEXT');
cy.get('body').contains('cypress-service is up!').should('exist');
cy.url().should('contain', '/account/signin');

Caution - to check for elements not visible, the element could be present but not visible

cy.get('[data=item]').should('not.be.visible'); // invisible 1

Or perhaps the element will not exist at all

cy.get('[data=item]').should('not.exist'); // invisible 2

Note that with expect in some code structures the Cypress automatic retry does not kick in - as in this example

cy.window().then((win) => {
    win.scrollTo(0, 300);
    expect($el.offset().top).closeTo($el.offset().top, 300, 10);
    expect($el).to.be.visible;
});

If you wrap it in a should, it will now retry (double window technique!)

cy.window()
    .window()
    .should(function (win) {
        win.scrollTo(0, 300);
        expect($el.offset().top).closeTo($el.offset().top, 300, 10);
        expect($el).to.be.visible;
    });

soft assertions, fuzzy assertions

Use a regular expression if the css is out by a small fraction of a pixel

cy.get('[data-testid="card-container"]')
    .first()
    .realHover()
    .invoke('css', 'box-shadow')
    .should('match', /rgba[(]0, 0, 0, 0[.]25[)] 0px 0px 5.*px 0px/);

stubbing links

cy.window().then((win) => {
    cy.stub(win, 'open').as('redirect');
});
cy.get(`[data=item-that-opens-tab-and-redirects]`)
    .first()
    .click()
    .then(() => {
        cy.get('@redirect').then((redirect) => {
            expect(redirect.args[0][0]).not.to.be.empty;
            expect(redirect.args[0][0]).contains('/my/desired/redirect/path');
            expect(redirect.args[0][1]).to.equal('_blank');
            expect(redirect.args[0][2]).to.equal('noopener,noreferrer');
        });
    });
Cypress.Commands.add('interceptReturnEmpty', (url) => {
    cy.intercept('GET', url, '').as('empty');
});
cy.get(`[data="title"]`)
    .first()
    .then((title) => {
        cy.interceptReturnEmpty(title.get(0).getAttribute('href'));
    });

cy.get(`[data="title"]`).first().click();

timeouts

cy.get('[data=item]', { timeout: 30000 }).then(($el) => {});

upload file and download file

Upload

npm install --save-dev cypress-file-upload

In support/index.js

import 'cypress-file-upload';
cy.intercept({
    method: /POST/,
    url: /api\/userData\/attachments/,
}).as('upload');

cy.contains('Upload icon').click();
cy.get('[type=file][name=file]').attachFile('MyCV.doc'); // target input element
cy.contains(/^Upload$/).click();
cy.wait('@upload');

Download

cy.intercept({
    method: /GET/,
    url: /userData\/attachments/,
}).as('download');

cy.contains('Download icon').click();

cy.wait('@download');

const downloadsFolder = Cypress.config('downloadsFolder');
const filename = path.join(downloadsFolder, 'MyCV.doc');

cy.readFile(filename, 'utf8').then((content) => {
    expect(content).to.contain('DOCTESTCV');
});

Tidy up downloads folder at start of test

Cypress.Commands.add('deleteDownloadsFolder', function () {
    const downloadsFolder = Cypress.config('downloadsFolder');
    cy.task('deleteFolder', downloadsFolder);
});

In plugins/index.js

const { rmdir } = require('fs');

module.exports = (on, config) => {
    on('task', {
        deleteFolder(folderName) {
            console.log('deleting folder %s', folderName);

            return new Promise((resolve, reject) => {
                rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => {
                    if (err && err.code !== 'ENOENT') {
                        console.error(err);

                        return reject(err);
                    }

                    resolve(null);
                });
            });
        },
    });
};
cy.deleteDownloadsFolder();

utility scripts

Set baseurl for session and navigate to a fake page so you can set cookies / localstorage before hitting a real url which might poison your desired start state.

Cypress.Commands.add('setBaseUrl', (baseUrl) => {
    Cypress.config('baseUrl', baseUrl);
    const html = `<!DOCTYPE html><html><body><h1>Initialise Cypress to ${baseUrl}</h1></body></html>`;
    cy.intercept('GET', '/initialise_cypress_session.html', html);
    cy.visit('/initialise_cypress_session.html');
});
cy.setBaseUrl('https://www.cypress.io');

Debug messages to a log file.

Cypress.Commands.add('checkPoint', (script, message, options = {}) => {
    const init = options.hasOwnProperty('init') ? options.init : false;
    if (init) {
        cy.writeFile(`check/${script}.json`, { checks: [] }, 'utf8');
    }
    cy.readFile(`check/${script}.json`, 'utf8').then((contents) => {
        let checks = contents.checks;
        const date = new Date();
        const utc = date.toISOString();
        checks.push({ utc, message });
        cy.writeFile(`check/${script}.json`, { checks }, 'utf8');
    });
});
cy.checkPoint('totaljobs', 'Starting script.', { init: true });

verifypositive (against html source)

Cypress.Commands.add('verifypositive', (regexString) => {
    cy.get('html:root')
        .eq(0)
        .invoke('prop', 'innerHTML')
        .then((doc) => {
            const regex = new RegExp(regexString, 'i');
            expect(doc).to.match(regex);
        });
});
cy.verifypositive('Job ads');

viewport

Cypress.Commands.add('isInViewport', { prevSubject: true }, (subject) => {
    const bottom = Cypress.$(cy.state('window')).height();
    const rect = subject[0].getBoundingClientRect();
    expect(rect.top).not.to.be.greaterThan(bottom);
    expect(rect.bottom).not.to.be.greaterThan(bottom);
    return subject;
});
cy.get('[data="results"]').scrollIntoView();
cy.get('[data=item]').then(($el) => {
    cy.get($el).isInViewport();
});
Cypress.Commands.add('setViewport', (size) => {
    if (Cypress._.isArray(size)) {
        return cy.viewport(size[0], size[1]);
    } else {
        return cy.viewport(size);
    }
});
cy.setViewport([1920, 780]);

VIEWSTATE

Must have form: true property. Must escape VIEWSTATE.

const util = require('../../util.js');

cy.request({
    method: 'GET',
    url: 'https://www.cypress.io',
    failOnStatusCode: true,
}).then((response) => {
    expect(response.status).to.eq(200);
    expect(response.body).to.contains('You are about to close your jobseeker account');
    const VIEWSTATE = util.escape(util.parsetext('id="__VIEWSTATE" value="([^"]*)"', response.body));
    const VIEWSTATEGENERATOR = util.parsetext('id="__VIEWSTATEGENERATOR" value="([^"]*)"', response.body);
    cy.request({
        method: 'POST',
        body: `__VIEWSTATE=${VIEWSTATE}&__VIEWSTATEGENERATOR=${VIEWSTATEGENERATOR}&Keywords=Totaljobs+Group&LTxt=&LocationType=10&Keywords=Totaljobs+Group&LTxt=&LocationType=10&btnUnsubscribe=Close+my+account`,
        url: util.totaljobsBaseUrl() + '/Authenticated/Unsubscribe.aspx',
        failOnStatusCode: true,
        form: true,
    }).then((response) => {
        expect(response.status).to.eq(200);
        expect(response.body).to.contains('UnsubscribeConfirm');
    });
});

function parsetext(regexString, text) {
    const regex = new RegExp(regexString);
    if (regex.test(text)) {
        const match = text.match(regex);
        cy.log(`Match: ${match[1]}`);
        return match[1];
    } else {
        cy.log('No matches could be found.');
    }
    return '';
}

function escape(value) {
    value = value.replace(/ /g, '%20');
    value = value.replace(/\\/g, '%22');
    value = value.replace(/\$/g, '%24');
    value = value.replace(/&/g, '%24');
    value = value.replace(/'/g, '%27');
    value = value.replace(/\+/g, '%2B');
    value = value.replace(/\//g, '%2F');
    value = value.replace(/</g, '%3C');
    value = value.replace(/>/g, '%3E');
    return value;
}

module.exports = {
    parsetext,
    escape,
};

Package Sidebar

Install

npm i cypress-page-object-model

Weekly Downloads

33

Version

1.0.26

License

MIT

Unpacked Size

45.2 kB

Total Files

13

Last publish

Collaborators

  • padmaraj.nidagundi