Untitled
import XAPI_VALIDATION_TYPES from '../constants/xApiConstants'; const fs = require('fs'); const path = require('path'); const eventReporter = require('./eventReporter'); const config = require('../../config'); class EventValidator { lastPostData = ''; occuranceNum = 0; testcase = {}; eventTestCases = []; results = []; eventData = {}; /** * Parse testcase from csv with the following format * |testcasename|scope|jsonPath(name)|expectedvalue|validationtype|identifiertype| * @param {*} testCaseFile csv file to be parsed * @param {*} testId testId for this test * @param {*} testCaseName name of testcase */ constructor(testCaseFile, testId, testCaseName) { this.testcases = []; this.testId = testId; this.testCaseName = testCaseName; fs.readFile( path.join(config.testData_root, 'events', 'testcases', testCaseFile), 'utf-8', (err, data) => { if (err) throw err.message; const lines = data.split('\n'); const headers = lines[0].split(','); let eventTestCaseName = ''; for (let i = 1; i < lines.length; i += 1) { // parse the line that has comma within delimited comma const lineWithoutEnclosedComma = this.parseValueEnclosedWithComma( lines[i], ); const lineData = lineWithoutEnclosedComma.split(','); const eventTestCase = {}; const testcaseName = lineData[0]; const scope = lineData[1]; if (testcaseName !== '') { // eslint-disable-next-line prefer-destructuring eventTestCaseName = `${scope}|${testcaseName}`; const newEventTestCase = {}; newEventTestCase[eventTestCaseName] = []; this.testcases.push(newEventTestCase); } for (let j = 1; j < headers.length; j += 1) { eventTestCase[headers[j]] = lineData[j].split('`').join(','); } this.testcases[this.testcases.length - 1][eventTestCaseName].push( eventTestCase, ); } }, ); } /** * Replace the comma for line that has comma within delimited comma with another character * Replace double quote for the line that has comma within delimited comma with single quote * @param {string} line line to be parsed * @return {string} parsed line */ parseValueEnclosedWithComma(line) { let startingIndex = line.indexOf(',"'); let priorStartingIndex = startingIndex; const startingIndex2 = startingIndex + 1; let newLine = line; do { if (startingIndex >= 0) { priorStartingIndex = startingIndex; newLine = newLine.substring(0, startingIndex + 1) + newLine.substring(startingIndex + 2); startingIndex = newLine.indexOf('""', startingIndex); } } while (startingIndex >= 0); const endingIndex = newLine.indexOf('",', priorStartingIndex); newLine = endingIndex >= 0 ? newLine.substring(0, endingIndex) + newLine.substring(endingIndex + 1) : newLine; const enclosedValueWithComma = newLine.substring( startingIndex2, endingIndex, ); const enclosedValueWithoutComma = enclosedValueWithComma.replace(/,/g, '`'); const lineWithoutEnclosedComma = newLine.substring(0, startingIndex2) + enclosedValueWithoutComma + newLine.substring(endingIndex); return lineWithoutEnclosedComma; } /** * Enable CDP network plugin to track network events */ async enableCDP() { browser.cdp('Network', 'enable'); } /** * Add event listener */ async addEventListener() { browser.on('Network.requestWillBeSent', (params) => { const { postData } = params.request; // eslint-disable-next-line prefer-destructuring this.testcase = this.testcases[0]; if (this.testcase) { const keys = Object.keys(this.testcase); const eventTestCaseName = keys[0]; const scope = eventTestCaseName.split('|')[0]; if (postData && postData.includes(scope)) { let postDataJson; try { postDataJson = JSON.parse(postData); } catch (error) { // } if (postDataJson && postDataJson.object) { if (this.occuranceNum < 1 && this.lastPostData !== postData) { this.validateEvent(eventTestCaseName, postData, postDataJson); } this.occuranceNum = this.lastPostData === postData ? (this.occuranceNum = +1) : 0; } } } }); } /** * Validate events * - validation types: * - baseline: use as the baseline for validation type 'same value as previous request' * and 'not same value as previous request' * - same value as previous request: validate value should be same with actual value saved from last event * - not same value as previous request: validate value should be different with actual value saved from last event * - exact: validate value is exactly equal to specified expected value in csv testcase * - pattern: validate value has same pattern as specified expected pattern in csv testcase * - variable: validate value is the same as the value saved in specified variable in csv testcase * - boolean: validate value is the same as the specified boolean value in csv testcase * @param {*} eventTestCaseName event test case name on csv * @param {*} postData post data of the event * @param {*} postDataJson post data of the event in json */ async validateEvent(eventTestCaseName, postData, postDataJson) { this.eventTestCases = this.testcase[eventTestCaseName]; const eventTestResults = {}; const eventResults = []; let passResultsCount = 0; for (const eventTestCase of this.eventTestCases) { const result = {}; result.eventTestCase = eventTestCase; const { name, value, type } = eventTestCase; // if name is skip, simply skip and do not validate the event if (name !== 'skip') { // transerve json path from delimited name to find the actual value const paths = name.split('|'); let subjson = postDataJson[paths[0]]; for (let i = 1; i < paths.length; i += 1) { subjson = subjson[paths[i]]; // if json path is not found, report missing element if (subjson === undefined) { result.actualValue = 'Missing element'; break; } } // if json path is found, validate actual value against expected value from testcase if (subjson !== undefined) { // if actual value is object, then stringify subjson = typeof subjson === 'object' ? JSON.stringify(subjson) : subjson.toString(); result.actualValue = subjson; // validation for different validation type switch (type) { case XAPI_VALIDATION_TYPES.BASELINE: result.status = 'Pass'; this.eventData[name] = result.actualValue; break; case XAPI_VALIDATION_TYPES.SAME_AS_PREVIOUS_REQUEST: result.status = subjson === this.eventData[name] ? 'Pass' : 'Fail'; result.eventTestCase.value = this.eventData[name]; break; case XAPI_VALIDATION_TYPES.NOT_SAME_AS_PREVIOUS_REQUEST: result.status = subjson !== this.eventData[name] ? 'Pass' : 'Fail'; eventTestCase.value = `not same as ${this.eventData[name]}`; this.eventData[name] = subjson; break; case XAPI_VALIDATION_TYPES.EXACT: result.status = subjson === value ? 'Pass' : 'Fail'; break; case XAPI_VALIDATION_TYPES.VARIABLE: // eslint-disable-next-line no-case-declarations const valueFromGame = global.xApiVariables[this.testId][value]; result.eventTestCase.value = valueFromGame; result.status = subjson === valueFromGame ? 'Pass' : 'Fail'; break; case XAPI_VALIDATION_TYPES.BOOLEAN: result.status = subjson === value.toLowerCase() ? 'Pass' : 'Fail'; break; case XAPI_VALIDATION_TYPES.PATTERN: result.status = new RegExp(value).test(subjson) ? 'Pass' : 'Fail'; break; default: result.status = 'Fail'; this.eventData[name] = result.actualValue; result.actualValue = 'unrecognized validation type'; break; } } passResultsCount = result.status === 'Pass' ? passResultsCount + 1 : passResultsCount; eventResults.push(result); } } // setting event test results attribute to be used in report eventTestResults[eventTestCaseName] = eventResults; // eslint-disable-next-line prefer-destructuring eventTestResults.testCaseName = eventTestCaseName; eventTestResults.passResultsCount = passResultsCount; eventTestResults.failResultsCount = eventResults.length - passResultsCount; if (eventResults.length > 0) this.results.push(eventTestResults); // shift to next test case this.testcases.shift(); this.lastPostData = postData; } /** * Generate result and report for this test */ async generateResult() { await eventReporter.registerGameEvents(this.testCaseName, this.results); await eventReporter.generateTestCaseReport(this.testCaseName); } } module.exports = EventValidator;
Leave a Comment