diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 1303d5921a..6e886f3011 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -71,7 +71,7 @@ jobs: - name: Upload to S3 if: github.ref == 'refs/heads/new-design-ui' run: | - tar -czvf new_ui.tar.gz packages/server/assets packages/server/index.html + tar -czvf new_ui.tar.gz packages/server/builder/assets packages/server/builder/index.html aws s3 cp new_ui.tar.gz s3://prod-budi-app-assets/beta:design_ui/ aws s3 cp packages/client/dist/budibase-client.js s3://prod-budi-app-assets/beta:design_ui/budibase-client.js aws cloudfront create-invalidation --distribution-id E3ELKP4RCEHVLW --paths "/beta:design_ui/*" diff --git a/lerna.json b/lerna.json index e65ce20401..dc0da83cc3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.0.212-alpha.6", + "version": "1.0.212-alpha.9", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 3f36482120..1de320ae27 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.0.212-alpha.6", + "version": "1.0.212-alpha.9", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "^1.0.212-alpha.6", + "@budibase/types": "^1.0.212-alpha.9", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", "bcrypt": "5.0.1", diff --git a/packages/backend-core/src/db/constants.js b/packages/backend-core/src/db/constants.js index 10c6e174d7..12626fb90e 100644 --- a/packages/backend-core/src/db/constants.js +++ b/packages/backend-core/src/db/constants.js @@ -1,4 +1,5 @@ exports.SEPARATOR = "_" +exports.UNICODE_MAX = "\ufff0" const PRE_APP = "app" const PRE_DEV = "dev" diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index dc7a0454c3..54af4fc7a2 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -1,7 +1,7 @@ import { newid } from "../hashing" import { DEFAULT_TENANT_ID, Configs } from "../constants" import env from "../environment" -import { SEPARATOR, DocumentTypes } from "./constants" +import { SEPARATOR, DocumentTypes, UNICODE_MAX } from "./constants" import { getTenantId, getGlobalDBName, getGlobalDB } from "../tenancy" import fetch from "node-fetch" import { doWithDB, allDbs } from "./index" @@ -12,8 +12,6 @@ import { isDevApp, isDevAppID } from "./conversions" import { APP_PREFIX } from "./constants" import * as events from "../events" -const UNICODE_MAX = "\ufff0" - export const ViewNames = { USER_BY_EMAIL: "by_email", BY_API_KEY: "by_api_key", @@ -93,13 +91,17 @@ export function generateGlobalUserID(id?: any) { /** * Gets parameters for retrieving users. */ -export function getGlobalUserParams(globalId: any, otherProps = {}) { +export function getGlobalUserParams(globalId: any, otherProps: any = {}) { if (!globalId) { globalId = "" } + const startkey = otherProps?.startkey return { ...otherProps, - startkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}`, + // need to include this incase pagination + startkey: startkey + ? startkey + : `${DocumentTypes.USER}${SEPARATOR}${globalId}`, endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`, } } @@ -434,6 +436,26 @@ export const getPlatformUrl = async (opts = { tenantAware: true }) => { return platformUrl } +export function pagination( + data: any[], + pageSize: number, + { paginate, property } = { paginate: true, property: "_id" } +) { + if (!paginate) { + return { data, hasNextPage: false } + } + const hasNextPage = data.length > pageSize + let nextPage = undefined + if (hasNextPage) { + nextPage = property ? data[pageSize]?.[property] : data[pageSize]?._id + } + return { + data: data.slice(0, pageSize), + hasNextPage, + nextPage, + } +} + export async function getScopedConfig(db: any, params: any) { const configDoc = await getScopedFullConfig(db, params) return configDoc && configDoc.config ? configDoc.config : configDoc diff --git a/packages/backend-core/src/users.js b/packages/backend-core/src/users.js index 4acccda2a0..0c1350a674 100644 --- a/packages/backend-core/src/users.js +++ b/packages/backend-core/src/users.js @@ -1,5 +1,6 @@ const { ViewNames } = require("./db/utils") const { queryGlobalView } = require("./db/views") +const { UNICODE_MAX } = require("./db/constants") /** * Given an email address this will use a view to search through @@ -19,3 +20,24 @@ exports.getGlobalUserByEmail = async email => { return response } + +/** + * Performs a starts with search on the global email view. + */ +exports.searchGlobalUsersByEmail = async (email, opts) => { + if (typeof email !== "string") { + throw new Error("Must provide a string to search by") + } + const lcEmail = email.toLowerCase() + // handle if passing up startkey for pagination + const startkey = opts && opts.startkey ? opts.startkey : lcEmail + let response = await queryGlobalView(ViewNames.USER_BY_EMAIL, { + ...opts, + startkey, + endkey: `${lcEmail}${UNICODE_MAX}`, + }) + if (!response) { + response = [] + } + return Array.isArray(response) ? response : [response] +} diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 6bb51dda7b..03145fb96f 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "1.0.212-alpha.6", + "version": "1.0.212-alpha.9", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "^1.0.212-alpha.6", + "@budibase/string-templates": "^1.0.212-alpha.9", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/cypress.json b/packages/builder/cypress.json index 06bf558946..46f85a52c8 100644 --- a/packages/builder/cypress.json +++ b/packages/builder/cypress.json @@ -13,7 +13,7 @@ "HOST_IP": "" }, "retries": { - "runMode": 2, + "runMode": 1, "openMode": 0 } } diff --git a/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js b/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js index 38ae881db8..32f62efe1f 100644 --- a/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js +++ b/packages/builder/cypress/integration/addMultiOptionDatatype.spec.js @@ -16,18 +16,15 @@ filterTests(['all'], () => { it("should add form with multi select picker, containing 5 options", () => { cy.navigateToFrontend() - cy.wait(500) // Add data provider - cy.get(interact.CATEGORY_DATA).click() + cy.get(interact.CATEGORY_DATA, { timeout: 500 }).click() cy.get(interact.COMPONENT_DATA_PROVIDER).click() cy.get(interact.DATASOURCE_PROP_CONTROL).click() cy.get(interact.DROPDOWN).contains("Multi Data").click() - cy.wait(500) // Add Form with schema to match table cy.addComponent("Form", "Form") cy.get(interact.DATASOURCE_PROP_CONTROL).click() cy.get(interact.DROPDOWN).contains("Multi Data").click() - cy.wait(500) // Add multi-select picker to form cy.addComponent("Form", "Multi-select Picker").then(componentId => { cy.get(interact.DATASOURCE_FIELD_CONTROL).type("Test Data").type("{enter}") diff --git a/packages/builder/cypress/integration/adminAndManagement/accountPortals.spec.js b/packages/builder/cypress/integration/adminAndManagement/accountPortals.spec.js new file mode 100644 index 0000000000..c615b2b4e6 --- /dev/null +++ b/packages/builder/cypress/integration/adminAndManagement/accountPortals.spec.js @@ -0,0 +1,131 @@ +import filterTests from "../../support/filterTests" +const interact = require('../../support/interact') + +filterTests(["smoke", "all"], () => { + context("Account Portals", () => { + + const bbUserEmail = "bbuser@test.com" + + before(() => { + cy.login() + cy.deleteApp("Cypress Tests") + cy.createApp("Cypress Tests") + + // Create new user + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 1000}) + cy.createUser(bbUserEmail) + cy.contains("bbuser").click() + cy.wait(500) + + // Reset password + cy.get(".spectrum-ActionButton-label", { timeout: 2000 }).contains("Force password reset").click({ force: true }) + + cy.get(".spectrum-Dialog-grid") + .find(interact.SPECTRUM_TEXTFIELD_INPUT).invoke('val').as('pwd') + + cy.get(interact.SPECTRUM_BUTTON).contains("Reset password").click({ force: true }) + + // Login as new user and set password + cy.logOut() + cy.get('@pwd').then((pwd) => { + cy.login(bbUserEmail, pwd) + }) + + for (let i = 0; i < 2; i++) { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).eq(i).type("test") + } + cy.get(interact.SPECTRUM_BUTTON).contains("Reset your password").click({ force: true }) + cy.logoutNoAppGrid() + }) + + it("should verify Admin Portal", () => { + cy.login() + cy.contains("Users").click() + cy.contains("bbuser").click() + + // Enable Development & Administration access + cy.wait(500) + for (let i = 4; i < 6; i++) { + cy.get(interact.FIELD).eq(i).within(() => { + cy.get(interact.SPECTRUM_SWITCH_INPUT).click({ force: true }) + cy.get(interact.SPECTRUM_SWITCH_INPUT).should('be.enabled') + }) + } + bbUserLogin() + + // Verify available options for Admin portal + cy.get(".spectrum-SideNav") + .should('contain', 'Apps') + //.and('contain', 'Usage') + .and('contain', 'Users') + .and('contain', 'Auth') + .and('contain', 'Email') + .and('contain', 'Organisation') + .and('contain', 'Theming') + .and('contain', 'Update') + //.and('contain', 'Upgrade') + + cy.logOut() + }) + + it("should verify Development Portal", () => { + // Only Development access should be enabled + cy.login() + cy.contains("Users").click() + cy.contains("bbuser").click() + cy.wait(500) + cy.get(interact.FIELD).eq(5).within(() => { + cy.get(interact.SPECTRUM_SWITCH_INPUT).click({ force: true }) + }) + + bbUserLogin() + + // Verify available options for Admin portal + cy.get(interact.SPECTRUM_SIDENAV) + .should('contain', 'Apps') + //.and('contain', 'Usage') + .and('not.contain', 'Users') + .and('not.contain', 'Auth') + .and('not.contain', 'Email') + .and('not.contain', 'Organisation') + .and('contain', 'Theming') + .and('not.contain', 'Update') + .and('not.contain', 'Upgrade') + + cy.logOut() + }) + + it("should verify Standard Portal", () => { + // Development access should be disabled (Admin access is already disabled) + cy.login() + cy.contains("Users").click() + cy.contains("bbuser").click() + cy.wait(500) + cy.get(interact.FIELD).eq(4).within(() => { + cy.get(interact.SPECTRUM_SWITCH_INPUT).click({ force: true }) + }) + + bbUserLogin() + + // Verify Standard Portal + cy.get(interact.SPECTRUM_SIDENAV).should('not.exist') // No config sections + cy.get(interact.CREATE_APP_BUTTON).should('not.exist') // No create app button + cy.get(".app").should('not.exist') // No apps -> no roles assigned to user + cy.get(interact.CONTAINER).should('contain', bbUserEmail) // Message containing users email + + cy.logoutNoAppGrid() + }) + + const bbUserLogin = () => { + // Login as bbuser + cy.logOut() + cy.login(bbUserEmail, "test") + } + + after(() => { + cy.login() + // Delete BB user + cy.deleteUser(bbUserEmail) + }) + }) +}) diff --git a/packages/builder/cypress/integration/createUserAndRoles.spec.js b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js similarity index 50% rename from packages/builder/cypress/integration/createUserAndRoles.spec.js rename to packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js index d47a96ed8d..3c23086136 100644 --- a/packages/builder/cypress/integration/createUserAndRoles.spec.js +++ b/packages/builder/cypress/integration/adminAndManagement/userManagement.spec.js @@ -1,28 +1,34 @@ -import filterTests from "../support/filterTests" -const interact = require('../support/interact') +import filterTests from "../../support/filterTests" +const interact = require('../../support/interact') filterTests(["smoke", "all"], () => { - context("Create a User and Assign Roles", () => { + context("User Management", () => { before(() => { cy.login() cy.deleteApp("Cypress Tests") cy.createApp("Cypress Tests") }) - it("should create a user", () => { - cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) + it("should create a user via basic onboarding", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 1000}) cy.createUser("bbuser@test.com") cy.get(interact.SPECTRUM_TABLE).should("contain", "bbuser") }) - it("should confirm there is No Access for a New User", () => { - // Click into the user + it("should confirm basic permission for a New User", () => { + // Basic permission = development & administraton disabled cy.contains("bbuser").click() - cy.wait(500) - // Get No Access table - Confirm it has apps in it - cy.get(interact.SPECTRUM_TABLE).eq(1).should("not.contain", "No rows found") - // Get Configure Roles table - Confirm it has no apps + // Confirm development and admin access are disabled + for (let i = 4; i < 6; i++) { + cy.wait(500) + cy.get(interact.FIELD).eq(i).within(() => { + //cy.get(interact.SPECTRUM_SWITCH_INPUT).should('be.disabled') + cy.get(".spectrum-Switch-switch").should('not.be.checked') + }) + } + // Existing apps appear within the No Access table + cy.get(interact.SPECTRUM_TABLE, { timeout: 500 }).eq(1).should("not.contain", "No rows found") + // Configure roles table should not contain apps cy.get(interact.SPECTRUM_TABLE).eq(0).contains("No rows found") }) @@ -40,20 +46,16 @@ filterTests(["smoke", "all"], () => { cy.createApp(name) } else { cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) - cy.get(interact.CREATE_APP_BUTTON).click({ force: true }) + cy.get(interact.CREATE_APP_BUTTON, { timeout: 1000 }).click({ force: true }) cy.createAppFromScratch(name) } } } }) // Navigate back to the user - cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 500}) cy.get(interact.SPECTRUM_SIDENAV).contains("Users").click() - cy.wait(500) - cy.get(interact.SPECTRUM_TABLE).contains("bbuser").click() - cy.wait(1000) + cy.get(interact.SPECTRUM_TABLE, { timeout: 500 }).contains("bbuser").click() for (let i = 0; i < 3; i++) { cy.get(interact.SPECTRUM_TABLE, { timeout: 3000}) .eq(1) @@ -62,31 +64,28 @@ filterTests(["smoke", "all"], () => { .find(interact.SPECTRUM_TABLE_CELL) .eq(0) .click() - cy.wait(500) - cy.get(interact.SPECTRUM_DIALOG_GRID) + cy.get(interact.SPECTRUM_DIALOG_GRID, { timeout: 500 }) .contains("Choose an option") .click() .then(() => { - cy.wait(1000) if (i == 0) { - cy.get(interact.SPECTRUM_MENU).contains("Admin").click({ force: true }) + cy.get(interact.SPECTRUM_MENU, { timeout: 1000 }).contains("Admin").click({ force: true }) } else if (i == 1) { - cy.get(interact.SPECTRUM_MENU).contains("Power").click({ force: true }) + cy.get(interact.SPECTRUM_MENU, { timeout: 1000 }).contains("Power").click({ force: true }) } else if (i == 2) { - cy.get(interact.SPECTRUM_MENU).contains("Basic").click({ force: true }) + cy.get(interact.SPECTRUM_MENU, { timeout: 1000 }).contains("Basic").click({ force: true }) } - cy.wait(1000) - cy.get(interact.SPECTRUM_BUTTON) + cy.get(interact.SPECTRUM_BUTTON, { timeout: 1000 }) .contains("Update role") .click({ force: true }) }) cy.reload() + cy.wait(1000) } // Confirm roles exist within Configure roles table - cy.wait(2000) - cy.get(interact.SPECTRUM_TABLE) + cy.get(interact.SPECTRUM_TABLE, { timeout: 2000 }) .eq(0) .within(assginedRoles => { expect(assginedRoles).to.contain("Admin") @@ -112,21 +111,19 @@ filterTests(["smoke", "all"], () => { .click() .then(() => { cy.get(interact.SPECTRUM_PICKER).eq(1).click({ force: true }) - cy.wait(500) - cy.get(interact.SPECTRUM_POPOVER).contains("No Access").click() + cy.get(interact.SPECTRUM_POPOVER, { timeout: 500 }).contains("No Access").click() }) cy.get(interact.SPECTRUM_BUTTON) .contains("Update role") .click({ force: true }) - cy.wait(1000) } }) // Confirm Configure roles table no longer has any apps in it - cy.get(interact.SPECTRUM_TABLE).eq(0).contains("No rows found") + cy.get(interact.SPECTRUM_TABLE, { timeout: 1000 }).eq(0).contains("No rows found") }) } - it("should enable Developer access", () => { + it("should enable Developer access and verify application access", () => { // Enable Developer access cy.get(interact.FIELD) .eq(4) @@ -158,15 +155,15 @@ filterTests(["smoke", "all"], () => { }) }) - it("should disable Developer access", () => { + it("should disable Developer access and verify application access", () => { // Disable Developer access - cy.get(".field") + cy.get(interact.FIELD) .eq(4) .within(() => { cy.get(".spectrum-Switch-input").click({ force: true }) }) // Configure roles table should now be empty - cy.get(".container") + cy.get(interact.CONTAINER) .contains("Configure roles") .parent() .within(() => { @@ -174,22 +171,64 @@ filterTests(["smoke", "all"], () => { }) }) + it("Should edit user details within user details page", () => { + // Add First name + cy.get(interact.FIELD, { timeout: 500 }).eq(2).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 500 }).type("bb") + }) + // Add Last name + cy.get(interact.FIELD).eq(3).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).type("test") + }) + cy.get(interact.FIELD).eq(0).click() + // Reload page + cy.reload() + + // Confirm details have been saved + cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', "bb") + }) + cy.get(interact.FIELD).eq(3).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 500 }).should('have.value', "test") + }) + }) + + it("should reset the users password", () => { + cy.get(interact.REGENERATE, { timeout: 500 }).contains("Force password reset").click({ force: true }) + + // Reset password modal + cy.get(interact.SPECTRUM_DIALOG_GRID) + .find(interact.SPECTRUM_TEXTFIELD_INPUT).invoke('val').as('pwd') + cy.get(interact.SPECTRUM_BUTTON).contains("Reset password").click({ force: true }) + + // Logout, then login with new password + cy.logOut() + cy.get('@pwd').then((pwd) => { + cy.login("bbuser@test.com", pwd) + }) + + // Reset password screen + for (let i = 0; i < 2; i++) { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).eq(i).type("test") + } + cy.get(interact.SPECTRUM_BUTTON).contains("Reset your password").click({ force: true }) + + // Confirm user logged in afer password change + cy.get(".avatar > .icon").click({ force: true }) + + cy.get(".spectrum-Menu-item").contains("Update user information").click({ force: true }) + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT) + .eq(0) + .invoke('val').should('eq', 'bbuser@test.com') + + // Logout and login as previous user + cy.logoutNoAppGrid() + cy.login() + }) + it("should delete a user", () => { - // Click Delete user button - cy.get(interact.SPECTRUM_BUTTON) - .contains("Delete user") - .click({ force: true }) - .then(() => { - // Confirm deletion within modal - cy.wait(500) - cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => { - cy.get(interact.SPECTRUM_BUTTON) - .contains("Delete user") - .click({ force: true }) - cy.wait(4000) - }) - }) - cy.get(interact.SPECTRUM_TABLE).should("not.have.text", "bbuser") + cy.deleteUser("bbuser@test.com") + cy.get(interact.SPECTRUM_TABLE, { timeout: 4000 }).should("not.have.text", "bbuser") }) }) }) diff --git a/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js b/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js new file mode 100644 index 0000000000..7827275620 --- /dev/null +++ b/packages/builder/cypress/integration/adminAndManagement/userSettings.spec.js @@ -0,0 +1,108 @@ +import filterTests from "../../support/filterTests" +const interact = require('../../support/interact') + +filterTests(["smoke", "all"], () => { + context("User Settings Menu", () => { + + before(() => { + cy.login() + }) + + it("should update user information via user settings menu", () => { + const fname = "test" + const lname = "user" + + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.updateUserInformation(fname, lname) + + // Go to user info and confirm name update + cy.contains("Users").click() + cy.contains("test@test.com").click() + + cy.get(interact.FIELD, { timeout: 1000 }).eq(2).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', fname) + }) + cy.get(interact.FIELD).eq(3).within(() => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).should('have.value', lname) + }) + }) + + it("should allow copying of the users API key", () => { + cy.get(".user-dropdown .avatar > .icon", { timeout: 2000 }).click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("View API key").click({ force: true }) + cy.get(interact.SPECTRUM_DIALOG_CONTENT).within(() => { + cy.get(interact.SPECTRUM_ICON).click({force: true}) + }) + // There may be timing issues with this on the smoke build + cy.wait(500) + cy.get(".spectrum-Toast-content") + .contains("URL copied to clipboard") + .should("be.visible") + }) + + it("should allow API key regeneration", () => { + // Get initial API key value + cy.get(interact.SPECTRUM_DIALOG_CONTENT) + .find(interact.SPECTRUM_TEXTFIELD_INPUT).invoke('val').as('keyOne') + + // Click re-generate key button + cy.get("button").contains("Re-generate key").click({ force: true }) + + // Verify API key was changed + cy.get(interact.SPECTRUM_DIALOG_CONTENT).within(() => { + cy.get('@keyOne').then((keyOne) => { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).invoke('val').should('not.eq', keyOne) + }) + }) + cy.closeModal() + }) + + it("should update password", () => { + // Access Update password modal + cy.get(".user-dropdown .avatar > .icon", { timeout: 2000 }).click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Update password").click({ force: true }) + + // Enter new password and update + cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => { + for (let i = 0; i < 2; i++) { + // password set to 'newpwd' + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).eq(i).type("newpwd") + } + cy.get("button").contains("Update password").click({ force: true }) + }) + + // Logout & in with new password + cy.logOut() + cy.login("test@test.com", "newpwd") + }) + + it("should open and close developer mode", () => { + cy.get(".user-dropdown .avatar > .icon", { timeout: 2000 }).click({ force: true }) + + // Close developer mode & verify + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Close developer mode").click({ force: true }) + cy.get(interact.SPECTRUM_SIDENAV).should('not.exist') // No config sections + cy.get(interact.CREATE_APP_BUTTON).should('not.exist') // No create app button + cy.get(".app").should('not.exist') // At least one app should be available + + // Open developer mode & verify + cy.get(".avatar > .icon").click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Open developer mode").click({ force: true }) + cy.get(interact.SPECTRUM_SIDENAV).should('exist') // config sections available + cy.get(interact.CREATE_APP_BUTTON).should('exist') // create app button available + cy.get(interact.APP_TABLE).should('exist') // App table available + }) + + after(() => { + // Change password back to original value + cy.get(".user-dropdown .avatar > .icon", { timeout: 2000 }).click({ force: true }) + cy.get(interact.SPECTRUM_MENU_ITEM).contains("Update password").click({ force: true }) + cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => { + for (let i = 0; i < 2; i++) { + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT).eq(i).type("test") + } + cy.get("button").contains("Update password").click({ force: true }) + }) + }) + }) +}) diff --git a/packages/builder/cypress/integration/appOverview.spec.js b/packages/builder/cypress/integration/appOverview.spec.js index 4b9d072338..d718f95b9f 100644 --- a/packages/builder/cypress/integration/appOverview.spec.js +++ b/packages/builder/cypress/integration/appOverview.spec.js @@ -136,7 +136,6 @@ filterTests(["all"], () => { .within(() => { cy.get(".confirm-wrap button").click({ force: true }) }) - cy.wait(1000) cy.visit(`${Cypress.config().baseUrl}/builder`) cy.get(".appTable .app-row-actions button") @@ -158,12 +157,9 @@ filterTests(["all"], () => { .contains("Manage") .eq(0) .click({ force: true }) - cy.wait(1000) - cy.get(".app-overview-actions-icon").within(() => { - cy.get(".spectrum-Icon").click({ force: true }) - }) - cy.get(".spectrum-Menu").contains("Edit icon").click() + cy.get(".edit-hover", { timeout: 1000 }).eq(0).click({ force: true }) // Select random icon + cy.wait(400) cy.get(".grid").within(() => { cy.get(".icon-item") .eq(Math.floor(Math.random() * 23) + 1) @@ -182,6 +178,7 @@ filterTests(["all"], () => { cy.get("@iconChange").its("response.statusCode").should("eq", 200) // Confirm icon has changed from default // Confirm colour has been applied + cy.get(".spectrum-ActionButton-label").contains("Back").click({ force: true }) cy.get(".appTable", { timeout: 2000 }).within(() => { cy.get("[aria-label]") .eq(0) @@ -265,10 +262,9 @@ filterTests(["all"], () => { //Downgrade the app for the test cy.alterAppVersion(appId, "0.0.1-alpha.0").then(() => { cy.reload() - cy.wait(1000) cy.log("Current deployment version: " + clientPackage.version) - cy.get(".version-status a").contains("Update").click() + cy.get(".version-status a", { timeout: 1000 }).contains("Update").click() cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".version-section .page-action button") @@ -336,8 +332,7 @@ filterTests(["all"], () => { }) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) - cy.get(".appTable .app-row-actions button") + cy.get(".appTable .app-row-actions button", { timeout: 1000 }) .contains("Manage") .eq(0) .click({ force: true }) @@ -345,8 +340,7 @@ filterTests(["all"], () => { cy.get(".spectrum-Tabs-item.is-selected").contains("Settings") cy.get(".details-section .page-action .spectrum-Button").scrollIntoView() - cy.wait(1000) - cy.get(".details-section .page-action .spectrum-Button").should( + cy.get(".details-section .page-action .spectrum-Button", { timeout: 1000 }).should( "be.disabled" ) }) @@ -375,27 +369,21 @@ filterTests(["all"], () => { .contains("Copy App ID") .click({ force: true }) }) - + cy.get(".spectrum-Toast-content") - .contains("App ID copied to clipboard.") - .should("be.visible") + .contains("App ID copied to clipboard.") + .should("be.visible") }) - it("Should allow unpublishing of the application", () => { + it("Should allow unpublishing of the application via the Unpublish link", () => { cy.visit(`${Cypress.config().baseUrl}/builder`) cy.get(".appTable .app-row-actions button") .contains("Manage") .eq(0) .click({ force: true }) - cy.get(".app-overview-actions-icon > .icon").click({ force: true }) - cy.get("[data-cy='app-overview-menu-popover']") - .eq(0) - .within(() => { - cy.get(".spectrum-Menu-item") - .contains("Unpublish") - .click({ force: true }) - cy.wait(500) + cy.get(`[data-cy="app-status"]`).within(() => { + cy.contains("Unpublish").click({ force: true }) }) cy.get("[data-cy='unpublish-modal']") @@ -406,9 +394,8 @@ filterTests(["all"], () => { cy.get(".overview-tab [data-cy='app-status']").within(() => { cy.get(".status-display").contains("Unpublished") - cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should( - "exist" - ) + cy.get(".status-display .icon svg[aria-label='GlobeStrike']") + .should("exist") }) }) diff --git a/packages/builder/cypress/integration/appPublishWorkflow.spec.js b/packages/builder/cypress/integration/appPublishWorkflow.spec.js index f0a7dd791a..a431051075 100644 --- a/packages/builder/cypress/integration/appPublishWorkflow.spec.js +++ b/packages/builder/cypress/integration/appPublishWorkflow.spec.js @@ -11,9 +11,8 @@ filterTests(['all'], () => { it("Should reflect the unpublished status correctly", () => { cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) - cy.get(interact.APP_TABLE_STATUS).eq(0) + cy.get(interact.APP_TABLE_STATUS, { timeout: 3000 }).eq(0) .within(() => { cy.contains("Unpublished") cy.get(interact.GLOBESTRIKE).should("exist") @@ -35,11 +34,10 @@ filterTests(['all'], () => { cy.get(interact.DEPLOY_APP_MODAL).should("be.visible") .within(() => { cy.get(interact.SPECTRUM_BUTTON).contains("Publish").click({ force : true }) - cy.wait(1000) }); //Verify that the app url is presented correctly to the user - cy.get(interact.DEPLOY_SUCCESS_MODAL) + cy.get(interact.DEPLOY_SUCCESS_MODAL, { timeout: 1000 }) .should("be.visible") .within(() => { let appUrl = Cypress.config().baseUrl + '/app/cypress-tests' @@ -48,9 +46,8 @@ filterTests(['all'], () => { }) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) - cy.get(interact.APP_TABLE_STATUS).eq(0) + cy.get(interact.APP_TABLE_STATUS, { timeout: 3000 }).eq(0) .within(() => { cy.contains("Published") cy.get(interact.GLOBE).should("exist") @@ -58,7 +55,7 @@ filterTests(['all'], () => { cy.get(interact.APP_TABLE_ROW_ACTION).eq(0) .within(() => { - cy.get(interact.SPECTRUM_BUTTON).contains("View") + cy.get(interact.SPECTRUM_BUTTON).contains("Manage") cy.get(interact.SPECTRUM_BUTTON).contains("Edit").click({ force: true }) }) @@ -83,24 +80,25 @@ filterTests(['all'], () => { cy.get("svg[aria-label='Globe']").should("exist") }) - cy.get(interact.APP_TABLE_ROW_ACTION).eq(0) + cy.get(interact.APP_TABLE).eq(0) .within(() => { - cy.get(interact.SPECTRUM_BUTTON).contains("View") cy.get(interact.APP_TABLE_APP_NAME).click({ force: true }) }) - cy.get(interact.SPECTRUM_LINK).contains('Unpublish').click(); + cy.get(interact.DEPLOYMENT_TOP_NAV).click() + cy.get(interact.PUBLISH_POPOVER_ACTION).click({ force: true }) + cy.get(interact.UNPUBLISH_MODAL) + .within(() => { + cy.get(interact.CONFIRM_WRAP_BUTTON).click({ force: true }) + }) cy.get(interact.UNPUBLISH_MODAL).should("be.visible") .within(() => { cy.get(interact.CONFIRM_WRAP_BUTTON).click({ force: true } )}) - cy.get(interact.DEPLOYMENT_TOP_NAV_GLOBESTRIKE).should("exist") - cy.visit(`${Cypress.config().baseUrl}/builder`) - - cy.get(interact.APP_TABLE_STATUS).eq(0).contains("Unpublished") + cy.get(interact.APP_TABLE_STATUS, { timeout: 1000 }).eq(0).contains("Unpublished") }) }) diff --git a/packages/builder/cypress/integration/autoScreensUI.spec.js b/packages/builder/cypress/integration/autoScreensUI.spec.js index eebeac3e71..ca997479ae 100644 --- a/packages/builder/cypress/integration/autoScreensUI.spec.js +++ b/packages/builder/cypress/integration/autoScreensUI.spec.js @@ -5,6 +5,7 @@ filterTests(['smoke', 'all'], () => { context("Auto Screens UI", () => { before(() => { cy.login() + cy.deleteAllApps() }) it("should disable the autogenerated screen options if no sources are available", () => { diff --git a/packages/builder/cypress/integration/createApp.spec.js b/packages/builder/cypress/integration/createApp.spec.js index df617e3d9f..097b70db30 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -6,14 +6,14 @@ filterTests(['smoke', 'all'], () => { before(() => { cy.login() - cy.deleteApp("Cypress Tests") + cy.deleteAllApps() }) if (!(Cypress.env("TEST_ENV"))) { it("should show the new user UI/UX", () => { - cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/create`) //added /portal/apps/create + cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/create`, { timeout: 5000 }) //added /portal/apps/create + cy.wait(1000) cy.get(interact.CREATE_APP_BUTTON).contains('Start from scratch').should("exist") - cy.get(interact.CREATE_APP_BUTTON).should("exist") cy.get(interact.TEMPLATE_CATEGORY_FILTER).should("exist") cy.get(interact.TEMPLATE_CATEGORY).should("exist") @@ -23,7 +23,7 @@ filterTests(['smoke', 'all'], () => { } it("should provide filterable templates", () => { - cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 }) cy.wait(500) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) @@ -48,11 +48,10 @@ filterTests(['smoke', 'all'], () => { }) it("should enforce a valid url before submission", () => { - cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 }) // Start create app process. If apps already exist, click second button - cy.get(interact.CREATE_APP_BUTTON).click({ force: true }) + cy.get(interact.CREATE_APP_BUTTON, { timeout: 1000 }).click({ force: true }) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) .its("body") .then(val => { @@ -92,21 +91,16 @@ filterTests(['smoke', 'all'], () => { it("should create the first application from scratch", () => { const appName = "Cypress Tests" - cy.createApp(appName) + cy.createApp(appName, false) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) cy.applicationInAppTable(appName) cy.deleteApp(appName) }) it("should create the first application from scratch with a default name", () => { - cy.createApp() - - cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) - + cy.createApp("", false) cy.applicationInAppTable("My app") cy.deleteApp("My app") }) @@ -116,26 +110,22 @@ filterTests(['smoke', 'all'], () => { cy.updateUserInformation("Ted", "Userman") - cy.createApp() + cy.createApp("", false) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) cy.applicationInAppTable("Teds app") cy.deleteApp("Teds app") - cy.wait(2000) //Accomodate names that end in 'S' cy.updateUserInformation("Chris", "Userman") - cy.createApp() + cy.createApp("", false) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) cy.applicationInAppTable("Chris app") cy.deleteApp("Chris app") - cy.wait(2000) cy.updateUserInformation("", "") }) @@ -145,7 +135,7 @@ filterTests(['smoke', 'all'], () => { cy.importApp(exportedApp, "") - cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 2000 }) cy.applicationInAppTable("My app") @@ -224,14 +214,12 @@ filterTests(['smoke', 'all'], () => { cy.createApp(appName) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) // Create second app const secondAppName = "Second App Demo" cy.createApp(secondAppName) cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) //Both applications should exist and be searchable cy.searchForApplication(appName) diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js index 6a4b70f8dc..b5ff406297 100644 --- a/packages/builder/cypress/integration/createAutomation.spec.js +++ b/packages/builder/cypress/integration/createAutomation.spec.js @@ -16,17 +16,15 @@ filterTests(['smoke', 'all'], () => { cy.get(interact.MODAL_INNER_WRAPPER).within(() => { cy.get("input").type("Add Row") cy.contains("Row Created").click({ force: true }) - cy.wait(500) - cy.get(interact.SPECTRUM_BUTTON_CTA).click() + cy.get(interact.SPECTRUM_BUTTON_CTA, { timeout: 500 }).click() }) // Setup trigger cy.get(interact.SPECTRUM_PICKER_LABEL).click() cy.wait(500) cy.contains("dog").click() - cy.wait(2000) // Create action - cy.get('[aria-label="AddCircle"]').eq(1).click() + cy.get('[aria-label="AddCircle"]', { timeout: 2000 }).eq(1).click() cy.get(interact.MODAL_INNER_WRAPPER).within(() => { cy.wait(1000) cy.contains("Create Row").trigger('mouseover').click().click() @@ -43,11 +41,9 @@ filterTests(['smoke', 'all'], () => { cy.contains("Finish and test automation").click() cy.get(interact.MODAL_INNER_WRAPPER).within(() => { - cy.wait(1000) - cy.get(interact.SPECTRUM_PICKER_LABEL).click() + cy.get(interact.SPECTRUM_PICKER_LABEL, { timeout: 1000 }).click() cy.contains("dog").click() - cy.wait(1000) - cy.get(interact.SPECTRUM_TEXTFIELD_INPUT) + cy.get(interact.SPECTRUM_TEXTFIELD_INPUT, { timeout: 1000 }) .first() .type("automationGoodboy") cy.get(interact.SPECTRUM_TEXTFIELD_INPUT) diff --git a/packages/builder/cypress/integration/createScreen.js b/packages/builder/cypress/integration/createScreen.spec.js similarity index 100% rename from packages/builder/cypress/integration/createScreen.js rename to packages/builder/cypress/integration/createScreen.spec.js diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js index 7d55a1f03c..da73c19fa6 100644 --- a/packages/builder/cypress/integration/createTable.spec.js +++ b/packages/builder/cypress/integration/createTable.spec.js @@ -10,9 +10,8 @@ filterTests(["smoke", "all"], () => { it("should create a new Table", () => { cy.createTable("dog") - cy.wait(1000) // Check if Table exists - cy.get(interact.TABLE_TITLE_H1).should("have.text", "dog") + cy.get(interact.TABLE_TITLE_H1, { timeout: 1000 }).should("have.text", "dog") }) it("adds a new column to the table", () => { @@ -40,7 +39,7 @@ filterTests(["smoke", "all"], () => { it("edits a row", () => { cy.contains("button", "Edit").click({ force: true }) - cy.wait(1000) + cy.wait(500) cy.get(interact.SPECTRUM_MODAL_INPUT).clear() cy.get(interact.SPECTRUM_MODAL_INPUT).type("Updated") cy.contains("Save").click() @@ -63,8 +62,7 @@ filterTests(["smoke", "all"], () => { cy.addRow([i]) } cy.reload() - cy.wait(2000) - cy.get(interact.SPECTRUM_PAGINATION).within(() => { + cy.get(interact.SPECTRUM_PAGINATION, { timeout: 2000 }).within(() => { cy.get(interact.SPECTRUM_ACTION_BUTTON).eq(1).click() }) cy.get(interact.SPECTRUM_PAGINATION).within(() => { @@ -79,10 +77,9 @@ filterTests(["smoke", "all"], () => { cy.get(interact.SPECTRUM_BUTTON).click({ force: true }) }) cy.get(interact.SPECTRUM_DIALOG_GRID).contains("Delete").click({ force: true }) - cy.wait(1000) // Confirm table only has one page - cy.get(interact.SPECTRUM_PAGINATION).within(() => { + cy.get(interact.SPECTRUM_PAGINATION, { timeout: 1000 }).within(() => { cy.get(interact.SPECTRUM_ACTION_BUTTON).eq(1).should("not.be.enabled") }) }) diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index e554f6f866..a2d09d97bf 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -69,8 +69,8 @@ filterTests(['smoke', 'all'], () => { cy.get(interact.SPECTRUM_BUTTON).contains("Save").click({ force: true }) }) - cy.wait(1000) + cy.wait(1000) cy.get(interact.TITLE).then($headers => { expect($headers).to.have.length(7) const headers = Array.from($headers).map(header => diff --git a/packages/builder/cypress/integration/customThemingProperties.spec.js b/packages/builder/cypress/integration/customThemingProperties.spec.js index ed3478ca67..e9de0985d0 100644 --- a/packages/builder/cypress/integration/customThemingProperties.spec.js +++ b/packages/builder/cypress/integration/customThemingProperties.spec.js @@ -34,7 +34,6 @@ filterTests(['all'], () => { Large = 16px */ it("should test button roundness", () => { const buttonRoundnessValues = ["0", "4px", "8px", "16px"] - cy.wait(1000) // Add button, change roundness and confirm value cy.addComponent("Button", null).then((componentId) => { buttonRoundnessValues.forEach(function (item, index){ diff --git a/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js b/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js index 1bee7b5ec1..14653d8286 100644 --- a/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js +++ b/packages/builder/cypress/integration/datasources/datasourceWizard.spec.js @@ -17,11 +17,10 @@ filterTests(['all'], () => { // Navigate back within datasource wizard cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Button").contains("Back").click({ force: true }) - cy.wait(1000) }) // Select PostgreSQL datasource again - cy.get(".item-list").contains(datasource).click() + cy.get(".item-list", { timeout: 1000 }).contains(datasource).click() cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Button").contains("Continue").click({ force: true }) }) diff --git a/packages/builder/cypress/integration/datasources/mySql.spec.js b/packages/builder/cypress/integration/datasources/mySql.spec.js index 98bb2f2acf..b79f5af9c6 100644 --- a/packages/builder/cypress/integration/datasources/mySql.spec.js +++ b/packages/builder/cypress/integration/datasources/mySql.spec.js @@ -111,10 +111,9 @@ filterTests(["all"], () => { // Save relationship & reload page cy.get(".spectrum-Button").contains("Save").click({ force: true }) cy.reload() - cy.wait(1000) }) // Confirm table length & relationship name - cy.get(".spectrum-Table") + cy.get(".spectrum-Table", { timeout: 1000 }) .eq(1) .find(".spectrum-Table-row") .its("length") @@ -136,15 +135,15 @@ filterTests(["all"], () => { cy.get(".spectrum-Table") .eq(1) .within(() => { - cy.get(".spectrum-Table-row").eq(0).click({ force: true }) - cy.wait(500) + cy.get(".spectrum-Table-cell").eq(0).click({ force: true }) }) - cy.get(".spectrum-Dialog-grid").within(() => { + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { cy.get(".spectrum-Button") .contains("Delete") .click({ force: true }) }) cy.reload() + cy.wait(500) } // Confirm relationships no longer exist cy.get(".spectrum-Body").should( @@ -217,9 +216,8 @@ filterTests(["all"], () => { cy.get(".spectrum-Button") .contains("Delete Query") .click({ force: true }) - cy.wait(1000) // Confirm deletion - cy.get(".nav-item").should("not.contain", queryName) + cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryName) }) } }) diff --git a/packages/builder/cypress/integration/datasources/oracle.spec.js b/packages/builder/cypress/integration/datasources/oracle.spec.js index 4c4d33d654..92a5737ff9 100644 --- a/packages/builder/cypress/integration/datasources/oracle.spec.js +++ b/packages/builder/cypress/integration/datasources/oracle.spec.js @@ -20,7 +20,7 @@ filterTests(["all"], () => { .click({ force: true }) cy.wait(500) // Confirm config contains localhost - cy.get(".spectrum-Textfield-input") + cy.get(".spectrum-Textfield-input", { timeout: 500 }) .eq(1) .should("have.value", "localhost") // Add another Oracle data source, configure & skip table fetch @@ -140,9 +140,8 @@ filterTests(["all"], () => { .eq(1) .within(() => { cy.get(".spectrum-Table-row").eq(0).click() - cy.wait(500) }) - cy.get(".spectrum-Dialog-grid").within(() => { + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { cy.get(".spectrum-Button") .contains("Delete") .click({ force: true }) @@ -221,10 +220,9 @@ filterTests(["all"], () => { cy.get(".spectrum-Button") .contains("Delete Query") .click({ force: true }) - cy.wait(1000) // Confirm deletion - cy.get(".nav-item").should("not.contain", queryName) + cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryName) }) } }) diff --git a/packages/builder/cypress/integration/datasources/postgreSql.spec.js b/packages/builder/cypress/integration/datasources/postgreSql.spec.js index 7448e6b27d..de959e203c 100644 --- a/packages/builder/cypress/integration/datasources/postgreSql.spec.js +++ b/packages/builder/cypress/integration/datasources/postgreSql.spec.js @@ -35,6 +35,7 @@ filterTests(["all"], () => { // Check response from datasource after adding configuration cy.wait("@datasource") cy.get("@datasource").its("response.statusCode").should("eq", 200) + cy.wait(2000) // Confirm fetch tables was successful cy.get(".spectrum-Table") .eq(0) @@ -113,13 +114,13 @@ filterTests(["all"], () => { cy.get(".spectrum-Table") .eq(1) .within(() => { - cy.get(".spectrum-Table-row").eq(0).click({ force: true }) - cy.wait(500) + cy.get(".spectrum-Table-cell").eq(0).click({ force: true }) }) - cy.get(".spectrum-Dialog-grid").within(() => { + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { cy.get(".spectrum-Button").contains("Delete").click({ force: true }) }) cy.reload() + cy.wait(500) // Confirm relationship was deleted cy.get(".spectrum-Table") .eq(1) @@ -159,7 +160,7 @@ filterTests(["all"], () => { switchSchema("randomText") // No tables displayed - cy.get(".spectrum-Body").eq(2).should("contain", "No tables found") + cy.get(".spectrum-Body", { timeout: 5000 }).eq(2).should("contain", "No tables found") // Previously created query should be visible cy.get(".spectrum-Table").should("contain", queryName) @@ -170,7 +171,7 @@ filterTests(["all"], () => { switchSchema("1") // Confirm tables exist - Check for specific one - cy.get(".spectrum-Table").eq(0).should("contain", "test") + cy.get(".spectrum-Table", { timeout: 5000 }).eq(0).should("contain", "test") cy.get(".spectrum-Table") .eq(0) .find(".spectrum-Table-row") @@ -184,7 +185,7 @@ filterTests(["all"], () => { switchSchema("public") // Confirm tables exist - again - cy.get(".spectrum-Table").eq(0).should("contain", "REGIONS") + cy.get(".spectrum-Table", { timeout: 5000 }).eq(0).should("contain", "REGIONS") cy.get(".spectrum-Table") .eq(0) .find(".spectrum-Table-row") @@ -230,7 +231,9 @@ filterTests(["all"], () => { // Run and Save query cy.get(".spectrum-Button").contains("Run Query").click({ force: true }) cy.wait(500) - cy.get(".spectrum-Button").contains("Save Query").click({ force: true }) + cy.get(".spectrum-Button", { timeout: 500 }).contains("Save Query").click({ force: true }) + //cy.reload() + //cy.wait(500) cy.get(".nav-item").should("contain", queryRename) }) @@ -247,9 +250,8 @@ filterTests(["all"], () => { cy.get(".spectrum-Button") .contains("Delete Query") .click({ force: true }) - cy.wait(1000) // Confirm deletion - cy.get(".nav-item").should("not.contain", queryName) + cy.get(".nav-item", { timeout: 1000 }).should("not.contain", queryName) }) const switchSchema = schema => { @@ -271,7 +273,7 @@ filterTests(["all"], () => { .click({ force: true }) }) cy.reload() - cy.wait(5000) + cy.wait(1000) } } }) diff --git a/packages/builder/cypress/integration/datasources/rest.spec.js b/packages/builder/cypress/integration/datasources/rest.spec.js index a15487c01b..488c30c0cf 100644 --- a/packages/builder/cypress/integration/datasources/rest.spec.js +++ b/packages/builder/cypress/integration/datasources/rest.spec.js @@ -14,8 +14,7 @@ filterTests(["smoke", "all"], () => { // Select REST data source cy.selectExternalDatasource(datasource) // Enter incorrect api & attempt to send query - cy.wait(500) - cy.get(".spectrum-Button").contains("Add query").click({ force: true }) + cy.get(".spectrum-Button", { timeout: 500 }).contains("Add query").click({ force: true }) cy.intercept("**/preview").as("queryError") cy.get("input").clear().type("random text") cy.get(".spectrum-Button").contains("Send").click({ force: true }) @@ -36,8 +35,7 @@ filterTests(["smoke", "all"], () => { // createRestQuery confirms query creation cy.createRestQuery("GET", restUrl, "/breweries") // Confirm status code response within REST datasource - cy.wait(1000) - cy.get(".stats").within(() => { + cy.get(".stats", { timeout: 1000 }).within(() => { cy.get(".spectrum-FieldLabel") .eq(0) .should("contain", 200) diff --git a/packages/builder/cypress/integration/renameAnApplication.spec.js b/packages/builder/cypress/integration/renameAnApplication.spec.js index 703535a507..370efadff2 100644 --- a/packages/builder/cypress/integration/renameAnApplication.spec.js +++ b/packages/builder/cypress/integration/renameAnApplication.spec.js @@ -13,16 +13,13 @@ filterTests(["all"], () => { const appRename = "Cypress Renamed" // Rename app, Search for app, Confirm name was changed cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) renameApp(appName, appRename) cy.reload() - cy.wait(1000) cy.searchForApplication(appRename) cy.get(interact.APP_TABLE).find(interact.TITLE).should("have.length", 1) cy.applicationInAppTable(appRename) // Set app name back to Cypress Tests cy.reload() - cy.wait(1000) renameApp(appRename, appName) }) @@ -43,7 +40,6 @@ filterTests(["all"], () => { }) // Rename app, Search for app, Confirm name was changed cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) renameApp(appName, appRename, true) cy.get(interact.APP_TABLE).find(interact.WRAPPER).should("have.length", 1) cy.applicationInAppTable(appRename) @@ -52,13 +48,9 @@ filterTests(["all"], () => { it("Should try to rename an application to have no name", () => { const appName = "Cypress Tests" cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) renameApp(appName, " ", false, true) - cy.wait(500) // Close modal and confirm name has not been changed - cy.get(interact.SPECTRUM_DIALOG_GRID).contains("Cancel").click() - cy.reload() - cy.wait(1000) + cy.get(interact.SPECTRUM_DIALOG_GRID, { timeout: 1000 }).contains("Cancel").click() cy.applicationInAppTable(appName) }) @@ -66,8 +58,7 @@ filterTests(["all"], () => { // It is not possible to have applications with the same name const appName = "Cypress Tests" cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) - cy.get(interact.SPECTRUM_BUTTON) + cy.get(interact.SPECTRUM_BUTTON), { timeout: 500 } .contains("Create app") .click({ force: true }) cy.contains(/Start from scratch/).click() @@ -90,13 +81,10 @@ filterTests(["all"], () => { const numberName = 12345 const specialCharName = "£$%^" cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(500) renameApp(appName, numberName) cy.reload() - cy.wait(1000) cy.applicationInAppTable(numberName) cy.reload() - cy.wait(1000) renameApp(numberName, specialCharName) cy.get(interact.ERROR).should( "have.text", @@ -104,13 +92,12 @@ filterTests(["all"], () => { ) // Set app name back to Cypress Tests cy.reload() - cy.wait(1000) renameApp(numberName, appName) }) const renameApp = (originalName, changedName, published, noName) => { cy.searchForApplication(originalName) - cy.get(interact.APP_TABLE).within(() => { + cy.get(interact.APP_TABLE, { timeout: 1000 }).within(() => { cy.get(".app-row-actions button") .contains("Manage") .eq(0) diff --git a/packages/builder/cypress/integration/revertApp.spec.js b/packages/builder/cypress/integration/revertApp.spec.js index 93501ab972..4c6f245b76 100644 --- a/packages/builder/cypress/integration/revertApp.spec.js +++ b/packages/builder/cypress/integration/revertApp.spec.js @@ -36,8 +36,8 @@ filterTests(['smoke', 'all'], () => { cy.get(interact.SPECTRUM_BUTTON_GROUP).within(() => { cy.get(interact.SPECTRUM_BUTTON).contains("Publish").click({ force: true }) }) - cy.wait(1000) - cy.get(interact.SPECTRUM_BUTTON_GROUP).within(() => { + cy.wait(1000) // Wait for next modal to finish loading + cy.get(interact.SPECTRUM_BUTTON_GROUP, { timeout: 1000 }).within(() => { cy.get(interact.SPECTRUM_BUTTON).contains("Done").click({ force: true }) }) @@ -50,18 +50,17 @@ filterTests(['smoke', 'all'], () => { cy.get(interact.SPECTRUM_DIALOG_GRID).within(() => { // Click Revert cy.get(interact.SPECTRUM_BUTTON).contains("Revert").click({ force: true }) - cy.wait(1000) + cy.wait(2000) // Wait for app to finish reverting }) // Confirm Paragraph component is still visible - cy.get(interact.ROOT).contains("New Paragraph") + cy.get(interact.ROOT, { timeout: 1000 }).contains("New Paragraph") // Confirm Button component is not visible - cy.get(interact.ROOT).should("not.have.text", "New Button") - cy.wait(500) + cy.get(interact.ROOT, { timeout: 1000 }).should("not.have.text", "New Button") }) it("should enter incorrect app name when reverting", () => { // Click Revert - cy.get(interact.TOP_RIGHT_NAV).within(() => { + cy.get(interact.TOP_RIGHT_NAV, { timeout: 1000 }).within(() => { cy.get(interact.AREA_LABEL_REVERT).click({ force: true }) }) // Enter incorrect app name diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 29aabc4611..9d77b89c57 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -3,7 +3,7 @@ Cypress.on("uncaught:exception", () => { }) // ACCOUNTS & USERS -Cypress.Commands.add("login", () => { +Cypress.Commands.add("login", (email, password) => { cy.visit(`${Cypress.config().baseUrl}/builder`) cy.wait(2000) cy.url().then(url => { @@ -17,8 +17,13 @@ Cypress.Commands.add("login", () => { if (url.includes("builder/auth/login") || url.includes("builder/admin")) { // login cy.contains("Sign in to Budibase").then(() => { - cy.get("input").first().type("test@test.com") - cy.get('input[type="password"]').type("test") + if (email == null) { + cy.get("input").first().type("test@test.com") + cy.get('input[type="password"]').type("test") + } else { + cy.get("input").first().type(email) + cy.get('input[type="password"]').type(password) + } cy.get("button").first().click({ force: true }) cy.wait(1000) }) @@ -27,7 +32,7 @@ Cypress.Commands.add("login", () => { }) Cypress.Commands.add("logOut", () => { - cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 2000 }) cy.get(".user-dropdown .avatar > .icon").click({ force: true }) cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => { cy.get("li[data-cy='user-logout']").click({ force: true }) @@ -35,24 +40,58 @@ Cypress.Commands.add("logOut", () => { cy.wait(2000) }) +Cypress.Commands.add("logoutNoAppGrid", () => { + // Logs user out when app grid is not present + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.get(".avatar > .icon").click({ force: true }) + cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => { + cy.get(".spectrum-Menu-item").contains("Log out").click({ force: true }) + }) + cy.wait(2000) +}) + Cypress.Commands.add("createUser", email => { // quick hacky recorded way to create a user cy.contains("Users").click() cy.get(`[data-cy="add-user"]`).click() - cy.get(".spectrum-Picker-label").click() - cy.get(".spectrum-Menu-item:nth-child(2) > .spectrum-Menu-itemLabel").click() + cy.get(".spectrum-Dialog-grid").within(() => { + cy.get(".spectrum-Picker-label").click() + cy.get( + ".spectrum-Menu-item:nth-child(2) > .spectrum-Menu-itemLabel" + ).click() - //Onboarding type selector - cy.get( - ":nth-child(2) > .spectrum-Form-itemField > .spectrum-Textfield > .spectrum-Textfield-input" - ) - .first() - .type(email, { force: true }) - cy.get(".spectrum-Button--cta").click({ force: true }) + // Onboarding type selector + cy.get(".spectrum-Textfield-input") + .eq(0) + .first() + .type(email, { force: true }) + cy.get(".spectrum-Button--cta").click({ force: true }) + }) +}) + +Cypress.Commands.add("deleteUser", email => { + // Assumes user has access to Users section + cy.contains("Users", { timeout: 2000 }).click() + cy.contains(email).click() + + // Click Delete user button + cy.get(".spectrum-Button") + .contains("Delete user") + .click({ force: true }) + .then(() => { + // Confirm deletion within modal + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { + cy.get(".spectrum-Button") + .contains("Delete user") + .click({ force: true }) + }) + }) }) Cypress.Commands.add("updateUserInformation", (firstName, lastName) => { - cy.get(".user-dropdown .avatar > .icon").click({ force: true }) + cy.get(".user-dropdown .avatar > .icon", { timeout: 2000 }).click({ + force: true, + }) cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => { cy.get("li[data-cy='user-info']").click({ force: true }) @@ -95,9 +134,8 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => { const shouldCreateDefaultTable = typeof addDefaultTable != "boolean" ? true : addDefaultTable - cy.visit(`${Cypress.config().baseUrl}/builder`) - cy.wait(1000) - cy.get(`[data-cy="create-app-btn"]`).click({ force: true }) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 }) + cy.get(`[data-cy="create-app-btn"]`, { timeout: 2000 }).click({ force: true }) // If apps already exist cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) @@ -117,7 +155,7 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => { cy.get(".spectrum-ButtonGroup") .contains("Create app") .click({ force: true }) - cy.wait(10000) + cy.wait(2000) }) if (shouldCreateDefaultTable) { cy.createTable("Cypress Tests", true) @@ -125,7 +163,7 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => { }) Cypress.Commands.add("deleteApp", name => { - cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 }) cy.wait(2000) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) .its("body") @@ -150,14 +188,15 @@ Cypress.Commands.add("deleteApp", name => { cy.get(actionEleId).within(() => { cy.contains("Manage").click({ force: true }) }) - cy.wait(1000) + cy.wait(500) // Unpublish first if needed cy.get(`[data-cy="app-status"]`).then($status => { - if ($status.text().includes("Last published")) { - cy.contains("Unpublish").click() + if ($status.text().includes("- Unpublish")) { + // Exact match for Unpublish + cy.contains("Unpublish").click({ force: true }) cy.get(".spectrum-Modal").within(() => { - cy.contains("Unpublish app").click() + cy.contains("Unpublish app").click({ force: true }) }) } }) @@ -276,16 +315,18 @@ Cypress.Commands.add("alterAppVersion", (appId, version) => { }) Cypress.Commands.add("importApp", (exportFilePath, name) => { - cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000 }) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) .its("body") .then(val => { if (val.length > 0) { cy.get(`[data-cy="create-app-btn"]`).click({ force: true }) - cy.wait(500) } - cy.get(`[data-cy="import-app-btn"]`).click({ force: true }) + cy.wait(500) + cy.get(`[data-cy="import-app-btn"]`).click({ + force: true, + }) }) cy.get(".spectrum-Modal").within(() => { @@ -303,7 +344,7 @@ Cypress.Commands.add("importApp", (exportFilePath, name) => { cy.get(".confirm-wrap button") .should("not.be.disabled") .click({ force: true }) - cy.wait(5000) + cy.wait(3000) }) }) @@ -332,7 +373,8 @@ Cypress.Commands.add("searchForApplication", appName => { // Assumes there are no others Cypress.Commands.add("applicationInAppTable", appName => { - cy.get(".appTable").within(() => { + cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 10000 }) + cy.get(".appTable", { timeout: 2000 }).within(() => { cy.get(".title").contains(appName).should("exist") }) }) @@ -360,7 +402,7 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => { cy.navigateToDataSection() cy.get(`[data-cy="new-table"]`).click() } - cy.wait(5000) + cy.wait(2000) cy.get(".item") .contains("Budibase DB") .click({ force: true }) @@ -368,8 +410,7 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => { cy.get(".spectrum-Button").contains("Continue").click({ force: true }) }) cy.get(".spectrum-Modal").within(() => { - cy.wait(1000) - cy.get("input").first().type(tableName).blur() + cy.get("input", { timeout: 1000 }).first().type(tableName).blur() cy.get(".spectrum-ButtonGroup").contains("Create").click() }) cy.contains(tableName).should("be.visible") @@ -451,8 +492,7 @@ Cypress.Commands.add("addCustomSourceOptions", totalOptions => { .contains("Add Option") .click({ force: true }) .then(() => { - cy.wait(500) - cy.get("[placeholder='Label']").eq(i).type(i) + cy.get("[placeholder='Label']", { timeout: 500 }).eq(i).type(i) cy.get("[placeholder='Value']").eq(i).type(i) }) } @@ -464,10 +504,14 @@ Cypress.Commands.add("addCustomSourceOptions", totalOptions => { // DESIGN AREA Cypress.Commands.add("addComponent", (category, component) => { if (category) { - cy.get(`[data-cy="category-${category}"]`).click({ force: true }) + cy.get(`[data-cy="category-${category}"]`, { timeout: 1000 }).click({ + force: true, + }) } if (component) { - cy.get(`[data-cy="component-${component}"]`).click({ force: true }) + cy.get(`[data-cy="component-${component}"]`, { timeout: 1000 }).click({ + force: true, + }) } cy.wait(1000) cy.location().then(loc => { @@ -496,15 +540,14 @@ Cypress.Commands.add("createScreen", (route, accessLevelLabel) => { cy.get(".spectrum-Modal").within(() => { cy.get("[data-cy='blank-screen']").click() cy.get(".spectrum-Button").contains("Continue").click({ force: true }) - cy.wait(500) }) - cy.get(".spectrum-Dialog-grid").within(() => { + cy.wait(500) + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { cy.get(".spectrum-Form-itemField").eq(0).type(route) - cy.get(".spectrum-Button").contains("Continue").click({ force: true }) - cy.wait(1000) + cy.get(".confirm-wrap").contains("Continue").click({ force: true }) }) - cy.get(".spectrum-Modal").within(() => { + cy.get(".spectrum-Modal", { timeout: 1000 }).within(() => { if (accessLevelLabel) { cy.get(".spectrum-Picker-label").click() cy.wait(500) @@ -522,10 +565,12 @@ Cypress.Commands.add( cy.get(".spectrum-Modal").within(() => { cy.get(".item").contains("Autogenerated screens").click() cy.get(".spectrum-Button").contains("Continue").click({ force: true }) - cy.wait(500) }) - cy.get(".spectrum-Modal [data-cy='data-source-modal']").within(() => { + cy.get(".spectrum-Modal [data-cy='data-source-modal']", { + timeout: 500, + }).within(() => { for (let i = 0; i < datasourceNames.length; i++) { + cy.wait(500) cy.get(".data-source-entry").contains(datasourceNames[i]).click() //Ensure the check mark is visible cy.get(".data-source-entry") @@ -574,7 +619,7 @@ Cypress.Commands.add( // NAVIGATION Cypress.Commands.add("navigateToFrontend", () => { // Clicks on Design tab and then the Home nav item - cy.wait(1000) + cy.wait(500) cy.contains("Design").click() cy.get(".spectrum-Search").type("/") cy.get(".nav-item").contains("home").click() @@ -606,11 +651,11 @@ Cypress.Commands.add("selectExternalDatasource", datasourceName => { cy.get(".add-button").click() }) // Clicks specified datasource & continue - cy.wait(1000) - cy.get(".item-list").contains(datasourceName).click() + cy.get(".item-list", { timeout: 1000 }).contains(datasourceName).click() cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Button").contains("Continue").click({ force: true }) }) + cy.wait(500) }) Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => { @@ -618,8 +663,7 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => { // Adds the config for specified datasource & fetches tables // Currently supports MySQL, PostgreSQL, Oracle // Host IP Address - cy.wait(500) - cy.get(".spectrum-Dialog-grid").within(() => { + cy.get(".spectrum-Dialog-grid", { timeout: 500 }).within(() => { cy.get(".form-row") .eq(0) .within(() => { @@ -719,16 +763,18 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => { Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => { // addExternalDatasource should be called prior to this // Configures REST datasource & sends query - cy.wait(1000) - cy.get(".spectrum-Button").contains("Add query").click({ force: true }) + cy.get(".spectrum-Button", { timeout: 1000 }) + .contains("Add query") + .click({ force: true }) // Select Method & add Rest URL cy.get(".spectrum-Picker-label").eq(1).click() cy.get(".spectrum-Menu").contains(method).click() cy.get("input").clear().type(restUrl) // Send query cy.get(".spectrum-Button").contains("Send").click({ force: true }) - cy.wait(500) - cy.get(".spectrum-Button").contains("Save").click({ force: true }) + cy.get(".spectrum-Button", { timeout: 500 }) + .contains("Save") + .click({ force: true }) cy.get(".hierarchy-items-container") .should("contain", method) .and("contain", queryPrettyName) diff --git a/packages/builder/cypress/support/interact.js b/packages/builder/cypress/support/interact.js index f0fae61660..0b31d8a8c5 100644 --- a/packages/builder/cypress/support/interact.js +++ b/packages/builder/cypress/support/interact.js @@ -62,6 +62,7 @@ export const GLOBESTRIKE = "svg[aria-label=GlobeStrike]" export const GLOBE = "svg[aria-label=Globe]" export const UNPUBLISH_MODAL = "[data-cy=unpublish-modal]" export const CONFIRM_WRAP_BUTTON = ".confirm-wrap button" +export const DEPLOYMENT_TOP_NAV = ".deployment-top-nav" //changeAppiconAndColour export const APP_ROW_ACTION = ".app-row-actions-icon" @@ -97,13 +98,16 @@ export const ACTION_SPECTRUM_ICON = ".actions .spectrum-Icon" export const SPECTRUM_MENU_CHILD2 = ".spectrum-Menu > :nth-child(2)" export const DELETE_TABLE_CONFIRM = '[data-cy="delete-table-confirm"]' -//createUSerAndRoles +//adminAndManagement Folder export const SPECTRUM_TABLE = ".spectrum-Table" export const SPECTRUM_SIDENAV = ".spectrum-SideNav" export const SPECTRUM_TABLE_ROW = ".spectrum-Table-row" export const SPECTRUM_TABLE_CELL = ".spectrum-Table-cell" export const FIELD = ".field" export const CONTAINER = ".container" +export const REGENERATE = ".regenerate" +export const SPECTRUM_DIALOG_CONTENT = ".spectrum-Dialog-content" +export const SPECTRUM_ICON = ".spectrum-Icon" //createView export const SPECTRUM_MENU_ITEM_LABEL = ".spectrum-Menu-itemLabel" @@ -113,14 +117,17 @@ export const TOP_RIGHT_NAV = ".toprightnav" export const AREA_LABEL_REVERT = "[aria-label=Revert]" export const ROOT = ".root" -//quertLevelTransformers +//queryLevelTransformers export const SPECTRUM_TABS_ITEM = ".spectrum-Tabs-itemLabel" export const CODEMIRROR_TEXTAREA = ".CodeMirror textarea" -//renemaApplication +//renameApplication export const WRAPPER = ".wrapper" export const ERROR = ".error" export const AREA_LABEL_MORE = "[aria-label=More]" export const APP_ROW_ACTION_MENU_POPOVER = '[data-cy="app-row-actions-menu-popover"]' -export const SPECTRUM_MENU_ITEMM = ".spectrum-Menu-item" +export const SPECTRUM_MENU_ITEM = ".spectrum-Menu-item" + +//commands +export const HOME_LOGO = ".home-logo" diff --git a/packages/builder/package.json b/packages/builder/package.json index 404fd3b1cc..7105d0817c 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.0.212-alpha.6", + "version": "1.0.212-alpha.9", "license": "GPL-3.0", "private": true, "scripts": { @@ -69,10 +69,10 @@ } }, "dependencies": { - "@budibase/bbui": "^1.0.212-alpha.6", - "@budibase/client": "^1.0.212-alpha.6", - "@budibase/frontend-core": "^1.0.212-alpha.6", - "@budibase/string-templates": "^1.0.212-alpha.6", + "@budibase/bbui": "^1.0.212-alpha.9", + "@budibase/client": "^1.0.212-alpha.9", + "@budibase/frontend-core": "^1.0.212-alpha.9", + "@budibase/string-templates": "^1.0.212-alpha.9", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/helpers/pagination.js b/packages/builder/src/helpers/pagination.js new file mode 100644 index 0000000000..122973f1a1 --- /dev/null +++ b/packages/builder/src/helpers/pagination.js @@ -0,0 +1,66 @@ +import { writable } from "svelte/store" + +function defaultValue() { + return { + nextPage: null, + page: undefined, + hasPrevPage: false, + hasNextPage: false, + loading: false, + pageNumber: 1, + pages: [], + } +} + +export function createPaginationStore() { + const { subscribe, set, update } = writable(defaultValue()) + + function prevPage() { + update(state => { + state.pageNumber-- + state.nextPage = state.pages.pop() + state.page = state.pages[state.pages.length - 1] + state.hasPrevPage = state.pageNumber > 1 + return state + }) + } + + function nextPage() { + update(state => { + state.pageNumber++ + state.page = state.nextPage + state.pages.push(state.page) + state.hasPrevPage = state.pageNumber > 1 + return state + }) + } + + function fetched(hasNextPage, nextPage) { + update(state => { + state.hasNextPage = hasNextPage + state.nextPage = nextPage + state.loading = false + return state + }) + } + + function loading(loading = true) { + update(state => { + state.loading = loading + return state + }) + } + + function reset() { + set(defaultValue()) + } + + return { + subscribe, + prevPage, + nextPage, + fetched, + loading, + reset, + } +} diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte index 5583a48b7d..88a8fb6c5d 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte @@ -10,7 +10,9 @@ } from "@budibase/bbui" import { createValidationStore, emailValidator } from "helpers/validation" import { users } from "stores/portal" + import { createEventDispatcher } from "svelte" + const dispatch = createEventDispatcher() const password = Math.random().toString(36).substring(2, 22) const options = ["Email onboarding", "Basic onboarding"] const [email, error, touched] = createValidationStore("", emailValidator) @@ -39,6 +41,7 @@ forceResetPassword: true, }) notifications.success("Successfully created user") + dispatch("created") } catch (error) { notifications.error("Error creating user") } diff --git a/packages/builder/src/pages/builder/portal/manage/users/index.svelte b/packages/builder/src/pages/builder/portal/manage/users/index.svelte index 1d9c245480..0da8c1345a 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/index.svelte @@ -12,41 +12,36 @@ Layout, Modal, notifications, + Pagination, } from "@budibase/bbui" import TagsRenderer from "./_components/TagsTableRenderer.svelte" import AddUserModal from "./_components/AddUserModal.svelte" import { users } from "stores/portal" - import { onMount } from "svelte" + import { createPaginationStore } from "helpers/pagination" const schema = { email: {}, developmentAccess: { displayName: "Development Access", type: "boolean" }, adminAccess: { displayName: "Admin Access", type: "boolean" }, - // role: { type: "options" }, group: {}, - // access: {}, - // group: {} } - let search - $: filteredUsers = $users - .filter(user => user.email.includes(search || "")) - .map(user => ({ - ...user, - group: ["All users"], - developmentAccess: !!user.builder?.global, - adminAccess: !!user.admin?.global, - })) + let pageInfo = createPaginationStore() + let search = undefined + $: page = $pageInfo.page + $: fetchUsers(page, search) let createUserModal - onMount(async () => { + async function fetchUsers(page, search) { try { - await users.init() + pageInfo.loading() + await users.search({ page, search }) + pageInfo.fetched($users.hasNextPage, $users.nextPage) } catch (error) { notifications.error("Error getting user list") } - }) + } @@ -75,17 +70,31 @@ $goto(`./${detail._id}`)} {schema} - data={filteredUsers || $users} + data={$users.data} allowEditColumns={false} allowEditRows={false} allowSelectRows={false} customRenderers={[{ column: "group", component: TagsRenderer }]} /> + - + { + pageInfo.reset() + await fetchUsers() + }} + />