1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00

F #3951: Initial testing (#690)

Co-authored-by: Jorge Lobo <jlobo@opennebula.systems>
This commit is contained in:
Jorge Miguel Lobo Escalona 2021-01-21 19:30:40 +01:00 committed by Tino Vazquez
parent 3220ba29d2
commit eca0414f74
No known key found for this signature in database
GPG Key ID: 14201E424D02047E
10 changed files with 774 additions and 0 deletions

16
src/fireedge/cypress.json Normal file
View File

@ -0,0 +1,16 @@
{
"port": 2617,
"numTestsKeptInMemory": 20,
"env":{},
"video": false,
"viewportHeight": 720,
"viewportWidth": 1280,
"baseUrl": "http://localhost:2616",
"reporter": "mochawesome",
"reporterOptions": {
"reportDir": "cypress/results",
"overwrite": false,
"html": false,
"json": true
}
}

View File

@ -0,0 +1,110 @@
const cypress = require('cypress')
const marge = require('mochawesome-report-generator')
const { merge } = require('mochawesome-merge')
const { existsSync, mkdirsSync, readFileSync, writeFileSync, removeSync } = require('fs-extra')
const { dirname } = require('path')
const config = {
browser: 'chrome',
headless: true,
spec: 'cypress/integration/**/*.spec.js'
}
const generateReport = (options = {
reportDir: 'cypress/results',
reportFilename: 'merge-results',
saveJson: true,
saveHtml: false,
files: [
'./cypress/results/*.json'
]
}) => {
return merge(options).then(report => marge.create(report, options))
}
const parseReport = (dataReport = '') => {
if (dataReport && Array.isArray(dataReport) && dataReport.slice(-1).pop()) {
const pathMergeJson = dataReport.slice(-1).pop()
const bsname = dirname(pathMergeJson)
const fileData = readFileSync(pathMergeJson, 'utf8')
if (fileData) {
const dataJSON = JSON.parse(fileData)
const parsedJSON = {}
// version
parsedJSON.version = dataJSON.meta.mochawesome.version
// examples
const examples = []
// results -19
if (dataJSON.results) {
dataJSON.results.forEach(results => {
const pathTest = results.fullFile
// results.suites -28
if (results && results.suites && Array.isArray(results.suites)) {
results.suites.forEach(suites => {
// result.suites.tests -36
if (suites && suites.tests && Array.isArray(suites.tests)) {
suites.tests.forEach(test => {
const parsedTest = {}
parsedTest.id = pathTest
parsedTest.description = test.title || null
parsedTest.full_description = test.fullTitle || null
parsedTest.status = test.pass ? 'passed' : 'failed'
parsedTest.file_path = pathTest
parsedTest.line_number = null
parsedTest.run_time = test.duration || null
parsedTest.pending_message = null
if (test.fail && test.code && test.code.length) {
parsedTest.exception = {
class: test.err.message,
backtrace: test.err.estack && [test.err.estack],
message: test.code
}
}
examples.push(parsedTest)
})
}
})
}
})
}
parsedJSON.examples = examples
// summary
parsedJSON.summary = {
duration: dataJSON.stats.duration,
example_count: dataJSON.stats.tests,
failure_count: dataJSON.stats.failures,
pending_count: dataJSON.stats.pending,
errors_outside_of_examples_count: dataJSON.stats.other
}
// summary_line
parsedJSON.summary_line = `${dataJSON.stats.testsRegistered} examples, ${dataJSON.stats.failures} failures`
// clear results folder
removeSync(bsname)
// create path
if (!existsSync(bsname)) {
mkdirsSync(bsname)
}
// create JSON file
writeFileSync(`${bsname}/results.json`, JSON.stringify(parsedJSON))
}
}
}
cypress.run(config).then(
() => {
generateReport().then(parseReport)
},
error => {
generateReport().then(parseReport)
console.error(error)
process.exit(1)
}
)

View File

@ -0,0 +1,27 @@
{
"auth": {
"jwtName": "FireedgeToken",
"username": "oneadmin",
"password": "opennebula"
},
"endpoints": {
"login": "/api/auth/",
"userInfo": "/api/user/info"
},
"provision": {
"provision": "hybrid+",
"provider": "aws",
"nameProvider": "aws-frankfurt",
"descriptionProvider": "Elastic cluster on AWS in Frakfurt",
"descriptionProviderEdited": "Elastic cluster on AWS in Frakfurt Edited!",
"descriptionProvision": "Provision description",
"key": "access key",
"keyEdited": "access key Edited!",
"secretKey": "secret access key",
"secretKeyEdited": "secret access key Edited!",
"nameProvision": "aws-cluster",
"numberInstances": "1",
"amiImage": "ami-0b793c1e0d1dc4d28",
"instanceType": "i3-metal"
}
}

View File

@ -0,0 +1,16 @@
// / <reference types="Cypress" />
context('Dashboard', () => {
before(() => {
cy.visit('/')
cy.login()
})
after(() => {
cy.logout()
})
it('validate items', () => {
})
})

View File

@ -0,0 +1,31 @@
// const { internalLog } = require('../../../support/utils')
// / <reference types="Cypress" />
context('Login', () => {
before(() => {
cy.goToIndex(__dirname, () => {
cy.login()
})
})
after(() => {
cy.logout()
})
it('has successfull login', () => {
cy.getJWT((JWT) => {
if (JWT && cy.wrap(JWT).should('exist')) {
cy.get('[data-cy=header]').should('be.visible')
}
})
})
it('has username on header', () => {
cy.get('[data-cy=header-user-button]').click()
cy.fixture('defaults').then(defaults => {
cy.get('[data-cy=header-username]').should(
'have.text',
defaults.auth.username
)
})
})
})

View File

@ -0,0 +1,218 @@
const { createIntercept, waitIntercept } = require('../../../support/utils')
const testType = 'Provider'
const reloadProviderComponent = () => {
// for reload the provider react-component this update the provider list
cy.navigateMenu('dashboard')
}
const interceptList = {
method: 'GET',
url: '/provider/list'
}
const interceptCreate = {
method: 'POST',
url: '/provider/create'
}
const interceptConfigure = {
method: 'PUT',
url: '/provider/update'
}
const interceptDelete = {
method: 'DELETE',
url: '/provider/delete'
}
let defaultSettings = {}
context(testType, () => {
before(() => {
// fill settings
cy.fixture('defaults').then(defaults => {
defaultSettings = defaults.provision
})
// go to index app
cy.visit('/')
cy.login()
})
after(() => {
cy.logout()
})
it('Create a Provider', () => {
cy.navigateMenu(`${testType}s`, () => {
cy.get('[data-cy=create-provider]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), () => {
cy.get('[data-cy=select-provision-type]').select(defaultSettings.provision || '')
cy.get('[data-cy=select-provider-type]').select(defaultSettings.provider || '')
let brk = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 200 })
}
// fill form "Configure Connection" tab
const fillFormConfigureConnection = () => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.get('[data-cy=form-provider-access_key]').clear().type(defaultSettings.key || '')
cy.get('[data-cy=form-provider-secret_key]').clear().type(defaultSettings.secretKey || '')
const intercept = createIntercept(interceptCreate)
cy.wrap($button).click({ force: true })
waitIntercept(intercept, validateTest)
})
}
// fill form "Provider Overview" tab
const fillFormProviderOverview = () => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.get('[data-cy=form-provider-name]').clear().type(defaultSettings.nameProvider || '')
cy.get('[data-cy=form-provider-description]').clear().type(defaultSettings.descriptionProvider || '')
cy.wrap($button).click({ force: true })
fillFormConfigureConnection()
})
}
// select template in "Provider template" tab
const selectTemplate = $el => {
brk = false
cy.wrap($el).find('>button').click({ force: true }).then(() => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.wrap($button).click({ force: true })
fillFormProviderOverview()
})
})
}
// find provider template in "Provider template" tab
cy.get('[data-cy=providers-templates]').find('[data-cy=provider-template-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvider && $el.find('[data-cy=provider-template-card-title]').text() === defaultSettings.nameProvider) {
selectTemplate($el)
}
return brk
})
})
})
})
})
it('Check information a Provider', () => {
reloadProviderComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
// select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('>button').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), info => {
cy.get('[data-cy=provider-name]').should('have.text', defaultSettings.nameProvider)
cy.get('[data-cy=provider-description]').should('have.text', defaultSettings.descriptionProvider)
cy.get('[data-cy=provider-type]').should('have.text', defaultSettings.provider)
cy.get('[data-cy=provider-access_key]').should('have.text', defaultSettings.key)
cy.get('[data-cy=provider-secret_key]').should('have.text', defaultSettings.secretKey)
cy.get('[data-cy=dg-cancel-button]').click({ force: true })
})
})
}
// find provider
cy.get('[data-cy=providers]').find('[data-cy=provider-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvider && $el.find('[data-cy=provider-card-title]').text() === defaultSettings.nameProvider) {
selectProvider($el)
}
return brk
})
})
})
})
it('Configure', () => {
reloadProviderComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 200 })
}
// fill "Configure connection" tab
const fillConfigureConnection = $button => {
const intercept = createIntercept(interceptConfigure)
cy.get('[data-cy=form-provider-access_key]').clear().type(defaultSettings.keyEdited || '')
cy.get('[data-cy=form-provider-secret_key]').clear().type(defaultSettings.secretKeyEdited || '')
cy.wrap($button).click({ force: true })
waitIntercept(intercept, validateTest)
}
// fill "Provider overview" tab
const fillProviderOverview = $button => {
cy.get('[data-cy=form-provider-description]').clear().type(defaultSettings.descriptionProviderEdited || '')
cy.wrap($button).click({ force: true })
cy.get('[data-cy=stepper-next-button]').then(fillConfigureConnection)
}
// select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('[data-cy=provider-edit]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), () => {
cy.get('[data-cy=stepper-next-button]').then(fillProviderOverview)
})
})
}
// find provider
cy.get('[data-cy=providers]').find('[data-cy=provider-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvider && $el.find('[data-cy=provider-card-title]').text() === defaultSettings.nameProvider) {
selectProvider($el)
}
return brk
})
})
})
})
it('Delete', () => {
reloadProviderComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 202 })
}
// select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('[data-cy=provider-delete]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), () => {
const intercept = createIntercept(interceptDelete)
cy.get('[data-cy=dg-accept-button]').click({ force: true }).then(() => {
waitIntercept(intercept, validateTest)
})
})
})
}
// find provider
cy.get('[data-cy=providers]').find('[data-cy=provider-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvider && $el.find('[data-cy=provider-card-title]').text() === defaultSettings.nameProvider) {
selectProvider($el)
}
return brk
})
})
})
})
})

View File

@ -0,0 +1,221 @@
const { createIntercept, waitIntercept } = require('../../../support/utils')
const testType = 'Provision'
const reloadProvisionComponent = () => {
// for reload the provision react-component this update the provider list
cy.navigateMenu('dashboard')
}
const interceptProviderList = {
method: 'GET',
url: '/provider/list'
}
const interceptList = {
method: 'GET',
url: '/provision/list'
}
const interceptCreate = {
method: 'POST',
url: '/provision/create'
}
const interceptConfigure = {
method: 'PUT',
url: '/provision/configure'
}
const interceptDelete = {
method: 'DELETE',
url: '/provision/delete'
}
let defaultSettings = {}
context(testType, () => {
before(() => {
// fill settings
cy.fixture('defaults').then(defaults => {
defaultSettings = defaults.provision
})
//go to index app
cy.visit('/')
cy.login()
})
after(() => {
cy.logout()
})
it('Create', () => {
cy.navigateMenu(`${testType}s`, () => {
cy.get('[data-cy=create-provision]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptProviderList), () => {
cy.get('[data-cy=select-provision-type]').select(defaultSettings.provision || '')
cy.get('[data-cy=select-provider-type]').select(defaultSettings.provider || '')
let brkProvisionTemplate = true
let brkProvider = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 202 })
}
// fill form "Configure inputs" tab
const fillFormConfigureInputs = () => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.get('[data-cy=form-provision-number_hosts]').clear().type(defaultSettings.numberInstances || '')
const intercept = createIntercept(interceptCreate)
cy.wrap($button).click({ force: true })
waitIntercept(intercept, validateTest)
})
}
// fill form "Provider overview" tab
const fillFormProvision = () => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.get('[data-cy=form-provision-name]').clear().type(defaultSettings.nameProvision || '')
cy.get('[data-cy=form-provision-description]').clear().type(defaultSettings.descriptionProvision || '')
cy.wrap($button).click({ force: true })
fillFormConfigureInputs()
})
}
// find provider in "Provider" tab
const selectProvider = $el => {
brkProvider = false
cy.wrap($el).find('>button').click({ force: true }).then(() => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.wrap($button).click({ force: true })
fillFormProvision()
})
})
}
// find provider in "Provider" tab
const findProvider = () => {
cy.get('[data-cy=stepper-next-button]').then($button => {
cy.wrap($button).click({ force: true })
cy.get('[data-cy=providers]').find('[data-cy=provider-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvision && $el.find('[data-cy=provider-card-title]').text().includes(defaultSettings.nameProvider)) {
selectProvider($el)
}
return brkProvider
})
})
}
// select provision template in "Provision template" tab
const selectProvisionTemplate = $el => {
brkProvisionTemplate = false
cy.wrap($el).find('>button').click({ force: true }).then(findProvider)
}
// find provision template in "Provision template" tab
cy.get('[data-cy=provisions-templates]').find('[data-cy=provision-template-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvision && $el.find('[data-cy=provision-template-card-title]').text() === defaultSettings.nameProvision) {
selectProvisionTemplate($el)
}
return brkProvisionTemplate
})
})
})
})
})
it('Check information', () => {
reloadProvisionComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
//select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('>button').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), info => {
cy.get('[data-cy=provision-name]').should('have.text', defaultSettings.nameProvision)
cy.get('[data-cy=provision-description]').should('have.text', defaultSettings.descriptionProvision)
cy.get('[data-cy=provider-provider-name]').should('have.text', defaultSettings.nameProvider)
cy.get('[data-cy=provider-cluster]').should('contain', defaultSettings.nameProvision)
})
})
}
//find provider
cy.get('[data-cy=provisions]').find('[data-cy=provision-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvider && $el.find('[data-cy=provision-card-title]').text() === defaultSettings.nameProvision) {
selectProvider($el)
}
return brk
})
})
})
})
it('Configure', () => {
reloadProvisionComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 202})
}
// select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('[data-cy=provision-configure]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptConfigure), validateTest)
})
}
// find provider
cy.get('[data-cy=provisions]').find('[data-cy=provision-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvision && $el.find('[data-cy=provision-card-title]').text() === defaultSettings.nameProvision) {
selectProvider($el)
}
return brk
})
})
})
})
it('Delete', () => {
reloadProvisionComponent()
cy.navigateMenu(`${testType}s`, () => {
waitIntercept(createIntercept(interceptList), () => {
let brk = true
// expect test
const validateTest = request => {
expect(request).to.be.a('object')
expect(request).to.nested.include({ 'response.body.id': 202})
}
// select provider
const selectProvider = $el => {
brk = false
cy.wrap($el).find('[data-cy=provision-delete]').click({ force: true }).then(() => {
waitIntercept(createIntercept(interceptList), ()=>{
const intercept = createIntercept(interceptDelete)
cy.get('[data-cy=dg-accept-button]').click({force:true}).then(()=>{
waitIntercept(intercept, validateTest)
})
})
})
}
// find provider
cy.get('[data-cy=provisions]').find('[data-cy=provision-card]').each($el => {
if (defaultSettings && defaultSettings.nameProvision && $el.find('[data-cy=provision-card-title]').text() === defaultSettings.nameProvision) {
selectProvider($el)
}
return brk
})
})
})
})
})

View File

@ -0,0 +1,89 @@
const { internalLog } = require('./utils')
const { sep } = require('path')
Cypress.Commands.add('login', () => {
cy.fixture('defaults').then(defaults => {
cy.get('[data-cy=login-user]').clear().type(defaults.auth.username)
cy.get('[data-cy=login-token]').clear().type(defaults.auth.password)
})
cy.get('[data-cy=login-button]').then(($btn) => {
cy.wrap($btn).click()
})
})
Cypress.Commands.add('openUserModal', (callback = () => undefined) => {
cy.get('[data-cy=header-user-button]').then(($btn) => {
if ($btn) {
cy.wrap($btn).click({ force: true })
}
callback()
})
})
Cypress.Commands.add('logout', () => {
cy.openUserModal(
() => {
cy.get('[data-cy=header-logout-button]').then(($btn) => {
if ($btn) {
cy.wrap($btn).click({ force: true })
cy.fixture('defaults').then(defaults => {
cy.window().its('sessionStorage').invoke('removeItem', defaults.auth.jwtName)
})
}
})
}
)
})
Cypress.Commands.add('getJWT', (callback = () => undefined) => {
cy.fixture('defaults').then(defaults => {
cy.window().then(win => {
callback(win.sessionStorage.getItem(defaults.auth.jwtName))
})
})
})
Cypress.Commands.add('navigateMenu', (contentText = '', callback = () => undefined) => {
if (contentText) {
let rtn = true
cy.get('[data-cy=main-menu]').find('li').each($li => {
if ($li && $li.text && typeof $li.text === 'function' && $li.text().toLowerCase() === contentText.toLowerCase()) {
rtn = false
cy.wrap($li).click({ force: true }).then(() => {
callback()
})
}
return rtn
})
}
})
Cypress.Commands.add('goToIndex', (path = '', callback = () => undefined) => {
if (expect(path).to.not.be.empty) {
const folders = path.split(sep)
cy.fixture('defaults').then(defaults => {
if (expect(defaults.apps).to.be.a('object')) {
const apps = Object.keys(defaults.apps)
let selectApp = ''
apps.forEach(app => {
if (folders.includes(app)) {
selectApp = app
}
})
if (expect(apps).to.be.a('array') && expect(apps).to.include(selectApp)) {
cy.visit(`/${selectApp}`, { timeout: 30000 })
cy.getJWT((JWT) => {
if (!JWT) {
cy.get('[data-cy=opennebula-logo]').then($window => {
if ($window) {
cy.wait(1500)
}
})
}
callback()
})
}
}
})
}
})

View File

@ -0,0 +1,41 @@
const { v4 } = require('uuid')
const createIntercept = (data = {}) => {
const uuidv4 = v4()
cy.intercept({
method: data.method || 'GET',
url: data.url || ''
}).as(uuidv4)
return uuidv4
}
const waitIntercept = (intercept = '', callback = () => undefined) => {
if (intercept) {
cy.wait(`@${intercept}`).then(interception => {
if (interception && interception.state && interception.state === 'Complete' && typeof callback === 'function') {
callback(interception)
}
})
}
}
const internalLog = (command = '', message = '') => {
Cypress.log({
name: command,
displayName: command,
consoleProps: () => {
return {
error: message
}
}
})
}
const utils = {
internalLog,
createIntercept,
waitIntercept
}
module.exports = utils

View File

@ -9,6 +9,8 @@
"build-server": "rimraf dist/index.js && rimraf dist/fireedge-server.conf && webpack --config ./webpack.config.prod.server.js", "build-server": "rimraf dist/index.js && rimraf dist/fireedge-server.conf && webpack --config ./webpack.config.prod.server.js",
"build": "npm run build-front && npm run build-server", "build": "npm run build-front && npm run build-server",
"start": "cross-env NODE_ENV=production babel-node ./dist/index.js", "start": "cross-env NODE_ENV=production babel-node ./dist/index.js",
"cypress:open": "cypress open",
"cypress:run": "rimraf cypress/results/* && node cypress/command/index.js",
"pot": "node potfile.js", "pot": "node potfile.js",
"po2json": "node po2json.js", "po2json": "node po2json.js",
"lint": "eslint ./src/client/", "lint": "eslint ./src/client/",
@ -39,6 +41,9 @@
"eslint-plugin-standard": "^4.0.1", "eslint-plugin-standard": "^4.0.1",
"fireedge-genpotfile": "^1.0.0", "fireedge-genpotfile": "^1.0.0",
"fireedge-pojson": "^1.0.2", "fireedge-pojson": "^1.0.2",
"mochawesome": "^6.2.1",
"mochawesome-merge": "^4.2.0",
"mochawesome-report-generator": "^5.1.0",
"react-hot-loader": "^4.13.0", "react-hot-loader": "^4.13.0",
"webpack-dev-middleware": "^3.7.2", "webpack-dev-middleware": "^3.7.2",
"webpack-hot-middleware": "^2.25.0" "webpack-hot-middleware": "^2.25.0"