From 505bb2654efce1e20278560da2aa5b62b8ef69ce Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 16:08:36 -0400 Subject: [PATCH 01/14] Cloned feature/alerts-and-frames into feature/alerts-and-frames-with-plugin --- cypress.config.js | 2 ++ cypress/support/commands.js | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cypress.config.js b/cypress.config.js index 97f47c4..06ea1a1 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -2,6 +2,8 @@ const { defineConfig } = require("cypress"); module.exports = defineConfig({ e2e: { + supportFile: 'cypress/support/commands.js', + specPattern: 'cypress/integration/**/*.spec.js', setupNodeEvents(on, config) { // implement node event listeners here }, diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 66ea16e..45b0ffa 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -22,4 +22,30 @@ // // // -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +// visitWithAdBlocker: Visits a page and blocks ads +Cypress.Commands.add('visitWithAdBlocker', (url) => { + // Setup intercepts + cy.intercept('https://pagead2.googlesyndication.com/**', {statusCode: 200, body: ''}).as('blockAds'); + cy.intercept('https://www.googletagservices.com/tag/js/gpt.js', {statusCode: 200, body: ''}).as('blockGpt'); + cy.intercept('https://www.googletagmanager.com/gtm.js?id=GTM-MX8DD4S', {statusCode: 200, body: ''}).as('blockGtm'); + cy.intercept('https://cdn.ad.plus/player/adplus.js', {statusCode: 200, body: ''}).as('blockAdPlus'); + cy.intercept('**/*.gif', {statusCode: 200, body: ''}).as('blockGif'); + cy.intercept('**/usermatch.gif**', {statusCode: 200, body: ''}).as('blockUserMatch'); + cy.intercept('**/merge**', {statusCode: 200, body: ''}).as('blockMerge'); + cy.intercept('**/*.js', (req) => { + if (req.url.includes('google') || req.url.includes('adsbygoogle')) { + req.destroy(); + } + }).as('blockScripts'); + + // Visit the page + cy.visit(url); +}); + +// enterFrame: Enters a frame by its selector +Cypress.Commands.add('enterFrame', (selector) => { + return cy.get(selector).its('0.contentDocument.body').should('not.be.undefined') + .then(cy.wrap); +}); \ No newline at end of file From 4dd5bafd5126f0154ffbbcad2a7ee7eb322b10f1 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 16:11:46 -0400 Subject: [PATCH 02/14] installed the 'cypress-iframe' plugin to do Lesson 4.2 tests --- cypress/support/commands.js | 1 + node_modules/.package-lock.json | 20 +++ node_modules/@types/cypress/LICENSE | 21 +++ node_modules/@types/cypress/README.md | 2 + node_modules/@types/cypress/package.json | 14 ++ node_modules/cypress-iframe/LICENSE | 7 + node_modules/cypress-iframe/README.md | 75 ++++++++ node_modules/cypress-iframe/dist/index.js | 170 ++++++++++++++++++ node_modules/cypress-iframe/dist/index.js.map | 1 + node_modules/cypress-iframe/index.d.ts | 18 ++ node_modules/cypress-iframe/package.json | 57 ++++++ package-lock.json | 23 ++- package.json | 3 +- 13 files changed, 410 insertions(+), 2 deletions(-) create mode 100644 node_modules/@types/cypress/LICENSE create mode 100644 node_modules/@types/cypress/README.md create mode 100644 node_modules/@types/cypress/package.json create mode 100644 node_modules/cypress-iframe/LICENSE create mode 100644 node_modules/cypress-iframe/README.md create mode 100644 node_modules/cypress-iframe/dist/index.js create mode 100644 node_modules/cypress-iframe/dist/index.js.map create mode 100644 node_modules/cypress-iframe/index.d.ts create mode 100644 node_modules/cypress-iframe/package.json diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 45b0ffa..c3a45c1 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -23,6 +23,7 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +import 'cypress-iframe' // visitWithAdBlocker: Visits a page and blocks ads Cypress.Commands.add('visitWithAdBlocker', (url) => { diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 782b6d0..633ce95 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -62,6 +62,17 @@ "ms": "^2.1.1" } }, + "node_modules/@types/cypress": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/cypress/-/cypress-1.1.3.tgz", + "integrity": "sha512-OXe0Gw8LeCflkG1oPgFpyrYWJmEKqYncBsD/J0r17r0ETx/TnIGDNLwXt/pFYSYuYTpzcq1q3g62M9DrfsBL4g==", + "deprecated": "This is a stub types definition for cypress (https://cypress.io). cypress provides its own type definitions, so you don't need @types/cypress installed!", + "dev": true, + "peer": true, + "dependencies": { + "cypress": "*" + } + }, "node_modules/@types/node": { "version": "20.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", @@ -581,6 +592,15 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-iframe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cypress-iframe/-/cypress-iframe-1.0.1.tgz", + "integrity": "sha512-Ne+xkZmWMhfq3x6wbfzK/SzsVTCrJru3R3cLXsoSAZyfUtJDamXyaIieHXeea3pQDXF4wE2w4iUuvCYHhoD31g==", + "dev": true, + "peerDependencies": { + "@types/cypress": "^1.1.0" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/node_modules/@types/cypress/LICENSE b/node_modules/@types/cypress/LICENSE new file mode 100644 index 0000000..2107107 --- /dev/null +++ b/node_modules/@types/cypress/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/node_modules/@types/cypress/README.md b/node_modules/@types/cypress/README.md new file mode 100644 index 0000000..8b58d85 --- /dev/null +++ b/node_modules/@types/cypress/README.md @@ -0,0 +1,2 @@ +This is a stub types definition for cypress (https://cypress.io). +cypress provides its own type definitions, so you don't need @types/cypress installed! \ No newline at end of file diff --git a/node_modules/@types/cypress/package.json b/node_modules/@types/cypress/package.json new file mode 100644 index 0000000..bbddfd2 --- /dev/null +++ b/node_modules/@types/cypress/package.json @@ -0,0 +1,14 @@ +{ + "name": "@types/cypress", + "version": "1.1.3", + "typings": null, + "description": "Stub TypeScript definitions entry for cypress, which provides its own types definitions", + "main": "", + "scripts": {}, + "author": "", + "repository": "https://cypress.io", + "license": "MIT", + "dependencies": { + "cypress": "*" + } +} \ No newline at end of file diff --git a/node_modules/cypress-iframe/LICENSE b/node_modules/cypress-iframe/LICENSE new file mode 100644 index 0000000..8c5040f --- /dev/null +++ b/node_modules/cypress-iframe/LICENSE @@ -0,0 +1,7 @@ +Copyright 2020 Kevin Groat (kgroat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/cypress-iframe/README.md b/node_modules/cypress-iframe/README.md new file mode 100644 index 0000000..53c6fa1 --- /dev/null +++ b/node_modules/cypress-iframe/README.md @@ -0,0 +1,75 @@ +# Cypress iframe +Adds iframe support to [Cypress](https://www.cypress.io/). + +## Installation +```bash +npm install -D cypress-iframe +``` + +In your `cypress/support/commands.js` file, add the following: +```js +import 'cypress-iframe'; +// or +require('cypress-iframe'); +``` + +If you're using typescript with cypress, and have not overridden the `types` or `typeRoots` in your tsc compiler options, then everything should work. + +If you have overridden them, or if it otherwise doesn't work out-of-the-box, you will also either want to: +1. Add `///` to the top of your cypress +1. Add a `globals.d.ts` in the root of your `cypress` directory and add `///` to it + +## Usage +You can now use the three included commands. + +### `frameLoaded` +This command checks that an iframe has loaded onto the page + +Example: +```js +// This will verify that the iframe is loaded to any page other than 'about:blank' +cy.frameLoaded() + +// This will verify that the iframe is loaded to any url containing the given path part +cy.frameLoaded({ url: 'https://google.com' }) +cy.frameLoaded({ url: '/join' }) +cy.frameLoaded({ url: '?some=query' }) +cy.frameLoaded({ url: '#/hash/path' }) + +// You can also give it a selector to check that a specific iframe has loaded +cy.frameLoaded('#my-frame') +cy.frameLoaded('#my-frame', { url: '/join' }) +``` + +### `iframe` +This will cause subsequent commands to be executed inside of the given iframe + +Example: +```js +// This will verify that the iframe is loaded to any page other than 'about:blank' +cy.iframe().find('.some-button').should('be.visible').click() +cy.iframe().contains('Some hidden element').should('not.be.visible') +cy.find('#outside-iframe').click() // this will be executed outside the iframe + +// You can also give it a selector to find elements inside of a specific iframe +cy.iframe('#my-frame').find('.some-button').should('be.visible').click() +cy.iframe('#my-second-frame').contains('Some hidden element').should('not.be.visible') +``` + +### `enter` +This can be used to execute a group of commands within an iframe + +Example: +```js +// This will verify that the iframe is loaded to any page other than 'about:blank' +cy.enter().then(getBody => { + getBody().find('.some-button').should('be.visible').click() + getBody().contains('Some hidden element').should('not.be.visible') +}) + +// You can also give it a selector to find elements inside of a specific iframe +cy.enter('#my-iframe').then(getBody => { + getBody().find('.some-button').should('be.visible').click() + getBody().contains('Some hidden element').should('not.be.visible') +}) +``` diff --git a/node_modules/cypress-iframe/dist/index.js b/node_modules/cypress-iframe/dist/index.js new file mode 100644 index 0000000..14e301e --- /dev/null +++ b/node_modules/cypress-iframe/dist/index.js @@ -0,0 +1,170 @@ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var _this = this; +var DEFAULT_OPTS = { + log: true, + timeout: 30000, +}; +var DEFAULT_IFRAME_SELECTOR = 'iframe'; +function sleep(timeout) { + return new Promise(function (resolve) { return setTimeout(resolve, timeout); }); +} +function timeout(cb, timeout) { + return new Promise(function (resolve) { + var done = false; + var finish = function () { return done || resolve(); }; + cb().then(finish); + sleep(timeout).then(finish); + }); +} +var frameLoaded = function (selector, opts) { + if (selector === undefined) { + selector = DEFAULT_IFRAME_SELECTOR; + } + else if (typeof selector === 'object') { + opts = selector; + selector = DEFAULT_IFRAME_SELECTOR; + } + var fullOpts = __assign(__assign({}, DEFAULT_OPTS), opts); + var log = fullOpts.log ? Cypress.log({ + name: 'frame loaded', + displayName: 'frame loaded', + message: [selector], + }).snapshot() : null; + return cy.get(selector, { log: false }).then({ timeout: fullOpts.timeout }, function ($frame) { return __awaiter(_this, void 0, void 0, function () { + var contentWindow, hasNavigated, loadLog; + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + (_a = log) === null || _a === void 0 ? void 0 : _a.set('$el', $frame); + if ($frame.length !== 1) { + throw new Error("cypress-iframe commands can only be applied to exactly one iframe at a time. Instead found " + $frame.length); + } + contentWindow = $frame.prop('contentWindow'); + hasNavigated = fullOpts.url + ? function () { + var _a; + return typeof fullOpts.url === 'string' + ? contentWindow.location.toString().includes(fullOpts.url) + : (_a = fullOpts.url) === null || _a === void 0 ? void 0 : _a.test(contentWindow.location.toString()); + } + : function () { return contentWindow.location.toString() !== 'about:blank'; }; + _c.label = 1; + case 1: + if (!!hasNavigated()) return [3, 3]; + return [4, sleep(100)]; + case 2: + _c.sent(); + return [3, 1]; + case 3: + if (contentWindow.document.readyState === 'complete') { + return [2, $frame]; + } + loadLog = Cypress.log({ + name: 'Frame Load', + message: [contentWindow.location.toString()], + event: true, + }).snapshot(); + return [4, new Promise(function (resolve) { + Cypress.$(contentWindow).on('load', resolve); + })]; + case 4: + _c.sent(); + loadLog.end(); + (_b = log) === null || _b === void 0 ? void 0 : _b.finish(); + return [2, $frame]; + } + }); + }); }); +}; +Cypress.Commands.add('frameLoaded', frameLoaded); +var iframe = function (selector, opts) { + if (selector === undefined) { + selector = DEFAULT_IFRAME_SELECTOR; + } + else if (typeof selector === 'object') { + opts = selector; + selector = DEFAULT_IFRAME_SELECTOR; + } + var fullOpts = __assign(__assign({}, DEFAULT_OPTS), opts); + var log = fullOpts.log ? Cypress.log({ + name: 'iframe', + displayName: 'iframe', + message: [selector], + }).snapshot() : null; + return cy.frameLoaded(selector, __assign(__assign({}, fullOpts), { log: false })).then(function ($frame) { + var _a; + (_a = log) === null || _a === void 0 ? void 0 : _a.set('$el', $frame).end(); + var contentWindow = $frame.prop('contentWindow'); + return Cypress.$(contentWindow.document.body); + }); +}; +Cypress.Commands.add('iframe', iframe); +var enter = function (selector, opts) { + if (selector === undefined) { + selector = DEFAULT_IFRAME_SELECTOR; + } + else if (typeof selector === 'object') { + opts = selector; + selector = DEFAULT_IFRAME_SELECTOR; + } + var fullOpts = __assign(__assign({}, DEFAULT_OPTS), opts); + var log = fullOpts.log ? Cypress.log({ + name: 'enter', + displayName: 'enter', + message: [selector], + }).snapshot() : null; + return cy.iframe(selector, __assign(__assign({}, fullOpts), { log: false })).then(function ($body) { + var _a; + (_a = log) === null || _a === void 0 ? void 0 : _a.set('$el', $body).end(); + return function () { return cy.wrap($body, { log: false }); }; + }); +}; +Cypress.Commands.add('enter', enter); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/node_modules/cypress-iframe/dist/index.js.map b/node_modules/cypress-iframe/dist/index.js.map new file mode 100644 index 0000000..d5a2c95 --- /dev/null +++ b/node_modules/cypress-iframe/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/cypress-iframe/index.d.ts b/node_modules/cypress-iframe/index.d.ts new file mode 100644 index 0000000..4042cc9 --- /dev/null +++ b/node_modules/cypress-iframe/index.d.ts @@ -0,0 +1,18 @@ +/// + +declare namespace Cypress { + interface IframeHandler { + (options?: Partial): Chainable + (selector: string, options?: Partial): Chainable + } + + interface IframeOptions extends Loggable, Timeoutable { + url?: RegExp | string + } + + interface Chainable { + frameLoaded: IframeHandler> + iframe: IframeHandler> + enter: IframeHandler<() => Chainable>> + } +} diff --git a/node_modules/cypress-iframe/package.json b/node_modules/cypress-iframe/package.json new file mode 100644 index 0000000..5afc4a4 --- /dev/null +++ b/node_modules/cypress-iframe/package.json @@ -0,0 +1,57 @@ +{ + "name": "cypress-iframe", + "version": "1.0.1", + "description": "Adds iframe support to Cypress", + "main": "dist/index.js", + "types": "index.d.ts", + "scripts": { + "preversion": "npm test", + "postversion": "npm publish", + "postpublish": "git push origin --all; git push origin --tags", + "prepare": "npm run build", + "build": "rm -rf dist && tsc --project ./tsconfig-build.json", + "start": "start-server-and-test wp http-get://localhost:9000 \"cypress open\"", + "test": "start-server-and-test wp http-get://localhost:9000 \"cypress run\"", + "record": "start-server-and-test wp http-get://localhost:9000 \"cypress run --record\"", + "wp": "webpack-dev-server" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@gitlab.com/kgroat/cypress-iframe.git" + }, + "keywords": [ + "cypress", + "iframe", + "command", + "e2e", + "test" + ], + "author": { + "name": "Kevin Groat (kgroat)", + "email": "kgroat09@gmail.com", + "url": "https://www.kgroat.dev/" + }, + "license": "MIT", + "bugs": { + "url": "https://gitlab.com/kgroat/cypress-iframe/issues" + }, + "homepage": "https://gitlab.com/kgroat/cypress-iframe#readme", + "peerDependencies": { + "@types/cypress": "^1.1.0" + }, + "devDependencies": { + "@cypress/webpack-preprocessor": "^4.1.1", + "@types/cypress": "^1.1.0", + "@types/node": "^13.7.0", + "@types/webpack": "^4.41.3", + "@types/webpack-dev-server": "^3.10.0", + "cypress": "^3.8.3", + "start-server-and-test": "^1.10.8", + "ts-loader": "^6.2.1", + "ts-node": "^8.6.2", + "typescript": "^3.7.5", + "webpack": "^4.41.5", + "webpack-cli": "^3.3.10", + "webpack-dev-server": "^3.10.2" + } +} diff --git a/package-lock.json b/package-lock.json index 9e8c880..d1cf4c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "cypress": "^13.7.3" + "cypress": "^13.7.3", + "cypress-iframe": "^1.0.1" } }, "node_modules/@colors/colors": { @@ -70,6 +71,17 @@ "ms": "^2.1.1" } }, + "node_modules/@types/cypress": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/cypress/-/cypress-1.1.3.tgz", + "integrity": "sha512-OXe0Gw8LeCflkG1oPgFpyrYWJmEKqYncBsD/J0r17r0ETx/TnIGDNLwXt/pFYSYuYTpzcq1q3g62M9DrfsBL4g==", + "deprecated": "This is a stub types definition for cypress (https://cypress.io). cypress provides its own type definitions, so you don't need @types/cypress installed!", + "dev": true, + "peer": true, + "dependencies": { + "cypress": "*" + } + }, "node_modules/@types/node": { "version": "20.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", @@ -589,6 +601,15 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-iframe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cypress-iframe/-/cypress-iframe-1.0.1.tgz", + "integrity": "sha512-Ne+xkZmWMhfq3x6wbfzK/SzsVTCrJru3R3cLXsoSAZyfUtJDamXyaIieHXeea3pQDXF4wE2w4iUuvCYHhoD31g==", + "dev": true, + "peerDependencies": { + "@types/cypress": "^1.1.0" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/package.json b/package.json index 2bfb481..95e1f3a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "author": "", "license": "ISC", "devDependencies": { - "cypress": "^13.7.3" + "cypress": "^13.7.3", + "cypress-iframe": "^1.0.1" } } From 1294ff504bba8fcc9d2a7203b05fc285331d9c40 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 16:28:54 -0400 Subject: [PATCH 03/14] revised the frames test to utilized the 'cypress-iframe' plugin --- cypress/integration/alerts.spec.js | 56 ++++++++++++++++++++++++++++++ cypress/integration/frames.spec.js | 31 +++++++++++++++++ cypress/support/commands.js | 31 ----------------- 3 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 cypress/integration/alerts.spec.js create mode 100644 cypress/integration/frames.spec.js diff --git a/cypress/integration/alerts.spec.js b/cypress/integration/alerts.spec.js new file mode 100644 index 0000000..f534539 --- /dev/null +++ b/cypress/integration/alerts.spec.js @@ -0,0 +1,56 @@ +describe('Alerts Handling on DEMOQA', () => { + beforeEach(() => { + cy.visitWithAdBlocker('https://demoqa.com/alerts'); + }); + + it('tests the [Alert] button which shows an alert box', () => { + cy.on('window:alert', (text) => { + expect(text).to.contains('You clicked a button'); + }); + cy.get('#alertButton').click(); + }); + + it('ensures the [Timer Alert] button triggers after exactly 5 seconds', () => { + cy.clock(); + + cy.on('window:alert', (text) => { + expect(text).to.equal('This alert appeared after 5 seconds'); + }); + + cy.get('#timerAlertButton').click(); + + cy.get('#statusMessage').should('not.exist'); + + cy.tick(5000); + }); + + + it('accepts a confirm alert and verifies actions taken on confirmation', () => { + cy.on('window:confirm', (str) => { + expect(str).to.eq('Do you confirm action?'); + return true; + }); + + cy.get('#confirmButton').click(); + cy.get('#confirmResult').should('contain', 'You selected Ok'); + }); + + it('cancels a confirm alert and verifies actions taken on cancellation', () => { + cy.on('window:confirm', (str) => { + expect(str).to.eq('Do you confirm action?'); + return false; + }); + + cy.get('#confirmButton').click(); + cy.get('#confirmResult').should('contain', 'You selected Cancel'); + }); + + it('accepts a prompt alert and verifies actions taken on confirmation', () => { + cy.window().then((win) => { + cy.stub(win, 'prompt').returns('Cypress Tester'); + }); + + cy.get('#promtButton').click(); + cy.get('#promptResult').should('contain', 'You entered Cypress Tester'); + }); +}); diff --git a/cypress/integration/frames.spec.js b/cypress/integration/frames.spec.js new file mode 100644 index 0000000..65b969c --- /dev/null +++ b/cypress/integration/frames.spec.js @@ -0,0 +1,31 @@ +describe('Frames Handling on DEMOQA', () => { + beforeEach(() => { + cy.visitWithAdBlocker('https://demoqa.com/frames'); + }); + + // Test to check content and dimensions of frame1 + it('verifies content and dimensions within frame1', () => { + cy.get('#frame1') + .should('have.attr', 'width', '500px') + .and('have.attr', 'height', '350px'); + + cy.frameLoaded('#frame1'); + cy.iframe('#frame1').within(() => { + cy.get('h1#sampleHeading').should('exist') + .and('have.text', 'This is a sample page'); + }); + }); + + // Test to check content and dimensions of frame2 + it('verifies content and dimensions within frame2', () => { + cy.get('#frame2') + .should('have.attr', 'width', '100px') + .and('have.attr', 'height', '100px'); + + cy.frameLoaded('#frame2'); + cy.iframe('#frame2').within(() => { + cy.get('h1#sampleHeading').should('exist') + .and('have.text', 'This is a sample page'); + }); + }); +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index c3a45c1..6d5655b 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,28 +1,3 @@ -// *********************************************** -// This example commands.js 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) => { ... }) import 'cypress-iframe' // visitWithAdBlocker: Visits a page and blocks ads @@ -43,10 +18,4 @@ Cypress.Commands.add('visitWithAdBlocker', (url) => { // Visit the page cy.visit(url); -}); - -// enterFrame: Enters a frame by its selector -Cypress.Commands.add('enterFrame', (selector) => { - return cy.get(selector).its('0.contentDocument.body').should('not.be.undefined') - .then(cy.wrap); }); \ No newline at end of file From 95cff281c841e3eeb5ba5a5903d0b0db7d9bf6a9 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 22:04:33 -0400 Subject: [PATCH 04/14] added 'chromeWebSecurity: false' for Sauce Demo page workaround --- cypress.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress.config.js b/cypress.config.js index 06ea1a1..c597056 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -2,6 +2,7 @@ const { defineConfig } = require("cypress"); module.exports = defineConfig({ e2e: { + chromeWebSecurity: false, supportFile: 'cypress/support/commands.js', specPattern: 'cypress/integration/**/*.spec.js', setupNodeEvents(on, config) { From 4d57305e1d31ed95e49f98b6e78efcc1c7a72cd1 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 22:05:17 -0400 Subject: [PATCH 05/14] added Sauce Demo credentials and AdBlock URLs in fixtures --- cypress/fixtures/adBlockerUrls.json | 12 ++++++++++ cypress/fixtures/credentials.json | 36 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 cypress/fixtures/adBlockerUrls.json create mode 100644 cypress/fixtures/credentials.json diff --git a/cypress/fixtures/adBlockerUrls.json b/cypress/fixtures/adBlockerUrls.json new file mode 100644 index 0000000..3a71cad --- /dev/null +++ b/cypress/fixtures/adBlockerUrls.json @@ -0,0 +1,12 @@ +{ + "blockAds": "https://pagead2.googlesyndication.com/**", + "blockGpt": "https://www.googletagservices.com/tag/js/gpt.js", + "blockGtm": "https://www.googletagmanager.com/gtm.js?id=GTM-MX8DD4S", + "blockAdPlus": "https://cdn.ad.plus/player/adplus.js", + "blockBacktrace": "https://events.backtrace.io/**", + "blockGif": "**/*.gif", + "blockUserMatch": "**/usermatch.gif**", + "blockMerge": "**/merge**", + "blockScripts": "**/*.js" +} + diff --git a/cypress/fixtures/credentials.json b/cypress/fixtures/credentials.json new file mode 100644 index 0000000..029333a --- /dev/null +++ b/cypress/fixtures/credentials.json @@ -0,0 +1,36 @@ +{ + "users": { + "standardUser": { + "username": "standard_user", + "password": "secret_sauce" + }, + "lockedOutUser": { + "username": "locked_out_user", + "password": "secret_sauce" + }, + "problemUser": { + "username": "problem_user", + "password": "secret_sauce" + }, + "performanceGlitchUser": { + "username": "performance_glitch_user", + "password": "secret_sauce" + }, + "invalidUser": { + "username": "invalid_user", + "password": "secret_sauce" + }, + "noPassword": { + "username": "standard_user", + "password": "" + }, + "invalidPassword": { + "username": "standard_user", + "password": "invalid_password" + }, + "noUsername": { + "username": "", + "password": "secret_sauce" + } + } +} From 38d4d21ce09f1475686c1c973f82f7061d980c04 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 22:05:58 -0400 Subject: [PATCH 06/14] refactored 'visitWithAdBlocker' to use fixture file --- cypress/support/commands.js | 80 +++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 6d5655b..81ab576 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -2,20 +2,74 @@ import 'cypress-iframe' // visitWithAdBlocker: Visits a page and blocks ads Cypress.Commands.add('visitWithAdBlocker', (url) => { - // Setup intercepts - cy.intercept('https://pagead2.googlesyndication.com/**', {statusCode: 200, body: ''}).as('blockAds'); - cy.intercept('https://www.googletagservices.com/tag/js/gpt.js', {statusCode: 200, body: ''}).as('blockGpt'); - cy.intercept('https://www.googletagmanager.com/gtm.js?id=GTM-MX8DD4S', {statusCode: 200, body: ''}).as('blockGtm'); - cy.intercept('https://cdn.ad.plus/player/adplus.js', {statusCode: 200, body: ''}).as('blockAdPlus'); - cy.intercept('**/*.gif', {statusCode: 200, body: ''}).as('blockGif'); - cy.intercept('**/usermatch.gif**', {statusCode: 200, body: ''}).as('blockUserMatch'); - cy.intercept('**/merge**', {statusCode: 200, body: ''}).as('blockMerge'); - cy.intercept('**/*.js', (req) => { + cy.fixture('adBlockerUrls').then((urls) => { + // Setup intercepts + Object.keys(urls).forEach((key) => { + if (key !== 'blockScripts') { + cy.intercept(urls[key], { statusCode: 200, body: '' }).as(key); + } + }); + + cy.intercept(urls['blockScripts'], (req) => { if (req.url.includes('google') || req.url.includes('adsbygoogle')) { - req.destroy(); + req.destroy(); } - }).as('blockScripts'); + }).as('blockScripts'); + + // Visit the page + cy.visit(url); + }); +}); + +// Custom command for logging into Sauce Demo +Cypress.Commands.add('login', (username, password) => { + cy.get('[data-test="username"]').type(username); + cy.get('[data-test="password"]').type(password); + cy.get('[data-test="login-button"]').click(); +}); + +// Custom command to login with a user type +Cypress.Commands.add('loginWithFixture', (userType) => { + cy.fixture('credentials').then((credentials) => { + const user = credentials.users[userType]; + cy.login(user.username, user.password); + }); +}); + +// Custom command to add a single item to the cart by its name +Cypress.Commands.add('addItemToCartByName', (itemName) => { + cy.contains('.inventory_item_name', itemName) + .parents('.inventory_item') + .within(() => { + cy.get('.btn_inventory').click(); + }); +}); + +// Custom command to add all items to the cart +Cypress.Commands.add('addAllItemsToCart', () => { + cy.get('.inventory_item').each((element) => { + // For each .inventory_item, find the button within it and click + cy.wrap(element).find('.btn_inventory').click(); + }); +}); + +// Custom command to complete the checkout process +Cypress.Commands.add('completeCheckout', (firstName, lastName, zipCode) => { + cy.get('.shopping_cart_link').click(); + cy.get('.checkout_button').click(); + cy.get('#first-name').type(firstName); + cy.get('#last-name').type(lastName); + cy.get('#postal-code').type(zipCode); + cy.get('.btn_primary.cart_button').click(); + // This clicks the final 'Finish' button on the checkout page + cy.get('.btn_action.cart_button').click(); +}); - // Visit the page - cy.visit(url); +// Example of overwriting the 'visit' command to always disable the cache +Cypress.Commands.overwrite('visit', (originalFn, url, options) => { + // Example usage of overwriting an existing command + // This will append cache busting query param to each visit + let noCacheStr = `_noCache=${Math.random()}`; + url.includes('?') ? (url += `&${noCacheStr}`) : (url += `?${noCacheStr}`); + return originalFn(url, options); }); \ No newline at end of file From d3b50d324563b7f20a2a358c606448b19056ac31 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 22:06:19 -0400 Subject: [PATCH 07/14] added Sauce Demo spec --- cypress/integration/sauce_demo.spec.js | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 cypress/integration/sauce_demo.spec.js diff --git a/cypress/integration/sauce_demo.spec.js b/cypress/integration/sauce_demo.spec.js new file mode 100644 index 0000000..fbd4c54 --- /dev/null +++ b/cypress/integration/sauce_demo.spec.js @@ -0,0 +1,42 @@ +// saucedemo_tests.cy.js +describe('Sauce Demo Tests', () => { + beforeEach(() => { + // Visits the Sauce Demo website before each test + cy.visitWithAdBlocker('http://www.saucedemo.com'); + }); + + it('TC_1: Successfully logs in as a standard user', () => { + cy.loginWithFixture('standardUser'); + cy.get('.inventory_list').should('be.visible'); + }); + + it('TC_2: Fails to log in with invalid credentials', () => { + cy.loginWithFixture('invalidPassword'); + cy.log('Attempted login with in with invalid password'); + cy.get('.error-message-container').then((elem) => { + console.log('Error message element:', elem); + }).should('be.visible'); + }); + + it('TC_3: Navigates through product pages', () => { + cy.loginWithFixture('standardUser'); + cy.get('.inventory_item_name').first().click(); + cy.get('.inventory_details').should('be.visible'); + cy.go('back'); + cy.get('.inventory_list').should('be.visible'); + }); + + it('TC_4: Adds an item to the cart', () => { + cy.loginWithFixture('standardUser'); + cy.get('.btn_inventory').first().click(); + cy.get('.shopping_cart_link').click(); + cy.get('.cart_list').should('be.visible'); + }); + + it('TC_5: Adds all items and completes checkout', () => { + cy.loginWithFixture('standardUser'); + cy.addAllItemsToCart(); + cy.completeCheckout('John', 'Dow', '12345'); + cy.get('.complete-header').should('be.visible'); + }); +}); \ No newline at end of file From 295ff6b9ae793293b3d5b7ed57e2cc19faee09e0 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 22:11:11 -0400 Subject: [PATCH 08/14] added error handling to commands.js --- cypress/support/commands.js | 85 +++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 81ab576..9bfb466 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,55 +1,68 @@ -import 'cypress-iframe' +import 'cypress-iframe'; -// visitWithAdBlocker: Visits a page and blocks ads +// Custom command to visit a URL with ad blockers enabled Cypress.Commands.add('visitWithAdBlocker', (url) => { cy.fixture('adBlockerUrls').then((urls) => { - // Setup intercepts - Object.keys(urls).forEach((key) => { - if (key !== 'blockScripts') { - cy.intercept(urls[key], { statusCode: 200, body: '' }).as(key); - } - }); - - cy.intercept(urls['blockScripts'], (req) => { - if (req.url.includes('google') || req.url.includes('adsbygoogle')) { - req.destroy(); - } - }).as('blockScripts'); - - // Visit the page - cy.visit(url); + try { + Object.keys(urls).forEach((key) => { + const routeMatcher = { statusCode: 200, body: '' }; + if (key === 'blockScripts') { + cy.intercept(urls[key], (req) => { + if (req.url.includes('google') || req.url.includes('adsbygoogle')) { + req.destroy(); + } + }).as(key); + } else { + cy.intercept(urls[key], routeMatcher).as(key); + } + }); + cy.visit(url).then(() => { + cy.log('Visited with ad blockers set up successfully.'); + }); + } catch (error) { + cy.log('Ad Blocker setup failed.'); + throw new Error(`Ad Blocker setup error: ${error.message}`); + } }); }); -// Custom command for logging into Sauce Demo +// Custom command for logging into a site Cypress.Commands.add('login', (username, password) => { - cy.get('[data-test="username"]').type(username); - cy.get('[data-test="password"]').type(password); - cy.get('[data-test="login-button"]').click(); + cy.get('[data-test="username"]').type(username).then(($username) => { + if (!$username.val()) throw new Error('Failed to type the username'); + }); + cy.get('[data-test="password"]').type(password).then(($password) => { + if (!$password.val()) throw new Error('Failed to type the password'); + }); + cy.get('[data-test="login-button"]').click().then(($button) => { + if ($button.is(':disabled')) throw new Error('Login button is disabled'); + }); }); -// Custom command to login with a user type +// Custom command to login with a user type from a fixture Cypress.Commands.add('loginWithFixture', (userType) => { cy.fixture('credentials').then((credentials) => { const user = credentials.users[userType]; + if (!user) throw new Error('User type not found in fixture'); cy.login(user.username, user.password); }); }); -// Custom command to add a single item to the cart by its name +// Custom command to add an item to the cart by name Cypress.Commands.add('addItemToCartByName', (itemName) => { - cy.contains('.inventory_item_name', itemName) - .parents('.inventory_item') - .within(() => { - cy.get('.btn_inventory').click(); + cy.contains('.inventory_item_name', itemName).parents('.inventory_item').within(() => { + cy.get('.btn_inventory').click().then(($btn) => { + if (!$btn.hasClass('btn_inventory')) throw new Error('Add to cart button not found'); }); + }); }); // Custom command to add all items to the cart Cypress.Commands.add('addAllItemsToCart', () => { cy.get('.inventory_item').each((element) => { - // For each .inventory_item, find the button within it and click - cy.wrap(element).find('.btn_inventory').click(); + cy.wrap(element).find('.btn_inventory').click().then(($btn) => { + if (!$btn.hasClass('btn_inventory')) throw new Error('Add to cart button not found'); + }); }); }); @@ -61,15 +74,13 @@ Cypress.Commands.add('completeCheckout', (firstName, lastName, zipCode) => { cy.get('#last-name').type(lastName); cy.get('#postal-code').type(zipCode); cy.get('.btn_primary.cart_button').click(); - // This clicks the final 'Finish' button on the checkout page - cy.get('.btn_action.cart_button').click(); + cy.get('.btn_action.cart_button').click(); // This clicks the final 'Finish' button on the checkout page }); -// Example of overwriting the 'visit' command to always disable the cache +// Overwriting the 'visit' command to always disable the cache Cypress.Commands.overwrite('visit', (originalFn, url, options) => { - // Example usage of overwriting an existing command - // This will append cache busting query param to each visit - let noCacheStr = `_noCache=${Math.random()}`; - url.includes('?') ? (url += `&${noCacheStr}`) : (url += `?${noCacheStr}`); + // This will append a cache busting query param to each visit + const noCacheStr = `_noCache=${Math.random()}`; + url = url.includes('?') ? `${url}&${noCacheStr}` : `${url}?${noCacheStr}`; return originalFn(url, options); -}); \ No newline at end of file +}); From 9bb6caa02ad93dd0148cda5a385043652ecc18cc Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:18:11 -0400 Subject: [PATCH 09/14] using the https URL --- cypress/integration/sauce_demo.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/sauce_demo.spec.js b/cypress/integration/sauce_demo.spec.js index fbd4c54..7902ad7 100644 --- a/cypress/integration/sauce_demo.spec.js +++ b/cypress/integration/sauce_demo.spec.js @@ -2,7 +2,7 @@ describe('Sauce Demo Tests', () => { beforeEach(() => { // Visits the Sauce Demo website before each test - cy.visitWithAdBlocker('http://www.saucedemo.com'); + cy.visitWithAdBlocker('https://www.saucedemo.com'); }); it('TC_1: Successfully logs in as a standard user', () => { From 20dfee054bae28cb334444448d74c3ac9709f5a6 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:18:41 -0400 Subject: [PATCH 10/14] removed placeholder comments --- cypress/support/e2e.js | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 0e7290a..f65e7f7 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -1,20 +1 @@ -// *********************************************************** -// This example support/e2e.js 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') \ No newline at end of file +import './commands' \ No newline at end of file From ae159bc569d3e4f4f97def395ca14ac843d7fac8 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:19:26 -0400 Subject: [PATCH 11/14] suppressing logs for the blocked external scripts --- cypress/support/commands.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 9bfb466..9c1ccca 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -5,15 +5,23 @@ Cypress.Commands.add('visitWithAdBlocker', (url) => { cy.fixture('adBlockerUrls').then((urls) => { try { Object.keys(urls).forEach((key) => { - const routeMatcher = { statusCode: 200, body: '' }; if (key === 'blockScripts') { - cy.intercept(urls[key], (req) => { - if (req.url.includes('google') || req.url.includes('adsbygoogle')) { - req.destroy(); - } + // Use middleware to modify requests conditionally and control logging + cy.intercept(urls[key], { + onResponse: (req) => { + if (req.url.includes('google') || req.url.includes('adsbygoogle')) { + req.destroy(); + } + }, + log: false }).as(key); } else { - cy.intercept(urls[key], routeMatcher).as(key); + // Intercept and stub response without any conditions + cy.intercept(urls[key], { + statusCode: 200, + body: '', + log: false + }).as(key); } }); cy.visit(url).then(() => { From d5b43e06117041caf5d3a7dad691cb015e534df2 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:22:33 -0400 Subject: [PATCH 12/14] removed a comment --- cypress/support/commands.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 9c1ccca..cd8fcd9 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -6,7 +6,6 @@ Cypress.Commands.add('visitWithAdBlocker', (url) => { try { Object.keys(urls).forEach((key) => { if (key === 'blockScripts') { - // Use middleware to modify requests conditionally and control logging cy.intercept(urls[key], { onResponse: (req) => { if (req.url.includes('google') || req.url.includes('adsbygoogle')) { From 16666cd581d5be57e2b34c378491b2f2709fa4c8 Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:34:56 -0400 Subject: [PATCH 13/14] optimized --- cypress/support/commands.js | 71 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index cd8fcd9..5c1c82d 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,56 +1,46 @@ import 'cypress-iframe'; +// Helper function to log and throw errors +function logAndThrow(message) { + cy.log(message); + throw new Error(message); +} + // Custom command to visit a URL with ad blockers enabled Cypress.Commands.add('visitWithAdBlocker', (url) => { - cy.fixture('adBlockerUrls').then((urls) => { + cy.fixture('adBlockerUrls').then(urls => { try { - Object.keys(urls).forEach((key) => { - if (key === 'blockScripts') { - cy.intercept(urls[key], { - onResponse: (req) => { - if (req.url.includes('google') || req.url.includes('adsbygoogle')) { - req.destroy(); - } - }, - log: false - }).as(key); - } else { - // Intercept and stub response without any conditions - cy.intercept(urls[key], { - statusCode: 200, - body: '', - log: false - }).as(key); - } - }); - cy.visit(url).then(() => { - cy.log('Visited with ad blockers set up successfully.'); + Object.entries(urls).forEach(([key, value]) => { + cy.intercept(value, { + statusCode: key === 'blockScripts' ? undefined : 200, + body: key === 'blockScripts' ? undefined : '', + onResponse: key === 'blockScripts' ? (req) => { + if (req.url.includes('google') || req.url.includes('adsbygoogle')) { + req.destroy(); + } + } : undefined, + log: false + }).as(key); }); + cy.visit(url).then(() => cy.log('Visited with ad blockers set up successfully.')); } catch (error) { - cy.log('Ad Blocker setup failed.'); - throw new Error(`Ad Blocker setup error: ${error.message}`); + logAndThrow(`Ad Blocker setup error: ${error.message}`); } }); }); // Custom command for logging into a site Cypress.Commands.add('login', (username, password) => { - cy.get('[data-test="username"]').type(username).then(($username) => { - if (!$username.val()) throw new Error('Failed to type the username'); - }); - cy.get('[data-test="password"]').type(password).then(($password) => { - if (!$password.val()) throw new Error('Failed to type the password'); - }); - cy.get('[data-test="login-button"]').click().then(($button) => { - if ($button.is(':disabled')) throw new Error('Login button is disabled'); - }); + cy.get('[data-test="username"]').type(username, { log: false }).should('have.value', username); + cy.get('[data-test="password"]').type(password, { log: false }).should('have.value', password); + cy.get('#login-button').click(); }); // Custom command to login with a user type from a fixture Cypress.Commands.add('loginWithFixture', (userType) => { - cy.fixture('credentials').then((credentials) => { + cy.fixture('credentials').then(credentials => { const user = credentials.users[userType]; - if (!user) throw new Error('User type not found in fixture'); + if (!user) logAndThrow('User type not found in fixture'); cy.login(user.username, user.password); }); }); @@ -58,18 +48,14 @@ Cypress.Commands.add('loginWithFixture', (userType) => { // Custom command to add an item to the cart by name Cypress.Commands.add('addItemToCartByName', (itemName) => { cy.contains('.inventory_item_name', itemName).parents('.inventory_item').within(() => { - cy.get('.btn_inventory').click().then(($btn) => { - if (!$btn.hasClass('btn_inventory')) throw new Error('Add to cart button not found'); - }); + cy.get('.btn_inventory').click().should('have.class', 'btn_inventory'); }); }); // Custom command to add all items to the cart Cypress.Commands.add('addAllItemsToCart', () => { - cy.get('.inventory_item').each((element) => { - cy.wrap(element).find('.btn_inventory').click().then(($btn) => { - if (!$btn.hasClass('btn_inventory')) throw new Error('Add to cart button not found'); - }); + cy.get('.inventory_item').each(element => { + cy.wrap(element).find('.btn_inventory').click(); }); }); @@ -86,7 +72,6 @@ Cypress.Commands.add('completeCheckout', (firstName, lastName, zipCode) => { // Overwriting the 'visit' command to always disable the cache Cypress.Commands.overwrite('visit', (originalFn, url, options) => { - // This will append a cache busting query param to each visit const noCacheStr = `_noCache=${Math.random()}`; url = url.includes('?') ? `${url}&${noCacheStr}` : `${url}?${noCacheStr}`; return originalFn(url, options); From d7d2d24b658f9f97ec022a1a32c6e3d6a1c611bd Mon Sep 17 00:00:00 2001 From: aasim Date: Sat, 13 Apr 2024 23:39:51 -0400 Subject: [PATCH 14/14] optimized the alerts/frames specs --- cypress/integration/alerts.spec.js | 25 +++++----------------- cypress/integration/frames.spec.js | 34 +++++++++++------------------- 2 files changed, 17 insertions(+), 42 deletions(-) diff --git a/cypress/integration/alerts.spec.js b/cypress/integration/alerts.spec.js index f534539..3d6798e 100644 --- a/cypress/integration/alerts.spec.js +++ b/cypress/integration/alerts.spec.js @@ -1,36 +1,25 @@ describe('Alerts Handling on DEMOQA', () => { beforeEach(() => { cy.visitWithAdBlocker('https://demoqa.com/alerts'); + cy.clock(); }); it('tests the [Alert] button which shows an alert box', () => { - cy.on('window:alert', (text) => { - expect(text).to.contains('You clicked a button'); - }); + cy.on('window:alert', (text) => expect(text).to.contain('You clicked a button')); cy.get('#alertButton').click(); }); it('ensures the [Timer Alert] button triggers after exactly 5 seconds', () => { - cy.clock(); - - cy.on('window:alert', (text) => { - expect(text).to.equal('This alert appeared after 5 seconds'); - }); - + cy.on('window:alert', (text) => expect(text).to.equal('This alert appeared after 5 seconds')); cy.get('#timerAlertButton').click(); - - cy.get('#statusMessage').should('not.exist'); - - cy.tick(5000); + cy.tick(5000); // Simulate passing of 5 seconds }); - it('accepts a confirm alert and verifies actions taken on confirmation', () => { cy.on('window:confirm', (str) => { expect(str).to.eq('Do you confirm action?'); return true; }); - cy.get('#confirmButton').click(); cy.get('#confirmResult').should('contain', 'You selected Ok'); }); @@ -40,16 +29,12 @@ describe('Alerts Handling on DEMOQA', () => { expect(str).to.eq('Do you confirm action?'); return false; }); - cy.get('#confirmButton').click(); cy.get('#confirmResult').should('contain', 'You selected Cancel'); }); it('accepts a prompt alert and verifies actions taken on confirmation', () => { - cy.window().then((win) => { - cy.stub(win, 'prompt').returns('Cypress Tester'); - }); - + cy.window().then((win) => cy.stub(win, 'prompt').returns('Cypress Tester')); cy.get('#promtButton').click(); cy.get('#promptResult').should('contain', 'You entered Cypress Tester'); }); diff --git a/cypress/integration/frames.spec.js b/cypress/integration/frames.spec.js index 65b969c..a682074 100644 --- a/cypress/integration/frames.spec.js +++ b/cypress/integration/frames.spec.js @@ -3,29 +3,19 @@ describe('Frames Handling on DEMOQA', () => { cy.visitWithAdBlocker('https://demoqa.com/frames'); }); - // Test to check content and dimensions of frame1 - it('verifies content and dimensions within frame1', () => { - cy.get('#frame1') - .should('have.attr', 'width', '500px') - .and('have.attr', 'height', '350px'); + const verifyFrameContentAndSize = (frameId, expectedWidth, expectedHeight, expectedText) => { + it(`verifies content and dimensions within ${frameId}`, () => { + cy.get(frameId) + .should('have.attr', 'width', expectedWidth) + .and('have.attr', 'height', expectedHeight); - cy.frameLoaded('#frame1'); - cy.iframe('#frame1').within(() => { - cy.get('h1#sampleHeading').should('exist') - .and('have.text', 'This is a sample page'); + cy.frameLoaded(frameId); + cy.iframe(frameId).within(() => { + cy.get('h1#sampleHeading').should('have.text', expectedText); + }); }); - }); - - // Test to check content and dimensions of frame2 - it('verifies content and dimensions within frame2', () => { - cy.get('#frame2') - .should('have.attr', 'width', '100px') - .and('have.attr', 'height', '100px'); + }; - cy.frameLoaded('#frame2'); - cy.iframe('#frame2').within(() => { - cy.get('h1#sampleHeading').should('exist') - .and('have.text', 'This is a sample page'); - }); - }); + verifyFrameContentAndSize('#frame1', '500px', '350px', 'This is a sample page'); + verifyFrameContentAndSize('#frame2', '100px', '100px', 'This is a sample page'); });