From 061ca938ca6998b4ec33a862c763854259cfeab1 Mon Sep 17 00:00:00 2001 From: ilanap Date: Sun, 4 Aug 2019 10:16:33 +0300 Subject: Issue-ID: SDC-2483 Adding https support for cucumber tests and slight refactoring Signed-off-by: ilanap Change-Id: Ib772d18cd4278238571daf54bcb6372c553d6e4b --- .../cucumber-common/package.json | 37 ++++ .../cucumber-common/plugins/README.md | 30 +++ .../cucumber-common/plugins/jsdoc_config.json | 16 ++ .../cucumber-common/plugins/reporter.js | 31 +++ .../cucumber-common/plugins/steps.js | 65 ++++++ .../cucumber-common/stepDefinitions/world.js | 107 ++++++++++ .../cucumber-common/utils/UpdateTestConfig.js | 81 ++++++++ .../cucumber-common/utils/Utils.js | 223 +++++++++++++++++++++ 8 files changed, 590 insertions(+) create mode 100644 cucumber-js-test-apis-ci/cucumber-common/package.json create mode 100644 cucumber-js-test-apis-ci/cucumber-common/plugins/README.md create mode 100644 cucumber-js-test-apis-ci/cucumber-common/plugins/jsdoc_config.json create mode 100644 cucumber-js-test-apis-ci/cucumber-common/plugins/reporter.js create mode 100644 cucumber-js-test-apis-ci/cucumber-common/plugins/steps.js create mode 100644 cucumber-js-test-apis-ci/cucumber-common/stepDefinitions/world.js create mode 100644 cucumber-js-test-apis-ci/cucumber-common/utils/UpdateTestConfig.js create mode 100644 cucumber-js-test-apis-ci/cucumber-common/utils/Utils.js (limited to 'cucumber-js-test-apis-ci/cucumber-common') diff --git a/cucumber-js-test-apis-ci/cucumber-common/package.json b/cucumber-js-test-apis-ci/cucumber-common/package.json new file mode 100644 index 0000000000..1efc8f12ad --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/package.json @@ -0,0 +1,37 @@ +{ + "name": "cucumber-common", + "version": "1.0.14", + "description": "Cucumber common methods and utilities", + "repository": "", + "main": "index.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "cucumber-js", + "test-and-report": "npm-run-all -c -s test cucumber-html-report", + "cucumber-html-report": "node plugins/reporter.js", + "cucumber-docs": "jsdoc ./stepDefinitions -c plugins/jsdoc_config.json --readme plugins/README.md" + }, + "author": "", + "license": "Apache-2.0", + "dependencies": { + "assert": "^1.4.1", + "btoa": "^1.2.1", + "cucumber": "^5.1.0", + "cucumber-html-reporter": "^4.0.4", + "docdash": "^1.0.2", + "find-up": "^4.1.0", + "jsdoc": "^3.5.5", + "jsdoc-one-page": "0.0.5", + "lodash": "^4.17.11", + "md5": "^2.2.1", + "needle": "^2.4.0", + "node-zip": "^1.1.1", + "normalize-newline": "^3.0.0", + "npm-run-all": "^4.1.2", + "request": "^2.83.0", + "yamljs": "^0.3.0" + }, + "devDependencies": {} +} diff --git a/cucumber-js-test-apis-ci/cucumber-common/plugins/README.md b/cucumber-js-test-apis-ci/cucumber-common/plugins/README.md new file mode 100644 index 0000000000..9eaeb8f3a6 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/plugins/README.md @@ -0,0 +1,30 @@ +
+

Welcome!

+This is the documentation for using the BDD testing framework for SDC.
+The Modules on the left contains all steps for particalar aress and/or explanations of what they do.
+

+

How to set the server configuration

+
  • Copy the config.json to devConfig.json +
  • Replace the server and user values with the correct values +

    How to run with Maven

    +
  • "mvn clean install" will install npm if needed, download all modules +
  • run "mvn install -DskipTests=true -P dev" to create the documentation under the "docs" folder +
  • "mvn test -P dev" will run all tests in the features folder and create an HTML report under the "reports" folder +

    How to develop tests

    +You can open the project in IntelliJ and Webstorm to run and develop scenarios.
    +
  • You will need to install the Cucumber.Js plugin In order to install, go to "Settings/Plugins". If cucumber.js in not on the list, go to "Browse repositories.." and install . +
  • First time only: Right click on feature file and try to run. Now go to "Run/edit configurations" and set the "executable path" to the "node_modules\.bin\cucumber-js.cmd" under your current project. +
  • Now you can run the feature files by right clicking on the file and selecting "Run" from IDEA.
    +
  • Add to existing scenarios or create new files under the "features" directory for additional tests +
    +
  • You can also run a specific test from the command line by running "npm run test -- [features/path to file] +

    More Information

    +
  • More on Cucumber +
  • More on Gherkin +
  • More on Cucumber-js +
    +

    How to run the docker

    +
  • "mvn clean install -P docker" will create the docker images +
  • the "docker_run.sh" script will start all ONAP images and run the cucumber docker against them +
  • environment variables that can be set to change the server/version: IMAGES_TAG (default 1.4-STAGING-latest), TEST_CI_BE_HOST (deafult - machine IP), TEST_CI_CATALOG_PORT (default 8080) + diff --git a/cucumber-js-test-apis-ci/cucumber-common/plugins/jsdoc_config.json b/cucumber-js-test-apis-ci/cucumber-common/plugins/jsdoc_config.json new file mode 100644 index 0000000000..2757000472 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/plugins/jsdoc_config.json @@ -0,0 +1,16 @@ +{ + "tags": { + "allowUnknownTags": true + }, + "templates": { + "default": { + "outputSourceFiles": false + } + }, + + "plugins": ["./steps"], + "opts": { + "template": "node_modules/jsdoc-one-page", + "destination": "docs/" + } +} \ No newline at end of file diff --git a/cucumber-js-test-apis-ci/cucumber-common/plugins/reporter.js b/cucumber-js-test-apis-ci/cucumber-common/plugins/reporter.js new file mode 100644 index 0000000000..8913789c95 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/plugins/reporter.js @@ -0,0 +1,31 @@ +/* + * Copyright © 2016-2017 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var reporter = require('cucumber-html-reporter'); + +var options = { + theme: 'bootstrap', + jsonFile: 'report/report.json', + output: 'report/report.html', + reportSuiteAsScenarios: true, + launchReport: false, + metadata: { + "ONAP" : "Some build", + "Executed": "Local" + } +}; + +reporter.generate(options); + diff --git a/cucumber-js-test-apis-ci/cucumber-common/plugins/steps.js b/cucumber-js-test-apis-ci/cucumber-common/plugins/steps.js new file mode 100644 index 0000000000..2faa7efbd8 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/plugins/steps.js @@ -0,0 +1,65 @@ +/* + * Copyright © 2016-2017 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @module plugins/steptag + */ +'use strict'; + + +exports.handlers = { + /** + * Support @step tag. + * + * @step description + */ + newDoclet: function(e) { + var tags = e.doclet.tags; + var tag; + var value; + + // any user-defined tags in this doclet? + if (typeof tags !== 'undefined') { + + tags = tags.filter(function($) { + return $.title === 'step' || $.title === 'examplefile'; + }); + + if (tags.length) { + // take the first one + tag = tags[0]; + let step = null; + let exampleFile = null; + for (tag in tags) { + if (tags[tag].title === "step") { + step = "" + tags[tag].value + "
    "; + } + if (tags[tag].title === "examplefile") { + exampleFile = " Example Features File: " + tags[tag].value + "
    "; + } + } + if (exampleFile !== null) { + step += exampleFile; + } + e.doclet.meta = e.doclet.meta || {}; + if (e.doclet.description !== undefined) { + e.doclet.description = step + e.doclet.description; + } else { + e.doclet.description = step; + } + } + } + } +}; \ No newline at end of file diff --git a/cucumber-js-test-apis-ci/cucumber-common/stepDefinitions/world.js b/cucumber-js-test-apis-ci/cucumber-common/stepDefinitions/world.js new file mode 100644 index 0000000000..7811aba886 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/stepDefinitions/world.js @@ -0,0 +1,107 @@ +/* + * Copyright © 2016-2017 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const { setWorldConstructor } = require('cucumber'); +const _ = require('lodash'); +const findUp = require('find-up'); + + + +let configPath = findUp.sync('config.json'); +configPath = configPath.replace(/\\/g, '/'); + +let config = require(configPath); +let localConfig = {}; +try { + let devConfigPath = findUp.sync('devConfig.json'); + devConfigPath = devConfigPath.replace(/\\/g, '/'); + localConfig = require(devConfigPath); +} catch (e) { + try { + let envdir = findUp.sync('environments', {type: 'directory'}); + envdir = envdir.replace(/\\/g, '/'); + localConfig = require(envdir + '/dockerConfig.json'); + } catch (e) { + console.error("no env configuration was found!"); + } +} + +config = _.merge(config, localConfig); +var {setDefaultTimeout} = require('cucumber'); + + +/** + * @module Context + * @description Context that is used per feature file and can be accessed as 'this.context' in all steps.
    + * This class can be extended in order to add additional configurations. + *
    + * Contains the following items:
    + *
  • this.context.server
      REST server and onboarding prefix including version. set either in configuration file or from the command line or SERVER environment variable
    + *
  • this.context.item
      When a VLM or VSP has been created, this has the an id and versionId set to the correct IDs.
    + *
  • this.context
      Object with properties that were saved in the steps.
    + *
  • this.context.inputdata
      Automatically updated with the last responseData from the Rest call
      Object with properties that were prepares in the steps.
    + *
  • this.context.responseData
      Response from the last REST call.
    + **/ +class CustomWorld { + constructor(options) { + this.context = {}; + this.context.headers = {}; + let typeName; + + this.context.defaultServerType = 'main'; + for (typeName in config) { + this.context.headers[typeName] = {}; + if (config[typeName].user) { + this.context.headers[typeName]['USER_ID'] = config[typeName].user; + } + // adding additional headers + if (config[typeName].additionalHeaders) { + _.assign(this.context.headers[typeName] , config[typeName].additionalHeaders); + } + if (config[typeName].isDefault !== undefined && config[typeName].isDefault) { + this.context.defaultServerType = typeName; + } + } + this.context.item = {id: null, versionId: null, componentId: null}; + // adding the default items that should also be initialized + if (config.initData) { + _.assign(this.context, config.initData); + } + + this.context.shouldFail = false; + this.context.errorCode = null; + this.context.inputData = null; + this.context.responseData = null; + + + this.config = config; + + let context = this.context; + this.context.getUrlForType = (function(type) { + var _server = context.server; + var _config = config; + return function(type) { + let typeData = _config[type]; + let _url = typeData.protocol + '://' + + typeData.server + ':' + + typeData.port + '/' + + typeData.prefix; + return _url; + } + })(); + setDefaultTimeout(60 * 1000); + } +} +setWorldConstructor(CustomWorld); diff --git a/cucumber-js-test-apis-ci/cucumber-common/utils/UpdateTestConfig.js b/cucumber-js-test-apis-ci/cucumber-common/utils/UpdateTestConfig.js new file mode 100644 index 0000000000..a2c1dae2f7 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/utils/UpdateTestConfig.js @@ -0,0 +1,81 @@ +/* + * Copyright © 2016-2017 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict' + +const fs = require('fs'); + +var pathToRoot = process.env.TESTS_BASE; +if (!pathToRoot.endsWith("/")) { + pathToRoot += "/"; +} +var envConfig = require(pathToRoot + 'config.json'); +var protocol = (process.env.PROTOCOL !== undefined) ? process.env.PROTOCOL : 'https'; + +try { + envConfig = require(pathToRoot + 'environments/dockerConfig.json'); +} catch (e) { +} + +function run() { + var inputArgs = process.argv.slice(2); + let changeConfig = false; + if (process.env.K8S_CONF_PATH !== undefined) { + console.log('updating with kubernetes services'); + let k8sConfig = require(pathToRoot + process.env.K8S_CONF_PATH); + mapK8sPod2Docker(k8sConfig, inputArgs[0], inputArgs[1]); + changeConfig = true; + } else { + console.log('not updating at all'); + } + if (changeConfig) { + let data = JSON.stringify(envConfig, null, 2); + console.log('writing config file: ' + pathToRoot+'environments/dockerConfig.json'); + console.log(data); + fs.writeFileSync(pathToRoot+'environments/dockerConfig.json', data); + } +} + +function mapK8sPod2Docker(k8sConfig, id, k8sid) { + let item = k8sConfig.items.find(item => { + if (item.spec !== undefined && item.spec.ports !== undefined) { + let spec = item.spec.ports.find(port => { + if (port.name === k8sid) { + return true; + } + }); + return (spec !== undefined); + } else { + return false; + } + }); + + item.spec.ports.forEach(port => { + if (port.name === k8sid) { + envConfig[id].port = port.nodePort; + let rancherData = JSON.parse(item.metadata.annotations["field.cattle.io/publicEndpoints"]); + let address = rancherData.find(address => { + return address.port === port.nodePort; + }); + envConfig[id].port = address.port; + envConfig[id].server = address.addresses[0]; + envConfig[id].protocol = protocol; + envConfig[id].user = process.env.SDC_USER_ID; + } + }); + +} + +run(); diff --git a/cucumber-js-test-apis-ci/cucumber-common/utils/Utils.js b/cucumber-js-test-apis-ci/cucumber-common/utils/Utils.js new file mode 100644 index 0000000000..22ee775080 --- /dev/null +++ b/cucumber-js-test-apis-ci/cucumber-common/utils/Utils.js @@ -0,0 +1,223 @@ +/* + * Copyright © 2016-2017 European Support Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const needle = require('needle'); +const fs = require('fs'); +require('node-zip'); +var btoa = require('btoa'); +const md5 = require('md5'); +const _ = require('lodash'); + +function getOptionsForRequest(context, method, path, type) { + if (type == undefined || type == null) { + type = context.defaultServerType + } + let server = context.getUrlForType(type); + let options = { + method: method, + url: server + path, + headers: _.clone(context.headers[type]) + }; +// options.headers["Content-Type"] = "application/json"; +// options.headers["accept"] = "application/json"; + return options; +} + +function _requestBinaryFormData(context, method, path, fileName, formInputName, type) { + let options = getOptionsForRequest(context, method, path, type); + let formData = {}; + if (method === 'POST' || method === 'PUT') { + //formData[formInputName] = fs.createReadStream(fileName); + //options.formData = formData; + let fileData = { + file: fileName + }; + fileData['content_type'] = 'multipart/form-data'; + options.formData = {}; + options.formData[formInputName] = fileData; + } + return _request(context, method, path, options); +} +function _requestBinaryBody(context, method, path, fileName, type) { + let options = getOptionsForRequest(context, method, path, type); + if (method === 'POST' || method === 'PUT') { + options.body = fs.createReadStream(fileName); + options.headers['Content-Type'] = 'application/octet-stream'; + + } + return _request(context, method, path, options); +} + + +function _requestPayload(context, method, path, filePath, type) { + let options = getOptionsForRequest(context, method, path, type); + options.json = _createPayload(filePath); + options.headers['Content-MD5'] = addCheckSum(options.json); + return _request(context, method, path, options); +} + +function _requestRest(context, method, path, data, type) { + let options = getOptionsForRequest(context, method, path, type); + if (method === 'POST' || method === 'PUT') { + options.json = data; + } + return _request(context, method, path, options); +} + +function _request(context, method, path, options) { + console.log('--> Calling REST ' + options.method +' url: ' + options.url); + let inputData = options.json; + let needleOptions = {headers: options.headers, rejectUnauthorized: false}; + if (inputData == undefined) { + if (options.formData != undefined) { + inputData = options.formData; + needleOptions.multipart = true; + } + if (inputData && inputData.body != undefined) { + inputData = options.body; + } + } else { + needleOptions.json = true; + } + return needle(method, options.url, inputData, needleOptions) + .then(function(result) { + context.inputData = null; + let isExpected = (context.shouldFail) ? (result.statusCode != 200 && result.statusCode != 201) : (result.statusCode == 200 || result.statusCode == 201); + data = result.body; + if (!isExpected) { + console.log('Did not get expected response code'); + throw 'Status Code was ' + result.statusCode ; + } + if (context.shouldFail && context.errorCode) { + if (typeof data === 'string' && data) { + data = JSON.parse(data); + } + let errorCode = data.errorCode; + let contextErrorCode = context.errorCode; + context.errorCode = null; + if (errorCode !== contextErrorCode) { + throw 'Error Code was ' + errorCode + ' instead of ' + contextErrorCode; + } + } + if (context.shouldFail && context.errorMessage) { + if (typeof data === 'string' && data) { + data = JSON.parse(data); + } + let errorMessage = data.message; + let contextErrorMessage = context.errorMessage; + context.errorMessage = null; + if (errorMessage !== contextErrorMessage) { + throw 'Error Message was ' + errorMessage + ' instead of ' + contextErrorMessage; + } + } + if (context.shouldFail) { + context.shouldFail = false; + return({statusCode: result.statusCode, data: {}}); + } + + if (typeof data === 'string' && data) { + if (data.startsWith('[') || data.startsWith('{')) { + data = JSON.parse(data); + } + } + context.responseData = data; + context.inputData = data; + return({statusCode: result.statusCode, data: data}); + + }) + .catch(function(err) { + console.error('Request URL: ' + options.url); + console.error('Request Method: ' + options.method); + console.log(err); + throw err; + }) +} + +function download(context, path, filePath, type) { + if (type == undefined || type == null) { + type = context.defaultServerType + } + let server = context.getUrlForType(type); + let options = { + method: 'GET', + url: server + path, + headers: context.headers[type] + }; + + console.log('--> Calling REST download url: ' + options.url); + return needle('GET', options.url, {}, { + headers: options.headers, + rejectUnauthorized: false, + output: filePath + }) + .then(function (result) { + let zipFile = fs.readFileSync(filePath, 'binary'); + let zip = new JSZip(zipFile, {base64: false, checkCRC32: true}); + if (zip.files['MANIFEST.json']) { + let manifestData = zip.files['MANIFEST.json']._data; + manifestData = manifestData.replace(/\\n/g, ''); + context.responseData = JSON.parse(manifestData); + } + return zip; + }) + .catch(function (err) { + console.error('Request URL: ' + options.url); + console.error('Request Method: ' + options.method); + throw err; + }) +} + +function _random() { + let d = new Date(); + return d.getTime().toString().split('').reverse().join(''); +} + +function _getJSONFromFile(file) { + return JSON.parse(fs.readFileSync(file, 'utf8')); +} + +function _createPayload(fileName) { + var body = fs.readFileSync(fileName); + let payload = { + payloadData: body.toString('base64'), + payloadName: fileName.substring(fileName.lastIndexOf("/") + 1 ) + }; + return payload; +} + +function addCheckSum(payloadData) { + let _md5 = md5(JSON.stringify(payloadData)); + return btoa(_md5.toLowerCase()); +} + +function _getFile(file, format) { + if(format === '' ){ + return fs.readFileSync(file) + } + return fs.readFileSync(file, format); +} + + +module.exports = { + getFile: _getFile, + request: _requestRest, + requestPayload: _requestPayload, + requestBinaryFormData: _requestBinaryFormData, + requestBinaryBody: _requestBinaryBody, + random : _random, + getJSONFromFile: _getJSONFromFile, + download: download, + payload: _createPayload +}; -- cgit 1.2.3-korg