Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Files API Client in Tests #1097

Merged
merged 9 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 131 additions & 107 deletions packages/files-ui/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import { ethers, Wallet } from "ethers"
import { testPrivateKey, testAccountPassword, localHost } from "../fixtures/loginData"
import { CustomizedBridge } from "./utils/CustomBridge"
import { FilesApiClient } from "@chainsafe/files-api-client"
import "cypress-file-upload"
import axios from "axios"

export type Storage = Record<string, string>[]
export type Storage = Record<string, string>[];

export interface Web3LoginOptions {
url?: string
Expand All @@ -45,28 +47,30 @@ const LOCAL_FILE = "cypress/fixtures/storage/localStorage.json"
const REFRESH_TOKEN_KEY = "csf.refreshToken"

Cypress.Commands.add("clearCsfBucket", (apiUrlBase: string) => {
const axiosInstance = axios.create({
// Disable the internal Axios JSON de serialization as this is handled by the client
transformResponse: []
})

const apiClient = new FilesApiClient({}, apiUrlBase, axiosInstance)

cy.window().then((win) => {
cy.request("POST", `${apiUrlBase}/user/refresh`, { "refresh": win.sessionStorage.getItem(REFRESH_TOKEN_KEY) })
.then((res) => res.body.access_token.token)
.then((accessToken) => {
cy.request({
method: "POST",
url: `${apiUrlBase}/files/ls`,
body: { "path": "/", "source": { "type": "csf" } },
auth: { "bearer": accessToken }
}).then((res) => {
const toDelete = res.body.map(({ name }: { name: string }) => `/${name}`)
cy.log(`clearCsfBucket - Deleting ${JSON.stringify(toDelete)}`)
cy.request({
method: "POST",
url: `${apiUrlBase}/files/rm`,
body: { "paths": toDelete, "source": { "type": "csf" } },
auth: { "bearer": accessToken }
}).then(res => {
if(!res.isOkStatusCode){
throw new Error(`unexpected answer when deleting files: ${JSON.stringify(res, null, 2)}`)
}
})
apiClient
.getRefreshToken({
refresh: win.sessionStorage.getItem(REFRESH_TOKEN_KEY) || ""
})
.then((tokens) => {
apiClient.setToken(tokens.access_token.token)
apiClient.listBuckets("csf").then((buckets) => {
apiClient
.getFPSChildList(buckets[0].id, { path: "/" })
.then((items) => {
const toDelete = items.map(
({ name }: { name: string }) => `/${name}`
)
cy.log(`clearCsfBucket - Deleting ${JSON.stringify(toDelete)}`)
apiClient.removeFPSObjects(buckets[0].id, { paths: toDelete }).catch()
})
})
})
})
Expand Down Expand Up @@ -94,117 +98,137 @@ Cypress.Commands.add("saveLocalAndSession", () => {
})
})

Cypress.Commands.add("web3Login", ({
saveBrowser = false,
url = localHost,
apiUrlBase = "https://stage.imploy.site/api/v1",
useLocalAndSessionStorage = true,
clearCSFBucket = false
}: Web3LoginOptions = {}) => {
let session: Storage = []
let local: Storage = []

cy.task<string | null>("readFileMaybe", SESSION_FILE)
.then((unparsedSession) => {
session = unparsedSession && JSON.parse(unparsedSession) || []
})

cy.task<string | null>("readFileMaybe", LOCAL_FILE)
.then((unparsedLocal) => {
local = unparsedLocal && JSON.parse(unparsedLocal) || []
})
Cypress.Commands.add(
"web3Login",
({
saveBrowser = false,
url = localHost,
apiUrlBase = "https://stage.imploy.site/api/v1",
useLocalAndSessionStorage = true,
clearCSFBucket = false
}: Web3LoginOptions = {}) => {
let session: Storage = []
let local: Storage = []

cy.task<string | null>("readFileMaybe", SESSION_FILE).then(
(unparsedSession) => {
session = (unparsedSession && JSON.parse(unparsedSession)) || []
}
)

cy.on("window:before:load", (win) => {
const provider = new ethers.providers.JsonRpcProvider("https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847", 4)
const signer = new Wallet(testPrivateKey, provider)
// inject ethereum object in the global window
Object.defineProperty(win, "ethereum", {
get: () => new CustomizedBridge(signer as any, provider as any)
})
cy.task<string | null>("readFileMaybe", LOCAL_FILE).then(
(unparsedLocal) => {
local = (unparsedLocal && JSON.parse(unparsedLocal)) || []
}
)

cy.on("window:before:load", (win) => {
const provider = new ethers.providers.JsonRpcProvider(
"https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847",
4
)
const signer = new Wallet(testPrivateKey, provider)
// inject ethereum object in the global window
Object.defineProperty(win, "ethereum", {
get: () => new CustomizedBridge(signer as any, provider as any)
})

// clear session storage in any case, if previous session storage should be
// kept will be decided after.
// Note that Cypress keep the session storage between test but clears localStorage
win.sessionStorage.clear()
win.localStorage.clear()
// clear session storage in any case, if previous session storage should be
// kept will be decided after.
// Note that Cypress keep the session storage between test but clears localStorage
win.sessionStorage.clear()
win.localStorage.clear()

if (useLocalAndSessionStorage) {
session.forEach(({ key, value }) => {
win.sessionStorage.setItem(key, value)
})
if (useLocalAndSessionStorage) {
session.forEach(({ key, value }) => {
win.sessionStorage.setItem(key, value)
})

local.forEach(({ key, value }) => {
win.localStorage.setItem(key, value)
})
}
})
local.forEach(({ key, value }) => {
win.localStorage.setItem(key, value)
})
}
})

cy.visit(url)

// with nothing in localstorage (and in session storage)
// the whole login flow should kick in
cy.then(() => {
cy.log("Logging in", local.length > 0 && "there is something in session storage ---> direct login")

if (local.length === 0) {
cy.log("nothing in session storage, --> click on web3 button")
cy.get("[data-cy=web3]").click()
cy.get(".bn-onboard-modal-select-wallets > :nth-child(1) > .bn-onboard-custom").click()
cy.get("[data-cy=sign-in-with-web3-button]").click()
cy.get("[data-cy=login-password-button]", { timeout: 20000 }).click()
cy.get("[data-cy=login-password-input]").type(`${testAccountPassword}{enter}`)

if (saveBrowser) {
// this is taking forever for test accounts
cy.get("[data-cy=save-browser-button]").click()
} else {
cy.get("[data-cy=do-not-save-browser-button]").click()
cy.visit(url)

// with nothing in localstorage (and in session storage)
// the whole login flow should kick in
cy.then(() => {
cy.log(
"Logging in",
local.length > 0 &&
"there is something in session storage ---> direct login"
)

if (local.length === 0) {
cy.log("nothing in session storage, --> click on web3 button")
cy.get("[data-cy=web3]").click()
cy.get(
".bn-onboard-modal-select-wallets > :nth-child(1) > .bn-onboard-custom"
).click()
cy.get("[data-cy=sign-in-with-web3-button]").click()
cy.get("[data-cy=login-password-button]", { timeout: 20000 }).click()
cy.get("[data-cy=login-password-input]").type(
`${testAccountPassword}{enter}`
)

if (saveBrowser) {
// this is taking forever for test accounts
cy.get("[data-cy=save-browser-button]").click()
} else {
cy.get("[data-cy=do-not-save-browser-button]").click()
}
}
}
})
})

cy.get("[data-cy=files-app-header]", { timeout: 20000 }).should("be.visible")
cy.get("[data-cy=files-app-header]", { timeout: 20000 }).should(
"be.visible"
)

cy.saveLocalAndSession()
cy.saveLocalAndSession()

if (clearCSFBucket) {
cy.clearCsfBucket(apiUrlBase)
cy.reload()
cy.get("[data-cy=files-app-header]", { timeout: 20000 }).should("be.visible")
if (clearCSFBucket) {
cy.clearCsfBucket(apiUrlBase)
cy.reload()
cy.get("[data-cy=files-app-header]", { timeout: 20000 }).should(
"be.visible"
)
}
}
})
)

// Must be declared global to be detected by typescript (allows import/export)
// eslint-disable @typescript/interface-name
declare global {
namespace Cypress {
interface Chainable {
/**
* Login using Metamask to an instance of Files.
* @param {String} options.url - (default: "http://localhost:3000") - what url to visit.
* @param {String} apiUrlBase - (default: "https://stage.imploy.site/api/v1") - what url to call for the api.
* @param {Boolean} options.saveBrowser - (default: false) - save the browser to localstorage.
* @param {Boolean} options.useLocalAndSessionStorage - (default: true) - use what could have been stored before to speedup login
* @param {Boolean} options.clearCSFBucket - (default: false) - whether any file in the csf bucket should be deleted.
* @example cy.web3Login({saveBrowser: true, url: 'http://localhost:8080'})
*/
* Login using Metamask to an instance of Files.
* @param {String} options.url - (default: "http://localhost:3000") - what url to visit.
* @param {String} apiUrlBase - (default: "https://stage.imploy.site/api/v1") - what url to call for the api.
* @param {Boolean} options.saveBrowser - (default: false) - save the browser to localstorage.
* @param {Boolean} options.useLocalAndSessionStorage - (default: true) - use what could have been stored before to speedup login
* @param {Boolean} options.clearCSFBucket - (default: false) - whether any file in the csf bucket should be deleted.
* @example cy.web3Login({saveBrowser: true, url: 'http://localhost:8080'})
*/
web3Login: (options?: Web3LoginOptions) => Chainable

/**
* Removed any file or folder at the root
* @param {String} apiUrlBase - what url to call for the api.
* @example cy.clearCsfBucket("https://stage.imploy.site/api/v1")
*/
* Removed any file or folder at the root
* @param {String} apiUrlBase - what url to call for the api.
* @example cy.clearCsfBucket("https://stage.imploy.site/api/v1")
*/
clearCsfBucket: (apiUrlBase: string) => Chainable

/**
* Save local and session storage to local files
* @example cy.saveLocalAndSession()
*/
* Save local and session storage to local files
* @example cy.saveLocalAndSession()
*/
saveLocalAndSession: () => Chainable
}
}
}

// Convert this to a module instead of script (allows import/export)
export { }
export {}
Loading