mirror of https://github.com/Budibase/budibase.git
118 changed files with 1535 additions and 565 deletions
@ -0,0 +1,4 @@ |
|||
{ |
|||
"baseUrl": "http://localhost:4001/_builder/", |
|||
"video": false |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"name": "Using fixtures to represent data", |
|||
"email": "hello@cypress.io", |
|||
"body": "Fixtures are a great way to mock data for responses to routes" |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
{ |
|||
"id": 8739, |
|||
"name": "Jane", |
|||
"email": "jane@example.com" |
|||
} |
|||
@ -0,0 +1,232 @@ |
|||
[ |
|||
{ |
|||
"id": 1, |
|||
"name": "Leanne Graham", |
|||
"username": "Bret", |
|||
"email": "Sincere@april.biz", |
|||
"address": { |
|||
"street": "Kulas Light", |
|||
"suite": "Apt. 556", |
|||
"city": "Gwenborough", |
|||
"zipcode": "92998-3874", |
|||
"geo": { |
|||
"lat": "-37.3159", |
|||
"lng": "81.1496" |
|||
} |
|||
}, |
|||
"phone": "1-770-736-8031 x56442", |
|||
"website": "hildegard.org", |
|||
"company": { |
|||
"name": "Romaguera-Crona", |
|||
"catchPhrase": "Multi-layered client-server neural-net", |
|||
"bs": "harness real-time e-markets" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 2, |
|||
"name": "Ervin Howell", |
|||
"username": "Antonette", |
|||
"email": "Shanna@melissa.tv", |
|||
"address": { |
|||
"street": "Victor Plains", |
|||
"suite": "Suite 879", |
|||
"city": "Wisokyburgh", |
|||
"zipcode": "90566-7771", |
|||
"geo": { |
|||
"lat": "-43.9509", |
|||
"lng": "-34.4618" |
|||
} |
|||
}, |
|||
"phone": "010-692-6593 x09125", |
|||
"website": "anastasia.net", |
|||
"company": { |
|||
"name": "Deckow-Crist", |
|||
"catchPhrase": "Proactive didactic contingency", |
|||
"bs": "synergize scalable supply-chains" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 3, |
|||
"name": "Clementine Bauch", |
|||
"username": "Samantha", |
|||
"email": "Nathan@yesenia.net", |
|||
"address": { |
|||
"street": "Douglas Extension", |
|||
"suite": "Suite 847", |
|||
"city": "McKenziehaven", |
|||
"zipcode": "59590-4157", |
|||
"geo": { |
|||
"lat": "-68.6102", |
|||
"lng": "-47.0653" |
|||
} |
|||
}, |
|||
"phone": "1-463-123-4447", |
|||
"website": "ramiro.info", |
|||
"company": { |
|||
"name": "Romaguera-Jacobson", |
|||
"catchPhrase": "Face to face bifurcated interface", |
|||
"bs": "e-enable strategic applications" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 4, |
|||
"name": "Patricia Lebsack", |
|||
"username": "Karianne", |
|||
"email": "Julianne.OConner@kory.org", |
|||
"address": { |
|||
"street": "Hoeger Mall", |
|||
"suite": "Apt. 692", |
|||
"city": "South Elvis", |
|||
"zipcode": "53919-4257", |
|||
"geo": { |
|||
"lat": "29.4572", |
|||
"lng": "-164.2990" |
|||
} |
|||
}, |
|||
"phone": "493-170-9623 x156", |
|||
"website": "kale.biz", |
|||
"company": { |
|||
"name": "Robel-Corkery", |
|||
"catchPhrase": "Multi-tiered zero tolerance productivity", |
|||
"bs": "transition cutting-edge web services" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 5, |
|||
"name": "Chelsey Dietrich", |
|||
"username": "Kamren", |
|||
"email": "Lucio_Hettinger@annie.ca", |
|||
"address": { |
|||
"street": "Skiles Walks", |
|||
"suite": "Suite 351", |
|||
"city": "Roscoeview", |
|||
"zipcode": "33263", |
|||
"geo": { |
|||
"lat": "-31.8129", |
|||
"lng": "62.5342" |
|||
} |
|||
}, |
|||
"phone": "(254)954-1289", |
|||
"website": "demarco.info", |
|||
"company": { |
|||
"name": "Keebler LLC", |
|||
"catchPhrase": "User-centric fault-tolerant solution", |
|||
"bs": "revolutionize end-to-end systems" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 6, |
|||
"name": "Mrs. Dennis Schulist", |
|||
"username": "Leopoldo_Corkery", |
|||
"email": "Karley_Dach@jasper.info", |
|||
"address": { |
|||
"street": "Norberto Crossing", |
|||
"suite": "Apt. 950", |
|||
"city": "South Christy", |
|||
"zipcode": "23505-1337", |
|||
"geo": { |
|||
"lat": "-71.4197", |
|||
"lng": "71.7478" |
|||
} |
|||
}, |
|||
"phone": "1-477-935-8478 x6430", |
|||
"website": "ola.org", |
|||
"company": { |
|||
"name": "Considine-Lockman", |
|||
"catchPhrase": "Synchronised bottom-line interface", |
|||
"bs": "e-enable innovative applications" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 7, |
|||
"name": "Kurtis Weissnat", |
|||
"username": "Elwyn.Skiles", |
|||
"email": "Telly.Hoeger@billy.biz", |
|||
"address": { |
|||
"street": "Rex Trail", |
|||
"suite": "Suite 280", |
|||
"city": "Howemouth", |
|||
"zipcode": "58804-1099", |
|||
"geo": { |
|||
"lat": "24.8918", |
|||
"lng": "21.8984" |
|||
} |
|||
}, |
|||
"phone": "210.067.6132", |
|||
"website": "elvis.io", |
|||
"company": { |
|||
"name": "Johns Group", |
|||
"catchPhrase": "Configurable multimedia task-force", |
|||
"bs": "generate enterprise e-tailers" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 8, |
|||
"name": "Nicholas Runolfsdottir V", |
|||
"username": "Maxime_Nienow", |
|||
"email": "Sherwood@rosamond.me", |
|||
"address": { |
|||
"street": "Ellsworth Summit", |
|||
"suite": "Suite 729", |
|||
"city": "Aliyaview", |
|||
"zipcode": "45169", |
|||
"geo": { |
|||
"lat": "-14.3990", |
|||
"lng": "-120.7677" |
|||
} |
|||
}, |
|||
"phone": "586.493.6943 x140", |
|||
"website": "jacynthe.com", |
|||
"company": { |
|||
"name": "Abernathy Group", |
|||
"catchPhrase": "Implemented secondary concept", |
|||
"bs": "e-enable extensible e-tailers" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 9, |
|||
"name": "Glenna Reichert", |
|||
"username": "Delphine", |
|||
"email": "Chaim_McDermott@dana.io", |
|||
"address": { |
|||
"street": "Dayna Park", |
|||
"suite": "Suite 449", |
|||
"city": "Bartholomebury", |
|||
"zipcode": "76495-3109", |
|||
"geo": { |
|||
"lat": "24.6463", |
|||
"lng": "-168.8889" |
|||
} |
|||
}, |
|||
"phone": "(775)976-6794 x41206", |
|||
"website": "conrad.com", |
|||
"company": { |
|||
"name": "Yost and Sons", |
|||
"catchPhrase": "Switchable contextually-based project", |
|||
"bs": "aggregate real-time technologies" |
|||
} |
|||
}, |
|||
{ |
|||
"id": 10, |
|||
"name": "Clementina DuBuque", |
|||
"username": "Moriah.Stanton", |
|||
"email": "Rey.Padberg@karina.biz", |
|||
"address": { |
|||
"street": "Kattie Turnpike", |
|||
"suite": "Suite 198", |
|||
"city": "Lebsackbury", |
|||
"zipcode": "31428-2261", |
|||
"geo": { |
|||
"lat": "-38.2386", |
|||
"lng": "57.2232" |
|||
} |
|||
}, |
|||
"phone": "024-648-3804", |
|||
"website": "ambrose.net", |
|||
"company": { |
|||
"name": "Hoeger LLC", |
|||
"catchPhrase": "Centralized empowering task-force", |
|||
"bs": "target end-to-end models" |
|||
} |
|||
} |
|||
] |
|||
@ -0,0 +1,17 @@ |
|||
context('Create an Application', () => { |
|||
|
|||
beforeEach(() => { |
|||
cy.visit('localhost:4001/_builder') |
|||
}) |
|||
|
|||
// https://on.cypress.io/interacting-with-elements
|
|||
|
|||
it('should create a new application', () => { |
|||
// https://on.cypress.io/type
|
|||
cy.createApp('My Cool App', 'This is a description') |
|||
|
|||
cy.visit('localhost:4001/_builder') |
|||
|
|||
cy.contains('My Cool App').should('exist') |
|||
}) |
|||
}) |
|||
@ -0,0 +1,53 @@ |
|||
context('Create Components', () => { |
|||
|
|||
before(() => { |
|||
cy.visit('localhost:4001/_builder') |
|||
// https://on.cypress.io/type
|
|||
cy.createApp('Model App', 'Model App Description') |
|||
cy.createModel('dog', 'name', 'age') |
|||
cy.addRecord('bob', '15') |
|||
}) |
|||
|
|||
// https://on.cypress.io/interacting-with-elements
|
|||
it('should add a container', () => { |
|||
cy.contains('frontend').click() |
|||
cy.get('.switcher > :nth-child(2)').click() |
|||
|
|||
cy.contains('Container').click() |
|||
}) |
|||
it('should add a headline', () => { |
|||
cy.addHeadlineComponent('An Amazing headline!') |
|||
|
|||
getIframeBody().contains('An Amazing headline!') |
|||
}) |
|||
it('change the font size of the headline', () => { |
|||
cy.contains('Typography').click() |
|||
cy.get('input[name="font-size"]') |
|||
.type('60px') |
|||
cy.contains('Design').click() |
|||
|
|||
getIframeBody().contains('An Amazing headline!').should('have.css', 'font-size', '60px') |
|||
}) |
|||
}) |
|||
|
|||
const getIframeDocument = () => { |
|||
return cy |
|||
.get('iframe') |
|||
// Cypress yields jQuery element, which has the real
|
|||
// DOM element under property "0".
|
|||
// From the real DOM iframe element we can get
|
|||
// the "document" element, it is stored in "contentDocument" property
|
|||
// Cypress "its" command can access deep properties using dot notation
|
|||
// https://on.cypress.io/its
|
|||
.its('0.contentDocument').should('exist') |
|||
} |
|||
|
|||
const getIframeBody = () => { |
|||
// get the document
|
|||
return getIframeDocument() |
|||
// automatically retries until body is loaded
|
|||
.its('body').should('not.be.undefined') |
|||
// wraps "body" DOM element to allow
|
|||
// chaining more Cypress commands, like ".find(...)"
|
|||
.then(cy.wrap) |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
context('Create a Model', () => { |
|||
|
|||
before(() => { |
|||
cy.visit('localhost:4001/_builder') |
|||
// https://on.cypress.io/type
|
|||
cy.createApp('Model App', 'Model App Description') |
|||
}) |
|||
|
|||
// https://on.cypress.io/interacting-with-elements
|
|||
it('should create a new model', () => { |
|||
|
|||
cy.createModel('dog', 'name', 'age') |
|||
|
|||
// Check if model exists
|
|||
cy.get('.title').should('have.text', 'dog') |
|||
}) |
|||
it('should add a record', () => { |
|||
cy.addRecord('bob', '15') |
|||
|
|||
cy.contains('bob').should('have.text', 'bob') |
|||
}) |
|||
}) |
|||
@ -0,0 +1,19 @@ |
|||
context('Create a User', () => { |
|||
|
|||
before(() => { |
|||
cy.visit('localhost:4001/_builder') |
|||
// https://on.cypress.io/type
|
|||
cy.createApp('User App', 'This app is used to test user creation') |
|||
}) |
|||
|
|||
// https://on.cypress.io/interacting-with-elements
|
|||
it('should create a user', () => { |
|||
// Close Model modal that shows up after creating an app
|
|||
cy.get('.close').click() |
|||
|
|||
cy.createUser('bbuser', 'test', 'ADMIN') |
|||
|
|||
// Check to make sure user was created!
|
|||
cy.contains('bbuser').should('have.text', 'bbuser') |
|||
}) |
|||
}) |
|||
@ -0,0 +1,46 @@ |
|||
context('Create a workflow', () => { |
|||
|
|||
before(() => { |
|||
cy.visit('localhost:4001/_builder') |
|||
|
|||
cy.createApp('Workflow Test App', 'This app is used to test that workflows do in fact work!') |
|||
}) |
|||
|
|||
// https://on.cypress.io/interacting-with-elements
|
|||
it('should create a workflow', () => { |
|||
cy.createModel('dog', 'name', 'age') |
|||
cy.createUser('bbuser', 'test', 'ADMIN') |
|||
|
|||
|
|||
cy.contains('workflow').click() |
|||
cy.get('.new-workflow-button').click() |
|||
cy.get('input').type('Add Record') |
|||
cy.contains('Save').click() |
|||
|
|||
// Add trigger
|
|||
cy.get('[data-cy=add-workflow-component]').click() |
|||
cy.get('[data-cy=RECORD_SAVED]').click() |
|||
cy.get('.budibase__input').select('dog') |
|||
|
|||
// Create action
|
|||
cy.get('[data-cy=SAVE_RECORD]').click() |
|||
cy.get(':nth-child(2) > .budibase__input').type('goodboy') |
|||
cy.get(':nth-child(3) > .budibase__input').type('11') |
|||
|
|||
// Save
|
|||
cy.get('[data-cy=save-workflow-setup]').click() |
|||
cy.get('.workflow-button').click() |
|||
|
|||
// Activate Workflow
|
|||
cy.get('[data-cy=activate-workflow]').click() |
|||
|
|||
}) |
|||
it('should add record when a new record is added', () => { |
|||
cy.contains('backend').click() |
|||
|
|||
cy.addRecord('bob', '15') |
|||
|
|||
cy.contains('goodboy').should('have.text', 'goodboy') |
|||
|
|||
}) |
|||
}) |
|||
@ -0,0 +1,22 @@ |
|||
/// <reference types="cypress" />
|
|||
// ***********************************************************
|
|||
// This example plugins/index.js can be used to load plugins
|
|||
//
|
|||
// You can change the location of this file or turn off loading
|
|||
// the plugins file with the 'pluginsFile' configuration option.
|
|||
//
|
|||
// You can read more here:
|
|||
// https://on.cypress.io/plugins-guide
|
|||
// ***********************************************************
|
|||
|
|||
// This function is called when a project is opened or re-opened (e.g. due to
|
|||
// the project's config changing)
|
|||
|
|||
/** |
|||
* @type {Cypress.PluginConfig} |
|||
*/ |
|||
// eslint-disable-next-line no-unused-vars
|
|||
module.exports = (on, config) => { |
|||
// `on` is used to hook into various events Cypress emits
|
|||
// `config` is the resolved Cypress config
|
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// What this script does:
|
|||
// 1. Removes the old test folder if it exists (.budibase-cypress)
|
|||
// 2. Initialises using `.budibase-cypress`
|
|||
// 3. Runs the server using said folder
|
|||
|
|||
const rimraf = require("rimraf") |
|||
const { join } = require("path") |
|||
const homedir = join(require("os").homedir(), ".budibase-cypress") |
|||
const init = require("../../cli/src/commands/init/initHandler") |
|||
const run = require("../../cli/src/commands/run/runHandler") |
|||
|
|||
rimraf.sync(homedir) |
|||
|
|||
init({ dir: homedir, clientId: "cypress-test" }).then(() => { |
|||
delete require.cache[require.resolve("../../server/src/environment")] |
|||
run({ dir: homedir }) |
|||
}) |
|||
@ -0,0 +1,105 @@ |
|||
// ***********************************************
|
|||
// This example commands.js shows you how to
|
|||
// create various custom commands and overwrite
|
|||
// existing commands.
|
|||
//
|
|||
// For more comprehensive examples of custom
|
|||
// commands please read more here:
|
|||
// https://on.cypress.io/custom-commands
|
|||
// ***********************************************
|
|||
//
|
|||
//
|
|||
// -- This is a parent command --
|
|||
// Cypress.Commands.add("login", (email, password) => { ... })
|
|||
//
|
|||
//
|
|||
// -- This is a child command --
|
|||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
|||
//
|
|||
//
|
|||
// -- This is a dual command --
|
|||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
|||
//
|
|||
//
|
|||
// -- This will overwrite an existing command --
|
|||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|||
|
|||
Cypress.Commands.add("createApp", (name, description) => { |
|||
cy.get(".banner-button") |
|||
.click() |
|||
.get('input[name="name"]') |
|||
.type(name) |
|||
.should("have.value", name) |
|||
|
|||
cy.get('textarea[name="description"]') |
|||
.type(description) |
|||
.should("have.value", description) |
|||
|
|||
cy.contains("Save").click() |
|||
}) |
|||
Cypress.Commands.add("createModel", (modelName, firstField, secondField) => { |
|||
// Enter model name
|
|||
cy.get("[data-cy=Name]") |
|||
.click() |
|||
.type(modelName) |
|||
|
|||
// Add 'name' field
|
|||
cy.get("[data-cy=add-new-model-field]").click() |
|||
cy.get("[data-cy=Name]") |
|||
.click() |
|||
.type(firstField) |
|||
cy.contains("Save").click() |
|||
|
|||
// Add 'age' field
|
|||
cy.get("[data-cy=add-new-model-field]").click() |
|||
|
|||
cy.get("[data-cy=Name]") |
|||
.click() |
|||
.type(secondField) |
|||
cy.get("select").select("number") |
|||
cy.contains("Save").click() |
|||
cy.contains(secondField).should("exist") |
|||
|
|||
// Save model
|
|||
cy.contains("Save").click() |
|||
}) |
|||
Cypress.Commands.add("addRecord", (firstField, secondField) => { |
|||
cy.contains("Create new record").click() |
|||
|
|||
cy.get("[data-cy=name-input]") |
|||
.click() |
|||
.type(firstField) |
|||
cy.get("[data-cy=age-input]") |
|||
.click() |
|||
.type(secondField) |
|||
|
|||
// Save
|
|||
cy.contains("Save").click() |
|||
}) |
|||
|
|||
Cypress.Commands.add("createUser", (username, password, level) => { |
|||
// Create User
|
|||
cy.get(".nav-group-header > .ri-add-line").click() |
|||
|
|||
cy.get("[data-cy=username]").type(username) |
|||
cy.get("[data-cy=password]").type(password) |
|||
cy.get("[data-cy=accessLevel]").select(level) |
|||
|
|||
// Save
|
|||
cy.contains("Save").click() |
|||
}) |
|||
|
|||
Cypress.Commands.add("addHeadlineComponent", text => { |
|||
cy.get(".switcher > :nth-child(2)").click() |
|||
|
|||
cy.get("[data-cy=Text]").click() |
|||
cy.get("[data-cy=Headline]").click() |
|||
cy.get(".tabs > :nth-child(2)").click() |
|||
cy.get('input[type="text"]').type(text) |
|||
cy.contains("Design").click() |
|||
}) |
|||
Cypress.Commands.add("addButtonComponent", () => { |
|||
cy.get(".switcher > :nth-child(2)").click() |
|||
|
|||
cy.get("[data-cy=Button]").click() |
|||
}) |
|||
@ -0,0 +1,3 @@ |
|||
Cypress.Cookies.defaults({ |
|||
whitelist: "builder:token", |
|||
}) |
|||
@ -0,0 +1,21 @@ |
|||
// ***********************************************************
|
|||
// This example support/index.js is processed and
|
|||
// loaded automatically before your test files.
|
|||
//
|
|||
// This is a great place to put global configuration and
|
|||
// behavior that modifies Cypress.
|
|||
//
|
|||
// You can change the location of this file or turn off
|
|||
// automatically serving support files with the
|
|||
// 'supportFile' configuration option.
|
|||
//
|
|||
// You can read more here:
|
|||
// https://on.cypress.io/configuration
|
|||
// ***********************************************************
|
|||
|
|||
// Import commands.js using ES2015 syntax:
|
|||
import "./cookies" |
|||
import "./commands" |
|||
|
|||
// Alternatively you can use CommonJS syntax:
|
|||
// require('./commands')
|
|||
@ -0,0 +1,53 @@ |
|||
<script> |
|||
import {createEventDispatcher} from "svelte" |
|||
import { fade } from 'svelte/transition'; |
|||
import CheckedBackground from "./CheckedBackground.svelte" |
|||
|
|||
export let hovered = false |
|||
export let color = "#fff" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
|
|||
</script> |
|||
|
|||
<style> |
|||
.swatch { |
|||
position: relative; |
|||
cursor: pointer; |
|||
border-radius: 6px; |
|||
border: 1px solid #dedada; |
|||
height: 20px; |
|||
width: 20px; |
|||
} |
|||
|
|||
.space { |
|||
padding: 3px 5px; |
|||
} |
|||
|
|||
.remove-icon { |
|||
position: absolute; |
|||
right: 0; |
|||
top: -5px; |
|||
right: -4px; |
|||
width:10px; |
|||
height: 10px; |
|||
border-radius: 50%; |
|||
background-color: #800000; |
|||
color: #fff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
</style> |
|||
|
|||
<div class="space"> |
|||
<CheckedBackground borderRadius="6px"> |
|||
<div in:fade class="swatch" style={`background: ${color};`} on:click|self on:mouseover={() => hovered = true} on:mouseleave={() => hovered = false}> |
|||
{#if hovered} |
|||
<div in:fade class="remove-icon" on:click|self={()=> dispatch("removeswatch")}> |
|||
<span on:click|self={()=> dispatch("removeswatch")}>×</span> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
</CheckedBackground> |
|||
</div> |
|||
@ -1,3 +1,4 @@ |
|||
<script> |
|||
import { params } from "@sveltech/routify" |
|||
store.setCurrentPage($params.page) |
|||
</script> |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
export const getAppId = docCookie => { |
|||
const cookie = |
|||
docCookie.split(";").find(c => c.trim().startsWith("budibase:token")) || |
|||
docCookie.split(";").find(c => c.trim().startsWith("builder:token")) |
|||
|
|||
const base64Token = cookie.substring(lengthOfKey) |
|||
|
|||
const user = JSON.parse(atob(base64Token.split(".")[1])) |
|||
return user.appId |
|||
} |
|||
|
|||
const lengthOfKey = "budibase:token=".length |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue