diff --git a/.vib/spring-cloud-dataflow/cypress/cypress.json b/.vib/spring-cloud-dataflow/cypress/cypress.json
new file mode 100644
index 0000000000..d900eab5a3
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress.json
@@ -0,0 +1,5 @@
+{
+ "baseUrl": "http://localhost",
+ "defaultCommandTimeout": 30000,
+ "pageLoadTimeout": 240000
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json
new file mode 100644
index 0000000000..061cfe650a
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json
@@ -0,0 +1,8 @@
+{
+ "newApplication": {
+ "streamApplicationType": "Stream application starters for Kafka/Maven",
+ "taskApplicationType": "Task application starters for Maven",
+ "streamApplication1": "mongodb",
+ "streamApplication2": "cassandra"
+ }
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json
new file mode 100644
index 0000000000..bee6cc078f
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json
@@ -0,0 +1,7 @@
+{
+ "newSchedule": {
+ "name": "test-schedule",
+ "cronExpression": "*/5 * * * *",
+ "taskType": "timestamp"
+ }
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json
new file mode 100644
index 0000000000..5165ee0b45
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json
@@ -0,0 +1,6 @@
+{
+ "newStream": {
+ "name": "test-stream",
+ "type": "mongodb | cassandra"
+ }
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json
new file mode 100644
index 0000000000..e351f44143
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json
@@ -0,0 +1,13 @@
+{
+ "date": 1650360393208,
+ "tasks": [
+ {
+ "name": "test-task-imported",
+ "dslText": "timestamp",
+ "composed": false,
+ "status": "UNKNOWN",
+ "composedTaskElement": false,
+ "description": ""
+ }
+ ]
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json
new file mode 100644
index 0000000000..5b2633be25
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json
@@ -0,0 +1,5 @@
+{
+ "newTask": {
+ "name": "test-task"
+ }
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js
new file mode 100644
index 0000000000..1b8f70d642
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js
@@ -0,0 +1,26 @@
+///
+import { random } from './utils';
+
+export const importAnApplication = (application) => {
+ cy.visit('/dashboard');
+ cy.contains('button', 'Add application(s)').click();
+ cy.contains('label', application).click();
+ cy.contains('.btn-primary', 'Import').click();
+ cy.get('.toast-container').should('contain', 'Application(s) Imported');
+};
+
+export const createATask = (taskType) => {
+ cy.visit('/dashboard/#/tasks-jobs/tasks');
+ cy.contains('button', 'Create task').click();
+ cy.get('.CodeMirror-line').type(taskType);
+ cy.contains('#v-2', taskType);
+ cy.contains('button', 'Create task').click();
+ cy.contains('.modal-content', 'Create task');
+ cy.fixture('tasks').then((task) => {
+ cy.get('input[placeholder="Task Name"]').type(
+ `${task.newTask.name}-${random}`
+ );
+ cy.get('input#desc').type('This is a task');
+ cy.contains('button', 'Create the task').click();
+ });
+};
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js
new file mode 100644
index 0000000000..8a5b971d0e
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js
@@ -0,0 +1,121 @@
+///
+import { random } from './utils.js';
+import { importAnApplication, createATask } from './prepare_app_state.js';
+
+it('allows getting Spring Cloud Dataflow info', () => {
+ cy.visit('/dashboard');
+ cy.get('.signpost-trigger').click();
+ cy.get('.signpost-content-body')
+ .invoke('text')
+ .should('match', /.*[0-9].*/);
+ cy.contains('button', 'More info').click();
+ cy.contains('.modal-content', 'Core')
+ .and('contain', 'Dashboard')
+ .and('contain', 'Shell');
+});
+
+it('allows a stream to be created and deployed', () => {
+ cy.fixture('applications').then((application) => {
+ importAnApplication(application.newApplication.streamApplicationType);
+ });
+ cy.visit('dashboard/#/streams/list');
+ cy.contains('button', 'Create stream(s)').click();
+ cy.fixture('streams').then((stream) => {
+ cy.get('.CodeMirror-line').type(stream.newStream.type);
+ });
+
+ cy.fixture('applications').then((application) => {
+ cy.contains('#v-2', application.newApplication.streamApplication1).and(
+ 'contain',
+ application.newApplication.streamApplication2
+ );
+ });
+
+ cy.contains('button', 'Create stream(s)').click();
+ cy.get('.modal-content').should('contain', 'Create Stream');
+ cy.fixture('streams').then((stream) => {
+ cy.get('input[placeholder="Stream Name"]').type(
+ `${stream.newStream.name}-${random}`
+ );
+ cy.contains('button', 'Create the stream').click();
+ cy.contains(
+ '.datagrid-inner-wrapper',
+ `${stream.newStream.name}-${random}`
+ );
+ cy.contains('.toast-container', 'successfully');
+ cy.contains('clr-dg-cell', 'UNDEPLOYED')
+ .siblings('clr-dg-cell', stream.newStream.name)
+ .first()
+ .click();
+ });
+
+ cy.contains('button#btn-deploy', 'Deploy stream').click();
+ cy.contains('button', 'Deploy stream').click();
+ cy.get('.toast-container').should('contain', 'Deploy success');
+});
+
+it('allows a task to be scheduled and destroyed', () => {
+ cy.fixture('applications').then((application) => {
+ importAnApplication(application.newApplication.taskApplicationType);
+ });
+ cy.fixture('schedules').then((schedule) => {
+ createATask(schedule.newSchedule.taskType);
+ });
+ cy.visit('dashboard/#/tasks-jobs/tasks');
+ cy.fixture('tasks').then((task) => {
+ cy.contains('clr-dg-cell', 'UNKNOWN')
+ .siblings('clr-dg-cell', task.newTask.name)
+ .first()
+ .click();
+ });
+ cy.contains('button', 'Schedule').click();
+ cy.fixture('schedules').then((schedule) => {
+ cy.get('input[name="example"]')
+ .first()
+ .type(`${schedule.newSchedule.name}-${random}`);
+
+ cy.get('input[name="example"]')
+ .last()
+ .type(`${schedule.newSchedule.cronExpression}`);
+ });
+
+ cy.contains('button', 'Create schedule(s)').click();
+ cy.contains('.toast-container', 'Successfully');
+ cy.contains('a', 'Tasks').click();
+ cy.contains('clr-dg-cell', 'UNKNOWN')
+ .siblings('clr-dg-cell', 'test-task-')
+ .first()
+ .click();
+ cy.contains('button', 'Destroy task').click();
+ cy.contains('button', 'Destroy the task').click();
+ cy.contains('1 task definition(s) destroyed.');
+});
+
+it('allows importing a task from a file and destroying it ', () => {
+ cy.visit('/dashboard/#/manage/tools');
+ cy.contains('a', 'Import tasks from a JSON file').click();
+ cy.get('input[type="file"]').selectFile(
+ 'cypress/fixtures/task-to-import.json',
+ { force: true }
+ );
+ cy.contains('button', 'Import').click();
+ cy.contains('1 task(s) created');
+ cy.get('.close').click();
+ cy.visit('dashboard/#/tasks-jobs/tasks');
+ cy.contains('button', 'Group Actions').click();
+ cy.get('[aria-label="Select All"]').click({ force: true });
+ cy.contains('button', 'Destroy task').click();
+ cy.contains('.modal-content', 'Confirm Destroy Task');
+ cy.contains('button', 'Destroy the task').click();
+ cy.get('.toast-container').should('contain', 'destroyed');
+});
+
+it('allows unregistering of an application', () => {
+ cy.fixture('applications').then((application) => {
+ importAnApplication(application.newApplication.streamApplicationType);
+ });
+ cy.get('clr-dg-cell').siblings('clr-dg-cell', 'PROCESSOR').first().click();
+ cy.contains('button', 'Unregister Application').click();
+ cy.contains('button', 'Unregister the application').click();
+ cy.contains('Successfully removed app');
+});
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js
new file mode 100644
index 0000000000..21de22ebf9
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js
@@ -0,0 +1 @@
+export let random = (Math.random() + 1).toString(36).substring(7);
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js b/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js
new file mode 100644
index 0000000000..42dac69cdc
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js
@@ -0,0 +1,26 @@
+const CLICK_DELAY = 800;
+const TYPE_DELAY = 200;
+
+for (const command of ['click']) {
+ Cypress.Commands.overwrite(command, (originalFn, ...args) => {
+ const origVal = originalFn(...args);
+
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(origVal);
+ }, CLICK_DELAY);
+ });
+ });
+}
+
+for (const command of ['type']) {
+ Cypress.Commands.overwrite(command, (originalFn, ...args) => {
+ const origVal = originalFn(...args);
+
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(origVal);
+ }, TYPE_DELAY);
+ });
+ });
+}
diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js b/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js
new file mode 100644
index 0000000000..37a498fb5b
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// 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 './commands';
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/.vib/spring-cloud-dataflow/goss/goss.yaml b/.vib/spring-cloud-dataflow/goss/goss.yaml
new file mode 100644
index 0000000000..be85b3d8a4
--- /dev/null
+++ b/.vib/spring-cloud-dataflow/goss/goss.yaml
@@ -0,0 +1,22 @@
+file:
+ /opt/bitnami/spring-cloud-dataflow/conf/application.yml:
+ exists: true
+ filetype: symlink
+ mode: "0777"
+ owner: root
+ /bitnami/spring-cloud-dataflow:
+ exists: true
+ filetype: directory
+ mode: "0775"
+ owner: root
+command:
+ user-id-test:
+ exec: if [ "$(id -u)" -eq 0 ]; then exit 1; fi
+ exit-status: 0
+ stdout: []
+ stderr: []
+ java-test:
+ exec: java -version
+ exit-status: 0
+ stdout:
+ stderr: []
diff --git a/.vib/spring-cloud-dataflow/vib-verify.json b/.vib/spring-cloud-dataflow/vib-verify.json
index e7fb7b730e..270ef1be94 100644
--- a/.vib/spring-cloud-dataflow/vib-verify.json
+++ b/.vib/spring-cloud-dataflow/vib-verify.json
@@ -22,7 +22,7 @@
"url": "{SHA_ARCHIVE}",
"path": "/bitnami/spring-cloud-dataflow"
},
- "runtime_parameters": "InNlcnZlciI6CiAgInNlcnZpY2UiOgogICAgInBvcnQiOiA4MAogICAgInR5cGUiOiAiTG9hZEJhbGFuY2VyIg==",
+ "runtime_parameters": "c2VydmVyOgogIHNlcnZpY2U6CiAgICB0eXBlOiBMb2FkQmFsYW5jZXIKICAgIHBvcnQ6IDgwCg==",
"target_platform": {
"target_platform_id": "{VIB_ENV_TARGET_PLATFORM}",
"size": {
@@ -35,7 +35,9 @@
"action_id": "trivy",
"params": {
"threshold": "CRITICAL",
- "vuln_type": ["OS"]
+ "vuln_type": [
+ "OS"
+ ]
}
},
{
@@ -44,6 +46,27 @@
"endpoint": "lb-spring-cloud-dataflow-server-http",
"app_protocol": "HTTP"
}
+ },
+ {
+ "action_id": "goss",
+ "params": {
+ "resources": {
+ "path": "/.vib/spring-cloud-dataflow/goss"
+ },
+ "remote": {
+ "workload": "deploy-spring-cloud-dataflow-server"
+ }
+ }
+ },
+ {
+ "action_id": "cypress",
+ "params": {
+ "resources": {
+ "path": "/.vib/spring-cloud-dataflow/cypress"
+ },
+ "endpoint": "lb-spring-cloud-dataflow-server-http",
+ "app_protocol": "HTTP"
+ }
}
]
}