diff --git a/.github/workflows/smoke_test.yaml b/.github/workflows/smoke_test.yaml index 7002c8335b..cffb914aaf 100644 --- a/.github/workflows/smoke_test.yaml +++ b/.github/workflows/smoke_test.yaml @@ -1,4 +1,4 @@ -name: Budibase Smoke Test +name: Budibase Nightly Tests on: workflow_dispatch: @@ -6,7 +6,7 @@ on: - cron: "0 5 * * *" # every day at 5AM jobs: - release: + nightly: runs-on: ubuntu-latest steps: @@ -43,6 +43,18 @@ jobs: name: Test Reports path: packages/builder/cypress/reports/testReport.html + # TODO: enable once running in QA test env + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v1 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: eu-west-1 + + # - name: Upload test results HTML + # uses: aws-actions/configure-aws-credentials@v1 + # run: aws s3 cp packages/builder/cypress/reports/testReport.html s3://{{ secrets.BUDI_QA_REPORTS_BUCKET_NAME }}/$GITHUB_RUN_ID/index.html + - name: Cypress Discord Notify run: yarn test:e2e:ci:notify env: diff --git a/packages/builder/cypress/integration/adminAndManagement/authentication.spec.js b/packages/builder/cypress/integration/adminAndManagement/authentication.spec.js new file mode 100644 index 0000000000..5cc42cb59a --- /dev/null +++ b/packages/builder/cypress/integration/adminAndManagement/authentication.spec.js @@ -0,0 +1,178 @@ +import filterTests from "../../support/filterTests" +// const interact = require("../support/interact") + +filterTests(["smoke", "all"], () => { + context("Auth Configuration", () => { + before(() => { + cy.login() + }) + + after(() => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + cy.location().should(loc => { + expect(loc.pathname).to.eq("/builder/portal/manage/auth") + }) + + cy.get("[data-cy=new-scope-input]").clear() + + cy.get("div.content").scrollTo("bottom") + cy.get("[data-cy=oidc-active]").click() + + cy.get("[data-cy=oidc-active]").should('not.be.checked') + + cy.intercept("POST", "/api/global/configs").as("updateAuth") + cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true}) + cy.wait("@updateAuth") + cy.get("@updateAuth").its("response.statusCode").should("eq", 200) + + cy.get(".spectrum-Toast-content") + .contains("Settings saved") + .should("be.visible") + }) + + it("Should allow updating of the OIDC config", () => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + cy.location().should(loc => { + expect(loc.pathname).to.eq("/builder/portal/manage/auth") + }) + cy.get("div.content").scrollTo("bottom") + cy.get(".spectrum-Toast .spectrum-ClearButton").click() + + cy.get("input[data-cy=configUrl]").type("http://budi-auth.com/v2") + cy.get("input[data-cy=clientID]").type("34ac6a13-f24a-4b52-c70d-fa544ffd11b2") + cy.get("input[data-cy=clientSecret]").type("12A8Q~4nS_DWhOOJ2vWIRsNyDVsdtXPD.Zxa9df_") + + cy.get("button[data-cy=oidc-save]").should("not.be.disabled"); + + cy.intercept("POST", "/api/global/configs").as("updateAuth") + cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true}) + cy.wait("@updateAuth") + cy.get("@updateAuth").its("response.statusCode").should("eq", 200) + + cy.get(".spectrum-Toast-content") + .contains("Settings saved") + .should("be.visible") + }) + + it("Should display default scopes in advanced config.", () => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + cy.location().should(loc => { + expect(loc.pathname).to.eq("/builder/portal/manage/auth") + }) + cy.get("div.content").scrollTo("bottom") + + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get(".spectrum-Tags-item").contains("openid") + cy.get(".spectrum-Tags-item").contains("openid").find(".spectrum-ClearButton").should("not.exist") + + cy.get(".spectrum-Tags-item").contains("offline_access") + cy.get(".spectrum-Tags-item").contains("email") + cy.get(".spectrum-Tags-item").contains("profile") + }) + + it("Add a new scopes", () => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + cy.location().should(loc => { + expect(loc.pathname).to.eq("/builder/portal/manage/auth") + }) + cy.get("div.content").scrollTo("bottom") + + cy.get("[data-cy=new-scope-input]").type("Sample{enter}") + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 5) + cy.get(".spectrum-Tags-item").contains("Sample") + + cy.get(".auth-form input.spectrum-Textfield-input").type("Another ") + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 6) + cy.get(".spectrum-Tags-item").contains("Another") + + cy.get("button[data-cy=oidc-save]").should("not.be.disabled"); + + cy.intercept("POST", "/api/global/configs").as("updateAuth") + cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true}) + cy.wait("@updateAuth") + cy.get("@updateAuth").its("response.statusCode").should("eq", 200) + + cy.reload() + + cy.get("div.content").scrollTo("bottom") + + cy.get(".spectrum-Tags-item").contains("openid") + cy.get(".spectrum-Tags-item").contains("offline_access") + cy.get(".spectrum-Tags-item").contains("email") + cy.get(".spectrum-Tags-item").contains("profile") + cy.get(".spectrum-Tags-item").contains("Sample") + cy.get(".spectrum-Tags-item").contains("Another") + }) + + it("Should allow the removal of auth scopes", () => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + cy.location().should(loc => { + expect(loc.pathname).to.eq("/builder/portal/manage/auth") + }) + cy.get("div.content").scrollTo("bottom") + + cy.get(".spectrum-Tags-item").contains("offline_access").parent().find(".spectrum-ClearButton").click() + cy.get(".spectrum-Tags-item").contains("profile").parent().find(".spectrum-ClearButton").click() + + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist") + cy.get(".spectrum-Tags-item").contains("profile").should("not.exist") + + cy.get("button[data-cy=oidc-save]").should("not.be.disabled"); + + cy.intercept("POST", "/api/global/configs").as("updateAuth") + cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true}) + cy.wait("@updateAuth") + cy.get("@updateAuth").its("response.statusCode").should("eq", 200) + + cy.get(".spectrum-Toast-content") + .contains("Settings saved") + .should("be.visible") + + cy.reload() + + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist") + cy.get(".spectrum-Tags-item").contains("profile").should("not.exist") + }) + + it("Should allow auth scopes to be reset to the core defaults.", () => { + cy.get(".spectrum-SideNav li").contains("Auth").click() + + cy.get("div.content").scrollTo("bottom") + + cy.get("[data-cy=restore-oidc-default-scopes]").click({force: true}) + + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get(".spectrum-Tags-item").contains("openid") + cy.get(".spectrum-Tags-item").contains("offline_access") + cy.get(".spectrum-Tags-item").contains("email") + cy.get(".spectrum-Tags-item").contains("profile") + }) + + it("Should not allow invalid characters in the auth scopes", () => { + cy.get("[data-cy=new-scope-input]").type("thisIsInvalid\\{enter}") + cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes") + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get("[data-cy=new-scope-input]").clear() + + cy.get("[data-cy=new-scope-input]").type("alsoInvalid\"{enter}") + cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes") + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + + cy.get("[data-cy=new-scope-input]").clear() + }) + + it("Should not allow duplicate auth scopes", () => { + cy.get("[data-cy=new-scope-input]").type("offline_access{enter}") + cy.get(".spectrum-Form-itemField .error").contains("Auth scope already exists") + cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4) + }) + + }) +}) \ No newline at end of file diff --git a/packages/builder/package.json b/packages/builder/package.json index ec8f13a6cb..b1aa40a56b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -13,11 +13,11 @@ "cy:setup:ci": "node ./cypress/setup.js", "cy:open": "cypress open", "cy:run": "cypress run", - "cy:run:ci": "cypress run --headed --browser chrome --spec cypress/integration/createApp.spec.js", + "cy:run:ci": "cypress run --headed --browser chrome --spec cypress/integration/createApp.spec.js", "cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record", "cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run", "cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci", - "cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record && npm run cy:ci:report", + "cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record; npm run cy:ci:report", "cy:ci:report": "mochawesome-merge cypress/reports/*.json > cypress/reports/testReport.json && marge cypress/reports/testReport.json --reportDir cypress/reports --inline", "cy:ci:notify": "node scripts/cypressResultsWebhook", "cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open", @@ -121,4 +121,4 @@ "vite": "^3.0.8" }, "gitHead": "115189f72a850bfb52b65ec61d932531bf327072" -} +} \ No newline at end of file diff --git a/packages/builder/scripts/cypressResultsWebhook.js b/packages/builder/scripts/cypressResultsWebhook.js index 457093e013..4de4c01cc7 100644 --- a/packages/builder/scripts/cypressResultsWebhook.js +++ b/packages/builder/scripts/cypressResultsWebhook.js @@ -5,7 +5,6 @@ const path = require("path") const fs = require("fs") const WEBHOOK_URL = process.env.CYPRESS_WEBHOOK_URL -const OUTCOME = process.env.CYPRESS_OUTCOME const DASHBOARD_URL = process.env.CYPRESS_DASHBOARD_URL const GIT_SHA = process.env.GITHUB_SHA const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL @@ -35,6 +34,8 @@ async function discordCypressResultsNotification(report) { skipped, } = report.stats + const OUTCOME = failures > 0 ? "failure" : "success" + const options = { method: "POST", headers: { @@ -114,7 +115,7 @@ async function discordCypressResultsNotification(report) { } const response = await fetch(WEBHOOK_URL, options) - if (response.status >= 400) { + if (response.status >= 201) { const text = await response.text() console.error( `Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}` diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 9b29f6fd7e..a785fff3e9 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -40,6 +40,7 @@ const INITIAL_FRONTEND_STATE = { devicePreview: false, messagePassing: false, continueIfAction: false, + showNotificationAction: false, }, errors: [], hasAppPackage: false, diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ShowNotification.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ShowNotification.svelte new file mode 100644 index 0000000000..55b00d215d --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ShowNotification.svelte @@ -0,0 +1,61 @@ + + +
+ + { + if (!scopesFields[0].inputText) { + scopesFields[0].error = null + } + if ( + e.key === "Enter" || + e.keyCode === 13 || + e.code == "Space" || + e.keyCode == 32 + ) { + let scopes = providers.oidc.config.configs[0]["scopes"] + ? providers.oidc.config.configs[0]["scopes"] + : [...defaultScopes] + + let update = scopesFields[0].inputText.trim() + + if (HasSpacesRegex.test(update)) { + scopesFields[0].error = + "Auth scopes cannot contain spaces, double quotes or backslashes" + return + } else if (scopes.indexOf(update) > -1) { + scopesFields[0].error = "Auth scope already exists" + return + } else if (!update.length) { + scopesFields[0].inputText = null + scopesFields[0].error = null + return + } else { + scopesFields[0].error = null + scopes.push(update) + providers.oidc.config.configs[0]["scopes"] = scopes + scopesFields[0].inputText = null + } + } + }} + /> + +
+ + + openid + {#each providers.oidc.config.configs[0]["scopes"] || [...defaultScopes] as tag, idx} + { + let idxScopes = providers.oidc.config.configs[0]["scopes"] + if (idxScopes.length == 1) { + idxScopes.pop() + } else { + idxScopes.splice(idx, 1) + refreshScopes(0) + } + }} + > + {tag} + + {/each} + +
+
+ + {/if}