diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index 5d9aee2619..98fdc8dfd0 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -96,6 +96,10 @@ spec: value: worker-service:{{ .Values.services.worker.port }} - name: COOKIE_DOMAIN value: {{ .Values.globals.cookieDomain | quote }} + - name: ACCOUNT_PORTAL_URL + value: {{ .Values.globals.accountPortalUrl | quote }} + - name: ACCOUNT_PORTAL_API_KEY + value: {{ .Values.globals.accountPortalApiKey | quote }} image: budibase/apps imagePullPolicy: Always name: bbapps diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index 98a921a8a6..08b40d3b6b 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -89,6 +89,8 @@ spec: value: {{ .Values.globals.selfHosted | quote }} - name: ACCOUNT_PORTAL_URL value: {{ .Values.globals.accountPortalUrl | quote }} + - name: ACCOUNT_PORTAL_API_KEY + value: {{ .Values.globals.accountPortalApiKey | quote }} - name: COOKIE_DOMAIN value: {{ .Values.globals.cookieDomain | quote }} image: budibase/worker diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index c9b2549b30..5999f9c4bc 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -90,6 +90,7 @@ globals: logLevel: info selfHosted: 1 accountPortalUrL: "" + accountPortalApiKey: "" cookieDomain: "" createSecrets: true # creates an internal API key, JWT secrets and redis password for you diff --git a/hosting/kubernetes/envoy/envoy.yaml b/hosting/kubernetes/envoy/envoy.yaml index 4bf751b3a3..25a774dc7e 100644 --- a/hosting/kubernetes/envoy/envoy.yaml +++ b/hosting/kubernetes/envoy/envoy.yaml @@ -50,6 +50,11 @@ static_resources: route: cluster: app-service + - match: { path: "/api/deploy" } + route: + timeout: 60s + cluster: app-service + # special case for when API requests are made, can just forward, not to minio - match: { prefix: "/api/" } route: diff --git a/lerna.json b/lerna.json index 016dec1b9b..1102d1e9d0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.146-alpha.4", + "version": "0.9.154-alpha.1", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/package.json b/package.json index 3df577ca58..3596ec7800 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,8 @@ "multi:disable": "lerna run multi:disable", "selfhost:enable": "lerna run selfhost:enable", "selfhost:disable": "lerna run selfhost:disable", + "localdomain:enable": "lerna run localdomain:enable", + "localdomain:disable": "lerna run localdomain:disable", "postinstall": "husky install" } } diff --git a/packages/auth/package.json b/packages/auth/package.json index e765f7fc02..bc890882f7 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.146-alpha.4", + "version": "0.9.154-alpha.1", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index 51bed0210e..60a2d341a8 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -12,7 +12,7 @@ const populateFromDB = async (userId, tenantId) => { const user = await getGlobalDB(tenantId).get(userId) user.budibaseAccess = true - if (!env.SELF_HOSTED) { + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { const account = await accounts.getAccount(user.email) if (account) { user.account = account diff --git a/packages/auth/src/cloud/accounts.js b/packages/auth/src/cloud/accounts.js index a102df8920..a02fe60926 100644 --- a/packages/auth/src/cloud/accounts.js +++ b/packages/auth/src/cloud/accounts.js @@ -1,16 +1,18 @@ const API = require("./api") const env = require("../environment") +const { Headers } = require("../constants") const api = new API(env.ACCOUNT_PORTAL_URL) -// TODO: Authorization - exports.getAccount = async email => { const payload = { email, } const response = await api.post(`/api/accounts/search`, { body: payload, + headers: { + [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, + }, }) const json = await response.json() diff --git a/packages/auth/src/environment.js b/packages/auth/src/environment.js index da24afc8a0..c36b469c4e 100644 --- a/packages/auth/src/environment.js +++ b/packages/auth/src/environment.js @@ -21,6 +21,8 @@ module.exports = { INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, MULTI_TENANCY: process.env.MULTI_TENANCY, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, + ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY, + DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL, SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED), COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, isTest, diff --git a/packages/auth/src/middleware/matchers.js b/packages/auth/src/middleware/matchers.js index a555823136..3d5065c069 100644 --- a/packages/auth/src/middleware/matchers.js +++ b/packages/auth/src/middleware/matchers.js @@ -7,6 +7,7 @@ exports.buildMatcherRegex = patterns => { return patterns.map(pattern => { const isObj = typeof pattern === "object" && pattern.route const method = isObj ? pattern.method : "GET" + const strict = pattern.strict ? pattern.strict : false let route = isObj ? pattern.route : pattern const matches = route.match(PARAM_REGEX) @@ -16,13 +17,19 @@ exports.buildMatcherRegex = patterns => { route = route.replace(match, pattern) } } - return { regex: new RegExp(route), method } + return { regex: new RegExp(route), method, strict, route } }) } exports.matches = (ctx, options) => { - return options.find(({ regex, method }) => { - const urlMatch = regex.test(ctx.request.url) + return options.find(({ regex, method, strict, route }) => { + let urlMatch + if (strict) { + urlMatch = ctx.request.url === route + } else { + urlMatch = regex.test(ctx.request.url) + } + const methodMatch = method === "ALL" ? true diff --git a/packages/auth/src/middleware/passport/tests/third-party-common.spec.js b/packages/auth/src/middleware/passport/tests/third-party-common.spec.js index 1ace65ba40..e2ad9a9300 100644 --- a/packages/auth/src/middleware/passport/tests/third-party-common.spec.js +++ b/packages/auth/src/middleware/passport/tests/third-party-common.spec.js @@ -20,6 +20,10 @@ const getErrorMessage = () => { return done.mock.calls[0][2].message } +const saveUser = async (user) => { + return await db.put(user) +} + describe("third party common", () => { describe("authenticateThirdParty", () => { let thirdPartyUser @@ -36,7 +40,7 @@ describe("third party common", () => { describe("validation", () => { const testValidation = async (message) => { - await authenticateThirdParty(thirdPartyUser, false, done) + await authenticateThirdParty(thirdPartyUser, false, done, saveUser) expect(done.mock.calls.length).toBe(1) expect(getErrorMessage()).toContain(message) } @@ -78,7 +82,7 @@ describe("third party common", () => { describe("when the user doesn't exist", () => { describe("when a local account is required", () => { it("returns an error message", async () => { - await authenticateThirdParty(thirdPartyUser, true, done) + await authenticateThirdParty(thirdPartyUser, true, done, saveUser) expect(done.mock.calls.length).toBe(1) expect(getErrorMessage()).toContain("Email does not yet exist. You must set up your local budibase account first.") }) @@ -86,7 +90,7 @@ describe("third party common", () => { describe("when a local account isn't required", () => { it("creates and authenticates the user", async () => { - await authenticateThirdParty(thirdPartyUser, false, done) + await authenticateThirdParty(thirdPartyUser, false, done, saveUser) const user = expectUserIsAuthenticated() expectUserIsSynced(user, thirdPartyUser) expect(user.roles).toStrictEqual({}) @@ -123,7 +127,7 @@ describe("third party common", () => { }) it("syncs and authenticates the user", async () => { - await authenticateThirdParty(thirdPartyUser, true, done) + await authenticateThirdParty(thirdPartyUser, true, done, saveUser) const user = expectUserIsAuthenticated() expectUserIsSynced(user, thirdPartyUser) @@ -139,7 +143,7 @@ describe("third party common", () => { }) it("syncs and authenticates the user", async () => { - await authenticateThirdParty(thirdPartyUser, true, done) + await authenticateThirdParty(thirdPartyUser, true, done, saveUser) const user = expectUserIsAuthenticated() expectUserIsSynced(user, thirdPartyUser) diff --git a/packages/auth/src/middleware/passport/third-party-common.js b/packages/auth/src/middleware/passport/third-party-common.js index c25aa3e0b0..54a5504712 100644 --- a/packages/auth/src/middleware/passport/third-party-common.js +++ b/packages/auth/src/middleware/passport/third-party-common.js @@ -1,6 +1,7 @@ const env = require("../../environment") const jwt = require("jsonwebtoken") const { generateGlobalUserID } = require("../../db/utils") +const { saveUser } = require("../../utils") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") @@ -14,7 +15,8 @@ const fetch = require("node-fetch") exports.authenticateThirdParty = async function ( thirdPartyUser, requireLocalAccount = true, - done + done, + saveUserFn = saveUser ) { if (!thirdPartyUser.provider) { return authError(done, "third party user provider required") @@ -71,7 +73,13 @@ exports.authenticateThirdParty = async function ( dbUser = await syncUser(dbUser, thirdPartyUser) // create or sync the user - const response = await db.put(dbUser) + let response + try { + response = await saveUserFn(dbUser, getTenantId(), false, false) + } catch (err) { + return authError(done, err) + } + dbUser._rev = response.rev // authenticate diff --git a/packages/auth/src/objectStore/index.js b/packages/auth/src/objectStore/index.js index 9f271ad80e..87b67d464e 100644 --- a/packages/auth/src/objectStore/index.js +++ b/packages/auth/src/objectStore/index.js @@ -265,7 +265,7 @@ exports.downloadTarball = async (url, bucketName, path) => { const tmpPath = join(budibaseTempDir(), path) await streamPipeline(response.body, zlib.Unzip(), tar.extract(tmpPath)) - if (!env.isTest()) { + if (!env.isTest() && env.SELF_HOSTED) { await exports.uploadDirectory(bucketName, tmpPath, path) } // return the temporary path incase there is a use for it diff --git a/packages/auth/src/redis/index.js b/packages/auth/src/redis/index.js index 48a24ad0bc..0ee17265ce 100644 --- a/packages/auth/src/redis/index.js +++ b/packages/auth/src/redis/index.js @@ -191,6 +191,12 @@ class RedisWrapper { } } + async getTTL(key) { + const db = this._db + const prefixedKey = addDbPrefix(db, key) + return CLIENT.ttl(prefixedKey) + } + async setExpiry(key, expirySeconds) { const db = this._db const prefixedKey = addDbPrefix(db, key) diff --git a/packages/auth/src/tenancy/deprovision.js b/packages/auth/src/tenancy/deprovision.js index b8e5bc82cf..608ca1b84a 100644 --- a/packages/auth/src/tenancy/deprovision.js +++ b/packages/auth/src/tenancy/deprovision.js @@ -19,6 +19,22 @@ const removeTenantFromInfoDB = async tenantId => { } } +exports.removeUserFromInfoDB = async dbUser => { + const infoDb = getDB(PLATFORM_INFO_DB) + const keys = [dbUser._id, dbUser.email] + const userDocs = await infoDb.allDocs({ + keys, + include_docs: true, + }) + const toDelete = userDocs.rows.map(row => { + return { + ...row.doc, + _deleted: true, + } + }) + await infoDb.bulkDocs(toDelete) +} + const removeUsersFromInfoDB = async tenantId => { try { const globalDb = getGlobalDB(tenantId) diff --git a/packages/auth/src/tenancy/tenancy.js b/packages/auth/src/tenancy/tenancy.js index ebd573496c..67dbfd5619 100644 --- a/packages/auth/src/tenancy/tenancy.js +++ b/packages/auth/src/tenancy/tenancy.js @@ -73,7 +73,7 @@ exports.tryAddTenant = async (tenantId, userId, email) => { await Promise.all(promises) } -exports.getGlobalDB = (tenantId = null) => { +exports.getGlobalDBName = (tenantId = null) => { // tenant ID can be set externally, for example user API where // new tenants are being created, this may be the case if (!tenantId) { @@ -81,13 +81,16 @@ exports.getGlobalDB = (tenantId = null) => { } let dbName - if (tenantId === DEFAULT_TENANT_ID) { dbName = StaticDatabases.GLOBAL.name } else { dbName = `${tenantId}${SEPARATOR}${StaticDatabases.GLOBAL.name}` } + return dbName +} +exports.getGlobalDB = (tenantId = null) => { + const dbName = exports.getGlobalDBName(tenantId) return getDB(dbName) } @@ -104,3 +107,13 @@ exports.lookupTenantId = async userId => { } return tenantId } + +// lookup, could be email or userId, either will return a doc +exports.getTenantUser = async identifier => { + const db = getDB(PLATFORM_INFO_DB) + try { + return await db.get(identifier) + } catch (err) { + return null + } +} diff --git a/packages/auth/src/utils.js b/packages/auth/src/utils.js index 93b483c6be..f509a626c1 100644 --- a/packages/auth/src/utils.js +++ b/packages/auth/src/utils.js @@ -1,10 +1,24 @@ -const { DocumentTypes, SEPARATOR, ViewNames } = require("./db/utils") +const { + DocumentTypes, + SEPARATOR, + ViewNames, + generateGlobalUserID, +} = require("./db/utils") const jwt = require("jsonwebtoken") const { options } = require("./middleware/passport/jwt") const { createUserEmailView } = require("./db/views") -const { Headers } = require("./constants") -const { getGlobalDB } = require("./tenancy") +const { Headers, UserStatus } = require("./constants") +const { + getGlobalDB, + updateTenantId, + getTenantUser, + tryAddTenant, +} = require("./tenancy") const environment = require("./environment") +const accounts = require("./cloud/accounts") +const { hash } = require("./hashing") +const userCache = require("./cache/user") +const env = require("./environment") const APP_PREFIX = DocumentTypes.APP + SEPARATOR @@ -131,3 +145,93 @@ exports.getGlobalUserByEmail = async email => { } } } + +exports.saveUser = async ( + user, + tenantId, + hashPassword = true, + requirePassword = true +) => { + if (!tenantId) { + throw "No tenancy specified." + } + // need to set the context for this request, as specified + updateTenantId(tenantId) + // specify the tenancy incase we're making a new admin user (public) + const db = getGlobalDB(tenantId) + let { email, password, _id } = user + // make sure another user isn't using the same email + let dbUser + if (email) { + // check budibase users inside the tenant + dbUser = await exports.getGlobalUserByEmail(email) + if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { + throw `Email address ${email} already in use.` + } + + // check budibase users in other tenants + if (env.MULTI_TENANCY) { + dbUser = await getTenantUser(email) + if (dbUser != null && dbUser.tenantId !== tenantId) { + throw `Email address ${email} already in use.` + } + } + + // check root account users in account portal + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + const account = await accounts.getAccount(email) + if (account && account.verified && account.tenantId !== tenantId) { + throw `Email address ${email} already in use.` + } + } + } else { + dbUser = await db.get(_id) + } + + // get the password, make sure one is defined + let hashedPassword + if (password) { + hashedPassword = hashPassword ? await hash(password) : password + } else if (dbUser) { + hashedPassword = dbUser.password + } else if (requirePassword) { + throw "Password must be specified." + } + + _id = _id || generateGlobalUserID() + user = { + createdAt: Date.now(), + ...dbUser, + ...user, + _id, + password: hashedPassword, + tenantId, + } + // make sure the roles object is always present + if (!user.roles) { + user.roles = {} + } + // add the active status to a user if its not provided + if (user.status == null) { + user.status = UserStatus.ACTIVE + } + try { + const response = await db.put({ + password: hashedPassword, + ...user, + }) + await tryAddTenant(tenantId, _id, email) + await userCache.invalidateUser(response.id) + return { + _id: response.id, + _rev: response.rev, + email, + } + } catch (err) { + if (err.status === 409) { + throw "User exists already" + } else { + throw err + } + } +} diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 6924e6db16..2657593a0c 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": "0.9.146-alpha.4", + "version": "0.9.154-alpha.1", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -78,7 +78,7 @@ "@spectrum-css/underlay": "^2.0.9", "@spectrum-css/vars": "^3.0.1", "dayjs": "^1.10.4", - "svelte-flatpickr": "^3.1.0", + "svelte-flatpickr": "^3.2.3", "svelte-portal": "^1.0.0" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" diff --git a/packages/bbui/src/Form/Core/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker.svelte index 2516cb659d..176db9f497 100644 --- a/packages/bbui/src/Form/Core/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker.svelte @@ -26,6 +26,7 @@ altFormat: enableTime ? "F j Y, H:i" : "F j, Y", wrap: true, appendTo, + disableMobile: "true", } const handleChange = event => { diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index 926c3eda11..37989a291e 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -90,6 +90,7 @@ on:input={onInput} on:keyup={updateValueOnEnter} {type} + inputmode={type === "number" ? "decimal" : "text"} class="spectrum-Textfield-input" /> diff --git a/packages/bbui/yarn.lock b/packages/bbui/yarn.lock index a3b20aa862..a492c83266 100644 --- a/packages/bbui/yarn.lock +++ b/packages/bbui/yarn.lock @@ -2415,10 +2415,10 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -svelte-flatpickr@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.1.0.tgz#ad83588430dbd55196a1a258b8ba27e7f9c1ee37" - integrity sha512-zKyV+ukeVuJ8CW0Ing3T19VSekc4bPkou/5Riutt1yATrLvSsanNqcgqi7Q5IePvIoOF9GJ5OtHvn1qK9Wx9BQ== +svelte-flatpickr@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.2.3.tgz#db5dd7ad832ef83262b45e09737955ad3d591fc8" + integrity sha512-PNkqK4Napx8nTvCwkaUXdnKo8dISThaxEOK+szTUXcY6H0dQM0TSyuoMaVWY2yX7pM+PN5cpCQCcVe8YvTRFSw== dependencies: flatpickr "^4.5.2" diff --git a/packages/builder/assets/budiworld.webp b/packages/builder/assets/budiworld.webp new file mode 100644 index 0000000000..347036a797 Binary files /dev/null and b/packages/builder/assets/budiworld.webp differ diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js index 24f370caf9..ccb9ae7929 100644 --- a/packages/builder/cypress/integration/createTable.spec.js +++ b/packages/builder/cypress/integration/createTable.spec.js @@ -24,9 +24,7 @@ context("Create a Table", () => { it("updates a column on the table", () => { cy.get(".title").click() cy.get(".spectrum-Table-editIcon > use").click() - cy.get("input") - .eq(1) - .type("updated", { force: true }) + cy.get("input").eq(1).type("updated", { force: true }) // Unset table display column cy.get(".spectrum-Switch-input").eq(1).click() cy.contains("Save Column").click() @@ -45,9 +43,7 @@ context("Create a Table", () => { it("deletes a row", () => { cy.get(".spectrum-Checkbox-input").check({ force: true }) cy.contains("Delete 1 row(s)").click() - cy.get(".spectrum-Modal") - .contains("Delete") - .click() + cy.get(".spectrum-Modal").contains("Delete").click() cy.contains("RoverUpdated").should("not.exist") }) @@ -56,15 +52,18 @@ context("Create a Table", () => { cy.get(".spectrum-Table-editIcon > use").click() cy.contains("Delete").click() cy.wait(50) - cy.contains("Delete Column") - .click() + cy.contains("Delete Column").click() cy.contains("nameupdated").should("not.exist") }) it("deletes a table", () => { - cy.get(".actions > :nth-child(1) > .icon > .spectrum-Icon > use") - .eq(1) - .click({ force: true }) + cy.get(".nav-item") + .contains("dog") + .parents(".nav-item") + .first() + .within(() => { + cy.get(".actions .spectrum-Icon").click({ force: true }) + }) cy.get(".spectrum-Menu > :nth-child(2)").click() cy.contains("Delete Table").click() cy.contains("dog").should("not.exist") diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index d7d9606cd7..e82ab67c0d 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -28,11 +28,7 @@ context("Create a View", () => { const headers = Array.from($headers).map(header => header.textContent.trim() ) - expect(removeSpacing(headers)).to.deep.eq([ - "group", - "age", - "rating", - ]) + expect(removeSpacing(headers)).to.deep.eq(["group", "age", "rating"]) }) }) @@ -62,7 +58,7 @@ context("Create a View", () => { cy.get(".modal-inner-wrapper").within(() => { cy.get(".spectrum-Picker-label").eq(0).click() cy.contains("Statistics").click() - + cy.get(".spectrum-Picker-label").eq(1).click() cy.contains("age").click({ force: true }) @@ -105,20 +101,20 @@ context("Create a View", () => { cy.get(".spectrum-Table-cell").then($values => { let values = Array.from($values).map(header => header.textContent.trim()) expect(values).to.deep.eq([ - "Students", - "70", - "20", - "25", - "3", - "1650", - "23.333333333333332", - "Teachers", - "85", - "36", - "49", - "2", - "3697", - "42.5", + "Students", + "70", + "20", + "25", + "3", + "1650", + "23.333333333333332", + "Teachers", + "85", + "36", + "49", + "2", + "3697", + "42.5", ]) }) }) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 0309299468..b10e7b9990 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -31,8 +31,7 @@ Cypress.Commands.add("login", () => { Cypress.Commands.add("createApp", name => { cy.visit(`localhost:${Cypress.env("PORT")}/builder`) cy.wait(500) - cy.contains(/Create (new )?app/).click() - cy.wait(500) + cy.contains(/Start from scratch/).click() cy.get(".spectrum-Modal") .within(() => { cy.get("input").eq(0).type(name).should("have.value", name).blur() @@ -187,7 +186,7 @@ Cypress.Commands.add("getComponent", componentId => { .its("body") .should("not.be.null") .then(cy.wrap) - .find(`[data-component-id=${componentId}]`) + .find(`[data-id=${componentId}]`) }) Cypress.Commands.add("navigateToFrontend", () => { diff --git a/packages/builder/index.html b/packages/builder/index.html index eb58dc74d2..e3383cda39 100644 --- a/packages/builder/index.html +++ b/packages/builder/index.html @@ -4,7 +4,7 @@