Bladeren bron

Add support for Cypress 'Run All Tests'

Made the login process programmatically using Cypress Request API
instead of using the UI, thus allowing the tests to run sequentially
automatically instead of manually running them one after the other.

NOTE: A new parameter has been added to `private/cypress/config.js`:
`coriolisUrl`, since Cypress now needs to know the Coriolis URL in order
to be able to login with its API. The `config.template.js` has been
updated to reflect this.
Sergiu Miclea 8 jaren geleden
bovenliggende
commit
d300b4413a
23 gewijzigde bestanden met toevoegingen van 127 en 105 verwijderingen
  1. 3 3
      cypress.json
  2. 1 0
      private/cypress/config.template.js
  3. 1 1
      private/cypress/integration/login/Invalid Login.js
  4. 2 5
      private/cypress/integration/migration/1 - Create Openstack Endpoint.js
  5. 2 6
      private/cypress/integration/migration/2 - Create VmWare Endpoint.js
  6. 2 5
      private/cypress/integration/migration/3 - Create VmWare Openstack Migration.js
  7. 3 7
      private/cypress/integration/migration/4 - Cancel first running migration.js
  8. 2 7
      private/cypress/integration/migration/6 - Delete first migration.js
  9. 2 5
      private/cypress/integration/migration/7 - Delete e2e Openstack endpoint.js
  10. 2 5
      private/cypress/integration/migration/8 - Delete e2e VmWare endpoint.js
  11. 2 5
      private/cypress/integration/replica/1 - Create Azure Endpoint.js
  12. 2 6
      private/cypress/integration/replica/2 - Create VmWare Endpoint.js
  13. 9 9
      private/cypress/integration/replica/3 - Create VmWare Azure Replica.js
  14. 3 7
      private/cypress/integration/replica/4 - Cancel first running replica.js
  15. 2 7
      private/cypress/integration/replica/5 - Cannot delete used endpoint.js
  16. 2 7
      private/cypress/integration/replica/6 - Delete first replica.js
  17. 2 5
      private/cypress/integration/replica/7 - Delete e2e Azure endpoint.js
  18. 2 5
      private/cypress/integration/replica/8 - Delete e2e VmWare endpoint.js
  19. 3 8
      private/cypress/integration/scheduler/Scheduler Operations.js
  20. 73 0
      private/cypress/support/commands.js
  21. 2 0
      private/cypress/support/index.js
  22. 4 1
      src/components/atoms/StatusPill/index.jsx
  23. 1 1
      src/components/organisms/DetailsContentHeader/index.jsx

+ 3 - 3
cypress.json

@@ -4,10 +4,10 @@
   "integrationFolder": "private/cypress/integration",
   "integrationFolder": "private/cypress/integration",
   "pluginsFile": false,
   "pluginsFile": false,
   "screenshotsFolder": "private/cypress/screenshots",
   "screenshotsFolder": "private/cypress/screenshots",
-  "supportFile": false,
   "videosFolder": "private/cypress/videos",
   "videosFolder": "private/cypress/videos",
+  "supportFile": "private/cypress/support/index.js",
   "viewportWidth": 1280,
   "viewportWidth": 1280,
   "viewportHeight": 660,
   "viewportHeight": 660,
-  "defaultCommandTimeout":	7000,
+  "defaultCommandTimeout": 7000,
   "execTimeout": 90000
   "execTimeout": 90000
-}
+}

+ 1 - 0
private/cypress/config.template.js

@@ -2,6 +2,7 @@
 
 
 export default {
 export default {
   nodeServer: 'http://localhost:3000/',
   nodeServer: 'http://localhost:3000/',
+  coriolisUrl: '',
   username: 'admin',
   username: 'admin',
   password: '',
   password: '',
   endpoints: {
   endpoints: {

+ 1 - 1
private/cypress/integration/login/Invalid Login.js

@@ -5,7 +5,7 @@ import config from '../../config'
 
 
 declare var cy: any
 declare var cy: any
 
 
-describe('Coriolis Login', () => {
+describe('Coriolis Login Failed', () => {
   it('Displays incorrect password', () => {
   it('Displays incorrect password', () => {
     cy.server()
     cy.server()
     cy.route({ url: '**/identity/**', method: 'POST' }).as('login')
     cy.route({ url: '**/identity/**', method: 'POST' }).as('login')

+ 2 - 5
private/cypress/integration/migration/1 - Create Openstack Endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Create Openstack Endpoint', () => {
 describe('Create Openstack Endpoint', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows new Openstack endpoint dialog', () => {
   it('Shows new Openstack endpoint dialog', () => {

+ 2 - 6
private/cypress/integration/migration/2 - Create VmWare Endpoint.js

@@ -5,15 +5,11 @@ import config from '../../config'
 
 
 describe('Create VmWare Endpoint', () => {
 describe('Create VmWare Endpoint', () => {
   before(() => {
   before(() => {
-    cy.server()
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows new VmWare endpoint dialog', () => {
   it('Shows new VmWare endpoint dialog', () => {

+ 2 - 5
private/cypress/integration/migration/3 - Create VmWare Openstack Migration.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Create VmWare to Openstack Migration', () => {
 describe('Create VmWare to Openstack Migration', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows Wizard page', () => {
   it('Shows Wizard page', () => {

+ 3 - 7
private/cypress/integration/migration/4 - Cancel first running migration.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
 describe('Cancel a running migration', () => {
 describe('Cancel a running migration', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Cancels migration', () => {
   it('Cancels migration', () => {
@@ -25,5 +20,6 @@ describe('Cancel a running migration', () => {
     cy.route({ url: '**/actions', method: 'POST' }).as('cancel')
     cy.route({ url: '**/actions', method: 'POST' }).as('cancel')
     cy.get('button').contains('Yes').click()
     cy.get('button').contains('Yes').click()
     cy.wait('@cancel')
     cy.wait('@cancel')
+    cy.get('div[data-test-id="mainStatusPill-ERROR"]', { timeout: 120000 })
   })
   })
 })
 })

+ 2 - 7
private/cypress/integration/migration/6 - Delete first migration.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
 describe('Delete the first migration', () => {
 describe('Delete the first migration', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Deletes migration', () => {
   it('Deletes migration', () => {

+ 2 - 5
private/cypress/integration/migration/7 - Delete e2e Openstack endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Delete the Openstack endpoint created for e2e testing', () => {
 describe('Delete the Openstack endpoint created for e2e testing', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Goes to endpoints page', () => {
   it('Goes to endpoints page', () => {

+ 2 - 5
private/cypress/integration/migration/8 - Delete e2e VmWare endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Delete the VmWare endpoint created for e2e testing', () => {
 describe('Delete the VmWare endpoint created for e2e testing', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Goes to endpoints page', () => {
   it('Goes to endpoints page', () => {

+ 2 - 5
private/cypress/integration/replica/1 - Create Azure Endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Create Azure Endpoint', () => {
 describe('Create Azure Endpoint', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows new Azure endpoint dialog', () => {
   it('Shows new Azure endpoint dialog', () => {

+ 2 - 6
private/cypress/integration/replica/2 - Create VmWare Endpoint.js

@@ -5,15 +5,11 @@ import config from '../../config'
 
 
 describe('Create VmWare Endpoint', () => {
 describe('Create VmWare Endpoint', () => {
   before(() => {
   before(() => {
-    cy.server()
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows new VmWare endpoint dialog', () => {
   it('Shows new VmWare endpoint dialog', () => {

+ 9 - 9
private/cypress/integration/replica/3 - Create VmWare Azure Replica.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Create VmWare to Azure Replica', () => {
 describe('Create VmWare to Azure Replica', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Shows Wizard page', () => {
   it('Shows Wizard page', () => {
@@ -49,10 +46,13 @@ describe('Create VmWare to Azure Replica', () => {
 
 
   it('Fills Azure replica info', () => {
   it('Fills Azure replica info', () => {
     cy.get('button').contains('Next').click()
     cy.get('button').contains('Next').click()
-    cy.get('div[data-test-id="dropdown-location"]').first().click()
-    cy.get('div[data-test-id="dropdownListItem"]').contains(config.wizard.azure.location.label).click()
-    cy.get('div[data-test-id="dropdown-resource_group"]').first().click()
-    cy.get('div[data-test-id="dropdownListItem"]').contains(config.wizard.azure.resourceGroup.label).click()
+    cy.get('input[placeholder="Location"]').type(config.wizard.azure.location.value)
+    cy.get('input[placeholder="Resource Group"]').type(config.wizard.azure.resourceGroup.value)
+
+    // cy.get('div[data-test-id="dropdown-location"]').first().click()
+    // cy.get('div[data-test-id="dropdownListItem"]').contains(config.wizard.azure.location.label).click()
+    // cy.get('div[data-test-id="dropdown-resource_group"]').first().click()
+    // cy.get('div[data-test-id="dropdownListItem"]').contains(config.wizard.azure.resourceGroup.label).click()
   })
   })
 
 
   it('Selects first available network mapping', () => {
   it('Selects first available network mapping', () => {

+ 3 - 7
private/cypress/integration/replica/4 - Cancel first running replica.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
 describe('Cancel a running replica', () => {
 describe('Cancel a running replica', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Cancels replica execution', () => {
   it('Cancels replica execution', () => {
@@ -23,5 +18,6 @@ describe('Cancel a running replica', () => {
     cy.route({ url: '**/actions', method: 'POST' }).as('cancel')
     cy.route({ url: '**/actions', method: 'POST' }).as('cancel')
     cy.get('button').contains('Yes').click()
     cy.get('button').contains('Yes').click()
     cy.wait('@cancel')
     cy.wait('@cancel')
+    cy.get('div[data-test-id="mainStatusPill-ERROR"]', { timeout: 120000 })
   })
   })
 })
 })

+ 2 - 7
private/cypress/integration/replica/5 - Cannot delete used endpoint.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
 describe('Cannot delete used endpoint', () => {
 describe('Cannot delete used endpoint', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Should show in usage message when trying to delete', () => {
   it('Should show in usage message when trying to delete', () => {

+ 2 - 7
private/cypress/integration/replica/6 - Delete first replica.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
 describe('Delete the first replica', () => {
 describe('Delete the first replica', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Delete replica', () => {
   it('Delete replica', () => {

+ 2 - 5
private/cypress/integration/replica/7 - Delete e2e Azure endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Delete the Azure endpoint created for e2e testing', () => {
 describe('Delete the Azure endpoint created for e2e testing', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Goes to endpoints page', () => {
   it('Goes to endpoints page', () => {

+ 2 - 5
private/cypress/integration/replica/8 - Delete e2e VmWare endpoint.js

@@ -5,14 +5,11 @@ import config from '../../config'
 
 
 describe('Delete the VmWare endpoint created for e2e testing', () => {
 describe('Delete the VmWare endpoint created for e2e testing', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Goes to endpoints page', () => {
   it('Goes to endpoints page', () => {

+ 3 - 8
private/cypress/integration/scheduler/Scheduler Operations.js

@@ -1,18 +1,13 @@
 
 
 // @flow
 // @flow
 
 
-import config from '../../config'
-
-describe('Create Azure Endpoint', () => {
+describe('Scheduler Operations', () => {
   before(() => {
   before(() => {
-    cy.visit(config.nodeServer)
-    cy.get('input[label="Username"]').type(config.username)
-    cy.get('input[label="Password"]').type(config.password)
-    cy.get('button').click()
+    cy.login()
   })
   })
 
 
   beforeEach(() => {
   beforeEach(() => {
-    Cypress.Cookies.preserveOnce('unscopedToken', 'token', 'projectId')
+    Cypress.Cookies.preserveOnce('token', 'projectId')
   })
   })
 
 
   it('Goes to scheduler\'s page', () => {
   it('Goes to scheduler\'s page', () => {

+ 73 - 0
private/cypress/support/commands.js

@@ -0,0 +1,73 @@
+// @flow
+
+import config from '../config.js'
+
+const identityUrl = `${config.coriolisUrl}identity/auth/tokens`
+const projectsUrl = `${config.coriolisUrl}identity/auth/projects`
+
+declare var expect: any
+
+Cypress.Commands.add('login', () => {
+  let unscopedBody = {
+    auth: {
+      identity: {
+        methods: ['password'],
+        password: {
+          user: {
+            name: config.username,
+            domain: { name: 'default' },
+            password: config.password,
+          },
+        },
+      },
+      scope: 'unscoped',
+    },
+  }
+
+  cy.request({
+    method: 'POST',
+    url: identityUrl,
+    body: unscopedBody,
+  }).then(unscopedResponse => {
+    let unscopedToken = unscopedResponse.headers['x-subject-token']
+    expect(unscopedToken).to.exist
+
+    cy.request({
+      method: 'GET',
+      url: projectsUrl,
+      headers: { 'X-Auth-Token': unscopedToken },
+    }).then(projectsReponse => {
+      let projectId = projectsReponse.body.projects[0].id
+      expect(projectId).to.exist
+
+      let scopedBody = {
+        auth: {
+          identity: {
+            methods: ['token'],
+            token: {
+              id: unscopedToken,
+            },
+          },
+          scope: {
+            project: {
+              id: projectId,
+            },
+          },
+        },
+      }
+
+      cy.request({
+        method: 'POST',
+        url: identityUrl,
+        body: scopedBody,
+      }).then(scopedResponse => {
+        let scopedToken = scopedResponse.headers['x-subject-token']
+        expect(scopedToken).to.exist
+
+        cy.setCookie('token', scopedToken)
+        cy.setCookie('projectId', projectId)
+        cy.visit(config.nodeServer)
+      })
+    })
+  })
+})

+ 2 - 0
private/cypress/support/index.js

@@ -0,0 +1,2 @@
+// @flow
+import './commands'

+ 4 - 1
src/components/atoms/StatusPill/index.jsx

@@ -102,6 +102,7 @@ type Props = {
   secondary: boolean,
   secondary: boolean,
   alert: boolean,
   alert: boolean,
   small: boolean,
   small: boolean,
+  'data-test-id': string,
 }
 }
 @observer
 @observer
 class StatusPill extends React.Component<Props> {
 class StatusPill extends React.Component<Props> {
@@ -110,6 +111,8 @@ class StatusPill extends React.Component<Props> {
   }
   }
 
 
   render() {
   render() {
+    const dataTestId = this.props['data-test-id'] ? this.props['data-test-id'] : `statusPill-${this.props.status || 'null'}`
+
     return (
     return (
       <Wrapper
       <Wrapper
         {...this.props}
         {...this.props}
@@ -118,7 +121,7 @@ class StatusPill extends React.Component<Props> {
         secondary={this.props.secondary}
         secondary={this.props.secondary}
         alert={this.props.alert}
         alert={this.props.alert}
         small={this.props.small}
         small={this.props.small}
-        data-test-id={`statusPill-${this.props.status || 'null'}`}
+        data-test-id={dataTestId}
       >
       >
         {this.props.label || this.props.status}
         {this.props.label || this.props.status}
       </Wrapper>
       </Wrapper>

+ 1 - 1
src/components/organisms/DetailsContentHeader/index.jsx

@@ -126,7 +126,7 @@ class DetailsContentHeader extends React.Component<Props> {
           primary={this.props.primaryInfoPill}
           primary={this.props.primaryInfoPill}
         />
         />
         <StatusPill
         <StatusPill
-          data-test-id={`statusPill-${statusLabel || ''}`}
+          data-test-id={`mainStatusPill-${statusLabel || ''}`}
           status={this.getStatus()}
           status={this.getStatus()}
           label={statusLabel || ''}
           label={statusLabel || ''}
         />
         />