From e0a9f4d6c879b13fe43b96246214032b2b3cd55b Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 12 May 2022 17:04:05 +0100 Subject: [PATCH] App name autofill on create. Initial cypress tests added for importing an application file with a sample fixture --- packages/bbui/src/Form/Core/TextField.svelte | 9 +++ packages/bbui/src/Form/Input.svelte | 2 + .../builder/cypress/fixtures/exported-app.txt | 3 + .../cypress/integration/createApp.spec.js | 61 +++++++++++++++++ packages/builder/cypress/support/commands.js | 68 ++++++++++++++++++- .../settings/UpdateUserInfoModal.svelte | 12 +++- .../components/start/CreateAppModal.svelte | 10 ++- .../src/pages/builder/apps/index.svelte | 2 +- .../src/pages/builder/portal/_layout.svelte | 8 ++- 9 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 packages/builder/cypress/fixtures/exported-app.txt diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index 78b698eed2..7319943802 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -13,10 +13,18 @@ export let quiet = false export let dataCy export let align + export let autofocus = false const dispatch = createEventDispatcher() + + let field let focus = false + $: if (autofocus === true && field) { + focus = true + field.focus() + } + const updateValue = newValue => { if (readonly) { return @@ -77,6 +85,7 @@ {/if} { @@ -33,6 +34,7 @@ {placeholder} {type} {quiet} + {autofocus} on:change={onChange} on:click on:input diff --git a/packages/builder/cypress/fixtures/exported-app.txt b/packages/builder/cypress/fixtures/exported-app.txt new file mode 100644 index 0000000000..28aeaf958a --- /dev/null +++ b/packages/builder/cypress/fixtures/exported-app.txt @@ -0,0 +1,3 @@ +{"version":"1.2.9","db_type":"http","start_time":"2022-05-12T13:47:50.453Z","db_info":{"db_name":"app_dev_7cae7fa4db8745da848f91430a8211bf","purge_seq":"0-g1AAAABXeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVB4LkGRoAFL_gSArkQGP2kSGpHqIoiwAtOgYRA","update_seq":"11-g1AAAACbeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVB4LkGRoAFL_gSArgzmRPRcowJ5klmZgaZmKTR8e0xIZkuqhxrBAjEk0NzcwMsSmIQsA89QoWg","sizes":{"file":94626,"external":5665,"active":7627},"props":{},"doc_del_count":0,"doc_count":7,"disk_format_version":8,"compact_running":false,"cluster":{"q":2,"n":1,"w":1,"r":1},"instance_start_time":"0","host":"http://localhost:4005/app_dev_7cae7fa4db8745da848f91430a8211bf/","auto_compaction":false,"adapter":"http"}} +{"docs":[{"_id":"_design/database","_rev":"4-ad6d41ef604ab34da380438c1be89521","views":{"by_link":{"map":"function (doc) {\n // everything in this must remain constant as its going to Pouch, no external variables\n if (doc.type === \"link\") {\n let doc1 = doc.doc1;\n let doc2 = doc.doc2;\n // eslint-disable-next-line no-undef\n emit([doc1.tableId, doc1.rowId], {\n id: doc2.rowId,\n thisId: doc1.rowId,\n fieldName: doc1.fieldName,\n });\n // if linking to same table can't emit twice\n if (doc1.tableId !== doc2.tableId) {\n // eslint-disable-next-line no-undef\n emit([doc2.tableId, doc2.rowId], {\n id: doc1.rowId,\n thisId: doc2.rowId,\n fieldName: doc2.fieldName,\n });\n }\n }\n }"},"screen_routes":{"map":"function(doc) {\n if (doc._id.startsWith(\"screen_\")) {\n emit(doc._id, {\n id: doc._id,\n routing: doc.routing,\n })\n }\n }"}},"indexes":{"rows":{"index":"function (doc) {\n function idx(input, prev) {\n for (let key of Object.keys(input)) {\n let idxKey = prev != null ? `${prev}.${key}` : key;\n idxKey = idxKey.replace(/ /g, \"_\");\n if (Array.isArray(input[key])) {\n for (let val of input[key]) {\n if (typeof val !== \"object\") {\n // eslint-disable-next-line no-undef\n index(idxKey, val, { store: true });\n }\n }\n }\n else if (key === \"_id\" || key === \"_rev\" || input[key] == null) {\n continue;\n }\n if (typeof input[key] === \"string\") {\n // eslint-disable-next-line no-undef\n index(idxKey, input[key].toLowerCase(), { store: true });\n }\n else if (typeof input[key] !== \"object\") {\n // eslint-disable-next-line no-undef\n index(idxKey, input[key], { store: true });\n }\n else {\n idx(input[key], idxKey);\n }\n }\n }\n if (doc._id.startsWith(\"ro_\")) {\n // eslint-disable-next-line no-undef\n index(\"default\", doc._id);\n idx(doc);\n }\n }","analyzer":"keyword"}},"_revisions":{"start":4,"ids":["ad6d41ef604ab34da380438c1be89521","cb47f4fd824d5e096ac8b76117e6f93d","68a9a1d1b01b327676ecbaa4b1e5b8a7","0b24e44a44af45e51e562fd124ce3007"]}},{"_id":"app_metadata","_rev":"2-a4fe55378bfec0fc71e58a1364ac9965","appId":"app_dev_7cae7fa4db8745da848f91430a8211bf","type":"app","version":"1.0.155-alpha.0","componentLibraries":["@budibase/standard-components"],"name":"My app","url":"/my-app","instance":{"_id":"app_dev_7cae7fa4db8745da848f91430a8211bf"},"tenantId":"default","updatedAt":"2022-05-12T13:47:28.756Z","createdAt":"2022-05-12T13:47:26.825Z","status":"development","_revisions":{"start":2,"ids":["a4fe55378bfec0fc71e58a1364ac9965","f0d06bdb6e6ae4781eb5c4aa224002ff"]}},{"_id":"layout_private_master","_rev":"1-c02411b58d697e38763889a375d52159","componentLibraries":["@budibase/standard-components"],"title":"My app","favicon":"./_shared/favicon.png","stylesheets":[],"name":"Navigation Layout","props":{"_id":"4f569166-a4f3-47ea-a09e-6d218c75586f","_instanceName":"Navigation Layout","_component":"@budibase/standard-components/layout","_children":[{"_id":"7fcf11e4-6f5b-4085-8e0d-9f3d44c98967","_component":"@budibase/standard-components/screenslot","_instanceName":"Screen slot","_styles":{"normal":{"flex":"1 1 auto","display":"flex","flex-direction":"column","justify-content":"flex-start","align-items":"stretch"},"hover":{},"active":{},"selected":{}},"_children":[]}],"_styles":{"active":{},"hover":{},"normal":{},"selected":{}},"title":"My app","navigation":"Top","width":"Large","links":[{"text":"Home","url":"/"}]}},{"_id":"layout_public_master","_rev":"1-d6bce47046d4d0de4f19a6ff95064109","componentLibraries":["@budibase/standard-components"],"title":"My app","favicon":"./_shared/favicon.png","stylesheets":[],"name":"Empty Layout","props":{"_id":"3723ffa1-f9e0-4c05-8013-98195c788ed6","_instanceName":"Empty Layout","_component":"@budibase/standard-components/layout","_children":[{"_id":"7fcf11e4-6f5b-4085-8e0d-9f3d44c98967","_component":"@budibase/standard-components/screenslot","_instanceName":"Screen slot","_styles":{"normal":{"flex":"1 1 auto","display":"flex","flex-direction":"column","justify-content":"flex-start","align-items":"stretch"},"hover":{},"active":{},"selected":{}},"_children":[]}],"_styles":{"active":{},"hover":{},"normal":{},"selected":{}},"navigation":"None","width":"Large","links":[{"text":"Home","url":"/"}]}},{"_id":"screen_b19c454fa5ae41ab874a8860623ab37c","_rev":"1-02dddd460ad14de50dc885aa362452c6","layoutId":"layout_private_master","props":{"_id":"c87457efbc05a43f39064d9dacc9d4b67","_component":"@budibase/standard-components/container","_styles":{"normal":{},"hover":{},"active":{},"selected":{}},"_children":[],"_instanceName":"New Screen","direction":"column","hAlign":"stretch","vAlign":"top","size":"grow","gap":"M"},"routing":{"route":"/home","roleId":"BASIC","roldId":"BASIC"},"name":"screen-id"},{"_id":"ta_users","_rev":"1-30a4344f056c24cf776d5736eb3c7ed5","type":"table","views":{},"name":"Users","schema":{"email":{"type":"string","constraints":{"type":"string","email":true,"length":{"maximum":""},"presence":true},"fieldName":"email","name":"email"},"firstName":{"name":"firstName","fieldName":"firstName","type":"string","constraints":{"type":"string","presence":false}},"lastName":{"name":"lastName","fieldName":"lastName","type":"string","constraints":{"type":"string","presence":false}},"roleId":{"fieldName":"roleId","name":"roleId","type":"options","constraints":{"type":"string","presence":false,"inclusion":["ADMIN","POWER","BASIC","PUBLIC"]}},"status":{"fieldName":"status","name":"status","type":"options","constraints":{"type":"string","presence":false,"inclusion":["active","inactive"]}}},"primaryDisplay":"email"}]} +{"seq":"11-g1AAAACbeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVCJDUv3___-zMpgTWXKBAuxJiebmBkaG2DTgMSaPBUgyNACp_1DT2CGmmaUZWFqmYtOXBQAWQiha"} diff --git a/packages/builder/cypress/integration/createApp.spec.js b/packages/builder/cypress/integration/createApp.spec.js index 13c5979df9..34300bf7b3 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -63,7 +63,10 @@ filterTests(['smoke', 'all'], () => { const appName = "Cypress Tests" cy.get(".spectrum-Modal").within(() => { + cy.get("input").eq(0).should('have.focus') + //Auto fill + cy.get("input").eq(0).clear() cy.get("input").eq(0).type(appName).should("have.value", appName).blur() cy.get("input").eq(1).should("have.value", "/cypress-tests") cy.get(".spectrum-ButtonGroup").contains("Create app").should('not.be.disabled') @@ -97,6 +100,64 @@ filterTests(['smoke', 'all'], () => { 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.applicationInAppTable("My app") + cy.deleteApp("My app") + }) + + it("should create the first application from scratch, using the users first name as the default app name", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.updateUserInformation("Ted", "Userman") + + cy.createApp() + + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.wait(1000) + + cy.applicationInAppTable("Teds app") + cy.deleteApp("Teds app") + + cy.updateUserInformation("", "") + }) + + it("should create an application from an export", () => { + const exportedApp = 'cypress/fixtures/exported-app.txt' + + cy.importApp(exportedApp, "") + + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.applicationInAppTable("My app") + + cy.get(".appTable .name").eq(0).click() + + cy.deleteApp("My app") + }) + + it("should create an application from an export, using the users first name as the default app name", () => { + const exportedApp = 'cypress/fixtures/exported-app.txt' + + cy.updateUserInformation("Ted", "Userman") + + cy.importApp(exportedApp, "") + + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.applicationInAppTable("Teds app") + + cy.get(".appTable .name").eq(0).click() + + cy.deleteApp("Teds app") + + cy.updateUserInformation("", "") + }) + it("should generate the first application from a template", () => { cy.visit(`${Cypress.config().baseUrl}/builder`) cy.wait(500) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 9f3b14a413..70832da2a5 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -39,6 +39,68 @@ Cypress.Commands.add("closeModal", () => { }) }) +Cypress.Commands.add("importApp", (exportFilePath, name) => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + 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.get(".spectrum-Modal").within(() => { + cy.get("input").eq(1).should("have.focus") + + cy.get(".spectrum-Dropzone").selectFile(exportFilePath, { + action: "drag-drop", + }) + + cy.get(".gallery .filename").contains("exported-app.txt") + + if (name && name != "") { + cy.get("input").eq(0).type(name).should("have.value", name).blur() + } + cy.get(".confirm-wrap button") + .should("not.be.disabled") + .click({ force: true }) + cy.wait(5000) + }) +}) + +Cypress.Commands.add("updateUserInformation", (firstName, lastName) => { + cy.get(".user-dropdown .avatar > .icon").click({ force: true }) + + cy.get(".spectrum-Popover[data-cy='user-menu']").within(() => { + cy.get("li[data-cy='user-info']").click({ force: true }) + }) + + cy.get(".spectrum-Modal.is-open").within(() => { + if (!firstName || firstName == "") { + cy.get("[data-cy='user-first-name']").clear() + cy.get("[data-cy='user-first-name']").invoke("val").should("be.empty") + } else { + cy.get("[data-cy='user-first-name']") + .type(firstName) + .should("have.value", firstName) + .blur() + } + if (!lastName || lastName == "") { + cy.get("[data-cy='user-last-name']").clear() + cy.get("[data-cy='user-last-name']").invoke("val").should("be.empty") + } else { + cy.get("[data-cy='user-last-name']") + .type(lastName) + .should("have.value", lastName) + .blur() + } + cy.get("button").contains("Update information").click({ force: true }) + }) +}) + Cypress.Commands.add("createApp", (name, addDefaultTable) => { const shouldCreateDefaultTable = typeof addDefaultTable != "boolean" ? true : addDefaultTable @@ -57,7 +119,11 @@ Cypress.Commands.add("createApp", (name, addDefaultTable) => { }) cy.get(".spectrum-Modal").within(() => { - cy.get("input").eq(0).type(name).should("have.value", name).blur() + cy.get("input").eq(0).should("have.focus") + if (name && name != "") { + cy.get("input").eq(0).clear() + cy.get("input").eq(0).type(name).should("have.value", name).blur() + } cy.get(".spectrum-ButtonGroup").contains("Create app").click() cy.wait(10000) }) diff --git a/packages/builder/src/components/settings/UpdateUserInfoModal.svelte b/packages/builder/src/components/settings/UpdateUserInfoModal.svelte index ea0cb827f8..5734f613ad 100644 --- a/packages/builder/src/components/settings/UpdateUserInfoModal.svelte +++ b/packages/builder/src/components/settings/UpdateUserInfoModal.svelte @@ -27,6 +27,14 @@ Personalise the platform by adding your first name and last name. - - + + diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 74d2e7675e..73ae95a81e 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -22,7 +22,10 @@ $: validation.check($values) onMount(async () => { - $values.name = resolveAppName(template, $values.name) + const defaultName = $auth.user?.firstName + ? `${$auth.user.firstName}s app` + : "My app" + $values.name = resolveAppName(template, defaultName) nameToUrl($values.name) await setupValidation() }) @@ -44,7 +47,7 @@ } const resolveAppName = (template, name) => { - if (template && !name) { + if (template && !template.fromFile) { return template.name } return name ? name.trim() : null @@ -83,7 +86,7 @@ } data.append("useTemplate", template != null) if (template) { - data.append("templateName", template.name) //or here? + data.append("templateName", template.name) data.append("templateKey", template.key) data.append("templateFile", $values.file) } @@ -159,6 +162,7 @@ /> {/if}
logo - +
- +
- userInfoModal.show()}> + userInfoModal.show()} + dataCy={"user-info"} + > Update user information {#if $auth.isBuilder}