diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js
index ba9a7831db..59c3dc9e16 100644
--- a/packages/backend-core/src/context/index.js
+++ b/packages/backend-core/src/context/index.js
@@ -174,9 +174,11 @@ function getDB(key, opts) {
if (db && isEqual(opts, storedOpts)) {
return db
}
+
const appId = exports.getAppId()
const CouchDB = getCouch()
let toUseAppId
+
switch (key) {
case ContextKeys.CURRENT_DB:
toUseAppId = appId
diff --git a/packages/bbui/src/ActionMenu/ActionMenu.svelte b/packages/bbui/src/ActionMenu/ActionMenu.svelte
index 08425e8f59..c5602d6b0c 100644
--- a/packages/bbui/src/ActionMenu/ActionMenu.svelte
+++ b/packages/bbui/src/ActionMenu/ActionMenu.svelte
@@ -6,6 +6,7 @@
export let disabled = false
export let align = "left"
export let portalTarget
+ export let dataCy
let anchor
let dropdown
@@ -36,7 +37,7 @@
-
+
diff --git a/packages/bbui/src/Icon/Icon.svelte b/packages/bbui/src/Icon/Icon.svelte
index fbaac7098c..eee1d7fbae 100644
--- a/packages/bbui/src/Icon/Icon.svelte
+++ b/packages/bbui/src/Icon/Icon.svelte
@@ -3,6 +3,9 @@
-
+
+ {#if tooltip && showTooltip}
+
+
+
+ {/if}
+
diff --git a/packages/bbui/src/Modal/ModalContent.svelte b/packages/bbui/src/Modal/ModalContent.svelte
index 89c10bb625..10cd4b10ba 100644
--- a/packages/bbui/src/Modal/ModalContent.svelte
+++ b/packages/bbui/src/Modal/ModalContent.svelte
@@ -23,6 +23,7 @@
export let secondaryButtonText = undefined
export let secondaryAction = undefined
export let secondaryButtonWarning = false
+ export let dataCy = null
const { hide, cancel } = getContext(Context.Modal)
let loading = false
@@ -63,21 +64,26 @@
role="dialog"
tabindex="-1"
aria-modal="true"
+ data-cy={dataCy}
>
- {#if title}
+ {#if title || $$slots.header}
{#if showDivider}
{/if}
{/if}
+
diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte
index 6c9c6cc9a3..1017ef71fc 100644
--- a/packages/bbui/src/Popover/Popover.svelte
+++ b/packages/bbui/src/Popover/Popover.svelte
@@ -10,6 +10,17 @@
export let anchor
export let align = "right"
export let portalTarget
+ export let dataCy
+
+ export let direction = "bottom"
+ export let showTip = false
+
+ let tipSvg =
+ ''
+
+ $: tooltipClasses = showTip
+ ? `spectrum-Popover--withTip spectrum-Popover--${direction}`
+ : ""
export const show = () => {
dispatch("open")
@@ -37,9 +48,14 @@
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
on:keydown={handleEscape}
- class="spectrum-Popover is-open"
+ class={"spectrum-Popover is-open " + (tooltipClasses || "")}
role="presentation"
+ data-cy={dataCy}
>
+ {#if showTip}
+ {@html tipSvg}
+ {/if}
+
@@ -49,4 +65,13 @@
.spectrum-Popover {
min-width: var(--spectrum-global-dimension-size-2000) !important;
}
+ .spectrum-Popover.is-open.spectrum-Popover--withTip {
+ margin-top: var(--spacing-xs);
+ margin-left: var(--spacing-xl);
+ }
+ :global(.spectrum-Popover--bottom .spectrum-Popover-tip),
+ :global(.spectrum-Popover--top .spectrum-Popover-tip) {
+ left: 90%;
+ margin-left: calc(var(--spectrum-global-dimension-size-150) * -1);
+ }
diff --git a/packages/builder/cypress/integration/appPublishWorkflow.spec.js b/packages/builder/cypress/integration/appPublishWorkflow.spec.js
new file mode 100644
index 0000000000..d18233e0e7
--- /dev/null
+++ b/packages/builder/cypress/integration/appPublishWorkflow.spec.js
@@ -0,0 +1,112 @@
+import filterTests from "../support/filterTests"
+
+filterTests(['all'], () => {
+ context("Publish Application Workflow", () => {
+ before(() => {
+ cy.login()
+ cy.createTestApp()
+ })
+
+ it("Should reflect the unpublished status correctly", () => {
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.wait(1000)
+
+ cy.get(".appTable .app-status").eq(0)
+ .within(() => {
+ cy.contains("Unpublished")
+ cy.get("svg[aria-label='GlobeStrike']").should("exist")
+ })
+
+ cy.get(".appTable .app-row-actions").eq(0)
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Preview")
+ cy.get(".spectrum-Button").contains("Edit").click({ force: true })
+ })
+
+ cy.get(".deployment-top-nav svg[aria-label='GlobeStrike']").should("exist")
+ cy.get(".deployment-top-nav svg[aria-label='Globe']").should("not.exist")
+ })
+
+ it("Should publish an application and correctly reflect that", () => {
+ //Assuming the previous test was run and the unpublished app is open in edit mode.
+ cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true })
+
+ cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".spectrum-Button").contains("Publish").click({ force : true })
+ cy.wait(1000)
+ });
+
+ //Verify that the app url is presented correctly to the user
+ cy.get(".spectrum-Modal [data-cy='deploy-app-success-modal']")
+ .should("be.visible")
+ .within(() => {
+ let appUrl = Cypress.config().baseUrl + '/app/cypress-tests'
+ cy.get("[data-cy='deployed-app-url'] input").should('have.value', appUrl)
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ })
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+ cy.wait(1000)
+
+ cy.get(".appTable .app-status").eq(0)
+ .within(() => {
+ cy.contains("Published")
+ cy.get("svg[aria-label='Globe']").should("exist")
+ })
+
+ cy.get(".appTable .app-row-actions").eq(0)
+ .within(() => {
+ cy.get(".spectrum-Button").contains("View app")
+ cy.get(".spectrum-Button").contains("Edit").click({ force: true })
+ })
+
+ cy.get(".deployment-top-nav svg[aria-label='Globe']").should("exist").click({ force: true })
+
+ cy.get("[data-cy='publish-popover-menu']").should("be.visible")
+ .within(() => {
+ cy.get("[data-cy='publish-popover-action']").should("exist")
+ cy.get("button").contains("View app").should("exist")
+ cy.get(".publish-popover-message").should("have.text", "Last published a few seconds ago")
+ })
+ })
+
+ it("Should unpublish an application from the top navigation and reflect the status change", () => {
+ //Assuming the previous test app exists and is published
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .app-status").eq(0)
+ .within(() => {
+ cy.contains("Published")
+ cy.get("svg[aria-label='Globe']").should("exist")
+ })
+
+ cy.get(".appTable .app-row-actions").eq(0)
+ .within(() => {
+ cy.get(".spectrum-Button").contains("View app")
+ cy.get(".spectrum-Button").contains("Edit").click({ force: true })
+ })
+
+ //The published status
+ cy.get(".deployment-top-nav svg[aria-label='Globe']").should("exist")
+ .click({ force: true })
+
+ cy.get("[data-cy='publish-popover-menu']").should("be.visible")
+ cy.get("[data-cy='publish-popover-menu'] [data-cy='publish-popover-action']")
+ .click({ force : true })
+
+ cy.get("[data-cy='unpublish-modal']").should("be.visible")
+ .within(() => {
+ cy.get(".confirm-wrap button").click({ force: true }
+ )})
+
+ cy.get(".deployment-top-nav svg[aria-label='GlobeStrike']").should("exist")
+
+ cy.visit(`${Cypress.config().baseUrl}/builder`)
+
+ cy.get(".appTable .app-status").eq(0).contains("Unpublished")
+
+ })
+ })
+})
diff --git a/packages/builder/cypress/integration/changeAppIconAndColour.spec.js b/packages/builder/cypress/integration/changeAppIconAndColour.spec.js
index 0db2d49e3f..0f623ddb04 100644
--- a/packages/builder/cypress/integration/changeAppIconAndColour.spec.js
+++ b/packages/builder/cypress/integration/changeAppIconAndColour.spec.js
@@ -11,7 +11,7 @@ filterTests(['all'], () => {
cy.applicationInAppTable("Cypress Tests")
cy.get(".appTable")
.within(() => {
- cy.get(".spectrum-Icon").eq(1).click()
+ cy.get(".app-row-actions-icon").eq(0).click()
})
cy.get(".spectrum-Menu").contains("Edit icon").click()
// Select random icon
@@ -38,6 +38,7 @@ filterTests(['all'], () => {
cy.get(".title").children().children()
.should('have.attr', 'style').and('contains', 'color')
})
+ cy.deleteAllApps()
})
})
})
diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js
index ff8065f544..69ef3f98a3 100644
--- a/packages/builder/cypress/integration/createAutomation.spec.js
+++ b/packages/builder/cypress/integration/createAutomation.spec.js
@@ -11,7 +11,7 @@ filterTests(['smoke', 'all'], () => {
cy.createTestTableWithData()
cy.wait(2000)
cy.contains("Automate").click()
- cy.get("[data-cy='new-screen'] > .spectrum-Icon").click()
+ cy.get(".add-button .spectrum-Icon").click()
cy.get(".modal-inner-wrapper").within(() => {
cy.get("input").type("Add Row")
cy.contains("Row Created").click({ force: true })
diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js
index a8c3b03cee..feaf1c3b5f 100644
--- a/packages/builder/cypress/integration/createView.spec.js
+++ b/packages/builder/cypress/integration/createView.spec.js
@@ -125,7 +125,7 @@ filterTests(['smoke', 'all'], () => {
it("renames a view", () => {
cy.contains(".nav-item", "Test View")
- .find(".actions .icon")
+ .find(".actions .icon.open-popover")
.click({ force: true })
cy.get(".spectrum-Menu-itemLabel").contains("Edit").click()
cy.get(".modal-inner-wrapper").within(() => {
@@ -138,7 +138,7 @@ filterTests(['smoke', 'all'], () => {
it("deletes a view", () => {
cy.contains(".nav-item", "Test View Updated")
- .find(".actions .icon")
+ .find(".actions .icon.open-popover")
.click({ force: true })
cy.contains("Delete").click()
cy.contains("Delete View").click()
diff --git a/packages/builder/cypress/integration/renameAnApplication.spec.js b/packages/builder/cypress/integration/renameAnApplication.spec.js
index f4899f98a0..120c0d54d7 100644
--- a/packages/builder/cypress/integration/renameAnApplication.spec.js
+++ b/packages/builder/cypress/integration/renameAnApplication.spec.js
@@ -99,30 +99,32 @@ filterTests(['all'], () => {
cy.searchForApplication(originalName)
cy.get(".appTable")
.within(() => {
- cy.get(".spectrum-Icon").eq(1).click()
- })
- // Check for when an app is published
- if (published == true) {
- // Should not have Edit as option, will unpublish app
- cy.should("not.have.value", "Edit")
- cy.get(".spectrum-Menu").contains("Unpublish").click()
- cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
- cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click()
- }
- cy.contains("Edit").click()
- cy.get(".spectrum-Modal")
- .within(() => {
- if (noName == true) {
- cy.get("input").clear()
- cy.get(".spectrum-Dialog-grid").click()
- .contains("App name must be letters, numbers and spaces only")
- return cy
- }
+ cy.get("[aria-label='More']").eq(0).click()
+ })
+ // Check for when an app is published
+ if (published == true) {
+ // Should not have Edit as option, will unpublish app
+ cy.should("not.have.value", "Edit")
+ cy.get(".spectrum-Menu").contains("Unpublish").click()
+ cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
+ cy.get(".appTable > :nth-child(5) > :nth-child(2) > .spectrum-Icon").click()
+ }
+ cy.get("[data-cy='app-row-actions-menu-popover']").eq(0).within(() => {
+ cy.get(".spectrum-Menu-item").contains("Edit").click({ force: true })
+ })
+ cy.get(".spectrum-Modal")
+ .within(() => {
+ if (noName == true) {
cy.get("input").clear()
- cy.get("input").eq(0).type(changedName).should("have.value", changedName).blur()
- cy.get(".spectrum-ButtonGroup").contains("Save").click({ force: true })
- cy.wait(500)
- })
- }
+ cy.get(".spectrum-Dialog-grid").click()
+ .contains("App name must be letters, numbers and spaces only")
+ return cy
+ }
+ cy.get("input").clear()
+ cy.get("input").eq(0).type(changedName).should("have.value", changedName).blur()
+ cy.get(".spectrum-ButtonGroup").contains("Save").click({ force: true })
+ cy.wait(500)
+ })
+ }
})
})
diff --git a/packages/builder/cypress/integration/revertApp.spec.js b/packages/builder/cypress/integration/revertApp.spec.js
index c64d19f230..9d5e4f0f63 100644
--- a/packages/builder/cypress/integration/revertApp.spec.js
+++ b/packages/builder/cypress/integration/revertApp.spec.js
@@ -10,9 +10,9 @@ filterTests(['smoke', 'all'], () => {
it("should try to revert an unpublished app", () => {
// Click revert icon
cy.get(".toprightnav").within(() => {
- cy.get(".spectrum-Icon").eq(1).click()
+ cy.get("[aria-label='Revert']").click({ force: true })
})
- cy.get(".spectrum-Dialog-grid").within(() => {
+ cy.get(".spectrum-Modal").within(() => {
// Enter app name before revert
cy.get("input").type("Cypress Tests")
cy.intercept('**/revert').as('revertApp')
@@ -33,11 +33,15 @@ filterTests(['smoke', 'all'], () => {
cy.get(".spectrum-ButtonGroup").within(() => {
cy.get(".spectrum-Button").contains("Publish").click({ force: true })
})
+ cy.wait(1000)
+ cy.get(".spectrum-ButtonGroup").within(() => {
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ })
// Add second component - Button
cy.addComponent("Elements", "Button")
// Click Revert
cy.get(".toprightnav").within(() => {
- cy.get(".spectrum-Icon").eq(1).click()
+ cy.get("[aria-label='Revert']").click({ force: true })
})
cy.get(".spectrum-Dialog-grid").within(() => {
// Click Revert
@@ -54,7 +58,7 @@ filterTests(['smoke', 'all'], () => {
it("should enter incorrect app name when reverting", () => {
// Click Revert
cy.get(".toprightnav").within(() => {
- cy.get(".spectrum-Icon").eq(1).click({ force: true })
+ cy.get("[aria-label='Revert']").click({ force: true })
})
// Enter incorrect app name
cy.get(".spectrum-Dialog-grid").within(() => {
diff --git a/packages/builder/src/analytics/constants.js b/packages/builder/src/analytics/constants.js
index 98408b5cb3..300b1e058d 100644
--- a/packages/builder/src/analytics/constants.js
+++ b/packages/builder/src/analytics/constants.js
@@ -36,6 +36,7 @@ export const Events = {
CREATED: "budibase:app_created",
PUBLISHED: "budibase:app_published",
UNPUBLISHED: "budibase:app_unpublished",
+ VIEW_PUBLISHED: "budibase:view_published_app",
},
ANALYTICS: {
OPT_IN: "budibase:analytics_opt_in",
@@ -51,3 +52,9 @@ export const Events = {
SAVED: "budibase:sso_saved",
},
}
+
+export const EventSource = {
+ PORTAL: "portal",
+ URL: "url",
+ NOTIFICATION: "notification",
+}
diff --git a/packages/builder/src/analytics/index.js b/packages/builder/src/analytics/index.js
index 3a4118347d..aa1ebf7e0e 100644
--- a/packages/builder/src/analytics/index.js
+++ b/packages/builder/src/analytics/index.js
@@ -2,7 +2,7 @@ import { API } from "api"
import PosthogClient from "./PosthogClient"
import IntercomClient from "./IntercomClient"
import SentryClient from "./SentryClient"
-import { Events } from "./constants"
+import { Events, EventSource } from "./constants"
const posthog = new PosthogClient(
process.env.POSTHOG_TOKEN,
@@ -57,5 +57,5 @@ class AnalyticsHub {
const analytics = new AnalyticsHub()
-export { Events }
+export { Events, EventSource }
export default analytics
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
index 67c7f493e8..9662bc8ade 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ResultsModal.svelte
@@ -10,11 +10,11 @@
-