diff --git a/cypress/components/app-card.ts b/cypress/components/app-card.ts new file mode 100644 index 0000000000..ec4406c194 --- /dev/null +++ b/cypress/components/app-card.ts @@ -0,0 +1,10 @@ +export default { + actions: { + clickAppCardWithName(name: string) { + return cy.get(`div[data-test-app-name="${name}"]`).click() + }, + clickAppCardSettingWithId(id: string) { + return cy.get(`div[data-test="app-settings_${id}"]`).click() + } + } +} diff --git a/cypress/components/app-detail-modal.ts b/cypress/components/app-detail-modal.ts new file mode 100644 index 0000000000..d085eec519 --- /dev/null +++ b/cypress/components/app-detail-modal.ts @@ -0,0 +1,13 @@ +export default { + selectors: { + buttonInstallApp: 'button[data-test="detail-modal-install-button"]', + buttonUninstallApp: 'button[data-test="detail-modal-uninstall-button"]', + buttonEditDetail: 'button[data-test="detail-modal-edit-button"]', + buttonAgree: 'button[data-test="agree-btn"]', + buttonDisagree: 'button[data-test="disagree-btn"]', + buttonSuccess: 'button[data-test="installations-success-button"]', + + divInstalled: 'div[data-test="detail-modal-installed"]', + divSuccessMessage: 'div[data-test="installations-success-message"]' + } +} diff --git a/cypress/components/client-welcome-modal.ts b/cypress/components/client-welcome-modal.ts new file mode 100644 index 0000000000..7ce35e4bf8 --- /dev/null +++ b/cypress/components/client-welcome-modal.ts @@ -0,0 +1,5 @@ +export default { + selectors: { + buttonAceptWelcome: 'button[data-test="button-accept-welcome-message-modal"]' + } +} diff --git a/cypress/fixtures/routes.ts b/cypress/fixtures/routes.ts index 58577ac748..d47cd53413 100644 --- a/cypress/fixtures/routes.ts +++ b/cypress/fixtures/routes.ts @@ -5,8 +5,14 @@ export default { scopes: `${apiEndPoint}/scopes`, appsOfDeveloper: `${apiEndPoint}/apps?developerId=**&PageNumber=**&PageSize=**`, apps: `${apiEndPoint}/apps`, + approvals: `${apiEndPoint}/approvals`, + appDetail: `${apiEndPoint}/apps/**?clientId=**`, + installedApps: `${apiEndPoint}/apps?clientId=**&OnlyInstalled=**&PageNumber=**&PageSize=**&IsDirectApi=**`, + manageApps: `${apiEndPoint}/apps?clientId=**&OnlyInstalled=**&PageNumber=**&PageSize=**`, developers: `${apiEndPoint}/developers`, approveApp: `${apiEndPoint}/apps/**/revisions/**/approve`, revision: `${apiEndPoint}/apps/**/revisions`, - changePassword: `${Cypress.env('COGNITO_API_BASE_URL')}/password/change` + changePassword: `${apiEndPoint}/password/change`, + installations: `${apiEndPoint}/installations`, + terminateApp: `${apiEndPoint}/installations/**/terminate` } diff --git a/cypress/integration/flow-install-app-happy-path.test.ts b/cypress/integration/flow-install-app-happy-path.test.ts new file mode 100644 index 0000000000..3bcaec3ee1 --- /dev/null +++ b/cypress/integration/flow-install-app-happy-path.test.ts @@ -0,0 +1,118 @@ +import loginPage from '../pages/login-page' +import apiRoutes from '../fixtures/routes' +import appRequest from '../requests/app' +import developerAppsPage from '../pages/developer-apps-page' +import adminApprovalsPage from '../pages/admin-approvals-page' +import installedAppsPage from '../pages/installed-apps-page' +import manageAppsPage from '../pages/manage-apps-page' + +import clientWelcomeModal from '../components/client-welcome-modal' +import appDetailModal from '../components/app-detail-modal' +import appCard from '../components/app-card' +import nanoid from 'nanoid' + +const { + actions: { loginUsingClientAccount, loginUsingDeveloperAccount, loginUsingAdminAccount } +} = loginPage + +const { + selectors: { buttonAceptWelcome } +} = clientWelcomeModal + +const { + actions: { listedAppWithName } +} = developerAppsPage + +const { + actions: { approveAppWithId } +} = adminApprovalsPage + +const { url: installedAppsPageUrl } = installedAppsPage + +const { url: manageAppsPageUrl } = manageAppsPage + +const { + selectors: { buttonAgree, buttonInstallApp, buttonUninstallApp, buttonSuccess, divSuccessMessage } +} = appDetailModal + +const { + actions: { clickAppCardSettingWithId } +} = appCard + +const appName = `E2E Test App - ${nanoid()}` +let appId = '' + +describe('Install app happy path', () => { + before(() => { + cy.server() + appRequest.createApp(appName) + }) + + after(() => { + cy.server() + appRequest.deleteApp(appId) + }) + + it('Log into developer and listed app successfully', () => { + cy.server() + loginUsingDeveloperAccount() + listedAppWithName(appName, res => { + appId = res + }) + }) + + it('Log into admin and approve app successfully', () => { + cy.server() + loginUsingAdminAccount() + approveAppWithId(appId) + }) + + it('Log into client and install and uninstall app successfully', () => { + cy.server() + cy.clearCookies() + + cy.route('GET', apiRoutes.manageApps).as('getManageApps') + cy.route('GET', apiRoutes.installedApps).as('getInstalledApps') + cy.route('POST', apiRoutes.installations).as('installApp') + cy.route('POST', apiRoutes.terminateApp).as('uninstallApp') + + loginUsingClientAccount() + + cy.get(buttonAceptWelcome).click() + + cy.get(`div[data-test-app-name="${appName}"]`).click() + + cy.get(buttonInstallApp).should('have.length', 1) + cy.get(buttonInstallApp).click() + cy.get(buttonAgree).click() + + cy.wait('@installApp') + + cy.route('GET', apiRoutes.appDetail).as('getAppDetail') + + cy.get(divSuccessMessage).should('have.length', 1) + cy.get(buttonSuccess).click() + + cy.wait('@getAppDetail') + + cy.visit(installedAppsPageUrl) + + cy.wait('@getInstalledApps') + + cy.visit(manageAppsPageUrl) + + cy.wait('@getManageApps') + + clickAppCardSettingWithId(appId) + + cy.get(buttonUninstallApp).click() + cy.get(buttonAgree).click() + + cy.wait('@uninstallApp') + + cy.get(divSuccessMessage).should('have.length', 1) + cy.get(buttonSuccess).click() + + cy.wait('@getManageApps') + }) +}) diff --git a/cypress/pages/admin-approvals-page.ts b/cypress/pages/admin-approvals-page.ts index 49f4d44d7d..c804e8a45d 100644 --- a/cypress/pages/admin-approvals-page.ts +++ b/cypress/pages/admin-approvals-page.ts @@ -1,4 +1,5 @@ import routes from '@/constants/routes' +import apiRoutes from '../fixtures/routes' const adminApprovalsPageMetaData = { url: routes.ADMIN_APPROVALS, @@ -16,6 +17,17 @@ const adminApprovalsPage = { actions: { clickViewDetailsButtonWithAppId(id: string) { cy.get(`button[data-test="view-details-button_${id}"]`).click() + }, + approveAppWithId(id: string) { + const { buttonApprove, btnConfirmApproval, divApproveAppSuccessfully } = adminApprovalsPageMetaData.selectors + cy.get(`button[data-test="view-details-button_${id}"]`).click() + cy.get(buttonApprove).click() + + cy.route('POST', apiRoutes.approveApp).as('requestApproveApp') + cy.get(btnConfirmApproval).click() + cy.wait('@requestApproveApp') + + cy.get(divApproveAppSuccessfully).should('have.length', 1) } } } diff --git a/cypress/pages/developer-apps-page.ts b/cypress/pages/developer-apps-page.ts index dcd9e75bd8..8f599c7b23 100644 --- a/cypress/pages/developer-apps-page.ts +++ b/cypress/pages/developer-apps-page.ts @@ -1,6 +1,7 @@ import webRoutes from '@/constants/routes' import appRequests from '../requests/app' import apiRoutes from '../fixtures/routes' +import developerSubmitAppPage from '../pages/developer-submit-app-page' const developerAppsPageMetadata = { url: webRoutes.DEVELOPER_MY_APPS, @@ -11,6 +12,10 @@ const developerAppsPageMetadata = { } } +const { + selectors: { checkBoxIsListed, buttonSubmit, checkboxAgreeTheTermsAndConditions } +} = developerSubmitAppPage + const developerAppsPageActions = { clickAppCardWithName(name: string) { return cy.get(`div[data-test-app-name="${name}"]`).click() @@ -26,6 +31,38 @@ const developerAppsPageActions = { .then(appId => { appRequests.deleteApp(appId as any) }) + }, + + listedAppWithName(name: string, callback: (appId: string) => void) { + const { buttonEditDetails } = developerAppsPageMetadata.selectors + cy.visit(developerAppsPage.url) + cy.route(apiRoutes.appsOfDeveloper).as('getAppsOfDeveloper') + cy.wait('@getAppsOfDeveloper') + cy.get(`div[data-test-app-name='${name}']`) + .click() + .invoke('attr', 'data-test-app-id') + .then(appId => { + callback(appId as any) + }) + + cy.route(apiRoutes.categories).as('requestGetCategories') + cy.route(apiRoutes.scopes).as('requestGetScopes') + + cy.get(buttonEditDetails).click() + + cy.wait('@requestGetCategories') + cy.wait('@requestGetScopes') + + cy.get(checkBoxIsListed).click({ force: true }) + cy.get(checkboxAgreeTheTermsAndConditions).click({ force: true }) + + cy.route('POST', apiRoutes.revision).as('requestSubmitRevision') + cy.get(buttonSubmit).click() + cy.wait('@requestSubmitRevision') + + cy.get(buttonEditDetails) + .should('have.text', 'Pending Revision') + .should('be.disabled') } } diff --git a/cypress/pages/installed-apps-page.ts b/cypress/pages/installed-apps-page.ts new file mode 100644 index 0000000000..55697e889f --- /dev/null +++ b/cypress/pages/installed-apps-page.ts @@ -0,0 +1,5 @@ +import routes from '@/constants/routes' + +export default { + url: routes.INSTALLED_APPS +} diff --git a/cypress/pages/manage-apps-page.ts b/cypress/pages/manage-apps-page.ts new file mode 100644 index 0000000000..764ab9453e --- /dev/null +++ b/cypress/pages/manage-apps-page.ts @@ -0,0 +1,5 @@ +import routes from '@/constants/routes' + +export default { + url: routes.MY_APPS +} diff --git a/src/components/ui/__tests__/__snapshots__/installed-app-card.tsx.snap b/src/components/ui/__tests__/__snapshots__/installed-app-card.tsx.snap index 854a16892c..ee9b933aab 100644 --- a/src/components/ui/__tests__/__snapshots__/installed-app-card.tsx.snap +++ b/src/components/ui/__tests__/__snapshots__/installed-app-card.tsx.snap @@ -1,20 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`InstalledAppCard should match a snapshot 1`] = ` - - } - onClick={[MockFunction]} - subHeading="Pete's Proptech World Ltd" +
-

- nXXT2zaK807ysWgy8F0WEhIYRP3TgosAtfuiLtQCImoSx0kynxbIF0nkGHU36Oz13kM3DG0Bcsicr8L6eWFKLBg4axlmiOEWcvwHAbBP9LRvoFkCl58k1wjhOExnpaZItEyOT1AXVKv8PE44aMGtVz -

- + + } + onClick={[MockFunction]} + subHeading="Pete's Proptech World Ltd" + > +

+ nXXT2zaK807ysWgy8F0WEhIYRP3TgosAtfuiLtQCImoSx0kynxbIF0nkGHU36Oz13kM3DG0Bcsicr8L6eWFKLBg4axlmiOEWcvwHAbBP9LRvoFkCl58k1wjhOExnpaZItEyOT1AXVKv8PE44aMGtVz +

+
+
`; diff --git a/src/components/ui/app-card.tsx b/src/components/ui/app-card.tsx index 041a9a0a32..82f1506d79 100644 --- a/src/components/ui/app-card.tsx +++ b/src/components/ui/app-card.tsx @@ -34,7 +34,9 @@ const AppCard: React.FunctionComponent = ({ app, onClick, onSettin menu={ app.installedOn && onSettingsClick && ( - +
+ +
) } > diff --git a/src/components/ui/app-detail-modal/__tests__/__snapshots__/app-detail-inner.tsx.snap b/src/components/ui/app-detail-modal/__tests__/__snapshots__/app-detail-inner.tsx.snap index 3073300f10..d0dbe62d39 100644 --- a/src/components/ui/app-detail-modal/__tests__/__snapshots__/app-detail-inner.tsx.snap +++ b/src/components/ui/app-detail-modal/__tests__/__snapshots__/app-detail-inner.tsx.snap @@ -45,6 +45,7 @@ exports[`AppDetailInner should match a snapshot when appDetailModalState = VIEW_ } footerItems={ ) @@ -71,7 +71,7 @@ export const renderFooterAppDetailBrowse = ({ appDetailData, setStateViewInstall export const renderFooterAppDetailManage = ({ setStateViewUninstall }) => { return ( - ) @@ -121,7 +121,8 @@ export const AppDetailInner: React.FunctionComponent = ({ diff --git a/src/components/ui/installed-app-card.tsx b/src/components/ui/installed-app-card.tsx index 9a7826a991..69addca604 100644 --- a/src/components/ui/installed-app-card.tsx +++ b/src/components/ui/installed-app-card.tsx @@ -12,7 +12,7 @@ export interface InstalledAppCardProps { const InstalledAppCard: React.FC = ({ app, onClick }: InstalledAppCardProps) => { if (isMobile()) { return ( -
+
iconUri
@@ -22,16 +22,18 @@ const InstalledAppCard: React.FC = ({ app, onClick }: Ins } return ( - void} - heading={app.name || ''} - subHeading={app.developer || ''} - image={ - {app.name} - } - > -

{app.summary}

-
+
+ void} + heading={app.name || ''} + subHeading={app.developer || ''} + image={ + {app.name} + } + > +

{app.summary}

+
+
) }