diff --git a/audit-resolve.json b/audit-resolve.json index 1e2dd8b0..f1873794 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -25,7 +25,7 @@ }, "1002718|json-ref-lite>property-expr": { "decision": "postpone", - "madeAt": 1634580037057 + "madeAt": 1635515288777 } }, "rules": {}, diff --git a/package-lock.json b/package-lock.json index 9709f3b1..b46adc5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ml-testing-toolkit", - "version": "14.0.1", + "version": "14.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 38fcf289..bff9b02b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ml-testing-toolkit", "description": "Testing Toolkit for Mojaloop implementations", - "version": "14.0.1", + "version": "14.0.2", "license": "Apache-2.0", "author": "Vijaya Kumar Guthi, ModusBox Inc. ", "contributors": [ diff --git a/spec_files/reports/templates/testcase_definition/table_view.html b/spec_files/reports/templates/testcase_definition/table_view.html index 27d2606f..13c536bf 100644 --- a/spec_files/reports/templates/testcase_definition/table_view.html +++ b/spec_files/reports/templates/testcase_definition/table_view.html @@ -1,81 +1,133 @@ - - + + -#customers .headerYellow th { - background-color: #FFFDDD; - color: black; - /* padding-left: 35px; */ -} +
- - - +
+
+
+ + +
+
+
+ +
+
+
+
Testcase
+
Request Description
+
Test Scenario
+
Expected Result
+
+
+
+ {{#each test_cases}} +
+
+
+
{{fileInfo.path}} + {{#if fileInfo.labels}} + - (Labels: {{fileInfo.labels}}) + {{/if}} +
+
+ + +
+
+ {{#each requests}} +
+
{{#if @first}}{{../meta.info}}{{/if}}
+
{{meta.info}}
+
{{description}}
({{method}} {{operationPath}})
+
+ {{#if tests.assertions}} +
    + {{#each tests.assertions}} +
  • {{description}}
  • + {{/each}} +
+ {{/if}} +
+
+ {{/each}} +
+
+
+
+ {{/each}} - - - - - - - - - - - {{#each test_cases}} - - - - {{#each requests}} - - - - - - - {{/each}} - {{/each}} + -
TestcaseRequest DescriptionTest ScenarioExpected Result
Folder Name > File Name
{{name}}
{{#if @first}}{{../meta.info}}{{/if}} {{meta.info}}{{description}}
({{method}} {{operationPath}})
- {{#if tests.assertions}} -
    - {{#each tests.assertions}} -
  • {{description}}
  • - {{/each}} -
- {{/if}} -
+ + + - + + + + + \ No newline at end of file diff --git a/src/cli_client/client.js b/src/cli_client/client.js index da3ebdfc..25d9a1ff 100644 --- a/src/cli_client/client.js +++ b/src/cli_client/client.js @@ -27,7 +27,7 @@ const router = require('./router') commander .version('1.0.0', '-v, --version') .option('-c, --config ', 'default configuration: {"mode": "outbound", "reportFormat": "json"}') - .option('-m, --mode ', 'default: "outbound" --- supported modes: "monitoring", "outbound"') + .option('-m, --mode ', 'default: "outbound" --- supported modes: "monitoring", "outbound", "testcaseDefinitionReport"') .option('-u, --base-url ', 'default: "http://localhost:5050"') .option('-i, --input-files ', 'csv list of json files or directories; required when the mode is "outbound" --- supported formats: "json"') .option('-e, --environment-file ', 'required when the mode is "outbound" --- supported formats: "json"') diff --git a/src/cli_client/modes/testcaseDefinitionReport.js b/src/cli_client/modes/testcaseDefinitionReport.js new file mode 100644 index 00000000..1cb30f07 --- /dev/null +++ b/src/cli_client/modes/testcaseDefinitionReport.js @@ -0,0 +1,53 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files 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, the Mojaloop files are 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. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + + * ModusBox + * Georgi Logodazhki (Original Author) + -------------- + ******/ + +const report = require('../utils/report') +const fStr = require('node-strings') +const utils = require('../../lib/utils.js') +const objectStore = require('../objectStore') +const templateGenerator = require('../utils/templateGenerator') + +const download = async () => { + const config = objectStore.get('config') + try { + const inputFiles = config.inputFiles.split(',') + const selectedLabels = config.labels ? config.labels.split(',') : null + const template = await templateGenerator.generateTemplate(inputFiles, selectedLabels) + if (config.environmentFile) { + const environmentFileContent = await utils.readFileAsync(config.environmentFile, 'utf8') + template.inputValues = JSON.parse(environmentFileContent).inputValues + } + await report.testcaseDefinition(template) + console.log(fStr.green('Terminate with exit code 0')) + process.exit(0) + } catch (err) { + console.log(err) + console.log(fStr.red('Terminate with exit code 1')) + process.exit(1) + } +} + +module.exports = { + download +} diff --git a/src/cli_client/router.js b/src/cli_client/router.js index 3718cc46..1e10945c 100644 --- a/src/cli_client/router.js +++ b/src/cli_client/router.js @@ -84,6 +84,18 @@ const cli = (commander) => { process.exit(1) } break + case 'testcaseDefinitionReport': + if (config.inputFiles) { + if (!commander.reportFormat) { + config.reportFormat = 'printhtml' + objectStore.set('config', config) + } + require('./modes/testcaseDefinitionReport').download() + } else { + console.log('error: required option \'-i, --input-files \' not specified') + process.exit(1) + } + break default: console.log('Mode is not supported') console.log('Terminate with exit code 1') diff --git a/src/cli_client/utils/report.js b/src/cli_client/utils/report.js index 29ac1d8e..36785195 100644 --- a/src/cli_client/utils/report.js +++ b/src/cli_client/utils/report.js @@ -29,6 +29,16 @@ const objectStore = require('../objectStore') const s3Upload = require('../extras/s3-upload') const outbound = async (data) => { + const testcaseReport = await report(data, 'testcase') + return testcaseReport +} + +const testcaseDefinition = async (template) => { + const testcaseDefinitionReport = await report(template, 'testcase_definition') + return testcaseDefinitionReport +} + +const report = async (data, reportType) => { const returnInfo = {} const config = objectStore.get('config') let reportData @@ -50,7 +60,7 @@ const outbound = async (data) => { } }) } - const response = await axios.post(`${config.baseURL}/api/reports/testcase/${config.reportFormat}`, data, { headers: { 'Content-Type': 'application/json' } }) + const response = await axios.post(`${config.baseURL}/api/reports/${reportType}/${config.reportFormat}`, data, { headers: { 'Content-Type': 'application/json' } }) reportData = response.data const disposition = response.headers['content-disposition'] if (disposition && disposition.indexOf('attachment') !== -1) { @@ -112,5 +122,6 @@ const replaceFileName = (fullPath, fileName) => { } module.exports = { + testcaseDefinition, outbound } diff --git a/src/cli_client/utils/templateGenerator.js b/src/cli_client/utils/templateGenerator.js index 143d5084..2956744f 100644 --- a/src/cli_client/utils/templateGenerator.js +++ b/src/cli_client/utils/templateGenerator.js @@ -1,13 +1,14 @@ const { FolderParser } = require('@mojaloop/ml-testing-toolkit-shared-lib') const { readFileAsync, readRecursiveAsync, fileStatAsync } = require('../../lib/utils') +const path = require('path') const getFileData = async (fileToRead, fileStat) => { try { const content = await readFileAsync(fileToRead, 'utf8') const fileContent = JSON.parse(content) return { - name: fileToRead, - path: fileToRead, + name: path.basename(fileToRead), + path: fileToRead.replace(/\\/g, '/'), size: fileStat.size, modified: '' + fileStat.mtime, content: fileContent @@ -38,57 +39,16 @@ const getFolderRawData = async (folderItem) => { return importFolderRawData } -const generateTemplate = async (fileList, selectedLabels = null) => { +const generateTemplate = async (inputFiles, selectedLabels = null) => { try { - const testCases = [] - for (let i = 0; i < fileList.length; i++) { - let masterFileIndex - const importFolderRawData = await getFolderRawData(fileList[i]) - for (let j = 0; j < importFolderRawData.length; j++) { - if (importFolderRawData[j].name.endsWith('master.json')) { - masterFileIndex = j - break - } - } - let masterFileContent - if (masterFileIndex != null) { - masterFileContent = importFolderRawData[masterFileIndex].content - importFolderRawData.splice(masterFileIndex, 1) - } - - let selectedFiles - if (selectedLabels && selectedLabels.length > 0 && masterFileContent) { - selectedFiles = [] - for (let o = 0; o < masterFileContent.order.length; o++) { - const order = masterFileContent.order[o] - for (let k = 0; k < importFolderRawData.length; k++) { - if (importFolderRawData[k].name.endsWith(order.name)) { - if (order.labels) { - let included = false - for (let l = 0; l < order.labels.length; l++) { - const label = order.labels[l] - if (selectedLabels.includes(label)) { - included = true - break - } - } - if (included) { - selectedFiles.push(importFolderRawData[k].name) - break - } - } - } - } - } - } - if ((!selectedLabels) || (selectedLabels && selectedLabels.length > 0 && selectedFiles && selectedFiles.length > 0)) { - const folderData = FolderParser.getFolderData(importFolderRawData) - const folderTestCases = FolderParser.getTestCases(folderData, selectedFiles) - if (folderTestCases) { - testCases.push(...folderTestCases) - } - } + const folderRawDataArray = [] + for (let i = 0; i < inputFiles.length; i++) { + const inputFile = inputFiles[i] + const folderRawData = await getFolderRawData(inputFile) + folderRawDataArray.push(...folderRawData) } + const folderData = FolderParser.getFolderData(folderRawDataArray) + const testCases = FolderParser.getTestCases(folderData, null, selectedLabels) FolderParser.sequenceTestCases(testCases) const template = {} template.test_cases = testCases diff --git a/test/unit/cli_client/report.test.js b/test/unit/cli_client/report.test.js index ca695519..8fb8e23b 100644 --- a/test/unit/cli_client/report.test.js +++ b/test/unit/cli_client/report.test.js @@ -44,7 +44,17 @@ const data = { } describe('Cli client', () => { - describe('run report functionality', () => { + describe('run testcaseDefinitionReport functionality', () => { + it('when the report format is json should not throw an error', async () => { + spyPromisify.mockReturnValueOnce(jest.fn()) + const config = { + reportFormat: 'json' + } + objectStore.set('config', config) + await expect(report.testcaseDefinition(data)).resolves.not.toBeNull + }) + }) + describe('run outbound functionality', () => { it('when the report format is json should not throw an error', async () => { spyPromisify.mockReturnValueOnce(jest.fn()) const config = { diff --git a/test/unit/cli_client/router.test.js b/test/unit/cli_client/router.test.js index 649d9765..da29c35c 100644 --- a/test/unit/cli_client/router.test.js +++ b/test/unit/cli_client/router.test.js @@ -28,6 +28,7 @@ const { cli } = require('../../../src/cli_client/router') jest.mock('../../../src/cli_client/utils/listeners') jest.mock('../../../src/cli_client/modes/outbound') +jest.mock('../../../src/cli_client/modes/testcaseDefinitionReport') describe('Cli client', () => { describe('running router', () => { @@ -70,6 +71,25 @@ describe('Cli client', () => { cli(config) }).not.toThrowError(); }) + it('when mode is testcaseDefinitionReport and inputFile was provided should not throw an error', async () => { + const config = { + "mode": "testcaseDefinitionReport", + "inputFiles": "test" + } + spyExit.mockImplementationOnce(jest.fn()) + expect(() => { + cli(config) + }).not.toThrowError(); + }) + it('when mode is testcaseDefinitionReport and inputFile was not provided should throw an error', async () => { + const config = { + "mode": "testcaseDefinitionReport" + } + spyExit.mockImplementationOnce(jest.fn()) + expect(() => { + cli(config) + }).not.toThrowError(); + }) it('when mode is not supported should not throw an error', async () => { const config = { "mode": "unsupported" diff --git a/test/unit/cli_client/testcaseDefinitionReport-mode.test.js b/test/unit/cli_client/testcaseDefinitionReport-mode.test.js new file mode 100644 index 00000000..975bdb7c --- /dev/null +++ b/test/unit/cli_client/testcaseDefinitionReport-mode.test.js @@ -0,0 +1,72 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files 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, the Mojaloop files are 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. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + + * ModusBox + * Georgi Logodazhki (Original Author) + -------------- + ******/ +'use strict' + +const spyReport = jest.spyOn(require('../../../src/cli_client/utils/report'), 'testcaseDefinition') +const spyExit = jest.spyOn(process, 'exit') +const spyReadFileAsync = jest.spyOn(require('../../../src/lib/utils'), 'readFileAsync') +const objectStore = require('../../../src/cli_client/objectStore') + +const testcaseDefinitionReport = require('../../../src/cli_client/modes/testcaseDefinitionReport') +const spyGenerateTemplate = jest.spyOn(require('../../../src/cli_client/utils/templateGenerator'), 'generateTemplate') + +describe('Cli client', () => { + + describe('run testcaseDefinitionReport', () => { + it('when download is successful should not throw an error', async () => { + const config = { + inputFiles: "sample-cli.json", + labels: "p2p" + } + spyGenerateTemplate.mockResolvedValueOnce({ + "test_cases": [ + { + "requests": [] + } + ] + }) + objectStore.set('config', config) + + spyReport.mockResolvedValueOnce({}) + spyExit.mockReturnValueOnce({}) + await expect(testcaseDefinitionReport.download()).resolves.toBe(undefined) + }) + it('when download is not successful should throw an error', async () => { + spyReadFileAsync.mockResolvedValueOnce(JSON.stringify({ + "inputValues": {} + })) + const config = { + inputFiles: "sample-cli.json", + environmentFile: "sample-environement.json" + } + spyGenerateTemplate.mockResolvedValueOnce({}) + + objectStore.set('config', config) + + spyReport.mockRejectedValueOnce({}) + spyExit.mockReturnValueOnce({}) + await expect(testcaseDefinitionReport.download()).resolves.toBe(undefined) + }) + }) +})