UNPKG

@ovotech/genesys-web-messaging-tester-cli

Version:
235 lines (234 loc) 13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createScriptedTestCommand = void 0; const ci_info_1 = __importDefault(require("ci-info")); const listr2_1 = require("listr2"); const commander = __importStar(require("commander")); const commander_1 = require("commander"); const fs_1 = require("fs"); const readableFileValidator_1 = require("../../fileSystem/readableFileValidator"); const yamlFileReader_1 = require("../../fileSystem/yamlFileReader"); const parseTestScript_1 = require("./testScript/parseTestScript"); const genesys_web_messaging_tester_1 = require("@ovotech/genesys-web-messaging-tester"); const ui_1 = require("./ui"); const validateSessionConfig_1 = require("./testScript/validateSessionConfig"); const validateTestScript_1 = require("./testScript/validateTestScript"); const validateGenesysEnvVariables_1 = require("../../genesysPlatform/validateGenesysEnvVariables"); const messageIdToConversationIdFactory_1 = require("../../genesysPlatform/messageIdToConversationIdFactory"); const CommandExpectedlyFailedError_1 = require("../CommandExpectedlyFailedError"); const taskRetry_1 = require("./taskRetry"); function parsePositiveInt(value) { const parsedValue = parseInt(value, 10); if (isNaN(parsedValue)) { throw new commander.InvalidArgumentError('Not a number.'); } if (parsedValue <= 0) { throw new commander.InvalidArgumentError('Must be greater than 0.'); } return parsedValue; } function createScriptedTestCommand({ command = new commander_1.Command(), ui = new ui_1.Ui(), reorderedMessageDelayerFactory = (delayBeforeEmittingInMs) => new genesys_web_messaging_tester_1.ReorderedMessageDelayer(delayBeforeEmittingInMs), webMessengerSessionFactory = (config, reorderedMessageDelayer) => new genesys_web_messaging_tester_1.WebMessengerGuestSession(config, { IsAutomatedTest: 'true' }, reorderedMessageDelayer), conversationFactory = (session) => new genesys_web_messaging_tester_1.Conversation(session), fsReadFileSync = fs_1.readFileSync, fsAccessSync = fs_1.accessSync, quietMode = !process.stdout.isTTY || ci_info_1.default.isCI, } = {}) { const yamlFileReader = (0, yamlFileReader_1.createYamlFileReader)(fsReadFileSync); if (!ui) { throw new Error('UI must be defined'); } return command .command('scripted') .description('Test a WM Deployment against a scenario') .argument('<filePath>', 'Path of the YAML test-script file', (0, readableFileValidator_1.readableFileValidator)(fsAccessSync)) .option('-id, --deployment-id <deploymentId>', "Web Messenger Deployment's ID") .option('-r, --region <region>', 'Region of Genesys instance that hosts the Web Messenger Deployment') .option('-o, --origin <origin>', 'Origin domain used for restricting Web Messenger Deployment') .option('-p, --parallel <number>', 'Maximum scenarios to run in parallel', parsePositiveInt, 1) .option('-a, --associate-id', `Associate tests their conversation ID. This requires the following environment variables to be set for an OAuth client with the role conversation:webmessaging:view: GENESYS_REGION GENESYSCLOUD_OAUTHCLIENT_ID GENESYSCLOUD_OAUTHCLIENT_SECRET`, false) .option('-fo, --failures-only', 'Only output failures', false) .option('-t, --timeout <number>', 'Seconds to wait for a response before failing the test', parsePositiveInt, 10) .action(async (testScriptPath, options) => { const outputConfig = command.configureOutput(); if (!outputConfig?.writeOut || !outputConfig?.writeErr) { throw new Error('No writeOut and/or writeErr'); } let associateId; if (!options.associateId) { associateId = { enabled: false }; } else { const genesysEnvValidationResult = (0, validateGenesysEnvVariables_1.validateGenesysEnvVariables)(process.env); if (!genesysEnvValidationResult.genesysVariables) { outputConfig.writeErr(ui.validatingAssociateConvoIdEnvValidationFailed(genesysEnvValidationResult.error)); } else { // Only load when required // Also removes 'You are trying to `import` a file after the Jest environment has been torn down' error due to // file-watcher it starts const { configurePlatformClients } = await Promise.resolve().then(() => __importStar(require('../../genesysPlatform/configurePlatformClients'))); const clients = await configurePlatformClients(genesysEnvValidationResult.genesysVariables); const messageIdToConversationIdClient = (0, messageIdToConversationIdFactory_1.messageIdToConversationIdFactory)(clients); associateId = { enabled: true, client: messageIdToConversationIdClient }; const checkResult = await messageIdToConversationIdClient.preflightCheck(); if (!checkResult.ok) { outputConfig.writeErr(ui.preflightCheckOfAssociateConvoIdFailed(checkResult)); throw new CommandExpectedlyFailedError_1.CommandExpectedlyFailedError(); } } } // 1. Read YAML file let testScriptFileContents; try { testScriptFileContents = yamlFileReader(testScriptPath); } catch (error) { outputConfig.writeErr(ui.errorReadingTestScriptFile(error)); throw new CommandExpectedlyFailedError_1.CommandExpectedlyFailedError(); } // 2. Validate Test Script const testScriptValidationResults = (0, validateTestScript_1.validateTestScript)(testScriptFileContents); if (testScriptValidationResults.error) { outputConfig.writeErr(ui.validatingTestScriptFailed(testScriptValidationResults.error)); throw new CommandExpectedlyFailedError_1.CommandExpectedlyFailedError(); } // 3. Merge session config from args and Test Script - args take priority const { validTestScript } = testScriptValidationResults; const mergedSessionConfig = { deploymentId: options.deploymentId ?? validTestScript.config?.deploymentId, region: options.region ?? validTestScript.config?.region, origin: options.origin ?? validTestScript.config?.origin, }; // 4. Validate session config const sessionConfigValidationResults = (0, validateSessionConfig_1.validateSessionConfig)(mergedSessionConfig); if (!sessionConfigValidationResults.validSessionConfig) { outputConfig.writeErr(ui.validatingSessionConfigFailed(sessionConfigValidationResults.error)); throw new CommandExpectedlyFailedError_1.CommandExpectedlyFailedError(); } // 5. Extract Scenarios from Test Script const testScriptScenarios = (0, parseTestScript_1.extractScenarios)(testScriptValidationResults.validTestScript, sessionConfigValidationResults.validSessionConfig); const hasMultipleTests = testScriptScenarios.length > 1; const tasks = new listr2_1.Listr(testScriptScenarios.map((scenario) => ({ title: ui.titleOfTask(scenario), task: (context, task) => (0, taskRetry_1.tryableTask)(async (isRetry) => { const transcription = []; const reorderedMessageDelayer = reorderedMessageDelayerFactory(isRetry ? 6000 : 1000); const session = webMessengerSessionFactory(scenario.sessionConfig, reorderedMessageDelayer); let conversationIdGetter = undefined; if (associateId.enabled) { conversationIdGetter = (0, messageIdToConversationIdFactory_1.createConversationIdGetter)(session, associateId.client); } new genesys_web_messaging_tester_1.SessionTranscriber(session).on('messageTranscribed', (event) => { transcription.push(event); if (!quietMode) { if (hasMultipleTests) { task.output = ui?.firstLineOfMessageTranscribed(event); } else { const message = ui?.messageTranscribed(event); if (task.output) { task.output += message; } else { task.output = message; } } } }); const convo = conversationFactory(session); await convo.waitForConversationToStart(); let stepError; try { for (const step of scenario.steps) { await step(convo, { timeoutInSeconds: options.timeout }); } } catch (e) { if (!isRetry && reorderedMessageDelayer.unorderdMessageDetected) { task.output = ui?.retryingTestDueToFailureLikelyByUnorderedMessage(); task.title = ui?.titleOfTask(scenario, true); throw new taskRetry_1.RetryTask(); } else { stepError = e; } } finally { session.close(); } if (stepError) { context.scenarioResults.push({ scenario, transcription, wasRetriedDueToUnorderedMessageFailure: isRetry, hasPassed: false, reasonForError: stepError instanceof Error ? stepError : new Error('Unexpected error occurred'), conversationId: conversationIdGetter ? { associateId: true, conversationIdGetter } : { associateId: false }, }); throw new Error(ui?.titleOfFinishedTask(scenario, false)); } task.title = ui?.titleOfFinishedTask(scenario, true); context.scenarioResults.push({ scenario, transcription, wasRetriedDueToUnorderedMessageFailure: isRetry, hasPassed: true, conversationId: conversationIdGetter ? { associateId: true, conversationIdGetter } : { associateId: false }, }); }), })), { concurrent: options.parallel, exitOnError: false, collectErrors: false, rendererFallback: () => quietMode, }); const results = await tasks.run({ scenarioResults: [] }); const scenariosThatPassed = results.scenarioResults.filter((s) => s.hasPassed); if (!options.failuresOnly) { for (const s of scenariosThatPassed) { outputConfig.writeOut(await ui.scenarioTestResult(s)); } } const scenariosThatFailed = results.scenarioResults.filter((s) => !s.hasPassed); for (const s of scenariosThatFailed) { outputConfig.writeOut(await ui.scenarioTestResult(s)); } outputConfig.writeOut(ui.testScriptSummary([...scenariosThatPassed, ...scenariosThatFailed])); if (results.scenarioResults.some((r) => !r.hasPassed)) { throw new CommandExpectedlyFailedError_1.CommandExpectedlyFailedError(); } }); } exports.createScriptedTestCommand = createScriptedTestCommand;