From 28e3c366930d29bd59f1a0f06cf3b140387dbb84 Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:42:30 +0530 Subject: [PATCH] ci(cypressV2): update cypress v2 framework to accommodate hyperswitch v2 changes (#6493) --- .../cypress/e2e/configs/Payment/Utils.js | 12 - .../cypress/e2e/configs/Payment/_Reusable.js | 15 +- .../spec/Payment/0001-[No3DS]Payments.cy.js | 132 ++++++++++ .../e2e/spec/Payment/0002-[3DS]Payments.cy.js | 8 + .../cypress/fixtures/organization.json | 2 +- cypress-tests-v2/cypress/support/commands.js | 233 +++++++++++------- .../cypress/utils/RequestBodyUtils.js | 48 ++++ cypress-tests-v2/package-lock.json | 153 +++++------- cypress-tests-v2/package.json | 5 +- 9 files changed, 412 insertions(+), 196 deletions(-) create mode 100644 cypress-tests-v2/cypress/e2e/spec/Payment/0001-[No3DS]Payments.cy.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/Payment/0002-[3DS]Payments.cy.js create mode 100644 cypress-tests-v2/cypress/utils/RequestBodyUtils.js diff --git a/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js b/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js index 6faea73d4ef9..9785839f4221 100644 --- a/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js +++ b/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js @@ -94,15 +94,3 @@ export function defaultErrorHandler(response, response_data) { expect(response.body.error).to.include(response_data.body.error); } } - -export function isoTimeTomorrow() { - const now = new Date(); - - // Create a new date object for tomorrow - const tomorrow = new Date(now); - tomorrow.setDate(now.getDate() + 1); - - // Convert to ISO string format - const isoStringTomorrow = tomorrow.toISOString(); - return isoStringTomorrow; -} diff --git a/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js b/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js index b69506ff0755..353d96260b55 100644 --- a/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js +++ b/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js @@ -15,12 +15,23 @@ function normalise(input) { paybox: "Paybox", paypal: "Paypal", wellsfargo: "Wellsfargo", + fiuu: "Fiuu", // Add more known exceptions here }; if (typeof input !== "string") { - const spec_name = Cypress.spec.name.split("-")[1].split(".")[0]; - return `${spec_name}`; + const specName = Cypress.spec.name; + + if (specName.includes("-")) { + const parts = specName.split("-"); + + if (parts.length > 1 && parts[1].includes(".")) { + return parts[1].split(".")[0]; + } + } + + // Fallback + return `${specName}`; } const lowerCaseInput = input.toLowerCase(); diff --git a/cypress-tests-v2/cypress/e2e/spec/Payment/0001-[No3DS]Payments.cy.js b/cypress-tests-v2/cypress/e2e/spec/Payment/0001-[No3DS]Payments.cy.js new file mode 100644 index 000000000000..21766617c09a --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Payment/0001-[No3DS]Payments.cy.js @@ -0,0 +1,132 @@ +/* +No 3DS Auto capture with Confirm True +No 3DS Auto capture with Confirm False +No 3DS Manual capture with Confirm True +No 3DS Manual capture with Confirm False +No 3DS Manual multiple capture with Confirm True +No 3DS Manual multiple capture with Confirm False +*/ + +import * as fixtures from "../../../fixtures/imports"; +import State from "../../../utils/State"; +import getConnectorDetails from "../../configs/Payment/Utils"; + +let globalState; + +// Below is an example of a test that is skipped just because it is not implemented yet +describe("[Payment] [No 3DS] [Payment Method: Card]", () => { + context("[Payment] [No 3DS] [Capture: Automatic] [Confirm: True]", () => { + let should_continue = true; + + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it.skip("Create payment intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "PaymentIntent" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.paymentIntentCreateCall( + fixtures.createPaymentBody, + req_data, + res_data, + "no_three_ds", + "automatic", + globalState + ); + }); + + it.skip("List payment methods", () => { + cy.paymentMethodsListCall(globalState); + }); + + it.skip("Confirm payment intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "No3DSAutoCapture" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.paymentIntentConfirmCall( + fixtures.confirmBody, + req_data, + res_data, + true, + globalState + ); + }); + + it.skip("Retrieve payment intent", () => { + cy.paymentIntentRetrieveCall(globalState); + }); + }); + context("[Payment] [No 3DS] [Capture: Automatic] [Confirm: False]", () => { + let should_continue = true; + + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it.skip("Create Payment Intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "PaymentIntent" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.paymentIntentCreateCall( + fixtures.createPaymentBody, + req_data, + res_data, + "no_three_ds", + "automatic", + globalState + ); + }); + + it.skip("Payment Methods", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it.skip("Confirm No 3DS", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "No3DSAutoCapture" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.paymentIntentConfirmCall( + fixtures.confirmBody, + req_data, + res_data, + true, + globalState + ); + }); + + it.skip("Retrieve payment intent", () => { + cy.paymentIntentRetrieveCall(globalState); + }); + }); +}); diff --git a/cypress-tests-v2/cypress/e2e/spec/Payment/0002-[3DS]Payments.cy.js b/cypress-tests-v2/cypress/e2e/spec/Payment/0002-[3DS]Payments.cy.js new file mode 100644 index 000000000000..2a1aa7d51528 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Payment/0002-[3DS]Payments.cy.js @@ -0,0 +1,8 @@ +/* +3DS Auto capture with Confirm True +3DS Auto capture with Confirm False +3DS Manual capture with Confirm True +3DS Manual capture with Confirm False +3DS Manual multiple capture with Confirm True +3DS Manual multiple capture with Confirm False +*/ diff --git a/cypress-tests-v2/cypress/fixtures/organization.json b/cypress-tests-v2/cypress/fixtures/organization.json index 24d084ab606f..f0577db88765 100644 --- a/cypress-tests-v2/cypress/fixtures/organization.json +++ b/cypress-tests-v2/cypress/fixtures/organization.json @@ -1,6 +1,6 @@ { "org_create": { - "organization_name": "Hyperswitch Organization" + "organization_name": "Hyperswitch" }, "org_update": { "organization_name": "Hyperswitch", diff --git a/cypress-tests-v2/cypress/support/commands.js b/cypress-tests-v2/cypress/support/commands.js index abdf95194bb6..eb4ca3423eb3 100644 --- a/cypress-tests-v2/cypress/support/commands.js +++ b/cypress-tests-v2/cypress/support/commands.js @@ -26,10 +26,9 @@ // cy.task can only be used in support files (spec files or commands file) -import { - getValueByKey, - isoTimeTomorrow, -} from "../e2e/configs/Payment/Utils.js"; +import { nanoid } from "nanoid"; +import { getValueByKey } from "../e2e/configs/Payment/Utils.js"; +import { isoTimeTomorrow, validateEnv } from "../utils/RequestBodyUtils.js"; function logRequestId(xRequestId) { if (xRequestId) { @@ -48,6 +47,9 @@ Cypress.Commands.add( const base_url = globalState.get("baseUrl"); const url = `${base_url}/v2/organization`; + // Update request body + organizationCreateBody.organization_name += " " + nanoid(); + cy.request({ method: "POST", url: url, @@ -71,7 +73,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Organization create call failed with status ${response.status} and message ${response.body.message}` + `Organization create call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -111,7 +113,7 @@ Cypress.Commands.add("organizationRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Organization retrieve call failed with status ${response.status} and message ${response.body.message}` + `Organization retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -125,6 +127,9 @@ Cypress.Commands.add( const organization_id = globalState.get("organizationId"); const url = `${base_url}/v2/organization/${organization_id}`; + // Update request body + organizationUpdateBody.organization_name += " " + nanoid(); + cy.request({ method: "PUT", url: url, @@ -152,7 +157,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Organization update call failed with status ${response.status} and message ${response.body.message}` + `Organization update call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -166,6 +171,8 @@ Cypress.Commands.add( // Define the necessary variables and constants const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "publishable_key"; + const key_id = validateEnv(base_url, key_id_type); const organization_id = globalState.get("organizationId"); const url = `${base_url}/v2/merchant_accounts`; @@ -192,14 +199,9 @@ Cypress.Commands.add( .and.to.include(`${merchant_name}_`) .and.to.be.a("string").and.not.be.empty; - if (base_url.includes("sandbox") || base_url.includes("integ")) - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_snd").and.to.not.be.empty; - else if (base_url.includes("localhost")) - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_dev").and.to.not.be.empty; + expect(response.body) + .to.have.property(key_id_type) + .and.to.include(key_id).and.to.not.be.empty; globalState.set("merchantId", response.body.id); globalState.set("publishableKey", response.body.publishable_key); @@ -208,7 +210,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Merchant create call failed with status ${response.status} and message ${response.body.message}` + `Merchant create call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -218,6 +220,8 @@ Cypress.Commands.add("merchantAccountRetrieveCall", (globalState) => { // Define the necessary variables and constants const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "publishable_key"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const url = `${base_url}/v2/merchant_accounts/${merchant_id}`; @@ -236,14 +240,8 @@ Cypress.Commands.add("merchantAccountRetrieveCall", (globalState) => { expect(response.body).to.have.property("id").and.to.be.a("string").and.not .be.empty; - if (base_url.includes("sandbox") || base_url.includes("integ")) - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_snd").and.to.not.be.empty; - else - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_dev").and.to.not.be.empty; + expect(response.body).to.have.property(key_id_type).and.to.include(key_id) + .and.to.not.be.empty; if (merchant_id === undefined || merchant_id === null) { globalState.set("merchantId", response.body.id); @@ -253,7 +251,7 @@ Cypress.Commands.add("merchantAccountRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Merchant account retrieve call failed with status ${response.status} and message ${response.body.message}` + `Merchant account retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -264,6 +262,8 @@ Cypress.Commands.add( // Define the necessary variables and constants const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "publishable_key"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const url = `${base_url}/v2/merchant_accounts/${merchant_id}`; @@ -284,14 +284,10 @@ Cypress.Commands.add( if (response.status === 200) { expect(response.body.id).to.equal(merchant_id); - if (base_url.includes("sandbox") || base_url.includes("integ")) - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_snd").and.to.not.be.empty; - else - expect(response.body) - .to.have.property("publishable_key") - .and.to.include("pk_dev").and.to.not.be.empty; + expect(response.body) + .to.have.property(key_id_type) + .and.to.include(key_id).and.to.not.be.empty; + expect(response.body.merchant_name).to.equal(merchant_name); if (merchant_id === undefined || merchant_id === null) { @@ -302,7 +298,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Merchant account update call failed with status ${response.status} and message ${response.body.message}` + `Merchant account update call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -349,7 +345,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Business profile create call failed with status ${response.status} and message ${response.body.message}` + `Business profile create call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -390,7 +386,7 @@ Cypress.Commands.add("businessProfileRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Business profile retrieve call failed with status ${response.status} and message ${response.body.message}` + `Business profile retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -436,7 +432,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Business profile update call failed with status ${response.status} and message ${response.body.message}` + `Business profile update call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -494,8 +490,8 @@ Cypress.Commands.add( authDetails.connector_account_details; if (authDetails && authDetails.metadata) { - createConnectorBody.metadata = { - ...createConnectorBody.metadata, // Preserve existing metadata fields + mcaCreateBody.metadata = { + ...mcaCreateBody.metadata, // Preserve existing metadata fields ...authDetails.metadata, // Merge with authDetails.metadata }; } @@ -525,7 +521,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Merchant connector account create call failed with status ${response.status} and message ${response.body.message}` + `Merchant connector account create call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -573,7 +569,7 @@ Cypress.Commands.add("mcaRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Merchant connector account retrieve call failed with status ${response.status} and message ${response.body.message}` + `Merchant connector account retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -638,7 +634,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Merchant connector account update call failed with status ${response.status} and message ${response.body.message}` + `Merchant connector account update call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -654,6 +650,8 @@ Cypress.Commands.add("apiKeyCreateCall", (apiKeyCreateBody, globalState) => { // We do not want to keep API Key forever, // so we set the expiry to tomorrow as new merchant accounts are created with every run const expiry = isoTimeTomorrow(); + const key_id_type = "key_id"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const url = `${base_url}/v2/api_keys`; @@ -682,13 +680,8 @@ Cypress.Commands.add("apiKeyCreateCall", (apiKeyCreateBody, globalState) => { expect(response.body.description).to.equal(apiKeyCreateBody.description); // API Key assertions are intentionally excluded to avoid being exposed in the logs - if (base_url.includes("sandbox") || base_url.includes("integ")) { - expect(response.body).to.have.property("key_id").and.to.include("snd_") - .and.to.not.be.empty; - } else if (base_url.includes("localhost")) { - expect(response.body).to.have.property("key_id").and.to.include("dev_") - .and.to.not.be.empty; - } + expect(response.body).to.have.property(key_id_type).and.to.include(key_id) + .and.to.not.be.empty; globalState.set("apiKeyId", response.body.key_id); globalState.set("apiKey", response.body.api_key); @@ -697,7 +690,7 @@ Cypress.Commands.add("apiKeyCreateCall", (apiKeyCreateBody, globalState) => { } else { // to be updated throw new Error( - `API Key create call failed with status ${response.status} and message ${response.body.message}` + `API Key create call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -706,6 +699,8 @@ Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => { // Define the necessary variables and constant const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "key_id"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const api_key_id = globalState.get("apiKeyId"); const url = `${base_url}/v2/api_keys/${api_key_id}`; @@ -728,15 +723,9 @@ Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => { if (response.status === 200) { expect(response.body.merchant_id).to.equal(merchant_id); - // API Key assertions are intentionally excluded to avoid being exposed in the logs - if (base_url.includes("sandbox") || base_url.includes("integ")) { - expect(response.body).to.have.property("key_id").and.to.include("snd_") - .and.to.not.be.empty; - } else if (base_url.includes("localhost")) { - expect(response.body).to.have.property("key_id").and.to.include("dev_") - .and.to.not.be.empty; - } + expect(response.body).to.have.property(key_id_type).and.to.include(key_id) + .and.to.not.be.empty; if (api_key === undefined || api_key === null) { globalState.set("apiKey", response.body.api_key); @@ -745,7 +734,7 @@ Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `API Key retrieve call failed with status ${response.status} and message ${response.body.message}` + `API Key retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -758,6 +747,8 @@ Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => { // We do not want to keep API Key forever, // so we set the expiry to tomorrow as new merchant accounts are created with every run const expiry = isoTimeTomorrow(); + const key_id_type = "key_id"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const url = `${base_url}/v2/api_keys/${api_key_id}`; @@ -786,13 +777,8 @@ Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => { expect(response.body.description).to.equal(apiKeyUpdateBody.description); // API Key assertions are intentionally excluded to avoid being exposed in the logs - if (base_url.includes("sandbox") || base_url.includes("integ")) { - expect(response.body).to.have.property("key_id").and.to.include("snd_") - .and.to.not.be.empty; - } else if (base_url.includes("localhost")) { - expect(response.body).to.have.property("key_id").and.to.include("dev_") - .and.to.not.be.empty; - } + expect(response.body).to.have.property(key_id_type).and.to.include(key_id) + .and.to.not.be.empty; if (api_key === undefined || api_key === null) { globalState.set("apiKey", response.body.api_key); @@ -801,7 +787,7 @@ Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => { } else { // to be updated throw new Error( - `API Key update call failed with status ${response.status} and message ${response.body.message}` + `API Key update call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -847,7 +833,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Routing algorithm setup call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm setup call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -886,7 +872,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Routing algorithm activation call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm activation call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -925,7 +911,7 @@ Cypress.Commands.add("routingActivationRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm activation retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -960,7 +946,7 @@ Cypress.Commands.add("routingDeactivateCall", (globalState) => { } else { // to be updated throw new Error( - `Routing algorithm deactivation call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm deactivation call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -997,7 +983,7 @@ Cypress.Commands.add("routingRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm activation retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1016,7 +1002,7 @@ Cypress.Commands.add( routingDefaultFallbackBody = payload; cy.request({ - method: "POST", + method: "PATCH", url: url, headers: { Authorization: `Bearer ${api_key}`, @@ -1032,7 +1018,7 @@ Cypress.Commands.add( } else { // to be updated throw new Error( - `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm activation retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1061,7 +1047,7 @@ Cypress.Commands.add("routingFallbackRetrieveCall", (globalState) => { } else { // to be updated throw new Error( - `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + `Routing algorithm activation retrieve call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1100,7 +1086,7 @@ Cypress.Commands.add("userLogin", (globalState) => { } else { // to be updated throw new Error( - `User login call failed to get totp token with status ${response.status} and message ${response.body.message}` + `User login call failed to get totp token with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1133,7 +1119,7 @@ Cypress.Commands.add("terminate2Fa", (globalState) => { } else { // to be updated throw new Error( - `2FA terminate call failed with status ${response.status} and message ${response.body.message}` + `2FA terminate call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1166,7 +1152,7 @@ Cypress.Commands.add("userInfo", (globalState) => { } else { // to be updated throw new Error( - `User login call failed to fetch user info with status ${response.status} and message ${response.body.message}` + `User login call failed to fetch user info with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1177,6 +1163,8 @@ Cypress.Commands.add("merchantAccountsListCall", (globalState) => { // Define the necessary variables and constants const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "publishable_key"; + const key_id = validateEnv(base_url, key_id_type); const organization_id = globalState.get("organizationId"); const url = `${base_url}/v2/organization/${organization_id}/merchant_accounts`; @@ -1198,21 +1186,15 @@ Cypress.Commands.add("merchantAccountsListCall", (globalState) => { expect(response.body[key]) .to.have.property("organization_id") .and.to.equal(organization_id); - if (base_url.includes("integ") || base_url.includes("sandbox")) { - expect(response.body[key]) - .to.have.property("publishable_key") - .and.include("pk_snd_").and.to.not.be.empty; - } else if (base_url.includes("localhost")) { - expect(response.body[key]) - .to.have.property("publishable_key") - .and.include("pk_dev_").and.to.not.be.empty; - } + expect(response.body[key]) + .to.have.property(key_id_type) + .and.include(key_id).and.to.not.be.empty; expect(response.body[key]).to.have.property("id").and.to.not.be.empty; } } else { // to be updated throw new Error( - `Merchant accounts list call failed with status ${response.status} and message ${response.body.message}` + `Merchant accounts list call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1253,7 +1235,7 @@ Cypress.Commands.add("businessProfilesListCall", (globalState) => { } else { // to be updated throw new Error( - `Business profiles list call failed with status ${response.status} and message ${response.body.message}` + `Business profiles list call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1314,7 +1296,7 @@ Cypress.Commands.add("mcaListCall", (globalState, service_type) => { } else { // to be updated throw new Error( - `Merchant connector account list call failed with status ${response.status} and message ${response.body.message}` + `Merchant connector account list call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); @@ -1323,6 +1305,8 @@ Cypress.Commands.add("apiKeysListCall", (globalState) => { // Define the necessary variables and constants const api_key = globalState.get("adminApiKey"); const base_url = globalState.get("baseUrl"); + const key_id_type = "key_id"; + const key_id = validateEnv(base_url, key_id_type); const merchant_id = globalState.get("merchantId"); const url = `${base_url}/v2/api_keys/list`; @@ -1347,8 +1331,8 @@ Cypress.Commands.add("apiKeysListCall", (globalState) => { expect(response.body).to.be.an("array").and.to.not.be.empty; for (const key in response.body) { expect(response.body[key]) - .to.have.property("key_id") - .and.to.include("dev_").and.to.not.be.empty; + .to.have.property(key_id_type) + .and.to.include(key_id).and.to.not.be.empty; expect(response.body[key]) .to.have.property("merchant_id") .and.to.equal(merchant_id).and.to.not.be.empty; @@ -1356,13 +1340,74 @@ Cypress.Commands.add("apiKeysListCall", (globalState) => { } else { // to be updated throw new Error( - `API Keys list call failed with status ${response.status} and message ${response.body.message}` + `API Keys list call failed with status ${response.status} and message: "${response.body.error.message}"` ); } }); }); -// templates +// Payment API calls +// Update the below commands while following the conventions +// Below is an example of how the payment intent create call should look like (update the below command as per the need) +Cypress.Commands.add( + "paymentIntentCreateCall", + ( + globalState, + paymentRequestBody, + paymentResponseBody + /* Add more variables based on the need*/ + ) => { + // Define the necessary variables and constants at the top + // Also construct the URL here + const api_key = globalState.get("apiKey"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/payments/create-intent`; + + // Update request body if needed + paymentRequestBody = {}; + + // Pass Custom Headers + const customHeaders = { + "x-profile-id": profile_id, + }; + + cy.request({ + method: "POST", + url: url, + headers: { + "api-key": api_key, + "Content-Type": "application/json", + ...customHeaders, + }, + body: paymentRequestBody, + failOnStatusCode: false, + }).then((response) => { + // Logging x-request-id is mandatory + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + // Update the assertions based on the need + expect(response.body).to.deep.equal(paymentResponseBody); + } else if (response.status === 400) { + // Add 4xx validations here + expect(response.body).to.deep.equal(paymentResponseBody); + } else if (response.status === 500) { + // Add 5xx validations here + expect(response.body).to.deep.equal(paymentResponseBody); + } else { + // If status code is other than the ones mentioned above, default should be thrown + throw new Error( + `Payment intent create call failed with status ${response.status} and message: "${response.body.error.message}"` + ); + } + }); + } +); +Cypress.Commands.add("paymentIntentConfirmCall", (globalState) => {}); +Cypress.Commands.add("paymentIntentRetrieveCall", (globalState) => {}); + +// templates for future use Cypress.Commands.add("", () => { cy.request({}).then((response) => {}); }); diff --git a/cypress-tests-v2/cypress/utils/RequestBodyUtils.js b/cypress-tests-v2/cypress/utils/RequestBodyUtils.js new file mode 100644 index 000000000000..0926cbd97bfb --- /dev/null +++ b/cypress-tests-v2/cypress/utils/RequestBodyUtils.js @@ -0,0 +1,48 @@ +const keyPrefixes = { + localhost: { + publishable_key: "pk_dev_", + key_id: "dev_", + }, + integ: { + publishable_key: "pk_snd_", + key_id: "snd_", + }, + sandbox: { + publishable_key: "pk_snd_", + key_id: "snd_", + }, +}; + +export function isoTimeTomorrow() { + const now = new Date(); + + // Create a new date object for tomorrow + const tomorrow = new Date(now); + tomorrow.setDate(now.getDate() + 1); + + // Convert to ISO string format + const isoStringTomorrow = tomorrow.toISOString(); + return isoStringTomorrow; +} + +export function validateEnv(baseUrl, keyIdType) { + if (!baseUrl) { + throw new Error("Please provide a baseUrl"); + } + + const environment = Object.keys(keyPrefixes).find((env) => + baseUrl.includes(env) + ); + + if (!environment) { + throw new Error("Unsupported baseUrl"); + } + + const prefix = keyPrefixes[environment][keyIdType]; + + if (!prefix) { + throw new Error(`Unsupported keyIdType: ${keyIdType}`); + } + + return prefix; +} diff --git a/cypress-tests-v2/package-lock.json b/cypress-tests-v2/package-lock.json index d9282b8d3ce9..36f801468a9f 100644 --- a/cypress-tests-v2/package-lock.json +++ b/cypress-tests-v2/package-lock.json @@ -10,9 +10,10 @@ "license": "ISC", "devDependencies": { "@types/fs-extra": "^11.0.4", - "cypress": "^13.14.2", + "cypress": "^13.15.2", "cypress-mochawesome-reporter": "^3.8.2", "jsqr": "^1.4.0", + "nanoid": "^5.0.8", "prettier": "^3.3.2" } }, @@ -28,9 +29,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", - "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.6.tgz", + "integrity": "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -49,7 +50,7 @@ "performance-now": "^2.1.0", "qs": "6.13.0", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -567,9 +568,9 @@ } }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", "dev": true, "funding": [ { @@ -741,14 +742,14 @@ } }, "node_modules/cypress": { - "version": "13.14.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.2.tgz", - "integrity": "sha512-lsiQrN17vHMB2fnvxIrKLAjOr9bPwsNbPZNrWf99s4u+DVmCY6U+w7O3GGG9FvP4EUVYaDu+guWeNLiUzBrqvA==", + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.2.tgz", + "integrity": "sha512-ARbnUorjcCM3XiPwgHKuqsyr5W9Qn+pIIBPaoilnoBkLdSC2oLQjV1BUpnmc7KR+b7Avah3Ly2RMFnfxr96E/A==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.1", + "@cypress/request": "^3.0.6", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -759,6 +760,7 @@ "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", + "ci-info": "^4.0.0", "cli-cursor": "^3.1.0", "cli-table3": "~0.6.1", "commander": "^6.2.1", @@ -773,7 +775,6 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -788,6 +789,7 @@ "semver": "^7.5.3", "supports-color": "^8.1.1", "tmp": "~0.2.3", + "tree-kill": "1.2.2", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -1203,9 +1205,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "license": "MIT", "dependencies": { @@ -1583,19 +1585,6 @@ "node": ">=8" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2457,6 +2446,25 @@ "dev": true, "license": "MIT" }, + "node_modules/nanoid": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.8.tgz", + "integrity": "sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2733,13 +2741,6 @@ "dev": true, "license": "MIT" }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "license": "MIT" - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -2751,16 +2752,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -2777,13 +2768,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2843,13 +2827,6 @@ "dev": true, "license": "ISC" }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -3150,6 +3127,26 @@ "dev": true, "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.59", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.59.tgz", + "integrity": "sha512-472ilPxsRuqBBpn+KuRBHJvZhk6tTo4yTVsmODrLBNLwRYJPkDfMEHivgNwp5iEl+cbrZzzRtLKRxZs7+QKkRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.59" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.59", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.59.tgz", + "integrity": "sha512-EiYgNf275AQyVORl8HQYYe7rTVnmLb4hkWK7wAk/12Ksy5EiHpmUmTICa4GojookBPC8qkLMBKKwCmzNA47ZPQ==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -3175,29 +3172,26 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" + "node": ">=16" } }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "bin": { + "tree-kill": "cli.js" } }, "node_modules/tslib": { @@ -3267,17 +3261,6 @@ "node": ">=8" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/cypress-tests-v2/package.json b/cypress-tests-v2/package.json index e06c1a63be20..20403f9e7774 100644 --- a/cypress-tests-v2/package.json +++ b/cypress-tests-v2/package.json @@ -15,9 +15,10 @@ "license": "ISC", "devDependencies": { "@types/fs-extra": "^11.0.4", - "cypress": "^13.14.2", + "cypress": "^13.15.2", "cypress-mochawesome-reporter": "^3.8.2", "jsqr": "^1.4.0", - "prettier": "^3.3.2" + "prettier": "^3.3.2", + "nanoid": "^5.0.8" } }