Skip to content

Commit

Permalink
add ui tests for add / update / remove card (#1843)
Browse files Browse the repository at this point in the history
  • Loading branch information
asnaith authored Jan 19, 2022
1 parent bd91921 commit 7cb726c
Show file tree
Hide file tree
Showing 19 changed files with 266 additions and 19 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test-files-on-demand.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
REACT_APP_FILES_VERIFIER_NAME: ${{ secrets.GH_REACT_APP_FILES_VERIFIER_NAME }}
REACT_APP_FILES_UUID_VERIFIER_NAME: 'chainsafe-uuid-testnet'
REACT_APP_TEST: 'true'
REACT_APP_STRIPE_PK: ${{secrets.GH_REACT_APP_STRIPE_PK}}
DEBUG: '@cypress/github-action'
with:
start: yarn start:files-ui
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
REACT_APP_FILES_VERIFIER_NAME: ${{ secrets.GH_REACT_APP_FILES_VERIFIER_NAME }}
REACT_APP_FILES_UUID_VERIFIER_NAME: 'chainsafe-uuid-testnet'
REACT_APP_TEST: 'true'
REACT_APP_STRIPE_PK: ${{secrets.GH_REACT_APP_STRIPE_PK}}
DEBUG: '@cypress/github-action'
with:
start: yarn start:files-ui
Expand Down
3 changes: 2 additions & 1 deletion packages/files-ui/cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"retries": {
"runMode": 2,
"openMode": 0
}
},
"chromeWebSecurity": false
}
6 changes: 6 additions & 0 deletions packages/files-ui/cypress/fixtures/cardData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const visaNumber = "4242424242424242"
export const visaCvc = "123"
export const visaExpiry = "12/30"
export const mastercardNumber = "5555555555554444"
export const mastercardCvc = "456"
export const mastercardExpiry = "01/31"
38 changes: 37 additions & 1 deletion packages/files-ui/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Web3LoginOptions {
clearTrashBucket?: boolean
deleteShareBucket?: boolean
withNewUser?: boolean
deleteCreditCard? : boolean
}

Cypress.Commands.add(
Expand All @@ -57,7 +58,8 @@ Cypress.Commands.add(
clearCSFBucket = false,
clearTrashBucket = false,
deleteShareBucket = false,
withNewUser = true
withNewUser = true,
deleteCreditCard = false
}: Web3LoginOptions = {}) => {

cy.on("window:before:load", (win) => {
Expand Down Expand Up @@ -124,6 +126,10 @@ Cypress.Commands.add(
apiTestHelper.clearBucket("trash")
}

if (deleteCreditCard) {
apiTestHelper.deleteCreditCards()
}

if(clearTrashBucket || clearCSFBucket || deleteShareBucket){
navigationMenu.binNavButton().click()
navigationMenu.homeNavButton().click()
Expand All @@ -135,6 +141,7 @@ Cypress.Commands.add(

Cypress.Commands.add("safeClick", { prevSubject: "element" }, ($element?: JQuery<HTMLElement>) => {
const click = ($el: JQuery<HTMLElement>) => $el.trigger("click")

return cy
.wrap($element)
.should("be.visible")
Expand All @@ -143,6 +150,31 @@ Cypress.Commands.add("safeClick", { prevSubject: "element" }, ($element?: JQuery
.should($el => expect($el).to.not.be.visible)
})

Cypress.Commands.add("iframeLoaded", { prevSubject: "element" }, ($iframe?: JQuery<HTMLElement>): any => {
const contentWindow = $iframe?.prop("contentWindow")
return new Promise(resolve => {
if (
contentWindow &&
contentWindow.document.readyState === "complete"
) {
resolve(contentWindow)
} else {
$iframe?.on("load", () => {
resolve(contentWindow)
})
}
})
})

Cypress.Commands.add("getInDocument", { prevSubject: "document" }, (document: any, selector: keyof HTMLElementTagNameMap) =>
Cypress.$(selector, document))

Cypress.Commands.add("getWithinIframe", (targetElement: any, selector: string) =>
cy.get(selector || "iframe", { timeout: 10000 })
.iframeLoaded()
.its("document")
.getInDocument(targetElement))

// Must be declared global to be detected by typescript (allows import/export)
// eslint-disable @typescript/interface-name
declare global {
Expand All @@ -156,6 +188,7 @@ declare global {
* @param {Boolean} options.clearTrashBucket - (default: false) - whether any file in the trash bucket should be deleted.
* @param {Boolean} options.deleteShareBucket - (default: false) - whether any shared bucket should be deleted.
* @param {Boolean} options.withNewUser - (default: true) - whether to create a new user for this session.
* @param {Boolean} options.deleteCreditCard - (default: false) - whether to delete the default credit card associate to the account.
* @example cy.web3Login({saveBrowser: true, url: 'http://localhost:8080'})
*/
web3Login: (options?: Web3LoginOptions) => void
Expand All @@ -180,6 +213,9 @@ declare global {
* @example cy.clearBucket("csf")
*/
clearBucket: (bucketType: ClearBucketType) => void
iframeLoaded: ($iframe?: JQuery<HTMLElement>) => any
getInDocument: (document: any, selector: keyof HTMLElementTagNameMap) => JQuery<HTMLElement>
getWithinIframe: (targetElement: string, selector: string) => Chainable
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const addOrUpdateCardModal = {
body: () => cy.get("[data-testid=modal-container-add-or-update-card]", { timeout: 10000 }),
addCardButton: () => cy.get("[data-testid=button-add-card]", { timeout: 10000 }),
cancelButton: () => cy.get("[data-cy=button-cancel-add-card]"),
cardErrorLabel: () => cy.get("[data-cy=label-add-card-error]"),
cardNumberInput: () => cy.getWithinIframe("[data-elements-stable-field-name=cardNumber]", "#iframe-card-number iframe"),
cvcNumberInput: () => cy.getWithinIframe("[data-elements-stable-field-name=cardCvc]", "#iframe-card-cvc iframe"),
expiryDateInput: () => cy.getWithinIframe("[data-elements-stable-field-name=cardExpiry]", "#iframe-card-expiry iframe"),
addCardHeader: () => cy.get("[data-cy=header-add-card]"),
updateCardHeader: () => cy.get("[data-cy=header-update-card]"),
updateCardButton: () => cy.get("[data-testid=button-update-card]", { timeout: 10000 }),

awaitStripeElementReady() {
// this waits for all of the posts from stripe to ensure the element is ready event is received
// by waiting for these to complete we can ensure the elements will be ready for interaction
cy.intercept("POST", "**/r.stripe.com/*").as("stripeElementActivation")
cy.wait("@stripeElementActivation")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const removeCardModal = {
body: () => cy.get("[data-testid=modal-container-remove-card-confirmation]"),
cancelButton: () => cy.get("[data-testid=button-cancel-remove]"),
confirmButton: () => cy.get("[data-testid=button-confirm-remove]", { timeout: 10000 })
}

27 changes: 26 additions & 1 deletion packages/files-ui/cypress/support/page-objects/settingsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { basePage } from "./basePage"

export const settingsPage = {
...basePage,

// profile tab
profileTabButton: () => cy.get("[data-testid=tab-profile]"),
profileTabHeader: () => cy.get("[data-cy=label-profile-header]"),
firstNameInput: () => cy.get("[data-cy=input-profile-firstname]"),
Expand All @@ -12,7 +14,30 @@ export const settingsPage = {
usernameErrorLabel: () => cy.get("[data-cy=input-profile-username] span.error"),
setUsernameButton: () => cy.get("[data-cy=button-set-username]"),
usernamePresentInput: () => cy.get("[data-cy=input-profile-username-present]"),

// security tab
securityTabButton: () => cy.get("[data-testid=tab-security]"),
securityTabHeader: () => cy.get("[data-cy=label-security-header]"),
subscriptionTabButton: () => cy.get("[data-testid=tab-subscription]")

// subscription tab
subscriptionTabButton: () => cy.get("[data-testid=tab-subscription]"),
addCardButton: () => cy.get("[data-testid=button-add-a-card]"),
updateCardButton: () => cy.get("[data-testid=button-update-a-card]"),
defaultCardLabel: () => cy.get("[data-cy=label-default-card]"),
noCardLabel: () => cy.get("[data-cy=label-no-card]"),
removeCardLink: () => cy.get("[data-cy=link-remove-card]"),

// helpers
awaitStripeConfirmation() {
cy.intercept("POST", "**/setup_intents/*/confirm").as("stripeConfirmation")
cy.wait("@stripeConfirmation")
},

awaitDefaultCardRequest() {
cy.intercept("GET", "**/billing/cards/default").as("defaultCard")

cy.wait("@defaultCard").its("response.body").should("contain", {
type: "credit"
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const cardAddedToast = {
body: () => cy.get("[data-testid=toast-card-added]", { timeout: 10000 }),
closeButton: () => cy.get("[data-testid=button-close-toast-card-added]")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const cardUpdatedToast = {
body: () => cy.get("[data-testid=toast-card-updated]", { timeout: 10000 }),
closeButton: () => cy.get("[data-testid=button-close-toast-card-updated]")
}
20 changes: 20 additions & 0 deletions packages/files-ui/cypress/support/utils/apiTestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ export const apiTestHelper = {
})
})
},
deleteCreditCards() {
const apiClient = getApiClient()

return new Cypress.Promise(async (resolve) => {
cy.window()
.then(async (win) => {
const tokens = await apiClient.getRefreshToken({ refresh: win.sessionStorage.getItem(REFRESH_TOKEN_KEY) || "" })

await apiClient.setToken(tokens.access_token.token)
try {
const card = await apiClient.getDefaultCard()
apiClient.deleteCard(card.id)
} catch {
cy.log("There's no card to delete")
}
cy.log("Done deleting the default card")
resolve()
})
})
},
clearBucket(bucketType: ClearBucketType) {
const apiClient = getApiClient()

Expand Down
2 changes: 2 additions & 0 deletions packages/files-ui/cypress/tests/file-management-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ describe("File management", () => {

// upload a file and ensure the storage space label adjusts
homePage.uploadFile("../fixtures/uploadedFiles/logo.png")
navigationMenu.spaceUsedProgressBar().should("be.visible")
navigationMenu.spaceUsedLabel().should("not.contain.text", "0 Bytes")

// delete the file from the bin and ensure the storage space label adjusts
Expand All @@ -407,6 +408,7 @@ describe("File management", () => {
deleteFileModal.confirmButton().safeClick()
deleteSuccessToast.body().should("be.visible")
deleteSuccessToast.closeButton().click()
navigationMenu.spaceUsedProgressBar().should("be.visible")
navigationMenu.spaceUsedLabel().should("contain.text", "0 Bytes")
})

Expand Down
32 changes: 24 additions & 8 deletions packages/files-ui/cypress/tests/main-navigation-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ describe("Main Navigation", () => {
})

it("can navigate to the bin page", () => {
navigationMenu.binNavButton().click()
navigationMenu.binNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/bin")
})

it("can navigate to the settings page", () => {
navigationMenu.settingsNavButton().click()
navigationMenu.settingsNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/settings")
})

Expand All @@ -24,7 +28,9 @@ describe("Main Navigation", () => {
})

it("can navigate to the home page", () => {
navigationMenu.homeNavButton().click()
navigationMenu.homeNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/drive")
})
})
Expand All @@ -36,26 +42,36 @@ describe("Main Navigation", () => {

beforeEach(() => {
cy.viewport("iphone-6")
homePage.hamburgerMenuButton().click()
homePage.hamburgerMenuButton()
.should("be.visible")
.click()
})

it("can navigate to the bin page", () => {
navigationMenu.binNavButton().click()
navigationMenu.binNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/bin")
})

it("can navigate to the settings page", () => {
navigationMenu.settingsNavButton().click()
navigationMenu.settingsNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/settings")
})

it("can navigate to the home page", () => {
navigationMenu.homeNavButton().click()
navigationMenu.homeNavButton()
.should("be.visible")
.click()
cy.url().should("include", "/drive")
})

it("can sign out from the navigation bar", () => {
navigationMenu.signOutButton().click()
navigationMenu.signOutButton()
.should("be.visible")
.click()
authenticationPage.web3Button().should("be.visible")
cy.url().should("not.include", "/drive")
cy.url().should("not.include", "/bin")
Expand Down
81 changes: 81 additions & 0 deletions packages/files-ui/cypress/tests/subscription-plan-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { navigationMenu } from "../support/page-objects/navigationMenu"
import { settingsPage } from "../support/page-objects/settingsPage"
import { addOrUpdateCardModal } from "../support/page-objects/modals/addCardModal"
import { visaNumber, visaExpiry, visaCvc } from "../fixtures/cardData"
import { mastercardNumber, mastercardExpiry, mastercardCvc } from "../fixtures/cardData"
import { cardAddedToast } from "../support/page-objects/toasts/cardAddedToast"
import { cardUpdatedToast } from "../support/page-objects/toasts/cardUpdatedToast"
import { removeCardModal } from "../support/page-objects/modals/removeCardModal"

describe("Subscription Plan", () => {

context("desktop", () => {

it("can add, update and remove a credit card for billing", () => {
cy.web3Login({ deleteCreditCard: true })

// navigate to settings
navigationMenu.settingsNavButton().click()
settingsPage.subscriptionTabButton().click()

// add a card
settingsPage.addCardButton()
.should("be.visible")
.click()
addOrUpdateCardModal.body().should("be.visible")
addOrUpdateCardModal.addCardHeader().should("be.visible")
addOrUpdateCardModal.awaitStripeElementReady()
addOrUpdateCardModal.cardNumberInput().type(visaNumber)
addOrUpdateCardModal.expiryDateInput().type(visaExpiry)
addOrUpdateCardModal.cvcNumberInput().type(visaCvc)
addOrUpdateCardModal.addCardButton().click()

// for reliability wait for stripe and default card responses / requests
settingsPage.awaitStripeConfirmation()
settingsPage.awaitDefaultCardRequest()

// close toast and ensure a card is now shown on profile
cardAddedToast.body().should("be.visible")
cardAddedToast.closeButton().click()
settingsPage.updateCardButton().should("be.visible")
settingsPage.defaultCardLabel().should("be.visible")

// store the displayed visa details as cypress alias
settingsPage.defaultCardLabel().invoke("text").as("partialMaskedVisa")

// update the card
settingsPage.updateCardButton().click()
addOrUpdateCardModal.body().should("be.visible")
addOrUpdateCardModal.updateCardHeader().should("be.visible")
addOrUpdateCardModal.awaitStripeElementReady()
addOrUpdateCardModal.cardNumberInput().type(mastercardNumber)
addOrUpdateCardModal.expiryDateInput().type(mastercardExpiry)
addOrUpdateCardModal.cvcNumberInput().type(mastercardCvc)
addOrUpdateCardModal.updateCardButton().click()

// for reliability wait for stripe and default card responses / requests
settingsPage.awaitStripeConfirmation()
settingsPage.awaitDefaultCardRequest()

// close toast and ensure a card is shown on profile
cardUpdatedToast.body().should("be.visible")
cardUpdatedToast.closeButton().click()
settingsPage.defaultCardLabel().should("be.visible")

// store the displayed mastercard details as cypress alias
settingsPage.defaultCardLabel().invoke("text").as("partialMaskedMastercard")

// ensure the card number was updated by comparing cypress aliases
cy.get("@partialMaskedVisa").then(($partialMaskedVisa) => {
cy.get("@partialMaskedMastercard").should("not.equal", $partialMaskedVisa)
})

// remove the card
settingsPage.removeCardLink().click()
removeCardModal.body().should("be.visible")
removeCardModal.confirmButton().safeClick()
settingsPage.noCardLabel().should("be.visible")
settingsPage.addCardButton().should("be.visible")
})
})
})
Loading

0 comments on commit 7cb726c

Please sign in to comment.