diff --git a/cypress/fixtures/routes.ts b/cypress/fixtures/routes.ts new file mode 100644 index 0000000000..1cb205964e --- /dev/null +++ b/cypress/fixtures/routes.ts @@ -0,0 +1,6 @@ +export default { + categories: 'https://dev.platformmarketplace.reapit.net/categories', + scopes: 'https://dev.platformmarketplace.reapit.net/scopes', + appsOfDeveloper: 'https://dev.platformmarketplace.reapit.net/apps?developerId=**&PageNumber=**&PageSize=**', + apps: 'https://dev.platformmarketplace.reapit.net/apps' +} diff --git a/cypress/integration/login-flow.test.ts b/cypress/integration/flow-login-happy-path.test.ts similarity index 74% rename from cypress/integration/login-flow.test.ts rename to cypress/integration/flow-login-happy-path.test.ts index 2ada8b014a..313828e325 100644 --- a/cypress/integration/login-flow.test.ts +++ b/cypress/integration/flow-login-happy-path.test.ts @@ -1,10 +1,10 @@ -import loginPage from '../pages/loginPage' -import clientAppsPage from '../pages/clientAppsPage' -import developerAppsPage from '../pages/developerAppsPage' -import adminApprovalsPage from '../pages/adminApprovalsPage' +import loginPage from '../pages/login-page' +import clientAppsPage from '../pages/client-apps-page' +import developerPage from '../pages/developer-page' +import adminApprovalsPage from '../pages/admin-approvals-page' -describe('Login flow', () => { - describe('works when testing the happycase flow', () => { +describe('Login happy path', () => { + describe('Should be able to login as client, developer and admin and navigate to correct landing pages', () => { const { selectors: { inputEmail, inputPassword, buttonLogin }, loginAsAdminUrl, @@ -20,7 +20,7 @@ describe('Login flow', () => { const { url: developerPageUrl, selectors: { container: developerPageContainer } - } = developerAppsPage + } = developerPage const { url: adminPageUrl, @@ -29,7 +29,7 @@ describe('Login flow', () => { const testCases = [ { - testCaseName: 'login successfully using DEVELOPER account', + testCaseName: 'Login successfully using DEVELOPER account', email: Cypress.env('DEVELOPER_ACCOUNT_EMAIL'), password: Cypress.env('DEVELOPER_ACCOUNT_PASSWORD'), container: developerPageContainer, @@ -37,7 +37,7 @@ describe('Login flow', () => { loginUrl: loginAsDeveloperUrl }, { - testCaseName: 'login successfully using CLIENT account', + testCaseName: 'Login successfully using CLIENT account', email: Cypress.env('CLIENT_ACCOUNT_EMAIL'), password: Cypress.env('CLIENT_ACCOUNT_PASSWORD'), container: clientAppsPageContainer, @@ -45,7 +45,7 @@ describe('Login flow', () => { loginUrl: loginAsClientUrl }, { - testCaseName: 'login successfully using ADMIN account', + testCaseName: 'Login successfully using ADMIN account', email: Cypress.env('ADMIN_ACCOUNT_EMAIL'), password: Cypress.env('ADMIN_ACCOUNT_PASSWORD'), container: adminPageContainer, diff --git a/cypress/integration/flow-submit-app-happy-path.test.ts b/cypress/integration/flow-submit-app-happy-path.test.ts new file mode 100644 index 0000000000..e07b404deb --- /dev/null +++ b/cypress/integration/flow-submit-app-happy-path.test.ts @@ -0,0 +1,94 @@ +import loginPage from '../pages/login-page' +import developerSubmitAppPage from '../pages/developer-submit-app-page' +import developerAppsPage from '../pages/developer-apps-page' +import appRequests from '../requests/app' +import routes from '../fixtures/routes' +import nanoid from 'nanoid' + +const appName = `E2E Test App -${nanoid()}` + +const { + actions: { loginUsingDeveloperAccount } +} = loginPage + +const { selectors: developerSubmitAppPageSelectors } = developerSubmitAppPage +const { checkBoxUserSession, buttonSubmit, selectCategory, submitSuccessSection } = developerSubmitAppPageSelectors + +describe('Submit app happy path', () => { + it('Log into dev and Submit an app successfully', () => { + loginUsingDeveloperAccount() + + cy.visit(developerSubmitAppPage.url) + cy.server() + + cy.route(routes.appsOfDeveloper).as('getAppsOfDeveloper') + cy.route(routes.categories).as('getCategories') + cy.route(routes.scopes).as('getScopes') + cy.route('POST', routes.apps).as('postSubmitApp') + + cy.wait('@getCategories') + cy.wait('@getScopes') + /** + * Both Object.keys and for in loop element is string + * string is not a key of developerSubmitAppPageSelectors or testData + * -> Have to cast to any + */ + const inputTestData: any = { + textBoxName: appName, + textBoxSupportEmail: 'submitAppHappyFlow@gmail.com', + textBoxTelephone: '01234567890', + textBoxHomePage: 'https://google.com', + textBoxLaunchUrl: 'https://google.com', + textAreaDescription: + 'Lorem ipsum dolor amet organic fashion axe man bun cray kitsch hashtag post-ironic normcore copper mug keytar fam actually street art air plant. Copper mug put a bird on it kombucha pop-up. Man bun kickstarter fam pour-over plaid, franzen blog. Activated charcoal letterpress mlkshk kickstarter master cleanse. Paleo austin actually blue bottle mixtape mustache bicycle rights gochujang humblebrag. Direct trade affogato cliche, asymmetrical sartorial pinterest chambray coloring book.', + textAreaSummary: + 'Lorem ipsum dolor amet messenger bag pinterest af umami. Master cleanse photo booth cardigan, jean shorts dreamcatcher butcher ethical YOLO.' + } + for (let inputTestDataSelector in inputTestData) { + const data = inputTestData[inputTestDataSelector] + const selector = (developerSubmitAppPageSelectors as any)[inputTestDataSelector] + cy.get(selector).type(data) + } + const fileUploadTestData: any = { + inputFileSubmitAppIcon: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==', + fileUploadScreenshot1: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8Xw8AAoMBgDTD2qgAAAAASUVORK5CYII=', + fileUploadScreenshot2: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HgAGgwJ/lK3Q6wAAAABJRU5ErkJggg==', + fileUploadScreenshot3: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk4PpfDwACpQGKFCvGMAAAAABJRU5ErkJggg==', + fileUploadScreenshot4: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOMK86uBwAEMAG9Q194bwAAAABJRU5ErkJggg==' + } + for (let fileUploadTestDataSelector in fileUploadTestData) { + const data = fileUploadTestData[fileUploadTestDataSelector] + const selector = (developerSubmitAppPageSelectors as any)[fileUploadTestDataSelector] + cy.get(selector).upload( + { + fileContent: data, + fileName: selector, + mimeType: 'image/jpeg', + encoding: 'binary' + }, + { subjectType: 'input' } + ) + } + cy.get(selectCategory).select('Game') + cy.get(checkBoxUserSession).click({ force: true }) + cy.get(buttonSubmit).click() + cy.wait(100) + cy.wait('@postSubmitApp') + cy.get(submitSuccessSection).should('have.length', 1) + + // Cleanup + cy.visit(developerAppsPage.url) + cy.wait('@getAppsOfDeveloper') + cy.get(`div[data-test-app-name='${appName}']`) + .should('have.length', 1) + .invoke('attr', 'data-test-app-id') + .then(appId => { + appRequests.deleteApp(appId as any) + }) + }) +}) diff --git a/cypress/pages/adminApprovalsPage.ts b/cypress/pages/admin-approvals-page.ts similarity index 54% rename from cypress/pages/adminApprovalsPage.ts rename to cypress/pages/admin-approvals-page.ts index b2cf36e221..6082daad65 100644 --- a/cypress/pages/adminApprovalsPage.ts +++ b/cypress/pages/admin-approvals-page.ts @@ -1,5 +1,7 @@ +import routes from '@/constants/routes' + export default { - url: '/admin/approvals', + url: routes.ADMIN_APPROVALS, selectors: { container: '#page-admin-approvals-container' } diff --git a/cypress/pages/clientAppsPage.ts b/cypress/pages/client-apps-page.ts similarity index 56% rename from cypress/pages/clientAppsPage.ts rename to cypress/pages/client-apps-page.ts index 0dcb1f9ec4..08c36533cf 100644 --- a/cypress/pages/clientAppsPage.ts +++ b/cypress/pages/client-apps-page.ts @@ -1,5 +1,7 @@ +import routes from '@/constants/routes' + export default { - url: '/client/apps', + url: routes.CLIENT, selectors: { container: '#page-client-apps-container' } diff --git a/cypress/pages/developerAppsPage.ts b/cypress/pages/developer-apps-page.ts similarity index 53% rename from cypress/pages/developerAppsPage.ts rename to cypress/pages/developer-apps-page.ts index ec027ed6bc..d2b34b734a 100644 --- a/cypress/pages/developerAppsPage.ts +++ b/cypress/pages/developer-apps-page.ts @@ -1,5 +1,7 @@ +import routes from '@/constants/routes' + export default { - url: '/developer/apps', + url: routes.DEVELOPER_MY_APPS, selectors: { container: '#page-developer-apps-container' } diff --git a/cypress/pages/developer-page.ts b/cypress/pages/developer-page.ts new file mode 100644 index 0000000000..de0f33fa7b --- /dev/null +++ b/cypress/pages/developer-page.ts @@ -0,0 +1,8 @@ +import routes from '@/constants/routes' + +export default { + url: routes.DEVELOPER, + selectors: { + container: '#page-developer-home-container' + } +} diff --git a/cypress/pages/developer-submit-app-page.ts b/cypress/pages/developer-submit-app-page.ts new file mode 100644 index 0000000000..52d8caeddd --- /dev/null +++ b/cypress/pages/developer-submit-app-page.ts @@ -0,0 +1,24 @@ +import routes from '@/constants/routes' + +export default { + url: routes.SUBMIT_APP, + selectors: { + buttonSubmit: 'button[type="submit"]', + selectCategory: 'select[name="categoryId"]', + checkBoxUserSession: 'input[id="USER SESSION"]', + checkBoxClientSecret: 'input["CLIENT SECRET"]', + textBoxName: 'input[name="name"]', + inputFileSubmitAppIcon: 'input#iconImage', + textBoxSupportEmail: 'input[name="supportEmail"]', + textBoxTelephone: 'input[name="telephone"]', + textBoxHomePage: 'input[name="homePage"]', + textBoxLaunchUrl: 'input[name="launchUri"]', + textAreaSummary: 'textarea[name="summary"]', + textAreaDescription: 'textarea[name="description"]', + fileUploadScreenshot1: 'input#screenshot1', + fileUploadScreenshot2: 'input#screenshot2', + fileUploadScreenshot3: 'input#screenshot3', + fileUploadScreenshot4: 'input#screenshot4', + submitSuccessSection: 'div[data-test="submit-success-section"]' + } +} diff --git a/cypress/pages/login-page.ts b/cypress/pages/login-page.ts new file mode 100644 index 0000000000..23ea3144d5 --- /dev/null +++ b/cypress/pages/login-page.ts @@ -0,0 +1,41 @@ +import routes from '@/constants/routes' +import developerAppsPage from './developer-page' + +const loginPageMetadata = { + loginAsClientUrl: routes.CLIENT_LOGIN, + loginAsDeveloperUrl: routes.DEVELOPER_LOGIN, + loginAsAdminUrl: routes.ADMIN_LOGIN, + selectors: { + inputEmail: 'input#userName', + inputPassword: 'input#password', + buttonLogin: "button[type='submit']" + }, + account: { + DEVELOPER: { + email: Cypress.env('DEVELOPER_ACCOUNT_EMAIL'), + password: Cypress.env('DEVELOPER_ACCOUNT_PASSWORD') + } + } +} + +const loginPageActions = { + loginUsingDeveloperAccount() { + const { + loginAsDeveloperUrl, + selectors: { buttonLogin, inputPassword, inputEmail } + } = loginPageMetadata + + cy.visit(loginAsDeveloperUrl) + cy.get(inputEmail).type(Cypress.env('DEVELOPER_ACCOUNT_EMAIL')) + cy.get(inputPassword).type(Cypress.env('DEVELOPER_ACCOUNT_PASSWORD')) + cy.get(buttonLogin).click() + cy.get(developerAppsPage.selectors.container).should('have.length', 1) + } +} + +const loginPage = { + ...loginPageMetadata, + actions: loginPageActions +} + +export default loginPage diff --git a/cypress/pages/loginPage.ts b/cypress/pages/loginPage.ts deleted file mode 100644 index 74eaeb4195..0000000000 --- a/cypress/pages/loginPage.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default { - loginAsClientUrl: '/client/login', - loginAsDeveloperUrl: '/developer/login', - loginAsAdminUrl: '/admin/login', - selectors: { - inputEmail: 'input#userName', - inputPassword: 'input#password', - buttonLogin: "button[type='submit']" - } -} diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 10dec64dde..865171bde0 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -1,5 +1,7 @@ const path = require('path') const wp = require('@cypress/webpack-preprocessor') +const ResolveTSPathsToWebpackAlias = require('ts-paths-to-webpack-alias') +const reapitConfig = require('../../reapit-config.json') module.exports = (on, config) => { // https://basarat.gitbooks.io/typescript/docs/testing/cypress.html @@ -22,7 +24,15 @@ module.exports = (on, config) => { options: { transpileOnly: true } } ] - } + }, + plugins: [ + /** + * This plugin mapped all data in the field named "paths" of tsconfig.json to webpack alias + */ + new ResolveTSPathsToWebpackAlias({ + tsconfig: path.resolve(__dirname, '../tsconfig.json') + }), + ] } } on('file:preprocessor', wp(options)) @@ -30,9 +40,19 @@ module.exports = (on, config) => { // Retries plugin require('cypress-plugin-retries/lib/plugin')(on) - + // Config ENV require('dotenv').config({ path: path.resolve(__dirname, '../../src/constants/.env') }) + + // Load ENV from config manager + let mergedEnv = process.env + const reapitEnv = process.env.REAPIT_ENV || 'LOCAL' + const reapitConfigMatchedEnv = reapitConfig[reapitEnv] + + if (config && typeof config === 'object') { + mergedEnv = {...mergedEnv, ...reapitConfigMatchedEnv} + } + const baseUrl = process.env.APPLICATION_URL - return { ...config, baseUrl, env: { ...process.env } } + return { ...config, baseUrl, env: {...mergedEnv} } } diff --git a/cypress/requests/app.ts b/cypress/requests/app.ts new file mode 100644 index 0000000000..81625317d4 --- /dev/null +++ b/cypress/requests/app.ts @@ -0,0 +1,16 @@ +import routes from '../fixtures/routes' + +export default { + deleteApp: (appId: string) => { + cy.server() + cy.route(`${routes.apps}/${appId}`, 'DELETE') + + cy.request({ + url: `${routes.apps}/${appId}`, + method: 'DELETE', + headers: { + 'x-api-key': Cypress.env('MARKETPLACE_API_KEY') + } + }) + } +} diff --git a/cypress/support/index.ts b/cypress/support/index.ts index dcf86a115a..35475e84d8 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -1 +1,12 @@ import 'cypress-plugin-retries' +import 'cypress-file-upload' + +/** + * See https://github.com/cypress-io/cypress/issues/95 + * Cypress doesn't support waiting for Fetch API atm + * Solution: delete window.fetch while developing -> whatwg-fetch will kick in + * whatwg-fetch is based on XMLHttpRequest and supported by Cypress + */ +Cypress.on('window:before:load', win => { + delete win.fetch +}) diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index b4ae79344e..e0b8e3673f 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -1,10 +1,15 @@ { "compilerOptions": { + "esModuleInterop": true, "strict": true, - "baseUrl": "../node_modules", + "baseUrl": "../", "target": "es5", - "lib": ["es5", "dom"], - "types": ["cypress"] + "lib": ["es5", "dom", "ES2015"], + "typeRoots": ["./node_modules/@types"], + "types": ["cypress"], + "paths": { + "@/*": ["./src/*"] + } }, "include": [ "**/*.ts" diff --git a/package.json b/package.json index 574dcabdb1..d642b19e89 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@types/jasmine": "~3.3.13", "@types/jest": "~24.0.13", "@types/jsonwebtoken": "^8.3.2", + "@types/nanoid": "^2.1.0", "@types/node": "~12.0.2", "@types/react": "^16.9.13", "@types/react-dom": "^16.9.4", @@ -74,6 +75,7 @@ "concurrently": "~4.1.1", "css-loader": "~3.0.0", "cypress": "^3.8.0", + "cypress-file-upload": "^3.5.1", "cypress-plugin-retries": "^1.5.2", "dotenv": "^8.2.0", "dotenv-webpack": "^1.7.0", @@ -94,6 +96,7 @@ "lint-staged": "~8.1.7", "loader-utils": "^1.2.3", "mini-css-extract-plugin": "~0.7.0", + "nanoid": "^2.1.8", "node-sass": "~4.12.0", "prettier": "^1.18.2", "purgecss": "^1.3.0", @@ -119,7 +122,8 @@ "webpack": "^4.41.4", "webpack-bundle-analyzer": "~3.3.2", "webpack-cli": "~3.3.2", - "webpack-dev-server": "~3.4.1" + "webpack-dev-server": "~3.4.1", + "whatwg-fetch": "^3.0.0" }, "husky": { "hooks": { @@ -132,10 +136,10 @@ "yarn lint:src", "git add" ], - "./cypress/*.{ts,tsx}": [ + "./cypress/*.{ts,tsx}": [ "yarn lint:cypress", "git add" - ] + ] }, "browserslist": [ "> 1%", diff --git a/src/components/pages/__tests__/__snapshots__/developer-home.tsx.snap b/src/components/pages/__tests__/__snapshots__/developer-home.tsx.snap index b1f19c350f..77803c8792 100644 --- a/src/components/pages/__tests__/__snapshots__/developer-home.tsx.snap +++ b/src/components/pages/__tests__/__snapshots__/developer-home.tsx.snap @@ -3,7 +3,7 @@ exports[`DeveloperHome should match a snapshot 1`] = `
`; exports[`DeveloperHome should match a snapshot 3`] = `
= ({ return ( -
+
- } - subHeading={ - - Pete's Proptech World Ltd - - (Direct API) - - - } +
-

- nXXT2zaK807ysWgy8F0WEhIYRP3TgosAtfuiLtQCImoSx0kynxbIF0nkGHU36Oz13kM3DG0Bcsicr8L6eWFKLBg4axlmiOEWcvwHAbBP9LRvoFkCl58k1wjhOExnpaZItEyOT1AXVKv8PE44aMGtVz -

- + + } + subHeading={ + + Pete's Proptech World Ltd + + (Direct API) + + + } + > +

+ nXXT2zaK807ysWgy8F0WEhIYRP3TgosAtfuiLtQCImoSx0kynxbIF0nkGHU36Oz13kM3DG0Bcsicr8L6eWFKLBg4axlmiOEWcvwHAbBP9LRvoFkCl58k1wjhOExnpaZItEyOT1AXVKv8PE44aMGtVz +

+
+
`; diff --git a/src/components/ui/app-card.tsx b/src/components/ui/app-card.tsx index 5828b7aced..041a9a0a32 100644 --- a/src/components/ui/app-card.tsx +++ b/src/components/ui/app-card.tsx @@ -17,28 +17,30 @@ const AppCard: React.FunctionComponent = ({ app, onClick, onSettin dataTest.push(app.name) return ( - void} - dataTest={dataTest.join('_')} - heading={app.name || ''} - subHeading={ - <> - {app.developer} - {app.isDirectApi ? (Direct API) : ''} - - } - image={ - {app.name} - } - menu={ - app.installedOn && - onSettingsClick && ( - - ) - } - > -

{app.summary}

-
+
+ void} + dataTest={dataTest.join('_')} + heading={app.name || ''} + subHeading={ + <> + {app.developer} + {app.isDirectApi ? (Direct API) : ''} + + } + image={ + {app.name} + } + menu={ + app.installedOn && + onSettingsClick && ( + + ) + } + > +

{app.summary}

+
+
) } diff --git a/src/docs/E2E_TESTING.md b/src/docs/E2E_TESTING.md index 5fbe5a93c3..b81bac4c57 100644 --- a/src/docs/E2E_TESTING.md +++ b/src/docs/E2E_TESTING.md @@ -1,5 +1,7 @@ -## Required ENV +## Base URL +Base url of the test application is defined by an ENV variable named `APPLICATION_URL` +## Required ENV (ENVs is loaded from a file named .env located at src/contants) * DEVELOPER_ACCOUNT_EMAIL - email of the developer account that will be used to testing * DEVELOPER_ACCOUNT_PASSWORD - password of the developer account that will be used to testing diff --git a/src/scripts/webpack-dev.js b/src/scripts/webpack-dev.js index f3a615fe68..1dd6ecc10c 100644 --- a/src/scripts/webpack-dev.js +++ b/src/scripts/webpack-dev.js @@ -9,7 +9,13 @@ const config = require(path.resolve(__dirname, '../..', 'reapit-config.json')) module.exports = { context: process.cwd(), - entry: './src/core/index.tsx', + /** + * See https://github.com/cypress-io/cypress/issues/95 + * Cypress doesn't support waiting for Fetch API atm + * Solution: delete window.fetch while developing -> whatwg-fetch will kick in + * whatwg-fetch is based on XMLHttpRequest and supported by Cypress + */ + entry: ['whatwg-fetch', './src/core/index.tsx'], output: { path: path.join(process.cwd(), 'public', 'dist'), filename: '[name].[hash].js' diff --git a/yarn.lock b/yarn.lock index fb72874254..97e24613ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1242,6 +1242,13 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/nanoid@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/nanoid/-/nanoid-2.1.0.tgz#41edfda78986e9127d0dc14de982de766f994020" + integrity sha512-xdkn/oRTA0GSNPLIKZgHWqDTWZsVrieKomxJBOQUK9YDD+zfSgmwD5t4WJYra5S7XyhTw7tfvwznW+pFexaepQ== + dependencies: + "@types/node" "*" + "@types/node@*": version "13.1.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.1.tgz#6d11a8c2d58405b3db9388ab740106cbfa64c3c9" @@ -3460,6 +3467,11 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cypress-file-upload@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-3.5.1.tgz#6dcd7a87c93242b8fe917abe39bdda1ff82e23a3" + integrity sha512-HUhnoLlhLTHmgRGsoflcGyv3n9WA/Kh96mmBLmTGlg9Fs/CP2fVVc4NdbKeT9fNYk6Qy3upjfUxYaavNnfQb/Q== + cypress-plugin-retries@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/cypress-plugin-retries/-/cypress-plugin-retries-1.5.2.tgz#21d5247cd77013b95bbfdd914f2de66f91f76a2e" @@ -8031,6 +8043,11 @@ nan@^2.12.1, nan@^2.13.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoid@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.8.tgz#2dbb0224231b246e3b4c819de7bfea6384dabf08" + integrity sha512-g1z+n5s26w0TGKh7gjn7HCqurNKMZWzH08elXzh/gM/csQHd/UqDV6uxMghQYg9IvqRPm1QpeMk50YMofHvEjQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -12255,7 +12272,7 @@ whatwg-fetch@2.0.4: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@>=0.10.0: +whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==