From b9b7d77baf7ed369a1955b1c20f02c1bcb247311 Mon Sep 17 00:00:00 2001 From: rconner46 Date: Thu, 11 Jul 2024 09:22:03 -0400 Subject: [PATCH] Allow for asset upload --- README.md | 74 +++++-- dist/index.cjs | 418 ++++++++++++++++++++++++++++++++++++--- dist/index.cjs.map | 2 +- dist/index.d.ts | 302 +++++++++++++++++++++++++++-- dist/index.min.js | 2 +- dist/index.min.js.map | 2 +- dist/index.mjs | 420 +++++++++++++++++++++++++++++++++++++--- dist/index.mjs.map | 2 +- package.json | 5 +- src/auto-api.ts | 88 ++++++++- src/config.ts | 52 ++++- src/dto.ts | 22 +++ src/email-helper.ts | 9 + src/email/attachment.ts | 10 + src/email/inbox.ts | 12 ++ src/heartbeat.ts | 26 ++- src/logging.ts | 64 ++++++ src/reporter.ts | 244 +++++++++++++++++++---- src/version.ts | 2 +- test/config.test.ts | 2 +- test/logging.test.ts | 46 +++++ yarn.lock | 179 ++++++++++++++++- 22 files changed, 1848 insertions(+), 135 deletions(-) create mode 100644 src/logging.ts create mode 100644 test/logging.test.ts diff --git a/README.md b/README.md index 573cfc3..d5d7c36 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,25 @@ -# Client API for automation backend -Used by reporter integrations for JS test frameworks. +# Applause Automation API Access -Written in TypeScript, transpiled to JS for NPM packaging using Rollup +This project contains a shared interface into the Applause Automation API for JS/TS projects that need to communicate with the Applause Services. Each testing framework +requires a custom integration to handle adding these methods into the correct hooks provided by the framework. These methods can also be called manually if a custom integration +is needed. -creates NPM package in /dist folder in ES, UMD, and CJS module formats +## Implementations -also publishes Typescript types and sourcemaps into NPM package +You can find the following reporter interfaces to be used with their corresponding JS/TS Test Runners: +- wdio-applause-reporter: https://github.com/ApplauseOSS/wdio-applause-reporter +- jest-applause-reporter: https://github.com/ApplauseOSS/jest-applause-reporter +- mocha-applause-reporter: https://github.com/ApplauseOSS/mocha-applause-reporter +- cucumber-applause-reporter: https://github.com/ApplauseOSS/cucumber-applause-reporter -runs tests using Node and Jest +## Requirements: +- Node 20.x +- TypeScript -Configured for Node 14+ . To update, change base tsconfig from "extends": "@tsconfig/node14/tsconfig.json", update "engines" section in package.json, and update .node-version file -# Running -Use `yarn run all` . It will configure and run all the build steps +## Running -## Run through local dev setup and build +### Run through local dev setup and build `yarn all` @@ -34,7 +39,52 @@ Use `yarn run all` . It will configure and run all the build steps `yarn lint` -## Publishing +### Publishing `yarn publish-verify` -CI will do actual pubish on merge +## Implementation Information + +### Applause Shared Reporter +The file reporter.ts contains the implementation of the ApplauseReporter class, which serves as a reporter for the Applause testing framework. This class provides methods to interact with the Applause API and perform various actions related to test runs, test cases, and test case results. + +Here's a breakdown of the key components and functionalities of the ApplauseReporter class: + +#### startTestCase(...) Method: + +Starts a test case by calling the startTestCase method on the reporter property. +If the reporter property is undefined, it throws an error indicating that a run was never initialized. +Returns a promise that resolves to the testCaseId. + +#### submitTestCaseResult Method: + +Submits a test case result by calling the submitTestCaseResult method on the reporter property. +If the reporter property is undefined, it throws an error indicating that a run was never initialized. +Returns a promise that resolves to the testCaseResultId. + +#### runnerEnd Method: + +Ends the Applause runner by calling the runnerEnd method on the reporter property. +If the reporter property is undefined, it throws an error indicating that a run was never initialized. +Returns a promise that resolves when the runner is ended. + +#### attachTestCaseAsset Method: + +Attaches an asset to a test case by calling the attachTestCaseAsset method on the reporter property. +If the reporter property is undefined, it throws an error indicating that a run was never initialized. +Returns a promise that resolves when the asset is attached. + +#### isSynchronized Method: + +Checks if the Applause runner is synchronized by verifying if the run has started and finished, and if there are no pending API calls. +Returns true if the runner is not yet started or has ended, and all calls made to the Applause API have finished. + +Overall, the ApplauseReporter class provides a convenient interface for interacting with the Applause testing framework, allowing you to start test runs, manage test cases, submit test case results, attach assets, and check the synchronization status of the runner. + +### Email Testing + +The file email-helper.ts, along with the src/email/** directory contains the implementation of our email helper utilities. It handes allocating an email address from the applause services for testing and fetching emails from it. We utilize the 'mailparser' dependency for interacting with the email content. + + +### Logging Integration + +The file logging.ts offers a winston Transport class that allows the Applause reporter to gain access to log messages from the tests. These get stored as assets attched to the test results in the Applause automation service. diff --git a/dist/index.cjs b/dist/index.cjs index edba57b..5df3a89 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -5,12 +5,25 @@ var fs = require('fs'); var path = require('path'); var Validator = require('validator'); var mailparser = require('mailparser'); +var winston = require('winston'); +var Transport = require('winston-transport'); -const API_VERSION = '1.0.0'; +const API_VERSION = '1.1.0'; +/** + * Represents the configuration options for the Applause Reporter. + */ const validator = Validator.default; +/** + * The default base URL for the Applause API. + */ const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/'; -// Loads the configuration +/** + * Loads the configuration for the Applause Reporter. + * @param loadOptions - The options for loading the configuration. + * @returns The loaded Applause configuration. + * @throws Error if the configuration is not complete or invalid. + */ function loadConfig(loadOptions) { // Setup the initial config with any default properties let config = { @@ -36,22 +49,43 @@ function loadConfig(loadOptions) { validateConfig(finalConfig); return finalConfig; } +/** + * Overrides the configuration with the provided overrides. + * @param config - The base configuration. + * @param overrides - The overrides to apply. + * @returns The overridden configuration. + */ function overrideConfig(config, overrides) { return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined))); } +/** + * Checks if the configuration is complete. + * @param config - The configuration to check. + * @returns True if the configuration is complete, false otherwise. + */ function isComplete(config) { return (config.baseUrl !== undefined && config.apiKey !== undefined && config.productId !== undefined); } +/** + * Loads the configuration from the specified file. + * @param configFile - The path to the configuration file. + * @returns The loaded configuration from the file. + */ function loadConfigFromFile(configFile) { const configFilePath = configFile || process.cwd() + '/applause.json'; if (!fs.existsSync(configFilePath)) { return {}; } - const fileCotents = fs.readFileSync(configFilePath, 'utf8'); - return JSON.parse(fileCotents); + const fileContents = fs.readFileSync(configFilePath, 'utf8'); + return JSON.parse(fileContents); } +/** + * Validates the configuration. + * @param config - The configuration to validate. + * @throws Error if the configuration is invalid. + */ function validateConfig(config) { if (!Number.isInteger(config.productId) || config.productId <= 0) { throw new Error(`productId must be a positive integer, was: '${config.productId}'`); @@ -67,12 +101,17 @@ function validateConfig(config) { require_host: true, require_protocol: true, })) { - throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}`); + throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}`); } if (validator.isEmpty(config.apiKey)) { throw new Error('apiKey is an empty string!'); } } +/** + * Validates a partial configuration. + * @param config - The partial configuration to validate. + * @throws Error if the partial configuration is invalid. + */ function validatePartialConfig(config) { if (config.productId !== undefined && (!Number.isInteger(config.productId) || config.productId <= 0)) { @@ -80,16 +119,26 @@ function validatePartialConfig(config) { } } +/** + * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform. + * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management. + * It also includes properties and methods to track the number of HTTP calls in progress. + */ class AutoApi { options; client; callsInFlight; /** - * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished + * Tracks the number of HTTP calls in progress. + * This property is used by reporters that want to know when the async work is finished. */ get getCallsInFlight() { return this.callsInFlight; } + /** + * Creates an instance of the `AutoApi` class. + * @param options - The configuration options for the Applause API. + */ constructor(options) { this.options = options; this.callsInFlight = 0; @@ -118,6 +167,11 @@ class AutoApi { return Promise.reject(error); }); } + /** + * Starts a new test run. + * @param info - The information for creating the test run. + * @returns A promise that resolves to the response containing the created test run. + */ async startTestRun(info) { this.callsInFlight += 1; try { @@ -142,6 +196,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Ends a test run. + * @param testRunId - The ID of the test run to end. + * @returns A promise that resolves to the response indicating the completion of the test run. + */ async endTestRun(testRunId) { this.callsInFlight += 1; try { @@ -151,6 +210,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Starts a new test case. + * @param params - The parameters for creating the test case. + * @returns A promise that resolves to the response containing the created test case. + */ async startTestCase(params) { this.callsInFlight += 1; try { @@ -161,6 +225,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Submits a test case result. + * @param params - The parameters for submitting the test case result. + * @returns A promise that resolves when the test case result is submitted. + */ async submitTestCaseResult(params) { this.callsInFlight += 1; try { @@ -170,6 +239,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the provider session links for the specified test results. + * @param resultIds - The IDs of the test results. + * @returns A promise that resolves to the response containing the provider session links. + */ async getProviderSessionLinks(resultIds) { this.callsInFlight += 1; try { @@ -181,6 +255,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Sends a heartbeat for the specified test run. + * @param testRunId - The ID of the test run. + * @returns A promise that resolves to the response indicating the heartbeat was sent. + */ async sendSdkHeartbeat(testRunId) { this.callsInFlight += 1; try { @@ -193,6 +272,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the email address for the specified email prefix. + * @param emailPrefix - The prefix of the email address. + * @returns A promise that resolves to the response containing the email address. + */ async getEmailAddress(emailPrefix) { this.callsInFlight += 1; try { @@ -203,6 +287,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the content of the specified email. + * @param request - The request parameters for retrieving the email content. + * @returns A promise that resolves to the response containing the email content. + */ async getEmailContent(request) { this.callsInFlight += 1; try { @@ -213,6 +302,30 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Uploads an asset for the specified test result. + * @param resultId - The ID of the test result. + * @param file - The file to upload as an asset. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The GUID of the provider session. + * @param assetType - The type of the asset. + * @returns A promise that resolves to the response indicating the asset was uploaded. + */ + async uploadAsset(resultId, file, assetName, providerSessionGuid, assetType) { + this.callsInFlight += 1; + try { + // this filters out falsy values (null, undefined, 0) + return await this.client.postForm(`/api/v1.0/test-result/${resultId}/upload`, { + file, + assetName, + providerSessionGuid, + assetType, + }); + } + finally { + this.callsInFlight -= 1; + } + } } /** @@ -228,14 +341,48 @@ exports.TestResultStatus = void 0; TestResultStatus["CANCELED"] = "CANCELED"; TestResultStatus["ERROR"] = "ERROR"; })(exports.TestResultStatus || (exports.TestResultStatus = {})); +exports.AssetType = void 0; +(function (AssetType) { + AssetType["SCREENSHOT"] = "SCREENSHOT"; + AssetType["FAILURE_SCREENSHOT"] = "FAILURE_SCREENSHOT"; + AssetType["VIDEO"] = "VIDEO"; + AssetType["NETWORK_HAR"] = "NETWORK_HAR"; + AssetType["VITALS_LOG"] = "VITALS_LOG"; + AssetType["CONSOLE_LOG"] = "CONSOLE_LOG"; + AssetType["NETWORK_LOG"] = "NETWORK_LOG"; + AssetType["DEVICE_LOG"] = "DEVICE_LOG"; + AssetType["SELENIUM_LOG"] = "SELENIUM_LOG"; + AssetType["SELENIUM_LOG_JSON"] = "SELENIUM_LOG_JSON"; + AssetType["BROWSER_LOG"] = "BROWSER_LOG"; + AssetType["FRAMEWORK_LOG"] = "FRAMEWORK_LOG"; + AssetType["EMAIL"] = "EMAIL"; + AssetType["PAGE_SOURCE"] = "PAGE_SOURCE"; + AssetType["CODE_BUNDLE"] = "CODE_BUNDLE"; + AssetType["RESULTS_ZIP"] = "RESULTS_ZIP"; + AssetType["SESSION_DETAILS"] = "SESSION_DETAILS"; + AssetType["DEVICE_DETAILS"] = "DEVICE_DETAILS"; + AssetType["UNKNOWN"] = "UNKNOWN"; +})(exports.AssetType || (exports.AssetType = {})); +/** + * Represents an email inbox. + */ class Inbox { emailAddress; autoApi; + /** + * Creates an instance of Inbox. + * @param emailAddress - The email address associated with the inbox. + * @param autoApi - An instance of the AutoApi class. + */ constructor(emailAddress, autoApi) { this.emailAddress = emailAddress; this.autoApi = autoApi; } + /** + * Retrieves the content of an email from the inbox. + * @returns A Promise that resolves to the parsed email content. + */ async getEmail() { const res = await this.autoApi.getEmailContent({ emailAddress: this.emailAddress, @@ -244,33 +391,60 @@ class Inbox { } } +/** + * Helper class for managing email functionality. + */ class EmailHelper { autoApi; constructor(autoApi) { this.autoApi = autoApi; } + /** + * Retrieves the inbox for the specified email prefix. + * + * @param emailPrefix - The prefix used to generate the email address. + * @returns A Promise that resolves to an Inbox object. + */ async getInbox(emailPrefix) { const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress; return new Inbox(generatedAddress, this.autoApi); } } +/** + * Represents a service for sending heartbeats during a test run. + */ class TestRunHeartbeatService { testRunId; autoApi; + logger; enabled = false; nextHeartbeat; - constructor(testRunId, autoApi) { + /** + * Creates an instance of TestRunHeartbeatService. + * @param testRunId - The ID of the test run. + * @param autoApi - The AutoApi instance used for sending heartbeats. + */ + constructor(testRunId, autoApi, logger) { this.testRunId = testRunId; this.autoApi = autoApi; + this.logger = logger; } + /** + * Starts sending heartbeats. + * @returns A promise that resolves when the heartbeats are started. + */ async start() { // End the current heartbeat if it has started await this.end(); - // Set up va new interval + // Set up a new interval this.enabled = true; this.scheduleNextHeartbeat(); } + /** + * Checks if the heartbeats are enabled. + * @returns True if the heartbeats are enabled, false otherwise. + */ isEnabled() { return this.enabled; } @@ -286,6 +460,10 @@ class TestRunHeartbeatService { console.log('Heartbeat sent'); this.scheduleNextHeartbeat(); } + /** + * Ends the heartbeats. + * @returns A promise that resolves when the heartbeats are ended. + */ async end() { if (this.nextHeartbeat !== undefined) { this.enabled = false; @@ -297,34 +475,138 @@ class TestRunHeartbeatService { } } +/** + * A simple Class for storing and retrieving log messages. + */ +class LoggingContainer { + logs = []; + /** + * Retrieves all logs stored in the container. + * + * @returns An array of log messages. + */ + getLogs() { + return this.logs; + } + /** + * Retrieves and clears all logs stored in the container. + * + * @returns An array of log messages. + */ + drainLogs() { + const logs = this.logs; + this.clearLogs(); + return logs; + } + /** + * Clears all logs stored in the container. + */ + clearLogs() { + this.logs = []; + } + /** + * Adds a log message to the container. + * + * @param log - The log message to add. + */ + addLog(log) { + this.logs.push(log); + } +} +// Create a new Shared LoggingContainer to store logs +const APPLAUSE_LOG_RECORDS = new LoggingContainer(); +/** + * A Custom Winston Transport that sends logs to the Applause LoggingContainer + */ +class ApplauseTransport extends Transport { + constructor() { + super({}); + } + log(info, callback) { + APPLAUSE_LOG_RECORDS.addLog(info.message); + // Continue to the next transport + callback(); + } +} + +const TEST_RAIL_CASE_ID_PREFIX = 'TestRail-'; +const APPLAUSE_CASE_ID_PREFIX = 'Applause-'; +/** + * Represents an Applause reporter. + */ class ApplauseReporter { + logger; autoApi; initializer; reporter; runStarted = false; runFinished = false; - constructor(config) { + /** + * Creates an instance of ApplauseReporter. + * @param config - The Applause configuration. + */ + constructor(config, logger) { + this.logger = logger; this.autoApi = new AutoApi(config); - this.initializer = new RunInitializer(this.autoApi); + this.initializer = new RunInitializer(this.autoApi, this.logger); + const runId = process.env['APPLAUSE_RUN_ID']; + if (runId !== undefined) { + const r = new RunReporter(this.autoApi, parseInt(runId)); + this.reporter = new Promise(resolve => resolve(r)); + this.runStarted = true; + } } - runnerStart(tests) { + /** + * Starts the Applause runner. + * @param tests - Optional array of test names to run. + * @returns A promise that resolves to the test run ID. + * @throws Error if a run is already started or finished. + */ + async runnerStart(tests) { + if (this.reporter !== undefined) { + throw new Error('Cannot start a run - run already started or run already finished'); + } this.reporter = this.initializer.initializeRun(tests); - void this.reporter.then(() => { - this.runStarted = true; - }); + const initializedReporter = await this.reporter; + this.runStarted = true; + process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString(); + return initializedReporter.testRunId; } - startTestCase(id, testCaseName, params) { + /** + * Starts a test case. + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Optional additional parameters for the test case. + * @returns A promise that resolves to the test case ID. + * @throws Error if a run was never initialized. + */ + async startTestCase(id, testCaseName, params) { if (this.reporter === undefined) { throw new Error('Cannot start a test case for a run that was never initialized'); } - void this.reporter.then(reporter => reporter.startTestCase(id, testCaseName, params)); + const reporter = await this.reporter; + return reporter.startTestCase(id, testCaseName, params); } - submitTestCaseResult(id, status, params) { + /** + * Submits a test case result. + * @param id - The ID of the test case. + * @param status - The status of the test case result. + * @param params - Optional additional parameters for the test case result. + * @returns A promise that resolves to the test case result ID. + * @throws Error if a run was never initialized. + */ + async submitTestCaseResult(id, status, params) { if (this.reporter === undefined) { throw new Error('Cannot submit test case result for a run that was never initialized'); } - void this.reporter.then(reporter => reporter.submitTestCaseResult(id, status, params)); + const reporter = await this.reporter; + return reporter.submitTestCaseResult(id, status, params); } + /** + * Ends the Applause runner. + * @returns A promise that resolves when the runner is ended. + * @throws Error if a run was never initialized. + */ async runnerEnd() { if (this.reporter === undefined) { throw new Error('Cannot end a run that was never initialized'); @@ -333,17 +615,51 @@ class ApplauseReporter { .then(reporter => reporter.runnerEnd()) .then(() => (this.runFinished = true)); } + /** + * Attaches an asset to a test case. + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset data as a Buffer. + * @returns A promise that resolves when the asset is attached. + * @throws Error if a run was never initialized. + */ + async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) { + if (this.reporter === undefined) { + throw new Error('Cannot attach an asset for a run that was never initialized'); + } + return await this.reporter.then(reporter => reporter.attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset)); + } + /** + * Checks if the Applause runner is synchronized. + * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished. + */ isSynchronized() { - // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished return ((!this.runStarted || (this.runStarted && this.runFinished)) && this.autoApi.getCallsInFlight == 0); } } +/** + * Represents a Run Initializer. + */ class RunInitializer { autoApi; - constructor(autoApi) { + logger; + constructor(autoApi, logger) { this.autoApi = autoApi; + this.logger = + logger || + winston.createLogger({ + transports: [new winston.transports.Console(), new ApplauseTransport()], + }); } + /** + * Initializes a test run. + * @param tests - An optional array of test names to include in the run. + * @returns A promise that resolves to a RunReporter instance. + * @throws An error if unable to create the test run. + */ async initializeRun(tests) { const cleanedTests = tests ?.map(parseTestCaseName) @@ -356,26 +672,43 @@ class RunInitializer { throw new Error('Unable to create test run'); } const runId = testRunCreateResponse.data.runId; - console.log('Test Run %d initialized', runId); - const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi); + this.logger.info('Test Run %d initialized', runId); + const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi, this.logger); await heartbeatService.start(); return new RunReporter(this.autoApi, runId, heartbeatService); } } +/** + * Handles reporting test results to the Applause API. + */ class RunReporter { autoApi; testRunId; heartbeatService; uidToResultIdMap = {}; resultSubmissionMap = {}; + /** + * Creates a new instance of the Reporter class. + * @param autoApi - The AutoApi instance. + * @param testRunId - The ID of the test run. + * @param heartbeatService - (Optional) The TestRunHeartbeatService instance. + */ constructor(autoApi, testRunId, heartbeatService) { this.autoApi = autoApi; this.testRunId = testRunId; this.heartbeatService = heartbeatService; } + /** + * Starts a test case and returns a promise that resolves to the test result ID. + * + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Additional parameters for the test case. + * @returns A promise that resolves to the test result ID. + */ startTestCase(id, testCaseName, params) { const parsedTestCase = parseTestCaseName(testCaseName); - this.uidToResultIdMap[id] = this.autoApi + const submission = this.autoApi .startTestCase({ testCaseName: parsedTestCase.testCaseName, testCaseId: parsedTestCase.testRailTestCaseId, @@ -387,21 +720,52 @@ class RunReporter { .then(res => { return res.data.testResultId; }); + this.uidToResultIdMap[id] = submission; + return submission; } + /** + * Submits the result of a test case. + * + * @param id - The ID of the test case. + * @param status - The status of the test result. + * @param params - Additional parameters for the test result. + * @returns A promise that resolves to the result ID. + */ submitTestCaseResult(id, status, params) { - this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => this.autoApi.submitTestCaseResult({ + const submission = this.uidToResultIdMap[id]?.then(resultId => this.autoApi + .submitTestCaseResult({ status: status, testResultId: resultId, ...params, - })); + }) + .then(() => resultId)); + this.resultSubmissionMap[id] = submission; + return submission; + } + /** + * Attaches a test case asset to a result. + * + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset to attach. + * @returns A promise that resolves when the asset is attached. + */ + async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) { + await this.uidToResultIdMap[id]?.then(resultId => this.autoApi.uploadAsset(resultId, asset, assetName, providerSessionGuid, assetType)); } + /** + * Ends the test runner and performs necessary cleanup tasks. + * @returns A promise that resolves when the runner has ended. + */ async runnerEnd() { // Wait for all results to be created const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || []; // Wait for the results to be submitted void (await Promise.all(Object.values(this.resultSubmissionMap))); // Wait the heartbeat to be ended - void (await this.heartbeatService.end()); + void (await this.heartbeatService?.end()); void (await this.autoApi.endTestRun(this.testRunId)); // Fetch the provider session asset links and save them off to a file const resp = await this.autoApi.getProviderSessionLinks(resultIds); @@ -414,8 +778,6 @@ class RunReporter { } } } -const TEST_RAIL_CASE_ID_PREFIX = 'TestRail-'; -const APPLAUSE_CASE_ID_PREFIX = 'Applause-'; function parseTestCaseName(testCaseName) { // Split the name on spaces. We will reassemble after parsing out the other ids const tokens = testCaseName.split(' '); diff --git a/dist/index.cjs.map b/dist/index.cjs.map index 16ad4fb..399bc7b 100644 --- a/dist/index.cjs.map +++ b/dist/index.cjs.map @@ -1 +1 @@ -{"version":3,"file":"index.cjs","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/email-helper.ts","../src/heartbeat.ts","../src/reporter.ts"],"sourcesContent":["export const API_VERSION = '1.0.0';\n//# sourceMappingURL=version.js.map","import { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n// Loads the configuration\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileCotents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileCotents);\n}\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","import axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\nexport class Inbox {\n emailAddress;\n autoApi;\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","import { Inbox } from './email/inbox.ts';\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map","export class TestRunHeartbeatService {\n testRunId;\n autoApi;\n enabled = false;\n nextHeartbeat;\n constructor(testRunId, autoApi) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n }\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up va new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nexport class ApplauseReporter {\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n constructor(config) {\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi);\n }\n runnerStart(tests) {\n this.reporter = this.initializer.initializeRun(tests);\n void this.reporter.then(() => {\n this.runStarted = true;\n });\n }\n startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.startTestCase(id, testCaseName, params));\n }\n submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.submitTestCaseResult(id, status, params));\n }\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n isSynchronized() {\n // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\nexport class RunInitializer {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n console.log('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n this.uidToResultIdMap[id] = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n }\n submitTestCaseResult(id, status, params) {\n this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => this.autoApi.submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n }));\n }\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map"],"names":["existsSync","readFileSync","TestResultStatus","simpleParser","writeFileSync","pathJoin"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.cjs","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/email-helper.ts","../src/heartbeat.ts","../src/logging.ts","../src/reporter.ts"],"sourcesContent":["export const API_VERSION = '1.1.0';\n//# sourceMappingURL=version.js.map","/**\n * Represents the configuration options for the Applause Reporter.\n */\nimport { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\n/**\n * The default base URL for the Applause API.\n */\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n/**\n * Loads the configuration for the Applause Reporter.\n * @param loadOptions - The options for loading the configuration.\n * @returns The loaded Applause configuration.\n * @throws Error if the configuration is not complete or invalid.\n */\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\n/**\n * Overrides the configuration with the provided overrides.\n * @param config - The base configuration.\n * @param overrides - The overrides to apply.\n * @returns The overridden configuration.\n */\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\n/**\n * Checks if the configuration is complete.\n * @param config - The configuration to check.\n * @returns True if the configuration is complete, false otherwise.\n */\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\n/**\n * Loads the configuration from the specified file.\n * @param configFile - The path to the configuration file.\n * @returns The loaded configuration from the file.\n */\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileContents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileContents);\n}\n/**\n * Validates the configuration.\n * @param config - The configuration to validate.\n * @throws Error if the configuration is invalid.\n */\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\n/**\n * Validates a partial configuration.\n * @param config - The partial configuration to validate.\n * @throws Error if the partial configuration is invalid.\n */\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","/**\n * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform.\n * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management.\n * It also includes properties and methods to track the number of HTTP calls in progress.\n */\nimport axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * Tracks the number of HTTP calls in progress.\n * This property is used by reporters that want to know when the async work is finished.\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n /**\n * Creates an instance of the `AutoApi` class.\n * @param options - The configuration options for the Applause API.\n */\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n /**\n * Starts a new test run.\n * @param info - The information for creating the test run.\n * @returns A promise that resolves to the response containing the created test run.\n */\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Ends a test run.\n * @param testRunId - The ID of the test run to end.\n * @returns A promise that resolves to the response indicating the completion of the test run.\n */\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Starts a new test case.\n * @param params - The parameters for creating the test case.\n * @returns A promise that resolves to the response containing the created test case.\n */\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Submits a test case result.\n * @param params - The parameters for submitting the test case result.\n * @returns A promise that resolves when the test case result is submitted.\n */\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the provider session links for the specified test results.\n * @param resultIds - The IDs of the test results.\n * @returns A promise that resolves to the response containing the provider session links.\n */\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Sends a heartbeat for the specified test run.\n * @param testRunId - The ID of the test run.\n * @returns A promise that resolves to the response indicating the heartbeat was sent.\n */\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the email address for the specified email prefix.\n * @param emailPrefix - The prefix of the email address.\n * @returns A promise that resolves to the response containing the email address.\n */\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the content of the specified email.\n * @param request - The request parameters for retrieving the email content.\n * @returns A promise that resolves to the response containing the email content.\n */\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Uploads an asset for the specified test result.\n * @param resultId - The ID of the test result.\n * @param file - The file to upload as an asset.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The GUID of the provider session.\n * @param assetType - The type of the asset.\n * @returns A promise that resolves to the response indicating the asset was uploaded.\n */\n async uploadAsset(resultId, file, assetName, providerSessionGuid, assetType) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.postForm(`/api/v1.0/test-result/${resultId}/upload`, {\n file,\n assetName,\n providerSessionGuid,\n assetType,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\nexport var AssetType;\n(function (AssetType) {\n AssetType[\"SCREENSHOT\"] = \"SCREENSHOT\";\n AssetType[\"FAILURE_SCREENSHOT\"] = \"FAILURE_SCREENSHOT\";\n AssetType[\"VIDEO\"] = \"VIDEO\";\n AssetType[\"NETWORK_HAR\"] = \"NETWORK_HAR\";\n AssetType[\"VITALS_LOG\"] = \"VITALS_LOG\";\n AssetType[\"CONSOLE_LOG\"] = \"CONSOLE_LOG\";\n AssetType[\"NETWORK_LOG\"] = \"NETWORK_LOG\";\n AssetType[\"DEVICE_LOG\"] = \"DEVICE_LOG\";\n AssetType[\"SELENIUM_LOG\"] = \"SELENIUM_LOG\";\n AssetType[\"SELENIUM_LOG_JSON\"] = \"SELENIUM_LOG_JSON\";\n AssetType[\"BROWSER_LOG\"] = \"BROWSER_LOG\";\n AssetType[\"FRAMEWORK_LOG\"] = \"FRAMEWORK_LOG\";\n AssetType[\"EMAIL\"] = \"EMAIL\";\n AssetType[\"PAGE_SOURCE\"] = \"PAGE_SOURCE\";\n AssetType[\"CODE_BUNDLE\"] = \"CODE_BUNDLE\";\n AssetType[\"RESULTS_ZIP\"] = \"RESULTS_ZIP\";\n AssetType[\"SESSION_DETAILS\"] = \"SESSION_DETAILS\";\n AssetType[\"DEVICE_DETAILS\"] = \"DEVICE_DETAILS\";\n AssetType[\"UNKNOWN\"] = \"UNKNOWN\";\n})(AssetType || (AssetType = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\n/**\n * Represents an email inbox.\n */\nexport class Inbox {\n emailAddress;\n autoApi;\n /**\n * Creates an instance of Inbox.\n * @param emailAddress - The email address associated with the inbox.\n * @param autoApi - An instance of the AutoApi class.\n */\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the content of an email from the inbox.\n * @returns A Promise that resolves to the parsed email content.\n */\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","import { Inbox } from './email/inbox.ts';\n/**\n * Helper class for managing email functionality.\n */\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the inbox for the specified email prefix.\n *\n * @param emailPrefix - The prefix used to generate the email address.\n * @returns A Promise that resolves to an Inbox object.\n */\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map","/**\n * Represents a service for sending heartbeats during a test run.\n */\nexport class TestRunHeartbeatService {\n testRunId;\n autoApi;\n logger;\n enabled = false;\n nextHeartbeat;\n /**\n * Creates an instance of TestRunHeartbeatService.\n * @param testRunId - The ID of the test run.\n * @param autoApi - The AutoApi instance used for sending heartbeats.\n */\n constructor(testRunId, autoApi, logger) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n this.logger = logger;\n }\n /**\n * Starts sending heartbeats.\n * @returns A promise that resolves when the heartbeats are started.\n */\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up a new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n /**\n * Checks if the heartbeats are enabled.\n * @returns True if the heartbeats are enabled, false otherwise.\n */\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n /**\n * Ends the heartbeats.\n * @returns A promise that resolves when the heartbeats are ended.\n */\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import Transport from 'winston-transport';\n/**\n * A simple Class for storing and retrieving log messages.\n */\nexport class LoggingContainer {\n logs = [];\n /**\n * Retrieves all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n getLogs() {\n return this.logs;\n }\n /**\n * Retrieves and clears all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n drainLogs() {\n const logs = this.logs;\n this.clearLogs();\n return logs;\n }\n /**\n * Clears all logs stored in the container.\n */\n clearLogs() {\n this.logs = [];\n }\n /**\n * Adds a log message to the container.\n *\n * @param log - The log message to add.\n */\n addLog(log) {\n this.logs.push(log);\n }\n}\n// Create a new Shared LoggingContainer to store logs\nexport const APPLAUSE_LOG_RECORDS = new LoggingContainer();\n/**\n * A Custom Winston Transport that sends logs to the Applause LoggingContainer\n */\nexport class ApplauseTransport extends Transport {\n constructor() {\n super({});\n }\n log(info, callback) {\n APPLAUSE_LOG_RECORDS.addLog(info.message);\n // Continue to the next transport\n callback();\n }\n}\n//# sourceMappingURL=logging.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nimport winston from 'winston';\nimport { ApplauseTransport } from './logging.ts';\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\n/**\n * Represents an Applause reporter.\n */\nexport class ApplauseReporter {\n logger;\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n /**\n * Creates an instance of ApplauseReporter.\n * @param config - The Applause configuration.\n */\n constructor(config, logger) {\n this.logger = logger;\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi, this.logger);\n const runId = process.env['APPLAUSE_RUN_ID'];\n if (runId !== undefined) {\n const r = new RunReporter(this.autoApi, parseInt(runId));\n this.reporter = new Promise(resolve => resolve(r));\n this.runStarted = true;\n }\n }\n /**\n * Starts the Applause runner.\n * @param tests - Optional array of test names to run.\n * @returns A promise that resolves to the test run ID.\n * @throws Error if a run is already started or finished.\n */\n async runnerStart(tests) {\n if (this.reporter !== undefined) {\n throw new Error('Cannot start a run - run already started or run already finished');\n }\n this.reporter = this.initializer.initializeRun(tests);\n const initializedReporter = await this.reporter;\n this.runStarted = true;\n process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString();\n return initializedReporter.testRunId;\n }\n /**\n * Starts a test case.\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Optional additional parameters for the test case.\n * @returns A promise that resolves to the test case ID.\n * @throws Error if a run was never initialized.\n */\n async startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.startTestCase(id, testCaseName, params);\n }\n /**\n * Submits a test case result.\n * @param id - The ID of the test case.\n * @param status - The status of the test case result.\n * @param params - Optional additional parameters for the test case result.\n * @returns A promise that resolves to the test case result ID.\n * @throws Error if a run was never initialized.\n */\n async submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.submitTestCaseResult(id, status, params);\n }\n /**\n * Ends the Applause runner.\n * @returns A promise that resolves when the runner is ended.\n * @throws Error if a run was never initialized.\n */\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n /**\n * Attaches an asset to a test case.\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset data as a Buffer.\n * @returns A promise that resolves when the asset is attached.\n * @throws Error if a run was never initialized.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n if (this.reporter === undefined) {\n throw new Error('Cannot attach an asset for a run that was never initialized');\n }\n return await this.reporter.then(reporter => reporter.attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset));\n }\n /**\n * Checks if the Applause runner is synchronized.\n * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished.\n */\n isSynchronized() {\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\n/**\n * Represents a Run Initializer.\n */\nexport class RunInitializer {\n autoApi;\n logger;\n constructor(autoApi, logger) {\n this.autoApi = autoApi;\n this.logger =\n logger ||\n winston.createLogger({\n transports: [new winston.transports.Console(), new ApplauseTransport()],\n });\n }\n /**\n * Initializes a test run.\n * @param tests - An optional array of test names to include in the run.\n * @returns A promise that resolves to a RunReporter instance.\n * @throws An error if unable to create the test run.\n */\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n this.logger.info('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi, this.logger);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\n/**\n * Handles reporting test results to the Applause API.\n */\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n /**\n * Creates a new instance of the Reporter class.\n * @param autoApi - The AutoApi instance.\n * @param testRunId - The ID of the test run.\n * @param heartbeatService - (Optional) The TestRunHeartbeatService instance.\n */\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n /**\n * Starts a test case and returns a promise that resolves to the test result ID.\n *\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Additional parameters for the test case.\n * @returns A promise that resolves to the test result ID.\n */\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n const submission = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n this.uidToResultIdMap[id] = submission;\n return submission;\n }\n /**\n * Submits the result of a test case.\n *\n * @param id - The ID of the test case.\n * @param status - The status of the test result.\n * @param params - Additional parameters for the test result.\n * @returns A promise that resolves to the result ID.\n */\n submitTestCaseResult(id, status, params) {\n const submission = this.uidToResultIdMap[id]?.then(resultId => this.autoApi\n .submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n })\n .then(() => resultId));\n this.resultSubmissionMap[id] = submission;\n return submission;\n }\n /**\n * Attaches a test case asset to a result.\n *\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset to attach.\n * @returns A promise that resolves when the asset is attached.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n await this.uidToResultIdMap[id]?.then(resultId => this.autoApi.uploadAsset(resultId, asset, assetName, providerSessionGuid, assetType));\n }\n /**\n * Ends the test runner and performs necessary cleanup tasks.\n * @returns A promise that resolves when the runner has ended.\n */\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService?.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map"],"names":["existsSync","readFileSync","TestResultStatus","AssetType","simpleParser","writeFileSync","pathJoin"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts index aed5c38..1169d05 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,5 +1,6 @@ import { AxiosResponse } from 'axios'; import { ParsedMail } from 'mailparser'; +import winston from 'winston'; /** * Configuration of the auto-api client @@ -95,104 +96,379 @@ interface EmailAddressResponse { interface EmailFetchRequest { emailAddress: string; } +declare enum AssetType { + SCREENSHOT = "SCREENSHOT", + FAILURE_SCREENSHOT = "FAILURE_SCREENSHOT", + VIDEO = "VIDEO", + NETWORK_HAR = "NETWORK_HAR", + VITALS_LOG = "VITALS_LOG", + CONSOLE_LOG = "CONSOLE_LOG", + NETWORK_LOG = "NETWORK_LOG", + DEVICE_LOG = "DEVICE_LOG", + SELENIUM_LOG = "SELENIUM_LOG", + SELENIUM_LOG_JSON = "SELENIUM_LOG_JSON", + BROWSER_LOG = "BROWSER_LOG", + FRAMEWORK_LOG = "FRAMEWORK_LOG", + EMAIL = "EMAIL", + PAGE_SOURCE = "PAGE_SOURCE", + CODE_BUNDLE = "CODE_BUNDLE", + RESULTS_ZIP = "RESULTS_ZIP", + SESSION_DETAILS = "SESSION_DETAILS", + DEVICE_DETAILS = "DEVICE_DETAILS", + UNKNOWN = "UNKNOWN" +} +/** + * Represents the configuration object for the Applause Reporter. + */ interface ApplauseConfig { readonly baseUrl: string; readonly apiKey: string; readonly productId: number; readonly testRailOptions?: TestRailOptions; readonly applauseTestCycleId?: number; + readonly logLevel?: string; } +/** + * The default base URL for the Applause API. + */ declare const DEFAULT_URL = "https://prod-auto-api.cloud.applause.com/"; +/** + * Represents the properties for loading the configuration. + */ interface ConfigLoadProperties { configFile?: string; properties?: Partial; } +/** + * Loads the configuration for the Applause Reporter. + * @param loadOptions - The options for loading the configuration. + * @returns The loaded Applause configuration. + * @throws Error if the configuration is not complete or invalid. + */ declare function loadConfig(loadOptions?: ConfigLoadProperties): ApplauseConfig; +/** + * Overrides the configuration with the provided overrides. + * @param config - The base configuration. + * @param overrides - The overrides to apply. + * @returns The overridden configuration. + */ declare function overrideConfig(config: Partial, overrides?: Partial): Partial; +/** + * Checks if the configuration is complete. + * @param config - The configuration to check. + * @returns True if the configuration is complete, false otherwise. + */ declare function isComplete(config: Partial): boolean; +/** + * Loads the configuration from the specified file. + * @param configFile - The path to the configuration file. + * @returns The loaded configuration from the file. + */ declare function loadConfigFromFile(configFile?: string): Partial; +/** + * Validates the configuration. + * @param config - The configuration to validate. + * @throws Error if the configuration is invalid. + */ declare function validateConfig(config: ApplauseConfig): void; +/** + * Validates a partial configuration. + * @param config - The partial configuration to validate. + * @throws Error if the partial configuration is invalid. + */ declare function validatePartialConfig(config: Partial): void; +/** + * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform. + * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management. + * It also includes properties and methods to track the number of HTTP calls in progress. + */ + declare class AutoApi { readonly options: ApplauseConfig; private readonly client; private callsInFlight; /** - * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished + * Tracks the number of HTTP calls in progress. + * This property is used by reporters that want to know when the async work is finished. */ get getCallsInFlight(): number; + /** + * Creates an instance of the `AutoApi` class. + * @param options - The configuration options for the Applause API. + */ constructor(options: ApplauseConfig); + /** + * Starts a new test run. + * @param info - The information for creating the test run. + * @returns A promise that resolves to the response containing the created test run. + */ startTestRun(info: TestRunCreateDto): Promise>; + /** + * Ends a test run. + * @param testRunId - The ID of the test run to end. + * @returns A promise that resolves to the response indicating the completion of the test run. + */ endTestRun(testRunId: number): Promise>; + /** + * Starts a new test case. + * @param params - The parameters for creating the test case. + * @returns A promise that resolves to the response containing the created test case. + */ startTestCase(params: CreateTestCaseResultDto): Promise>; + /** + * Submits a test case result. + * @param params - The parameters for submitting the test case result. + * @returns A promise that resolves when the test case result is submitted. + */ submitTestCaseResult(params: SubmitTestCaseResultDto): Promise; + /** + * Retrieves the provider session links for the specified test results. + * @param resultIds - The IDs of the test results. + * @returns A promise that resolves to the response containing the provider session links. + */ getProviderSessionLinks(resultIds: number[]): Promise>; + /** + * Sends a heartbeat for the specified test run. + * @param testRunId - The ID of the test run. + * @returns A promise that resolves to the response indicating the heartbeat was sent. + */ sendSdkHeartbeat(testRunId: number): Promise>; + /** + * Retrieves the email address for the specified email prefix. + * @param emailPrefix - The prefix of the email address. + * @returns A promise that resolves to the response containing the email address. + */ getEmailAddress(emailPrefix: string): Promise>; + /** + * Retrieves the content of the specified email. + * @param request - The request parameters for retrieving the email content. + * @returns A promise that resolves to the response containing the email content. + */ getEmailContent(request: EmailFetchRequest): Promise>; + /** + * Uploads an asset for the specified test result. + * @param resultId - The ID of the test result. + * @param file - The file to upload as an asset. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The GUID of the provider session. + * @param assetType - The type of the asset. + * @returns A promise that resolves to the response indicating the asset was uploaded. + */ + uploadAsset(resultId: number, file: Buffer, assetName: string, providerSessionGuid: string, assetType: AssetType): Promise>; } +/** + * Represents an email inbox. + */ declare class Inbox { readonly emailAddress: string; private autoApi; + /** + * Creates an instance of Inbox. + * @param emailAddress - The email address associated with the inbox. + * @param autoApi - An instance of the AutoApi class. + */ constructor(emailAddress: string, autoApi: AutoApi); + /** + * Retrieves the content of an email from the inbox. + * @returns A Promise that resolves to the parsed email content. + */ getEmail(): Promise; } +/** + * Represents an email attachment. + */ interface Attachment { + /** + * The name of the file. + */ fileName: string; + /** + * The content of the file as a Uint16Array. + */ context: Uint16Array; } +/** + * Helper class for managing email functionality. + */ declare class EmailHelper { private autoApi; constructor(autoApi: AutoApi); + /** + * Retrieves the inbox for the specified email prefix. + * + * @param emailPrefix - The prefix used to generate the email address. + * @returns A Promise that resolves to an Inbox object. + */ getInbox(emailPrefix: string): Promise; } +/** + * Represents a service for sending heartbeats during a test run. + */ declare class TestRunHeartbeatService { readonly testRunId: number; readonly autoApi: AutoApi; + readonly logger: winston.Logger; private enabled; private nextHeartbeat?; - constructor(testRunId: number, autoApi: AutoApi); + /** + * Creates an instance of TestRunHeartbeatService. + * @param testRunId - The ID of the test run. + * @param autoApi - The AutoApi instance used for sending heartbeats. + */ + constructor(testRunId: number, autoApi: AutoApi, logger: winston.Logger); + /** + * Starts sending heartbeats. + * @returns A promise that resolves when the heartbeats are started. + */ start(): Promise; + /** + * Checks if the heartbeats are enabled. + * @returns True if the heartbeats are enabled, false otherwise. + */ isEnabled(): boolean; private scheduleNextHeartbeat; private sendHeartbeat; + /** + * Ends the heartbeats. + * @returns A promise that resolves when the heartbeats are ended. + */ end(): Promise; } +/** + * Represents an Applause reporter. + */ declare class ApplauseReporter { + private logger?; private autoApi; private initializer; private reporter?; private runStarted; private runFinished; - constructor(config: ApplauseConfig); - runnerStart(tests?: string[]): void; - startTestCase(id: string, testCaseName: string, params?: AdditionalTestCaseParams): void; - submitTestCaseResult(id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams): void; + /** + * Creates an instance of ApplauseReporter. + * @param config - The Applause configuration. + */ + constructor(config: ApplauseConfig, logger?: winston.Logger | undefined); + /** + * Starts the Applause runner. + * @param tests - Optional array of test names to run. + * @returns A promise that resolves to the test run ID. + * @throws Error if a run is already started or finished. + */ + runnerStart(tests?: string[]): Promise; + /** + * Starts a test case. + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Optional additional parameters for the test case. + * @returns A promise that resolves to the test case ID. + * @throws Error if a run was never initialized. + */ + startTestCase(id: string, testCaseName: string, params?: AdditionalTestCaseParams): Promise; + /** + * Submits a test case result. + * @param id - The ID of the test case. + * @param status - The status of the test case result. + * @param params - Optional additional parameters for the test case result. + * @returns A promise that resolves to the test case result ID. + * @throws Error if a run was never initialized. + */ + submitTestCaseResult(id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams): Promise; + /** + * Ends the Applause runner. + * @returns A promise that resolves when the runner is ended. + * @throws Error if a run was never initialized. + */ runnerEnd(): Promise; + /** + * Attaches an asset to a test case. + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset data as a Buffer. + * @returns A promise that resolves when the asset is attached. + * @throws Error if a run was never initialized. + */ + attachTestCaseAsset(id: string, assetName: string, providerSessionGuid: string, assetType: AssetType, asset: Buffer): Promise; + /** + * Checks if the Applause runner is synchronized. + * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished. + */ isSynchronized(): boolean; } +/** + * Represents a Run Initializer. + */ declare class RunInitializer { private autoApi; - constructor(autoApi: AutoApi); + private logger; + constructor(autoApi: AutoApi, logger?: winston.Logger); + /** + * Initializes a test run. + * @param tests - An optional array of test names to include in the run. + * @returns A promise that resolves to a RunReporter instance. + * @throws An error if unable to create the test run. + */ initializeRun(tests?: string[]): Promise; } +/** + * Handles reporting test results to the Applause API. + */ declare class RunReporter { private autoApi; - private testRunId; - private heartbeatService; + readonly testRunId: number; + private heartbeatService?; private uidToResultIdMap; private resultSubmissionMap; - constructor(autoApi: AutoApi, testRunId: number, heartbeatService: TestRunHeartbeatService); - startTestCase(id: string, testCaseName: string, params?: AdditionalTestCaseParams): void; - submitTestCaseResult(id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams): void; + /** + * Creates a new instance of the Reporter class. + * @param autoApi - The AutoApi instance. + * @param testRunId - The ID of the test run. + * @param heartbeatService - (Optional) The TestRunHeartbeatService instance. + */ + constructor(autoApi: AutoApi, testRunId: number, heartbeatService?: TestRunHeartbeatService | undefined); + /** + * Starts a test case and returns a promise that resolves to the test result ID. + * + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Additional parameters for the test case. + * @returns A promise that resolves to the test result ID. + */ + startTestCase(id: string, testCaseName: string, params?: AdditionalTestCaseParams): Promise; + /** + * Submits the result of a test case. + * + * @param id - The ID of the test case. + * @param status - The status of the test result. + * @param params - Additional parameters for the test result. + * @returns A promise that resolves to the result ID. + */ + submitTestCaseResult(id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams): Promise; + /** + * Attaches a test case asset to a result. + * + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset to attach. + * @returns A promise that resolves when the asset is attached. + */ + attachTestCaseAsset(id: string, assetName: string, providerSessionGuid: string, assetType: AssetType, asset: Buffer): Promise; + /** + * Ends the test runner and performs necessary cleanup tasks. + * @returns A promise that resolves when the runner has ended. + */ runnerEnd(): Promise; } -export { type AdditionalTestCaseParams, type AdditionalTestCaseResultParams, type ApplauseConfig, ApplauseReporter, type Attachment, AutoApi, type ClientConfig, type ConfigLoadProperties, type CreateTestCaseResultDto, type CreateTestCaseResultResponseDto, DEFAULT_URL, type EmailAddressResponse, type EmailFetchRequest, EmailHelper, Inbox, RunInitializer, RunReporter, type SubmitTestCaseResultDto, type TestRailOptions, type TestResultProviderInfo, TestResultStatus, type TestRunCreateDto, type TestRunCreateResponseDto, TestRunHeartbeatService, isComplete, loadConfig, loadConfigFromFile, overrideConfig, validateConfig, validatePartialConfig }; +export { type AdditionalTestCaseParams, type AdditionalTestCaseResultParams, type ApplauseConfig, ApplauseReporter, AssetType, type Attachment, AutoApi, type ClientConfig, type ConfigLoadProperties, type CreateTestCaseResultDto, type CreateTestCaseResultResponseDto, DEFAULT_URL, type EmailAddressResponse, type EmailFetchRequest, EmailHelper, Inbox, RunInitializer, RunReporter, type SubmitTestCaseResultDto, type TestRailOptions, type TestResultProviderInfo, TestResultStatus, type TestRunCreateDto, type TestRunCreateResponseDto, TestRunHeartbeatService, isComplete, loadConfig, loadConfigFromFile, overrideConfig, validateConfig, validatePartialConfig }; diff --git a/dist/index.min.js b/dist/index.min.js index be516db..c1bb38b 100644 --- a/dist/index.min.js +++ b/dist/index.min.js @@ -1,2 +1,2 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("axios"),require("fs"),require("path"),require("validator"),require("mailparser")):"function"==typeof define&&define.amd?define(["exports","axios","fs","path","validator","mailparser"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["applause-reporter-common"]={},t.axios,t.fs,t.path,t.Validator,t.mailparser)}(this,(function(t,e,s,i,a,r){"use strict";const n=a.default,o="https://prod-auto-api.cloud.applause.com/";function l(t,e){return Object.assign({},t,Object.fromEntries(Object.entries(e||{}).filter((([t,e])=>void 0!==e))))}function u(t){return void 0!==t.baseUrl&&void 0!==t.apiKey&&void 0!==t.productId}function d(t){const e=t||process.cwd()+"/applause.json";if(!s.existsSync(e))return{};const i=s.readFileSync(e,"utf8");return JSON.parse(i)}function p(t){if(!Number.isInteger(t.productId)||t.productId<=0)throw new Error(`productId must be a positive integer, was: '${t.productId}'`);if(!n.isURL(t.baseUrl,{protocols:["http","https"],require_tld:!1,allow_query_components:!1,disallow_auth:!0,allow_fragments:!1,allow_protocol_relative_urls:!1,allow_trailing_dot:!1,require_host:!0,require_protocol:!0}))throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${t.baseUrl}`);if(n.isEmpty(t.apiKey))throw new Error("apiKey is an empty string!")}class c{options;client;callsInFlight;get getCallsInFlight(){return this.callsInFlight}constructor(t){this.options=t,this.callsInFlight=0,p(t),this.client=e.create({baseURL:t.baseUrl,timeout:1e4,headers:{"X-Api-Key":t.apiKey,"Context-Type":"application/json"},responseType:"json"}),this.client.interceptors.response.use((function(t){return t}),(function(t){const e=void 0!==t.data?t.data:`error-code [${t.response.status}] with error [${t.response.statusText}]`;return console.error(`Auto-Api returned ${e}`),Promise.reject(t)}))}async startTestRun(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/test-run/create",{...t,sdkVersion:"js:1.0.0",productId:this.options.productId,testRailReportingEnabled:void 0!==this.options.testRailOptions,addAllTestsToPlan:this.options.testRailOptions?.addAllTestsToPlan,testRailProjectId:this.options.testRailOptions?.projectId,testRailSuiteId:this.options.testRailOptions?.suiteId,testRailPlanName:this.options.testRailOptions?.planName,testRailRunName:this.options.testRailOptions?.runName,overrideTestRailRunNameUniqueness:this.options.testRailOptions?.overrideTestRailRunUniqueness})}finally{this.callsInFlight-=1}}async endTestRun(t){this.callsInFlight+=1;try{return await this.client.delete(`/api/v1.0/test-run/${t}?endingStatus=COMPLETE`)}finally{this.callsInFlight-=1}}async startTestCase(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/test-result/create-result",t)}finally{this.callsInFlight-=1}}async submitTestCaseResult(t){this.callsInFlight+=1;try{await this.client.post("/api/v1.0/test-result",t)}finally{this.callsInFlight-=1}}async getProviderSessionLinks(t){this.callsInFlight+=1;try{const e=t.filter((t=>t));return await this.client.post("/api/v1.0/test-result/provider-info",e)}finally{this.callsInFlight-=1}}async sendSdkHeartbeat(t){this.callsInFlight+=1;try{return await this.client.post("/api/v2.0/sdk-heartbeat",{testRunId:t})}finally{this.callsInFlight-=1}}async getEmailAddress(t){this.callsInFlight+=1;try{return await this.client.get(`/api/v1.0/email/get-address?prefix=${t}`)}finally{this.callsInFlight-=1}}async getEmailContent(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/email/download-email",t)}finally{this.callsInFlight-=1}}}var h;t.TestResultStatus=void 0,(h=t.TestResultStatus||(t.TestResultStatus={})).NOT_RUN="NOT_RUN",h.IN_PROGRESS="IN_PROGRESS",h.PASSED="PASSED",h.FAILED="FAILED",h.SKIPPED="SKIPPED",h.CANCELED="CANCELED",h.ERROR="ERROR";class R{emailAddress;autoApi;constructor(t,e){this.emailAddress=t,this.autoApi=e}async getEmail(){const t=await this.autoApi.getEmailContent({emailAddress:this.emailAddress});return await r.simpleParser(t.data)}}class f{testRunId;autoApi;enabled=!1;nextHeartbeat;constructor(t,e){this.testRunId=t,this.autoApi=e}async start(){await this.end(),this.enabled=!0,this.scheduleNextHeartbeat()}isEnabled(){return this.enabled}scheduleNextHeartbeat(){this.enabled&&(this.nextHeartbeat=new Promise((t=>setTimeout(t,5e3))).then((()=>this.sendHeartbeat())))}async sendHeartbeat(){console.log("Sending heartbeat"),await this.autoApi.sendSdkHeartbeat(this.testRunId),console.log("Heartbeat sent"),this.scheduleNextHeartbeat()}async end(){void 0!==this.nextHeartbeat&&(this.enabled=!1,console.debug("Ending Applause SDK Heartbeat"),await this.nextHeartbeat,console.debug("Applause SDK Heartbeat Ended Successfully")),this.nextHeartbeat=void 0}}class I{autoApi;constructor(t){this.autoApi=t}async initializeRun(t){const e=t?.map(w).map((t=>t.testCaseName.trim())),s=await this.autoApi.startTestRun({tests:e||[]});if(s.status<200||s.status>300)throw new Error("Unable to create test run");const i=s.data.runId;console.log("Test Run %d initialized",i);const a=new f(i,this.autoApi);return await a.start(),new g(this.autoApi,i,a)}}class g{autoApi;testRunId;heartbeatService;uidToResultIdMap={};resultSubmissionMap={};constructor(t,e,s){this.autoApi=t,this.testRunId=e,this.heartbeatService=s}startTestCase(t,e,s){const i=w(e);this.uidToResultIdMap[t]=this.autoApi.startTestCase({testCaseName:i.testCaseName,testCaseId:i.testRailTestCaseId,itwTestCaseId:i.applauseTestCaseId,testRunId:this.testRunId,...Object.fromEntries(Object.entries(s||{}).filter((([t,e])=>void 0!==e)))}).then((t=>t.data.testResultId))}submitTestCaseResult(t,e,s){this.resultSubmissionMap[t]=this.uidToResultIdMap[t]?.then((t=>this.autoApi.submitTestCaseResult({status:e,testResultId:t,...s})))}async runnerEnd(){const t=await Promise.all(Object.values(this.uidToResultIdMap))||[];await Promise.all(Object.values(this.resultSubmissionMap)),await this.heartbeatService.end(),await this.autoApi.endTestRun(this.testRunId);const e=(await this.autoApi.getProviderSessionLinks(t)).data||[];if(e.length>0){console.info(JSON.stringify(e));const t=".";s.writeFileSync(i.join(t,"providerUrls.txt"),JSON.stringify(e,null,1))}}}const m="TestRail-",b="Applause-";function w(t){const e=t.split(" ");let s,i;return e.forEach((t=>{t?.startsWith(m)?(void 0!==s&&console.warn("Multiple TestRail case ids detected in testCase name"),s=t.substring(m.length)):t?.startsWith(b)&&(void 0!==i&&console.warn("Multiple Applause case ids detected in testCase name"),i=t.substring(b.length))})),{applauseTestCaseId:i,testRailTestCaseId:s,testCaseName:e.filter((t=>t!==`${m}${s||""}`)).filter((t=>t!==`${b}${i||""}`)).join(" ").trim()}}t.ApplauseReporter=class{autoApi;initializer;reporter;runStarted=!1;runFinished=!1;constructor(t){this.autoApi=new c(t),this.initializer=new I(this.autoApi)}runnerStart(t){this.reporter=this.initializer.initializeRun(t),this.reporter.then((()=>{this.runStarted=!0}))}startTestCase(t,e,s){if(void 0===this.reporter)throw new Error("Cannot start a test case for a run that was never initialized");this.reporter.then((i=>i.startTestCase(t,e,s)))}submitTestCaseResult(t,e,s){if(void 0===this.reporter)throw new Error("Cannot submit test case result for a run that was never initialized");this.reporter.then((i=>i.submitTestCaseResult(t,e,s)))}async runnerEnd(){if(void 0===this.reporter)throw new Error("Cannot end a run that was never initialized");await this.reporter.then((t=>t.runnerEnd())).then((()=>this.runFinished=!0))}isSynchronized(){return(!this.runStarted||this.runStarted&&this.runFinished)&&0==this.autoApi.getCallsInFlight}},t.AutoApi=c,t.DEFAULT_URL=o,t.EmailHelper=class{autoApi;constructor(t){this.autoApi=t}async getInbox(t){const e=(await this.autoApi.getEmailAddress(t)).data.emailAddress;return new R(e,this.autoApi)}},t.Inbox=R,t.RunInitializer=I,t.RunReporter=g,t.TestRunHeartbeatService=f,t.isComplete=u,t.loadConfig=function(t){let e={baseUrl:o};if(e=void 0!==t&&void 0!==t.configFile?l(e,d(i.join(process.cwd(),t.configFile))):l(e,d()),void 0!==t&&void 0!==t.properties&&(e=l(e,t.properties)),!u(e))throw new Error("Config is not complete");const s=e;return p(s),s},t.loadConfigFromFile=d,t.overrideConfig=l,t.validateConfig=p,t.validatePartialConfig=function(t){if(void 0!==t.productId&&(!Number.isInteger(t.productId)||t.productId<=0))throw new Error(`productId must be a positive integer, was: '${t.productId}'`)}})); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("axios"),require("fs"),require("path"),require("validator"),require("mailparser"),require("winston"),require("winston-transport")):"function"==typeof define&&define.amd?define(["exports","axios","fs","path","validator","mailparser","winston","winston-transport"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["applause-reporter-common"]={},t.axios,t.fs,t.path,t.Validator,t.mailparser,t.winston,t.Transport)}(this,(function(t,e,s,i,a,r,n,o){"use strict";const l=a.default,u="https://prod-auto-api.cloud.applause.com/";function d(t,e){return Object.assign({},t,Object.fromEntries(Object.entries(e||{}).filter((([t,e])=>void 0!==e))))}function h(t){return void 0!==t.baseUrl&&void 0!==t.apiKey&&void 0!==t.productId}function p(t){const e=t||process.cwd()+"/applause.json";if(!s.existsSync(e))return{};const i=s.readFileSync(e,"utf8");return JSON.parse(i)}function c(t){if(!Number.isInteger(t.productId)||t.productId<=0)throw new Error(`productId must be a positive integer, was: '${t.productId}'`);if(!l.isURL(t.baseUrl,{protocols:["http","https"],require_tld:!1,allow_query_components:!1,disallow_auth:!0,allow_fragments:!1,allow_protocol_relative_urls:!1,allow_trailing_dot:!1,require_host:!0,require_protocol:!0}))throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${t.baseUrl}`);if(l.isEmpty(t.apiKey))throw new Error("apiKey is an empty string!")}class E{options;client;callsInFlight;get getCallsInFlight(){return this.callsInFlight}constructor(t){this.options=t,this.callsInFlight=0,c(t),this.client=e.create({baseURL:t.baseUrl,timeout:1e4,headers:{"X-Api-Key":t.apiKey,"Context-Type":"application/json"},responseType:"json"}),this.client.interceptors.response.use((function(t){return t}),(function(t){const e=void 0!==t.data?t.data:`error-code [${t.response.status}] with error [${t.response.statusText}]`;return console.error(`Auto-Api returned ${e}`),Promise.reject(t)}))}async startTestRun(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/test-run/create",{...t,sdkVersion:"js:1.1.0",productId:this.options.productId,testRailReportingEnabled:void 0!==this.options.testRailOptions,addAllTestsToPlan:this.options.testRailOptions?.addAllTestsToPlan,testRailProjectId:this.options.testRailOptions?.projectId,testRailSuiteId:this.options.testRailOptions?.suiteId,testRailPlanName:this.options.testRailOptions?.planName,testRailRunName:this.options.testRailOptions?.runName,overrideTestRailRunNameUniqueness:this.options.testRailOptions?.overrideTestRailRunUniqueness})}finally{this.callsInFlight-=1}}async endTestRun(t){this.callsInFlight+=1;try{return await this.client.delete(`/api/v1.0/test-run/${t}?endingStatus=COMPLETE`)}finally{this.callsInFlight-=1}}async startTestCase(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/test-result/create-result",t)}finally{this.callsInFlight-=1}}async submitTestCaseResult(t){this.callsInFlight+=1;try{await this.client.post("/api/v1.0/test-result",t)}finally{this.callsInFlight-=1}}async getProviderSessionLinks(t){this.callsInFlight+=1;try{const e=t.filter((t=>t));return await this.client.post("/api/v1.0/test-result/provider-info",e)}finally{this.callsInFlight-=1}}async sendSdkHeartbeat(t){this.callsInFlight+=1;try{return await this.client.post("/api/v2.0/sdk-heartbeat",{testRunId:t})}finally{this.callsInFlight-=1}}async getEmailAddress(t){this.callsInFlight+=1;try{return await this.client.get(`/api/v1.0/email/get-address?prefix=${t}`)}finally{this.callsInFlight-=1}}async getEmailContent(t){this.callsInFlight+=1;try{return await this.client.post("/api/v1.0/email/download-email",t)}finally{this.callsInFlight-=1}}async uploadAsset(t,e,s,i,a){this.callsInFlight+=1;try{return await this.client.postForm(`/api/v1.0/test-result/${t}/upload`,{file:e,assetName:s,providerSessionGuid:i,assetType:a})}finally{this.callsInFlight-=1}}}var g,I;t.TestResultStatus=void 0,(g=t.TestResultStatus||(t.TestResultStatus={})).NOT_RUN="NOT_RUN",g.IN_PROGRESS="IN_PROGRESS",g.PASSED="PASSED",g.FAILED="FAILED",g.SKIPPED="SKIPPED",g.CANCELED="CANCELED",g.ERROR="ERROR",t.AssetType=void 0,(I=t.AssetType||(t.AssetType={})).SCREENSHOT="SCREENSHOT",I.FAILURE_SCREENSHOT="FAILURE_SCREENSHOT",I.VIDEO="VIDEO",I.NETWORK_HAR="NETWORK_HAR",I.VITALS_LOG="VITALS_LOG",I.CONSOLE_LOG="CONSOLE_LOG",I.NETWORK_LOG="NETWORK_LOG",I.DEVICE_LOG="DEVICE_LOG",I.SELENIUM_LOG="SELENIUM_LOG",I.SELENIUM_LOG_JSON="SELENIUM_LOG_JSON",I.BROWSER_LOG="BROWSER_LOG",I.FRAMEWORK_LOG="FRAMEWORK_LOG",I.EMAIL="EMAIL",I.PAGE_SOURCE="PAGE_SOURCE",I.CODE_BUNDLE="CODE_BUNDLE",I.RESULTS_ZIP="RESULTS_ZIP",I.SESSION_DETAILS="SESSION_DETAILS",I.DEVICE_DETAILS="DEVICE_DETAILS",I.UNKNOWN="UNKNOWN";class R{emailAddress;autoApi;constructor(t,e){this.emailAddress=t,this.autoApi=e}async getEmail(){const t=await this.autoApi.getEmailContent({emailAddress:this.emailAddress});return await r.simpleParser(t.data)}}class w{testRunId;autoApi;logger;enabled=!1;nextHeartbeat;constructor(t,e,s){this.testRunId=t,this.autoApi=e,this.logger=s}async start(){await this.end(),this.enabled=!0,this.scheduleNextHeartbeat()}isEnabled(){return this.enabled}scheduleNextHeartbeat(){this.enabled&&(this.nextHeartbeat=new Promise((t=>setTimeout(t,5e3))).then((()=>this.sendHeartbeat())))}async sendHeartbeat(){console.log("Sending heartbeat"),await this.autoApi.sendSdkHeartbeat(this.testRunId),console.log("Heartbeat sent"),this.scheduleNextHeartbeat()}async end(){void 0!==this.nextHeartbeat&&(this.enabled=!1,console.debug("Ending Applause SDK Heartbeat"),await this.nextHeartbeat,console.debug("Applause SDK Heartbeat Ended Successfully")),this.nextHeartbeat=void 0}}const S=new class{logs=[];getLogs(){return this.logs}drainLogs(){const t=this.logs;return this.clearLogs(),t}clearLogs(){this.logs=[]}addLog(t){this.logs.push(t)}};class A extends o{constructor(){super({})}log(t,e){S.addLog(t.message),e()}}const f="TestRail-",T="Applause-";class m{autoApi;logger;constructor(t,e){this.autoApi=t,this.logger=e||n.createLogger({transports:[new n.transports.Console,new A]})}async initializeRun(t){const e=t?.map(y).map((t=>t.testCaseName.trim())),s=await this.autoApi.startTestRun({tests:e||[]});if(s.status<200||s.status>300)throw new Error("Unable to create test run");const i=s.data.runId;this.logger.info("Test Run %d initialized",i);const a=new w(i,this.autoApi,this.logger);return await a.start(),new O(this.autoApi,i,a)}}class O{autoApi;testRunId;heartbeatService;uidToResultIdMap={};resultSubmissionMap={};constructor(t,e,s){this.autoApi=t,this.testRunId=e,this.heartbeatService=s}startTestCase(t,e,s){const i=y(e),a=this.autoApi.startTestCase({testCaseName:i.testCaseName,testCaseId:i.testRailTestCaseId,itwTestCaseId:i.applauseTestCaseId,testRunId:this.testRunId,...Object.fromEntries(Object.entries(s||{}).filter((([t,e])=>void 0!==e)))}).then((t=>t.data.testResultId));return this.uidToResultIdMap[t]=a,a}submitTestCaseResult(t,e,s){const i=this.uidToResultIdMap[t]?.then((t=>this.autoApi.submitTestCaseResult({status:e,testResultId:t,...s}).then((()=>t))));return this.resultSubmissionMap[t]=i,i}async attachTestCaseAsset(t,e,s,i,a){await(this.uidToResultIdMap[t]?.then((t=>this.autoApi.uploadAsset(t,a,e,s,i))))}async runnerEnd(){const t=await Promise.all(Object.values(this.uidToResultIdMap))||[];await Promise.all(Object.values(this.resultSubmissionMap)),await(this.heartbeatService?.end()),await this.autoApi.endTestRun(this.testRunId);const e=(await this.autoApi.getProviderSessionLinks(t)).data||[];if(e.length>0){console.info(JSON.stringify(e));const t=".";s.writeFileSync(i.join(t,"providerUrls.txt"),JSON.stringify(e,null,1))}}}function y(t){const e=t.split(" ");let s,i;return e.forEach((t=>{t?.startsWith(f)?(void 0!==s&&console.warn("Multiple TestRail case ids detected in testCase name"),s=t.substring(9)):t?.startsWith(T)&&(void 0!==i&&console.warn("Multiple Applause case ids detected in testCase name"),i=t.substring(9))})),{applauseTestCaseId:i,testRailTestCaseId:s,testCaseName:e.filter((t=>t!==`${f}${s||""}`)).filter((t=>t!==`${T}${i||""}`)).join(" ").trim()}}t.ApplauseReporter=class{logger;autoApi;initializer;reporter;runStarted=!1;runFinished=!1;constructor(t,e){this.logger=e,this.autoApi=new E(t),this.initializer=new m(this.autoApi,this.logger);const s=process.env.APPLAUSE_RUN_ID;if(void 0!==s){const t=new O(this.autoApi,parseInt(s));this.reporter=new Promise((e=>e(t))),this.runStarted=!0}}async runnerStart(t){if(void 0!==this.reporter)throw new Error("Cannot start a run - run already started or run already finished");this.reporter=this.initializer.initializeRun(t);const e=await this.reporter;return this.runStarted=!0,process.env.APPLAUSE_RUN_ID=e.testRunId.toString(),e.testRunId}async startTestCase(t,e,s){if(void 0===this.reporter)throw new Error("Cannot start a test case for a run that was never initialized");return(await this.reporter).startTestCase(t,e,s)}async submitTestCaseResult(t,e,s){if(void 0===this.reporter)throw new Error("Cannot submit test case result for a run that was never initialized");return(await this.reporter).submitTestCaseResult(t,e,s)}async runnerEnd(){if(void 0===this.reporter)throw new Error("Cannot end a run that was never initialized");await this.reporter.then((t=>t.runnerEnd())).then((()=>this.runFinished=!0))}async attachTestCaseAsset(t,e,s,i,a){if(void 0===this.reporter)throw new Error("Cannot attach an asset for a run that was never initialized");return await this.reporter.then((r=>r.attachTestCaseAsset(t,e,s,i,a)))}isSynchronized(){return(!this.runStarted||this.runStarted&&this.runFinished)&&0==this.autoApi.getCallsInFlight}},t.AutoApi=E,t.DEFAULT_URL=u,t.EmailHelper=class{autoApi;constructor(t){this.autoApi=t}async getInbox(t){const e=(await this.autoApi.getEmailAddress(t)).data.emailAddress;return new R(e,this.autoApi)}},t.Inbox=R,t.RunInitializer=m,t.RunReporter=O,t.TestRunHeartbeatService=w,t.isComplete=h,t.loadConfig=function(t){let e={baseUrl:u};if(e=void 0!==t&&void 0!==t.configFile?d(e,p(i.join(process.cwd(),t.configFile))):d(e,p()),void 0!==t&&void 0!==t.properties&&(e=d(e,t.properties)),!h(e))throw new Error("Config is not complete");const s=e;return c(s),s},t.loadConfigFromFile=p,t.overrideConfig=d,t.validateConfig=c,t.validatePartialConfig=function(t){if(void 0!==t.productId&&(!Number.isInteger(t.productId)||t.productId<=0))throw new Error(`productId must be a positive integer, was: '${t.productId}'`)}})); //# sourceMappingURL=index.min.js.map diff --git a/dist/index.min.js.map b/dist/index.min.js.map index c4b087b..7aeeb83 100644 --- a/dist/index.min.js.map +++ b/dist/index.min.js.map @@ -1 +1 @@ -{"version":3,"file":"index.min.js","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/heartbeat.ts","../src/reporter.ts","../src/email-helper.ts"],"sourcesContent":["export const API_VERSION = '1.0.0';\n//# sourceMappingURL=version.js.map","import { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n// Loads the configuration\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileCotents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileCotents);\n}\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","import axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\nexport class Inbox {\n emailAddress;\n autoApi;\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","export class TestRunHeartbeatService {\n testRunId;\n autoApi;\n enabled = false;\n nextHeartbeat;\n constructor(testRunId, autoApi) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n }\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up va new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nexport class ApplauseReporter {\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n constructor(config) {\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi);\n }\n runnerStart(tests) {\n this.reporter = this.initializer.initializeRun(tests);\n void this.reporter.then(() => {\n this.runStarted = true;\n });\n }\n startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.startTestCase(id, testCaseName, params));\n }\n submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.submitTestCaseResult(id, status, params));\n }\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n isSynchronized() {\n // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\nexport class RunInitializer {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n console.log('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n this.uidToResultIdMap[id] = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n }\n submitTestCaseResult(id, status, params) {\n this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => this.autoApi.submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n }));\n }\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map","import { Inbox } from './email/inbox.ts';\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map"],"names":["validator","Validator","default","DEFAULT_URL","overrideConfig","config","overrides","Object","assign","fromEntries","entries","filter","_","v","undefined","isComplete","baseUrl","apiKey","productId","loadConfigFromFile","configFile","configFilePath","process","cwd","existsSync","fileCotents","readFileSync","JSON","parse","validateConfig","Number","isInteger","Error","isURL","protocols","require_tld","allow_query_components","disallow_auth","allow_fragments","allow_protocol_relative_urls","allow_trailing_dot","require_host","require_protocol","isEmpty","AutoApi","options","client","callsInFlight","getCallsInFlight","this","constructor","axios","create","baseURL","timeout","headers","responseType","interceptors","response","use","error","errText","data","status","statusText","console","Promise","reject","startTestRun","info","post","sdkVersion","testRailReportingEnabled","testRailOptions","addAllTestsToPlan","testRailProjectId","projectId","testRailSuiteId","suiteId","testRailPlanName","planName","testRailRunName","runName","overrideTestRailRunNameUniqueness","overrideTestRailRunUniqueness","endTestRun","testRunId","delete","startTestCase","params","submitTestCaseResult","getProviderSessionLinks","resultIds","validIds","id","sendSdkHeartbeat","getEmailAddress","emailPrefix","get","getEmailContent","request","TestResultStatus","exports","Inbox","emailAddress","autoApi","getEmail","res","simpleParser","TestRunHeartbeatService","enabled","nextHeartbeat","start","end","scheduleNextHeartbeat","isEnabled","resolve","setTimeout","then","sendHeartbeat","log","debug","RunInitializer","initializeRun","tests","cleanedTests","map","parseTestCaseName","parsed","testCaseName","trim","testRunCreateResponse","runId","heartbeatService","RunReporter","uidToResultIdMap","resultSubmissionMap","parsedTestCase","testCaseId","testRailTestCaseId","itwTestCaseId","applauseTestCaseId","testResultId","resultId","runnerEnd","all","values","jsonArray","length","stringify","outputPath","writeFileSync","pathJoin","join","TEST_RAIL_CASE_ID_PREFIX","APPLAUSE_CASE_ID_PREFIX","tokens","split","forEach","token","startsWith","warn","substring","initializer","reporter","runStarted","runFinished","runnerStart","isSynchronized","getInbox","generatedAddress","loadOptions","path","properties","finalConfig"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.min.js","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/heartbeat.ts","../src/logging.ts","../src/reporter.ts","../src/email-helper.ts"],"sourcesContent":["export const API_VERSION = '1.1.0';\n//# sourceMappingURL=version.js.map","/**\n * Represents the configuration options for the Applause Reporter.\n */\nimport { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\n/**\n * The default base URL for the Applause API.\n */\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n/**\n * Loads the configuration for the Applause Reporter.\n * @param loadOptions - The options for loading the configuration.\n * @returns The loaded Applause configuration.\n * @throws Error if the configuration is not complete or invalid.\n */\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\n/**\n * Overrides the configuration with the provided overrides.\n * @param config - The base configuration.\n * @param overrides - The overrides to apply.\n * @returns The overridden configuration.\n */\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\n/**\n * Checks if the configuration is complete.\n * @param config - The configuration to check.\n * @returns True if the configuration is complete, false otherwise.\n */\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\n/**\n * Loads the configuration from the specified file.\n * @param configFile - The path to the configuration file.\n * @returns The loaded configuration from the file.\n */\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileContents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileContents);\n}\n/**\n * Validates the configuration.\n * @param config - The configuration to validate.\n * @throws Error if the configuration is invalid.\n */\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\n/**\n * Validates a partial configuration.\n * @param config - The partial configuration to validate.\n * @throws Error if the partial configuration is invalid.\n */\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","/**\n * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform.\n * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management.\n * It also includes properties and methods to track the number of HTTP calls in progress.\n */\nimport axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * Tracks the number of HTTP calls in progress.\n * This property is used by reporters that want to know when the async work is finished.\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n /**\n * Creates an instance of the `AutoApi` class.\n * @param options - The configuration options for the Applause API.\n */\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n /**\n * Starts a new test run.\n * @param info - The information for creating the test run.\n * @returns A promise that resolves to the response containing the created test run.\n */\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Ends a test run.\n * @param testRunId - The ID of the test run to end.\n * @returns A promise that resolves to the response indicating the completion of the test run.\n */\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Starts a new test case.\n * @param params - The parameters for creating the test case.\n * @returns A promise that resolves to the response containing the created test case.\n */\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Submits a test case result.\n * @param params - The parameters for submitting the test case result.\n * @returns A promise that resolves when the test case result is submitted.\n */\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the provider session links for the specified test results.\n * @param resultIds - The IDs of the test results.\n * @returns A promise that resolves to the response containing the provider session links.\n */\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Sends a heartbeat for the specified test run.\n * @param testRunId - The ID of the test run.\n * @returns A promise that resolves to the response indicating the heartbeat was sent.\n */\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the email address for the specified email prefix.\n * @param emailPrefix - The prefix of the email address.\n * @returns A promise that resolves to the response containing the email address.\n */\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the content of the specified email.\n * @param request - The request parameters for retrieving the email content.\n * @returns A promise that resolves to the response containing the email content.\n */\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Uploads an asset for the specified test result.\n * @param resultId - The ID of the test result.\n * @param file - The file to upload as an asset.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The GUID of the provider session.\n * @param assetType - The type of the asset.\n * @returns A promise that resolves to the response indicating the asset was uploaded.\n */\n async uploadAsset(resultId, file, assetName, providerSessionGuid, assetType) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.postForm(`/api/v1.0/test-result/${resultId}/upload`, {\n file,\n assetName,\n providerSessionGuid,\n assetType,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\nexport var AssetType;\n(function (AssetType) {\n AssetType[\"SCREENSHOT\"] = \"SCREENSHOT\";\n AssetType[\"FAILURE_SCREENSHOT\"] = \"FAILURE_SCREENSHOT\";\n AssetType[\"VIDEO\"] = \"VIDEO\";\n AssetType[\"NETWORK_HAR\"] = \"NETWORK_HAR\";\n AssetType[\"VITALS_LOG\"] = \"VITALS_LOG\";\n AssetType[\"CONSOLE_LOG\"] = \"CONSOLE_LOG\";\n AssetType[\"NETWORK_LOG\"] = \"NETWORK_LOG\";\n AssetType[\"DEVICE_LOG\"] = \"DEVICE_LOG\";\n AssetType[\"SELENIUM_LOG\"] = \"SELENIUM_LOG\";\n AssetType[\"SELENIUM_LOG_JSON\"] = \"SELENIUM_LOG_JSON\";\n AssetType[\"BROWSER_LOG\"] = \"BROWSER_LOG\";\n AssetType[\"FRAMEWORK_LOG\"] = \"FRAMEWORK_LOG\";\n AssetType[\"EMAIL\"] = \"EMAIL\";\n AssetType[\"PAGE_SOURCE\"] = \"PAGE_SOURCE\";\n AssetType[\"CODE_BUNDLE\"] = \"CODE_BUNDLE\";\n AssetType[\"RESULTS_ZIP\"] = \"RESULTS_ZIP\";\n AssetType[\"SESSION_DETAILS\"] = \"SESSION_DETAILS\";\n AssetType[\"DEVICE_DETAILS\"] = \"DEVICE_DETAILS\";\n AssetType[\"UNKNOWN\"] = \"UNKNOWN\";\n})(AssetType || (AssetType = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\n/**\n * Represents an email inbox.\n */\nexport class Inbox {\n emailAddress;\n autoApi;\n /**\n * Creates an instance of Inbox.\n * @param emailAddress - The email address associated with the inbox.\n * @param autoApi - An instance of the AutoApi class.\n */\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the content of an email from the inbox.\n * @returns A Promise that resolves to the parsed email content.\n */\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","/**\n * Represents a service for sending heartbeats during a test run.\n */\nexport class TestRunHeartbeatService {\n testRunId;\n autoApi;\n logger;\n enabled = false;\n nextHeartbeat;\n /**\n * Creates an instance of TestRunHeartbeatService.\n * @param testRunId - The ID of the test run.\n * @param autoApi - The AutoApi instance used for sending heartbeats.\n */\n constructor(testRunId, autoApi, logger) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n this.logger = logger;\n }\n /**\n * Starts sending heartbeats.\n * @returns A promise that resolves when the heartbeats are started.\n */\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up a new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n /**\n * Checks if the heartbeats are enabled.\n * @returns True if the heartbeats are enabled, false otherwise.\n */\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n /**\n * Ends the heartbeats.\n * @returns A promise that resolves when the heartbeats are ended.\n */\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import Transport from 'winston-transport';\n/**\n * A simple Class for storing and retrieving log messages.\n */\nexport class LoggingContainer {\n logs = [];\n /**\n * Retrieves all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n getLogs() {\n return this.logs;\n }\n /**\n * Retrieves and clears all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n drainLogs() {\n const logs = this.logs;\n this.clearLogs();\n return logs;\n }\n /**\n * Clears all logs stored in the container.\n */\n clearLogs() {\n this.logs = [];\n }\n /**\n * Adds a log message to the container.\n *\n * @param log - The log message to add.\n */\n addLog(log) {\n this.logs.push(log);\n }\n}\n// Create a new Shared LoggingContainer to store logs\nexport const APPLAUSE_LOG_RECORDS = new LoggingContainer();\n/**\n * A Custom Winston Transport that sends logs to the Applause LoggingContainer\n */\nexport class ApplauseTransport extends Transport {\n constructor() {\n super({});\n }\n log(info, callback) {\n APPLAUSE_LOG_RECORDS.addLog(info.message);\n // Continue to the next transport\n callback();\n }\n}\n//# sourceMappingURL=logging.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nimport winston from 'winston';\nimport { ApplauseTransport } from './logging.ts';\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\n/**\n * Represents an Applause reporter.\n */\nexport class ApplauseReporter {\n logger;\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n /**\n * Creates an instance of ApplauseReporter.\n * @param config - The Applause configuration.\n */\n constructor(config, logger) {\n this.logger = logger;\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi, this.logger);\n const runId = process.env['APPLAUSE_RUN_ID'];\n if (runId !== undefined) {\n const r = new RunReporter(this.autoApi, parseInt(runId));\n this.reporter = new Promise(resolve => resolve(r));\n this.runStarted = true;\n }\n }\n /**\n * Starts the Applause runner.\n * @param tests - Optional array of test names to run.\n * @returns A promise that resolves to the test run ID.\n * @throws Error if a run is already started or finished.\n */\n async runnerStart(tests) {\n if (this.reporter !== undefined) {\n throw new Error('Cannot start a run - run already started or run already finished');\n }\n this.reporter = this.initializer.initializeRun(tests);\n const initializedReporter = await this.reporter;\n this.runStarted = true;\n process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString();\n return initializedReporter.testRunId;\n }\n /**\n * Starts a test case.\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Optional additional parameters for the test case.\n * @returns A promise that resolves to the test case ID.\n * @throws Error if a run was never initialized.\n */\n async startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.startTestCase(id, testCaseName, params);\n }\n /**\n * Submits a test case result.\n * @param id - The ID of the test case.\n * @param status - The status of the test case result.\n * @param params - Optional additional parameters for the test case result.\n * @returns A promise that resolves to the test case result ID.\n * @throws Error if a run was never initialized.\n */\n async submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.submitTestCaseResult(id, status, params);\n }\n /**\n * Ends the Applause runner.\n * @returns A promise that resolves when the runner is ended.\n * @throws Error if a run was never initialized.\n */\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n /**\n * Attaches an asset to a test case.\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset data as a Buffer.\n * @returns A promise that resolves when the asset is attached.\n * @throws Error if a run was never initialized.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n if (this.reporter === undefined) {\n throw new Error('Cannot attach an asset for a run that was never initialized');\n }\n return await this.reporter.then(reporter => reporter.attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset));\n }\n /**\n * Checks if the Applause runner is synchronized.\n * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished.\n */\n isSynchronized() {\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\n/**\n * Represents a Run Initializer.\n */\nexport class RunInitializer {\n autoApi;\n logger;\n constructor(autoApi, logger) {\n this.autoApi = autoApi;\n this.logger =\n logger ||\n winston.createLogger({\n transports: [new winston.transports.Console(), new ApplauseTransport()],\n });\n }\n /**\n * Initializes a test run.\n * @param tests - An optional array of test names to include in the run.\n * @returns A promise that resolves to a RunReporter instance.\n * @throws An error if unable to create the test run.\n */\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n this.logger.info('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi, this.logger);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\n/**\n * Handles reporting test results to the Applause API.\n */\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n /**\n * Creates a new instance of the Reporter class.\n * @param autoApi - The AutoApi instance.\n * @param testRunId - The ID of the test run.\n * @param heartbeatService - (Optional) The TestRunHeartbeatService instance.\n */\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n /**\n * Starts a test case and returns a promise that resolves to the test result ID.\n *\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Additional parameters for the test case.\n * @returns A promise that resolves to the test result ID.\n */\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n const submission = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n this.uidToResultIdMap[id] = submission;\n return submission;\n }\n /**\n * Submits the result of a test case.\n *\n * @param id - The ID of the test case.\n * @param status - The status of the test result.\n * @param params - Additional parameters for the test result.\n * @returns A promise that resolves to the result ID.\n */\n submitTestCaseResult(id, status, params) {\n const submission = this.uidToResultIdMap[id]?.then(resultId => this.autoApi\n .submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n })\n .then(() => resultId));\n this.resultSubmissionMap[id] = submission;\n return submission;\n }\n /**\n * Attaches a test case asset to a result.\n *\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset to attach.\n * @returns A promise that resolves when the asset is attached.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n await this.uidToResultIdMap[id]?.then(resultId => this.autoApi.uploadAsset(resultId, asset, assetName, providerSessionGuid, assetType));\n }\n /**\n * Ends the test runner and performs necessary cleanup tasks.\n * @returns A promise that resolves when the runner has ended.\n */\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService?.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map","import { Inbox } from './email/inbox.ts';\n/**\n * Helper class for managing email functionality.\n */\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the inbox for the specified email prefix.\n *\n * @param emailPrefix - The prefix used to generate the email address.\n * @returns A Promise that resolves to an Inbox object.\n */\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map"],"names":["validator","Validator","default","DEFAULT_URL","overrideConfig","config","overrides","Object","assign","fromEntries","entries","filter","_","v","undefined","isComplete","baseUrl","apiKey","productId","loadConfigFromFile","configFile","configFilePath","process","cwd","existsSync","fileContents","readFileSync","JSON","parse","validateConfig","Number","isInteger","Error","isURL","protocols","require_tld","allow_query_components","disallow_auth","allow_fragments","allow_protocol_relative_urls","allow_trailing_dot","require_host","require_protocol","isEmpty","AutoApi","options","client","callsInFlight","getCallsInFlight","this","constructor","axios","create","baseURL","timeout","headers","responseType","interceptors","response","use","error","errText","data","status","statusText","console","Promise","reject","startTestRun","info","post","sdkVersion","testRailReportingEnabled","testRailOptions","addAllTestsToPlan","testRailProjectId","projectId","testRailSuiteId","suiteId","testRailPlanName","planName","testRailRunName","runName","overrideTestRailRunNameUniqueness","overrideTestRailRunUniqueness","endTestRun","testRunId","delete","startTestCase","params","submitTestCaseResult","getProviderSessionLinks","resultIds","validIds","id","sendSdkHeartbeat","getEmailAddress","emailPrefix","get","getEmailContent","request","uploadAsset","resultId","file","assetName","providerSessionGuid","assetType","postForm","TestResultStatus","AssetType","exports","Inbox","emailAddress","autoApi","getEmail","res","simpleParser","TestRunHeartbeatService","logger","enabled","nextHeartbeat","start","end","scheduleNextHeartbeat","isEnabled","resolve","setTimeout","then","sendHeartbeat","log","debug","APPLAUSE_LOG_RECORDS","logs","getLogs","drainLogs","clearLogs","addLog","push","ApplauseTransport","Transport","super","callback","message","TEST_RAIL_CASE_ID_PREFIX","APPLAUSE_CASE_ID_PREFIX","RunInitializer","winston","createLogger","transports","Console","initializeRun","tests","cleanedTests","map","parseTestCaseName","parsed","testCaseName","trim","testRunCreateResponse","runId","heartbeatService","RunReporter","uidToResultIdMap","resultSubmissionMap","parsedTestCase","submission","testCaseId","testRailTestCaseId","itwTestCaseId","applauseTestCaseId","testResultId","attachTestCaseAsset","asset","runnerEnd","all","values","jsonArray","length","stringify","outputPath","writeFileSync","pathJoin","join","tokens","split","forEach","token","startsWith","warn","substring","initializer","reporter","runStarted","runFinished","env","r","parseInt","runnerStart","initializedReporter","toString","isSynchronized","getInbox","generatedAddress","loadOptions","path","properties","finalConfig"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.mjs b/dist/index.mjs index 2149ac9..4b7371f 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -3,12 +3,25 @@ import { existsSync, readFileSync, writeFileSync } from 'fs'; import path, { join } from 'path'; import Validator from 'validator'; import { simpleParser } from 'mailparser'; +import winston from 'winston'; +import Transport from 'winston-transport'; -const API_VERSION = '1.0.0'; +const API_VERSION = '1.1.0'; +/** + * Represents the configuration options for the Applause Reporter. + */ const validator = Validator.default; +/** + * The default base URL for the Applause API. + */ const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/'; -// Loads the configuration +/** + * Loads the configuration for the Applause Reporter. + * @param loadOptions - The options for loading the configuration. + * @returns The loaded Applause configuration. + * @throws Error if the configuration is not complete or invalid. + */ function loadConfig(loadOptions) { // Setup the initial config with any default properties let config = { @@ -34,22 +47,43 @@ function loadConfig(loadOptions) { validateConfig(finalConfig); return finalConfig; } +/** + * Overrides the configuration with the provided overrides. + * @param config - The base configuration. + * @param overrides - The overrides to apply. + * @returns The overridden configuration. + */ function overrideConfig(config, overrides) { return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined))); } +/** + * Checks if the configuration is complete. + * @param config - The configuration to check. + * @returns True if the configuration is complete, false otherwise. + */ function isComplete(config) { return (config.baseUrl !== undefined && config.apiKey !== undefined && config.productId !== undefined); } +/** + * Loads the configuration from the specified file. + * @param configFile - The path to the configuration file. + * @returns The loaded configuration from the file. + */ function loadConfigFromFile(configFile) { const configFilePath = configFile || process.cwd() + '/applause.json'; if (!existsSync(configFilePath)) { return {}; } - const fileCotents = readFileSync(configFilePath, 'utf8'); - return JSON.parse(fileCotents); + const fileContents = readFileSync(configFilePath, 'utf8'); + return JSON.parse(fileContents); } +/** + * Validates the configuration. + * @param config - The configuration to validate. + * @throws Error if the configuration is invalid. + */ function validateConfig(config) { if (!Number.isInteger(config.productId) || config.productId <= 0) { throw new Error(`productId must be a positive integer, was: '${config.productId}'`); @@ -65,12 +99,17 @@ function validateConfig(config) { require_host: true, require_protocol: true, })) { - throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}`); + throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}`); } if (validator.isEmpty(config.apiKey)) { throw new Error('apiKey is an empty string!'); } } +/** + * Validates a partial configuration. + * @param config - The partial configuration to validate. + * @throws Error if the partial configuration is invalid. + */ function validatePartialConfig(config) { if (config.productId !== undefined && (!Number.isInteger(config.productId) || config.productId <= 0)) { @@ -78,16 +117,26 @@ function validatePartialConfig(config) { } } +/** + * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform. + * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management. + * It also includes properties and methods to track the number of HTTP calls in progress. + */ class AutoApi { options; client; callsInFlight; /** - * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished + * Tracks the number of HTTP calls in progress. + * This property is used by reporters that want to know when the async work is finished. */ get getCallsInFlight() { return this.callsInFlight; } + /** + * Creates an instance of the `AutoApi` class. + * @param options - The configuration options for the Applause API. + */ constructor(options) { this.options = options; this.callsInFlight = 0; @@ -116,6 +165,11 @@ class AutoApi { return Promise.reject(error); }); } + /** + * Starts a new test run. + * @param info - The information for creating the test run. + * @returns A promise that resolves to the response containing the created test run. + */ async startTestRun(info) { this.callsInFlight += 1; try { @@ -140,6 +194,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Ends a test run. + * @param testRunId - The ID of the test run to end. + * @returns A promise that resolves to the response indicating the completion of the test run. + */ async endTestRun(testRunId) { this.callsInFlight += 1; try { @@ -149,6 +208,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Starts a new test case. + * @param params - The parameters for creating the test case. + * @returns A promise that resolves to the response containing the created test case. + */ async startTestCase(params) { this.callsInFlight += 1; try { @@ -159,6 +223,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Submits a test case result. + * @param params - The parameters for submitting the test case result. + * @returns A promise that resolves when the test case result is submitted. + */ async submitTestCaseResult(params) { this.callsInFlight += 1; try { @@ -168,6 +237,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the provider session links for the specified test results. + * @param resultIds - The IDs of the test results. + * @returns A promise that resolves to the response containing the provider session links. + */ async getProviderSessionLinks(resultIds) { this.callsInFlight += 1; try { @@ -179,6 +253,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Sends a heartbeat for the specified test run. + * @param testRunId - The ID of the test run. + * @returns A promise that resolves to the response indicating the heartbeat was sent. + */ async sendSdkHeartbeat(testRunId) { this.callsInFlight += 1; try { @@ -191,6 +270,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the email address for the specified email prefix. + * @param emailPrefix - The prefix of the email address. + * @returns A promise that resolves to the response containing the email address. + */ async getEmailAddress(emailPrefix) { this.callsInFlight += 1; try { @@ -201,6 +285,11 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Retrieves the content of the specified email. + * @param request - The request parameters for retrieving the email content. + * @returns A promise that resolves to the response containing the email content. + */ async getEmailContent(request) { this.callsInFlight += 1; try { @@ -211,6 +300,30 @@ class AutoApi { this.callsInFlight -= 1; } } + /** + * Uploads an asset for the specified test result. + * @param resultId - The ID of the test result. + * @param file - The file to upload as an asset. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The GUID of the provider session. + * @param assetType - The type of the asset. + * @returns A promise that resolves to the response indicating the asset was uploaded. + */ + async uploadAsset(resultId, file, assetName, providerSessionGuid, assetType) { + this.callsInFlight += 1; + try { + // this filters out falsy values (null, undefined, 0) + return await this.client.postForm(`/api/v1.0/test-result/${resultId}/upload`, { + file, + assetName, + providerSessionGuid, + assetType, + }); + } + finally { + this.callsInFlight -= 1; + } + } } /** @@ -226,14 +339,48 @@ var TestResultStatus; TestResultStatus["CANCELED"] = "CANCELED"; TestResultStatus["ERROR"] = "ERROR"; })(TestResultStatus || (TestResultStatus = {})); +var AssetType; +(function (AssetType) { + AssetType["SCREENSHOT"] = "SCREENSHOT"; + AssetType["FAILURE_SCREENSHOT"] = "FAILURE_SCREENSHOT"; + AssetType["VIDEO"] = "VIDEO"; + AssetType["NETWORK_HAR"] = "NETWORK_HAR"; + AssetType["VITALS_LOG"] = "VITALS_LOG"; + AssetType["CONSOLE_LOG"] = "CONSOLE_LOG"; + AssetType["NETWORK_LOG"] = "NETWORK_LOG"; + AssetType["DEVICE_LOG"] = "DEVICE_LOG"; + AssetType["SELENIUM_LOG"] = "SELENIUM_LOG"; + AssetType["SELENIUM_LOG_JSON"] = "SELENIUM_LOG_JSON"; + AssetType["BROWSER_LOG"] = "BROWSER_LOG"; + AssetType["FRAMEWORK_LOG"] = "FRAMEWORK_LOG"; + AssetType["EMAIL"] = "EMAIL"; + AssetType["PAGE_SOURCE"] = "PAGE_SOURCE"; + AssetType["CODE_BUNDLE"] = "CODE_BUNDLE"; + AssetType["RESULTS_ZIP"] = "RESULTS_ZIP"; + AssetType["SESSION_DETAILS"] = "SESSION_DETAILS"; + AssetType["DEVICE_DETAILS"] = "DEVICE_DETAILS"; + AssetType["UNKNOWN"] = "UNKNOWN"; +})(AssetType || (AssetType = {})); +/** + * Represents an email inbox. + */ class Inbox { emailAddress; autoApi; + /** + * Creates an instance of Inbox. + * @param emailAddress - The email address associated with the inbox. + * @param autoApi - An instance of the AutoApi class. + */ constructor(emailAddress, autoApi) { this.emailAddress = emailAddress; this.autoApi = autoApi; } + /** + * Retrieves the content of an email from the inbox. + * @returns A Promise that resolves to the parsed email content. + */ async getEmail() { const res = await this.autoApi.getEmailContent({ emailAddress: this.emailAddress, @@ -242,33 +389,60 @@ class Inbox { } } +/** + * Helper class for managing email functionality. + */ class EmailHelper { autoApi; constructor(autoApi) { this.autoApi = autoApi; } + /** + * Retrieves the inbox for the specified email prefix. + * + * @param emailPrefix - The prefix used to generate the email address. + * @returns A Promise that resolves to an Inbox object. + */ async getInbox(emailPrefix) { const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress; return new Inbox(generatedAddress, this.autoApi); } } +/** + * Represents a service for sending heartbeats during a test run. + */ class TestRunHeartbeatService { testRunId; autoApi; + logger; enabled = false; nextHeartbeat; - constructor(testRunId, autoApi) { + /** + * Creates an instance of TestRunHeartbeatService. + * @param testRunId - The ID of the test run. + * @param autoApi - The AutoApi instance used for sending heartbeats. + */ + constructor(testRunId, autoApi, logger) { this.testRunId = testRunId; this.autoApi = autoApi; + this.logger = logger; } + /** + * Starts sending heartbeats. + * @returns A promise that resolves when the heartbeats are started. + */ async start() { // End the current heartbeat if it has started await this.end(); - // Set up va new interval + // Set up a new interval this.enabled = true; this.scheduleNextHeartbeat(); } + /** + * Checks if the heartbeats are enabled. + * @returns True if the heartbeats are enabled, false otherwise. + */ isEnabled() { return this.enabled; } @@ -284,6 +458,10 @@ class TestRunHeartbeatService { console.log('Heartbeat sent'); this.scheduleNextHeartbeat(); } + /** + * Ends the heartbeats. + * @returns A promise that resolves when the heartbeats are ended. + */ async end() { if (this.nextHeartbeat !== undefined) { this.enabled = false; @@ -295,34 +473,138 @@ class TestRunHeartbeatService { } } +/** + * A simple Class for storing and retrieving log messages. + */ +class LoggingContainer { + logs = []; + /** + * Retrieves all logs stored in the container. + * + * @returns An array of log messages. + */ + getLogs() { + return this.logs; + } + /** + * Retrieves and clears all logs stored in the container. + * + * @returns An array of log messages. + */ + drainLogs() { + const logs = this.logs; + this.clearLogs(); + return logs; + } + /** + * Clears all logs stored in the container. + */ + clearLogs() { + this.logs = []; + } + /** + * Adds a log message to the container. + * + * @param log - The log message to add. + */ + addLog(log) { + this.logs.push(log); + } +} +// Create a new Shared LoggingContainer to store logs +const APPLAUSE_LOG_RECORDS = new LoggingContainer(); +/** + * A Custom Winston Transport that sends logs to the Applause LoggingContainer + */ +class ApplauseTransport extends Transport { + constructor() { + super({}); + } + log(info, callback) { + APPLAUSE_LOG_RECORDS.addLog(info.message); + // Continue to the next transport + callback(); + } +} + +const TEST_RAIL_CASE_ID_PREFIX = 'TestRail-'; +const APPLAUSE_CASE_ID_PREFIX = 'Applause-'; +/** + * Represents an Applause reporter. + */ class ApplauseReporter { + logger; autoApi; initializer; reporter; runStarted = false; runFinished = false; - constructor(config) { + /** + * Creates an instance of ApplauseReporter. + * @param config - The Applause configuration. + */ + constructor(config, logger) { + this.logger = logger; this.autoApi = new AutoApi(config); - this.initializer = new RunInitializer(this.autoApi); + this.initializer = new RunInitializer(this.autoApi, this.logger); + const runId = process.env['APPLAUSE_RUN_ID']; + if (runId !== undefined) { + const r = new RunReporter(this.autoApi, parseInt(runId)); + this.reporter = new Promise(resolve => resolve(r)); + this.runStarted = true; + } } - runnerStart(tests) { + /** + * Starts the Applause runner. + * @param tests - Optional array of test names to run. + * @returns A promise that resolves to the test run ID. + * @throws Error if a run is already started or finished. + */ + async runnerStart(tests) { + if (this.reporter !== undefined) { + throw new Error('Cannot start a run - run already started or run already finished'); + } this.reporter = this.initializer.initializeRun(tests); - void this.reporter.then(() => { - this.runStarted = true; - }); + const initializedReporter = await this.reporter; + this.runStarted = true; + process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString(); + return initializedReporter.testRunId; } - startTestCase(id, testCaseName, params) { + /** + * Starts a test case. + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Optional additional parameters for the test case. + * @returns A promise that resolves to the test case ID. + * @throws Error if a run was never initialized. + */ + async startTestCase(id, testCaseName, params) { if (this.reporter === undefined) { throw new Error('Cannot start a test case for a run that was never initialized'); } - void this.reporter.then(reporter => reporter.startTestCase(id, testCaseName, params)); + const reporter = await this.reporter; + return reporter.startTestCase(id, testCaseName, params); } - submitTestCaseResult(id, status, params) { + /** + * Submits a test case result. + * @param id - The ID of the test case. + * @param status - The status of the test case result. + * @param params - Optional additional parameters for the test case result. + * @returns A promise that resolves to the test case result ID. + * @throws Error if a run was never initialized. + */ + async submitTestCaseResult(id, status, params) { if (this.reporter === undefined) { throw new Error('Cannot submit test case result for a run that was never initialized'); } - void this.reporter.then(reporter => reporter.submitTestCaseResult(id, status, params)); + const reporter = await this.reporter; + return reporter.submitTestCaseResult(id, status, params); } + /** + * Ends the Applause runner. + * @returns A promise that resolves when the runner is ended. + * @throws Error if a run was never initialized. + */ async runnerEnd() { if (this.reporter === undefined) { throw new Error('Cannot end a run that was never initialized'); @@ -331,17 +613,51 @@ class ApplauseReporter { .then(reporter => reporter.runnerEnd()) .then(() => (this.runFinished = true)); } + /** + * Attaches an asset to a test case. + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset data as a Buffer. + * @returns A promise that resolves when the asset is attached. + * @throws Error if a run was never initialized. + */ + async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) { + if (this.reporter === undefined) { + throw new Error('Cannot attach an asset for a run that was never initialized'); + } + return await this.reporter.then(reporter => reporter.attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset)); + } + /** + * Checks if the Applause runner is synchronized. + * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished. + */ isSynchronized() { - // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished return ((!this.runStarted || (this.runStarted && this.runFinished)) && this.autoApi.getCallsInFlight == 0); } } +/** + * Represents a Run Initializer. + */ class RunInitializer { autoApi; - constructor(autoApi) { + logger; + constructor(autoApi, logger) { this.autoApi = autoApi; + this.logger = + logger || + winston.createLogger({ + transports: [new winston.transports.Console(), new ApplauseTransport()], + }); } + /** + * Initializes a test run. + * @param tests - An optional array of test names to include in the run. + * @returns A promise that resolves to a RunReporter instance. + * @throws An error if unable to create the test run. + */ async initializeRun(tests) { const cleanedTests = tests ?.map(parseTestCaseName) @@ -354,26 +670,43 @@ class RunInitializer { throw new Error('Unable to create test run'); } const runId = testRunCreateResponse.data.runId; - console.log('Test Run %d initialized', runId); - const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi); + this.logger.info('Test Run %d initialized', runId); + const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi, this.logger); await heartbeatService.start(); return new RunReporter(this.autoApi, runId, heartbeatService); } } +/** + * Handles reporting test results to the Applause API. + */ class RunReporter { autoApi; testRunId; heartbeatService; uidToResultIdMap = {}; resultSubmissionMap = {}; + /** + * Creates a new instance of the Reporter class. + * @param autoApi - The AutoApi instance. + * @param testRunId - The ID of the test run. + * @param heartbeatService - (Optional) The TestRunHeartbeatService instance. + */ constructor(autoApi, testRunId, heartbeatService) { this.autoApi = autoApi; this.testRunId = testRunId; this.heartbeatService = heartbeatService; } + /** + * Starts a test case and returns a promise that resolves to the test result ID. + * + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Additional parameters for the test case. + * @returns A promise that resolves to the test result ID. + */ startTestCase(id, testCaseName, params) { const parsedTestCase = parseTestCaseName(testCaseName); - this.uidToResultIdMap[id] = this.autoApi + const submission = this.autoApi .startTestCase({ testCaseName: parsedTestCase.testCaseName, testCaseId: parsedTestCase.testRailTestCaseId, @@ -385,21 +718,52 @@ class RunReporter { .then(res => { return res.data.testResultId; }); + this.uidToResultIdMap[id] = submission; + return submission; } + /** + * Submits the result of a test case. + * + * @param id - The ID of the test case. + * @param status - The status of the test result. + * @param params - Additional parameters for the test result. + * @returns A promise that resolves to the result ID. + */ submitTestCaseResult(id, status, params) { - this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => this.autoApi.submitTestCaseResult({ + const submission = this.uidToResultIdMap[id]?.then(resultId => this.autoApi + .submitTestCaseResult({ status: status, testResultId: resultId, ...params, - })); + }) + .then(() => resultId)); + this.resultSubmissionMap[id] = submission; + return submission; + } + /** + * Attaches a test case asset to a result. + * + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset to attach. + * @returns A promise that resolves when the asset is attached. + */ + async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) { + await this.uidToResultIdMap[id]?.then(resultId => this.autoApi.uploadAsset(resultId, asset, assetName, providerSessionGuid, assetType)); } + /** + * Ends the test runner and performs necessary cleanup tasks. + * @returns A promise that resolves when the runner has ended. + */ async runnerEnd() { // Wait for all results to be created const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || []; // Wait for the results to be submitted void (await Promise.all(Object.values(this.resultSubmissionMap))); // Wait the heartbeat to be ended - void (await this.heartbeatService.end()); + void (await this.heartbeatService?.end()); void (await this.autoApi.endTestRun(this.testRunId)); // Fetch the provider session asset links and save them off to a file const resp = await this.autoApi.getProviderSessionLinks(resultIds); @@ -412,8 +776,6 @@ class RunReporter { } } } -const TEST_RAIL_CASE_ID_PREFIX = 'TestRail-'; -const APPLAUSE_CASE_ID_PREFIX = 'Applause-'; function parseTestCaseName(testCaseName) { // Split the name on spaces. We will reassemble after parsing out the other ids const tokens = testCaseName.split(' '); @@ -444,5 +806,5 @@ function parseTestCaseName(testCaseName) { }; } -export { ApplauseReporter, AutoApi, DEFAULT_URL, EmailHelper, Inbox, RunInitializer, RunReporter, TestResultStatus, TestRunHeartbeatService, isComplete, loadConfig, loadConfigFromFile, overrideConfig, validateConfig, validatePartialConfig }; +export { ApplauseReporter, AssetType, AutoApi, DEFAULT_URL, EmailHelper, Inbox, RunInitializer, RunReporter, TestResultStatus, TestRunHeartbeatService, isComplete, loadConfig, loadConfigFromFile, overrideConfig, validateConfig, validatePartialConfig }; //# sourceMappingURL=index.mjs.map diff --git a/dist/index.mjs.map b/dist/index.mjs.map index 0dd5046..a26691f 100644 --- a/dist/index.mjs.map +++ b/dist/index.mjs.map @@ -1 +1 @@ -{"version":3,"file":"index.mjs","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/email-helper.ts","../src/heartbeat.ts","../src/reporter.ts"],"sourcesContent":["export const API_VERSION = '1.0.0';\n//# sourceMappingURL=version.js.map","import { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n// Loads the configuration\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileCotents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileCotents);\n}\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","import axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\nexport class Inbox {\n emailAddress;\n autoApi;\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","import { Inbox } from './email/inbox.ts';\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map","export class TestRunHeartbeatService {\n testRunId;\n autoApi;\n enabled = false;\n nextHeartbeat;\n constructor(testRunId, autoApi) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n }\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up va new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nexport class ApplauseReporter {\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n constructor(config) {\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi);\n }\n runnerStart(tests) {\n this.reporter = this.initializer.initializeRun(tests);\n void this.reporter.then(() => {\n this.runStarted = true;\n });\n }\n startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.startTestCase(id, testCaseName, params));\n }\n submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n void this.reporter.then(reporter => reporter.submitTestCaseResult(id, status, params));\n }\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n isSynchronized() {\n // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\nexport class RunInitializer {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n console.log('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n this.uidToResultIdMap[id] = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n }\n submitTestCaseResult(id, status, params) {\n this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => this.autoApi.submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n }));\n }\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map"],"names":["pathJoin"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.mjs","sources":["../src/version.ts","../src/config.ts","../src/auto-api.ts","../src/dto.ts","../src/email/inbox.ts","../src/email-helper.ts","../src/heartbeat.ts","../src/logging.ts","../src/reporter.ts"],"sourcesContent":["export const API_VERSION = '1.1.0';\n//# sourceMappingURL=version.js.map","/**\n * Represents the configuration options for the Applause Reporter.\n */\nimport { existsSync, readFileSync } from 'fs';\nimport path from 'path';\nimport Validator from 'validator';\nconst validator = Validator.default;\n/**\n * The default base URL for the Applause API.\n */\nexport const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/';\n/**\n * Loads the configuration for the Applause Reporter.\n * @param loadOptions - The options for loading the configuration.\n * @returns The loaded Applause configuration.\n * @throws Error if the configuration is not complete or invalid.\n */\nexport function loadConfig(loadOptions) {\n // Setup the initial config with any default properties\n let config = {\n baseUrl: DEFAULT_URL,\n };\n // Load properties from the provided config file\n if (loadOptions !== undefined && loadOptions.configFile !== undefined) {\n config = overrideConfig(config, loadConfigFromFile(path.join(process.cwd(), loadOptions.configFile)));\n }\n else {\n // Override from the default config file\n config = overrideConfig(config, loadConfigFromFile());\n }\n // Then load in the file override properties\n if (loadOptions !== undefined && loadOptions.properties !== undefined) {\n config = overrideConfig(config, loadOptions.properties);\n }\n if (!isComplete(config)) {\n throw new Error('Config is not complete');\n }\n // We know that the config is complete, so we can cast\n const finalConfig = config;\n validateConfig(finalConfig);\n return finalConfig;\n}\n/**\n * Overrides the configuration with the provided overrides.\n * @param config - The base configuration.\n * @param overrides - The overrides to apply.\n * @returns The overridden configuration.\n */\nexport function overrideConfig(config, overrides) {\n return Object.assign({}, config, Object.fromEntries(Object.entries(overrides || {}).filter(([_, v]) => v !== undefined)));\n}\n/**\n * Checks if the configuration is complete.\n * @param config - The configuration to check.\n * @returns True if the configuration is complete, false otherwise.\n */\nexport function isComplete(config) {\n return (config.baseUrl !== undefined &&\n config.apiKey !== undefined &&\n config.productId !== undefined);\n}\n/**\n * Loads the configuration from the specified file.\n * @param configFile - The path to the configuration file.\n * @returns The loaded configuration from the file.\n */\nexport function loadConfigFromFile(configFile) {\n const configFilePath = configFile || process.cwd() + '/applause.json';\n if (!existsSync(configFilePath)) {\n return {};\n }\n const fileContents = readFileSync(configFilePath, 'utf8');\n return JSON.parse(fileContents);\n}\n/**\n * Validates the configuration.\n * @param config - The configuration to validate.\n * @throws Error if the configuration is invalid.\n */\nexport function validateConfig(config) {\n if (!Number.isInteger(config.productId) || config.productId <= 0) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n if (!validator.isURL(config.baseUrl, {\n protocols: ['http', 'https'],\n require_tld: false,\n allow_query_components: false,\n disallow_auth: true,\n allow_fragments: false,\n allow_protocol_relative_urls: false,\n allow_trailing_dot: false,\n require_host: true,\n require_protocol: true,\n })) {\n throw new Error(`baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}`);\n }\n if (validator.isEmpty(config.apiKey)) {\n throw new Error('apiKey is an empty string!');\n }\n}\n/**\n * Validates a partial configuration.\n * @param config - The partial configuration to validate.\n * @throws Error if the partial configuration is invalid.\n */\nexport function validatePartialConfig(config) {\n if (config.productId !== undefined &&\n (!Number.isInteger(config.productId) || config.productId <= 0)) {\n throw new Error(`productId must be a positive integer, was: '${config.productId}'`);\n }\n}\n//# sourceMappingURL=config.js.map","/**\n * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform.\n * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management.\n * It also includes properties and methods to track the number of HTTP calls in progress.\n */\nimport axios from 'axios';\nimport { API_VERSION } from './version.ts';\nimport { validateConfig } from './config.ts';\nexport class AutoApi {\n options;\n client;\n callsInFlight;\n /**\n * Tracks the number of HTTP calls in progress.\n * This property is used by reporters that want to know when the async work is finished.\n */\n get getCallsInFlight() {\n return this.callsInFlight;\n }\n /**\n * Creates an instance of the `AutoApi` class.\n * @param options - The configuration options for the Applause API.\n */\n constructor(options) {\n this.options = options;\n this.callsInFlight = 0;\n validateConfig(options);\n this.client = axios.create({\n baseURL: options.baseUrl,\n timeout: 10000,\n headers: {\n 'X-Api-Key': options.apiKey,\n 'Context-Type': 'application/json',\n },\n responseType: 'json',\n });\n this.client.interceptors.response.use(function (response) {\n return response;\n }, function (error) {\n // log and rethrow\n const errText = \n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data !== undefined\n ? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n error.data\n : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n `error-code [${error.response.status}] with error [${error.response.statusText}]`;\n console.error(`Auto-Api returned ${errText}`);\n return Promise.reject(error);\n });\n }\n /**\n * Starts a new test run.\n * @param info - The information for creating the test run.\n * @returns A promise that resolves to the response containing the created test run.\n */\n async startTestRun(info) {\n this.callsInFlight += 1;\n try {\n return await this.client.post('/api/v1.0/test-run/create', {\n // Provided params\n ...info,\n // API Version\n sdkVersion: `js:${API_VERSION}`,\n // Copy over the product id\n productId: this.options.productId,\n // Copy over test rail parameters\n testRailReportingEnabled: this.options.testRailOptions !== undefined,\n addAllTestsToPlan: this.options.testRailOptions?.addAllTestsToPlan,\n testRailProjectId: this.options.testRailOptions?.projectId,\n testRailSuiteId: this.options.testRailOptions?.suiteId,\n testRailPlanName: this.options.testRailOptions?.planName,\n testRailRunName: this.options.testRailOptions?.runName,\n overrideTestRailRunNameUniqueness: this.options.testRailOptions?.overrideTestRailRunUniqueness,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Ends a test run.\n * @param testRunId - The ID of the test run to end.\n * @returns A promise that resolves to the response indicating the completion of the test run.\n */\n async endTestRun(testRunId) {\n this.callsInFlight += 1;\n try {\n return await this.client.delete(`/api/v1.0/test-run/${testRunId}?endingStatus=COMPLETE`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Starts a new test case.\n * @param params - The parameters for creating the test case.\n * @returns A promise that resolves to the response containing the created test case.\n */\n async startTestCase(params) {\n this.callsInFlight += 1;\n try {\n const res = await this.client.post('/api/v1.0/test-result/create-result', params);\n return res;\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Submits a test case result.\n * @param params - The parameters for submitting the test case result.\n * @returns A promise that resolves when the test case result is submitted.\n */\n async submitTestCaseResult(params) {\n this.callsInFlight += 1;\n try {\n await this.client.post('/api/v1.0/test-result', params);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the provider session links for the specified test results.\n * @param resultIds - The IDs of the test results.\n * @returns A promise that resolves to the response containing the provider session links.\n */\n async getProviderSessionLinks(resultIds) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n const validIds = resultIds.filter(id => id);\n return await this.client.post('/api/v1.0/test-result/provider-info', validIds);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Sends a heartbeat for the specified test run.\n * @param testRunId - The ID of the test run.\n * @returns A promise that resolves to the response indicating the heartbeat was sent.\n */\n async sendSdkHeartbeat(testRunId) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v2.0/sdk-heartbeat', {\n testRunId: testRunId,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the email address for the specified email prefix.\n * @param emailPrefix - The prefix of the email address.\n * @returns A promise that resolves to the response containing the email address.\n */\n async getEmailAddress(emailPrefix) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.get(`/api/v1.0/email/get-address?prefix=${emailPrefix}`);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Retrieves the content of the specified email.\n * @param request - The request parameters for retrieving the email content.\n * @returns A promise that resolves to the response containing the email content.\n */\n async getEmailContent(request) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.post('/api/v1.0/email/download-email', request);\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n /**\n * Uploads an asset for the specified test result.\n * @param resultId - The ID of the test result.\n * @param file - The file to upload as an asset.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The GUID of the provider session.\n * @param assetType - The type of the asset.\n * @returns A promise that resolves to the response indicating the asset was uploaded.\n */\n async uploadAsset(resultId, file, assetName, providerSessionGuid, assetType) {\n this.callsInFlight += 1;\n try {\n // this filters out falsy values (null, undefined, 0)\n return await this.client.postForm(`/api/v1.0/test-result/${resultId}/upload`, {\n file,\n assetName,\n providerSessionGuid,\n assetType,\n });\n }\n finally {\n this.callsInFlight -= 1;\n }\n }\n}\n//# sourceMappingURL=auto-api.js.map","/**\n * Enum representing a test result's status\n */\nexport var TestResultStatus;\n(function (TestResultStatus) {\n TestResultStatus[\"NOT_RUN\"] = \"NOT_RUN\";\n TestResultStatus[\"IN_PROGRESS\"] = \"IN_PROGRESS\";\n TestResultStatus[\"PASSED\"] = \"PASSED\";\n TestResultStatus[\"FAILED\"] = \"FAILED\";\n TestResultStatus[\"SKIPPED\"] = \"SKIPPED\";\n TestResultStatus[\"CANCELED\"] = \"CANCELED\";\n TestResultStatus[\"ERROR\"] = \"ERROR\";\n})(TestResultStatus || (TestResultStatus = {}));\nexport var AssetType;\n(function (AssetType) {\n AssetType[\"SCREENSHOT\"] = \"SCREENSHOT\";\n AssetType[\"FAILURE_SCREENSHOT\"] = \"FAILURE_SCREENSHOT\";\n AssetType[\"VIDEO\"] = \"VIDEO\";\n AssetType[\"NETWORK_HAR\"] = \"NETWORK_HAR\";\n AssetType[\"VITALS_LOG\"] = \"VITALS_LOG\";\n AssetType[\"CONSOLE_LOG\"] = \"CONSOLE_LOG\";\n AssetType[\"NETWORK_LOG\"] = \"NETWORK_LOG\";\n AssetType[\"DEVICE_LOG\"] = \"DEVICE_LOG\";\n AssetType[\"SELENIUM_LOG\"] = \"SELENIUM_LOG\";\n AssetType[\"SELENIUM_LOG_JSON\"] = \"SELENIUM_LOG_JSON\";\n AssetType[\"BROWSER_LOG\"] = \"BROWSER_LOG\";\n AssetType[\"FRAMEWORK_LOG\"] = \"FRAMEWORK_LOG\";\n AssetType[\"EMAIL\"] = \"EMAIL\";\n AssetType[\"PAGE_SOURCE\"] = \"PAGE_SOURCE\";\n AssetType[\"CODE_BUNDLE\"] = \"CODE_BUNDLE\";\n AssetType[\"RESULTS_ZIP\"] = \"RESULTS_ZIP\";\n AssetType[\"SESSION_DETAILS\"] = \"SESSION_DETAILS\";\n AssetType[\"DEVICE_DETAILS\"] = \"DEVICE_DETAILS\";\n AssetType[\"UNKNOWN\"] = \"UNKNOWN\";\n})(AssetType || (AssetType = {}));\n//# sourceMappingURL=dto.js.map","import { simpleParser } from 'mailparser';\n/**\n * Represents an email inbox.\n */\nexport class Inbox {\n emailAddress;\n autoApi;\n /**\n * Creates an instance of Inbox.\n * @param emailAddress - The email address associated with the inbox.\n * @param autoApi - An instance of the AutoApi class.\n */\n constructor(emailAddress, autoApi) {\n this.emailAddress = emailAddress;\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the content of an email from the inbox.\n * @returns A Promise that resolves to the parsed email content.\n */\n async getEmail() {\n const res = await this.autoApi.getEmailContent({\n emailAddress: this.emailAddress,\n });\n return await simpleParser(res.data);\n }\n}\n//# sourceMappingURL=inbox.js.map","import { Inbox } from './email/inbox.ts';\n/**\n * Helper class for managing email functionality.\n */\nexport class EmailHelper {\n autoApi;\n constructor(autoApi) {\n this.autoApi = autoApi;\n }\n /**\n * Retrieves the inbox for the specified email prefix.\n *\n * @param emailPrefix - The prefix used to generate the email address.\n * @returns A Promise that resolves to an Inbox object.\n */\n async getInbox(emailPrefix) {\n const generatedAddress = (await this.autoApi.getEmailAddress(emailPrefix)).data.emailAddress;\n return new Inbox(generatedAddress, this.autoApi);\n }\n}\nexport * from './email/attachment.ts';\nexport * from './email/inbox.ts';\n//# sourceMappingURL=email-helper.js.map","/**\n * Represents a service for sending heartbeats during a test run.\n */\nexport class TestRunHeartbeatService {\n testRunId;\n autoApi;\n logger;\n enabled = false;\n nextHeartbeat;\n /**\n * Creates an instance of TestRunHeartbeatService.\n * @param testRunId - The ID of the test run.\n * @param autoApi - The AutoApi instance used for sending heartbeats.\n */\n constructor(testRunId, autoApi, logger) {\n this.testRunId = testRunId;\n this.autoApi = autoApi;\n this.logger = logger;\n }\n /**\n * Starts sending heartbeats.\n * @returns A promise that resolves when the heartbeats are started.\n */\n async start() {\n // End the current heartbeat if it has started\n await this.end();\n // Set up a new interval\n this.enabled = true;\n this.scheduleNextHeartbeat();\n }\n /**\n * Checks if the heartbeats are enabled.\n * @returns True if the heartbeats are enabled, false otherwise.\n */\n isEnabled() {\n return this.enabled;\n }\n scheduleNextHeartbeat() {\n if (!this.enabled) {\n return;\n }\n this.nextHeartbeat = new Promise(resolve => setTimeout(resolve, 5000)).then(() => this.sendHeartbeat());\n }\n async sendHeartbeat() {\n console.log('Sending heartbeat');\n await this.autoApi.sendSdkHeartbeat(this.testRunId);\n console.log('Heartbeat sent');\n this.scheduleNextHeartbeat();\n }\n /**\n * Ends the heartbeats.\n * @returns A promise that resolves when the heartbeats are ended.\n */\n async end() {\n if (this.nextHeartbeat !== undefined) {\n this.enabled = false;\n console.debug('Ending Applause SDK Heartbeat');\n await this.nextHeartbeat;\n console.debug('Applause SDK Heartbeat Ended Successfully');\n }\n this.nextHeartbeat = undefined;\n }\n}\n//# sourceMappingURL=heartbeat.js.map","import Transport from 'winston-transport';\n/**\n * A simple Class for storing and retrieving log messages.\n */\nexport class LoggingContainer {\n logs = [];\n /**\n * Retrieves all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n getLogs() {\n return this.logs;\n }\n /**\n * Retrieves and clears all logs stored in the container.\n *\n * @returns An array of log messages.\n */\n drainLogs() {\n const logs = this.logs;\n this.clearLogs();\n return logs;\n }\n /**\n * Clears all logs stored in the container.\n */\n clearLogs() {\n this.logs = [];\n }\n /**\n * Adds a log message to the container.\n *\n * @param log - The log message to add.\n */\n addLog(log) {\n this.logs.push(log);\n }\n}\n// Create a new Shared LoggingContainer to store logs\nexport const APPLAUSE_LOG_RECORDS = new LoggingContainer();\n/**\n * A Custom Winston Transport that sends logs to the Applause LoggingContainer\n */\nexport class ApplauseTransport extends Transport {\n constructor() {\n super({});\n }\n log(info, callback) {\n APPLAUSE_LOG_RECORDS.addLog(info.message);\n // Continue to the next transport\n callback();\n }\n}\n//# sourceMappingURL=logging.js.map","import { writeFileSync } from 'fs';\nimport { AutoApi } from './auto-api.ts';\nimport { TestRunHeartbeatService } from './heartbeat.ts';\nimport { join as pathJoin } from 'path';\nimport winston from 'winston';\nimport { ApplauseTransport } from './logging.ts';\nconst TEST_RAIL_CASE_ID_PREFIX = 'TestRail-';\nconst APPLAUSE_CASE_ID_PREFIX = 'Applause-';\n/**\n * Represents an Applause reporter.\n */\nexport class ApplauseReporter {\n logger;\n autoApi;\n initializer;\n reporter;\n runStarted = false;\n runFinished = false;\n /**\n * Creates an instance of ApplauseReporter.\n * @param config - The Applause configuration.\n */\n constructor(config, logger) {\n this.logger = logger;\n this.autoApi = new AutoApi(config);\n this.initializer = new RunInitializer(this.autoApi, this.logger);\n const runId = process.env['APPLAUSE_RUN_ID'];\n if (runId !== undefined) {\n const r = new RunReporter(this.autoApi, parseInt(runId));\n this.reporter = new Promise(resolve => resolve(r));\n this.runStarted = true;\n }\n }\n /**\n * Starts the Applause runner.\n * @param tests - Optional array of test names to run.\n * @returns A promise that resolves to the test run ID.\n * @throws Error if a run is already started or finished.\n */\n async runnerStart(tests) {\n if (this.reporter !== undefined) {\n throw new Error('Cannot start a run - run already started or run already finished');\n }\n this.reporter = this.initializer.initializeRun(tests);\n const initializedReporter = await this.reporter;\n this.runStarted = true;\n process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString();\n return initializedReporter.testRunId;\n }\n /**\n * Starts a test case.\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Optional additional parameters for the test case.\n * @returns A promise that resolves to the test case ID.\n * @throws Error if a run was never initialized.\n */\n async startTestCase(id, testCaseName, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot start a test case for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.startTestCase(id, testCaseName, params);\n }\n /**\n * Submits a test case result.\n * @param id - The ID of the test case.\n * @param status - The status of the test case result.\n * @param params - Optional additional parameters for the test case result.\n * @returns A promise that resolves to the test case result ID.\n * @throws Error if a run was never initialized.\n */\n async submitTestCaseResult(id, status, params) {\n if (this.reporter === undefined) {\n throw new Error('Cannot submit test case result for a run that was never initialized');\n }\n const reporter = await this.reporter;\n return reporter.submitTestCaseResult(id, status, params);\n }\n /**\n * Ends the Applause runner.\n * @returns A promise that resolves when the runner is ended.\n * @throws Error if a run was never initialized.\n */\n async runnerEnd() {\n if (this.reporter === undefined) {\n throw new Error('Cannot end a run that was never initialized');\n }\n await this.reporter\n .then(reporter => reporter.runnerEnd())\n .then(() => (this.runFinished = true));\n }\n /**\n * Attaches an asset to a test case.\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset data as a Buffer.\n * @returns A promise that resolves when the asset is attached.\n * @throws Error if a run was never initialized.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n if (this.reporter === undefined) {\n throw new Error('Cannot attach an asset for a run that was never initialized');\n }\n return await this.reporter.then(reporter => reporter.attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset));\n }\n /**\n * Checks if the Applause runner is synchronized.\n * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished.\n */\n isSynchronized() {\n return ((!this.runStarted || (this.runStarted && this.runFinished)) &&\n this.autoApi.getCallsInFlight == 0);\n }\n}\n/**\n * Represents a Run Initializer.\n */\nexport class RunInitializer {\n autoApi;\n logger;\n constructor(autoApi, logger) {\n this.autoApi = autoApi;\n this.logger =\n logger ||\n winston.createLogger({\n transports: [new winston.transports.Console(), new ApplauseTransport()],\n });\n }\n /**\n * Initializes a test run.\n * @param tests - An optional array of test names to include in the run.\n * @returns A promise that resolves to a RunReporter instance.\n * @throws An error if unable to create the test run.\n */\n async initializeRun(tests) {\n const cleanedTests = tests\n ?.map(parseTestCaseName)\n .map(parsed => parsed.testCaseName.trim());\n const testRunCreateResponse = await this.autoApi.startTestRun({\n tests: cleanedTests || [],\n });\n if (testRunCreateResponse.status < 200 ||\n testRunCreateResponse.status > 300) {\n throw new Error('Unable to create test run');\n }\n const runId = testRunCreateResponse.data.runId;\n this.logger.info('Test Run %d initialized', runId);\n const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi, this.logger);\n await heartbeatService.start();\n return new RunReporter(this.autoApi, runId, heartbeatService);\n }\n}\n/**\n * Handles reporting test results to the Applause API.\n */\nexport class RunReporter {\n autoApi;\n testRunId;\n heartbeatService;\n uidToResultIdMap = {};\n resultSubmissionMap = {};\n /**\n * Creates a new instance of the Reporter class.\n * @param autoApi - The AutoApi instance.\n * @param testRunId - The ID of the test run.\n * @param heartbeatService - (Optional) The TestRunHeartbeatService instance.\n */\n constructor(autoApi, testRunId, heartbeatService) {\n this.autoApi = autoApi;\n this.testRunId = testRunId;\n this.heartbeatService = heartbeatService;\n }\n /**\n * Starts a test case and returns a promise that resolves to the test result ID.\n *\n * @param id - The ID of the test case.\n * @param testCaseName - The name of the test case.\n * @param params - Additional parameters for the test case.\n * @returns A promise that resolves to the test result ID.\n */\n startTestCase(id, testCaseName, params) {\n const parsedTestCase = parseTestCaseName(testCaseName);\n const submission = this.autoApi\n .startTestCase({\n testCaseName: parsedTestCase.testCaseName,\n testCaseId: parsedTestCase.testRailTestCaseId,\n itwTestCaseId: parsedTestCase.applauseTestCaseId,\n testRunId: this.testRunId,\n // If the additional params provides either test case id, it will override the parsed value we set above\n ...Object.fromEntries(Object.entries(params || {}).filter(([_, v]) => v !== undefined)),\n })\n .then(res => {\n return res.data.testResultId;\n });\n this.uidToResultIdMap[id] = submission;\n return submission;\n }\n /**\n * Submits the result of a test case.\n *\n * @param id - The ID of the test case.\n * @param status - The status of the test result.\n * @param params - Additional parameters for the test result.\n * @returns A promise that resolves to the result ID.\n */\n submitTestCaseResult(id, status, params) {\n const submission = this.uidToResultIdMap[id]?.then(resultId => this.autoApi\n .submitTestCaseResult({\n status: status,\n testResultId: resultId,\n ...params,\n })\n .then(() => resultId));\n this.resultSubmissionMap[id] = submission;\n return submission;\n }\n /**\n * Attaches a test case asset to a result.\n *\n * @param id - The ID of the test case.\n * @param assetName - The name of the asset.\n * @param providerSessionGuid - The provider session GUID.\n * @param assetType - The type of the asset.\n * @param asset - The asset to attach.\n * @returns A promise that resolves when the asset is attached.\n */\n async attachTestCaseAsset(id, assetName, providerSessionGuid, assetType, asset) {\n await this.uidToResultIdMap[id]?.then(resultId => this.autoApi.uploadAsset(resultId, asset, assetName, providerSessionGuid, assetType));\n }\n /**\n * Ends the test runner and performs necessary cleanup tasks.\n * @returns A promise that resolves when the runner has ended.\n */\n async runnerEnd() {\n // Wait for all results to be created\n const resultIds = (await Promise.all(Object.values(this.uidToResultIdMap))) || [];\n // Wait for the results to be submitted\n void (await Promise.all(Object.values(this.resultSubmissionMap)));\n // Wait the heartbeat to be ended\n void (await this.heartbeatService?.end());\n void (await this.autoApi.endTestRun(this.testRunId));\n // Fetch the provider session asset links and save them off to a file\n const resp = await this.autoApi.getProviderSessionLinks(resultIds);\n const jsonArray = resp.data || [];\n if (jsonArray.length > 0) {\n console.info(JSON.stringify(jsonArray));\n // this is the wdio.conf outputDir\n const outputPath = '.';\n writeFileSync(pathJoin(outputPath, 'providerUrls.txt'), JSON.stringify(jsonArray, null, 1));\n }\n }\n}\nfunction parseTestCaseName(testCaseName) {\n // Split the name on spaces. We will reassemble after parsing out the other ids\n const tokens = testCaseName.split(' ');\n let testRailTestCaseId;\n let applauseTestCaseId;\n tokens.forEach(token => {\n if (token?.startsWith(TEST_RAIL_CASE_ID_PREFIX)) {\n if (testRailTestCaseId !== undefined) {\n console.warn('Multiple TestRail case ids detected in testCase name');\n }\n testRailTestCaseId = token.substring(TEST_RAIL_CASE_ID_PREFIX.length);\n }\n else if (token?.startsWith(APPLAUSE_CASE_ID_PREFIX)) {\n if (applauseTestCaseId !== undefined) {\n console.warn('Multiple Applause case ids detected in testCase name');\n }\n applauseTestCaseId = token.substring(APPLAUSE_CASE_ID_PREFIX.length);\n }\n });\n return {\n applauseTestCaseId,\n testRailTestCaseId,\n testCaseName: tokens\n .filter(token => token !== `${TEST_RAIL_CASE_ID_PREFIX}${testRailTestCaseId || ''}`)\n .filter(token => token !== `${APPLAUSE_CASE_ID_PREFIX}${applauseTestCaseId || ''}`)\n .join(' ')\n .trim(),\n };\n}\n//# sourceMappingURL=reporter.js.map"],"names":["pathJoin"],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index b7902a4..1ceb40f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.0.1", + "version": "1.1.0", "name": "applause-reporter-common", "umd:name": "applause-reporter-common", "repository": "https://github.com/ApplauseOSS/applause-reporter-common", @@ -64,8 +64,11 @@ }, "dependencies": { "axios": "^1.5.0", + "logform": "^2.6.0", "mailparser": "^3.6.5", "validator": "^13.11.0", + "winston": "^3.13.0", + "winston-transport": "^4.7.0", "yarn": "^1.22.19" } } diff --git a/src/auto-api.ts b/src/auto-api.ts index 7d182de..28311bc 100644 --- a/src/auto-api.ts +++ b/src/auto-api.ts @@ -1,5 +1,12 @@ +/** + * This file contains the implementation of the `AutoApi` class, which is responsible for making API calls to interact with the Applause platform. + * The `AutoApi` class provides methods for starting and ending test runs, creating test cases, submitting test case results, and performing other operations related to test management. + * It also includes properties and methods to track the number of HTTP calls in progress. + */ + import axios, { AxiosInstance, AxiosResponse } from 'axios'; import { + AssetType, CreateTestCaseResultDto, CreateTestCaseResultResponseDto, EmailAddressResponse, @@ -17,12 +24,17 @@ export class AutoApi { private callsInFlight: number; /** - * tracks number of HTTP calls in progress, used by reporters that want to know when our async work is finished + * Tracks the number of HTTP calls in progress. + * This property is used by reporters that want to know when the async work is finished. */ public get getCallsInFlight(): number { return this.callsInFlight; } + /** + * Creates an instance of the `AutoApi` class. + * @param options - The configuration options for the Applause API. + */ constructor(readonly options: ApplauseConfig) { this.callsInFlight = 0; validateConfig(options); @@ -54,6 +66,11 @@ export class AutoApi { ); } + /** + * Starts a new test run. + * @param info - The information for creating the test run. + * @returns A promise that resolves to the response containing the created test run. + */ async startTestRun( info: TestRunCreateDto ): Promise> { @@ -87,6 +104,11 @@ export class AutoApi { } } + /** + * Ends a test run. + * @param testRunId - The ID of the test run to end. + * @returns A promise that resolves to the response indicating the completion of the test run. + */ async endTestRun(testRunId: number): Promise> { this.callsInFlight += 1; try { @@ -98,6 +120,11 @@ export class AutoApi { } } + /** + * Starts a new test case. + * @param params - The parameters for creating the test case. + * @returns A promise that resolves to the response containing the created test case. + */ async startTestCase( params: CreateTestCaseResultDto ): Promise> { @@ -113,6 +140,11 @@ export class AutoApi { } } + /** + * Submits a test case result. + * @param params - The parameters for submitting the test case result. + * @returns A promise that resolves when the test case result is submitted. + */ async submitTestCaseResult(params: SubmitTestCaseResultDto): Promise { this.callsInFlight += 1; try { @@ -122,6 +154,11 @@ export class AutoApi { } } + /** + * Retrieves the provider session links for the specified test results. + * @param resultIds - The IDs of the test results. + * @returns A promise that resolves to the response containing the provider session links. + */ async getProviderSessionLinks( resultIds: number[] ): Promise> { @@ -138,6 +175,11 @@ export class AutoApi { } } + /** + * Sends a heartbeat for the specified test run. + * @param testRunId - The ID of the test run. + * @returns A promise that resolves to the response indicating the heartbeat was sent. + */ async sendSdkHeartbeat(testRunId: number): Promise> { this.callsInFlight += 1; try { @@ -150,6 +192,11 @@ export class AutoApi { } } + /** + * Retrieves the email address for the specified email prefix. + * @param emailPrefix - The prefix of the email address. + * @returns A promise that resolves to the response containing the email address. + */ async getEmailAddress( emailPrefix: string ): Promise> { @@ -164,6 +211,11 @@ export class AutoApi { } } + /** + * Retrieves the content of the specified email. + * @param request - The request parameters for retrieving the email content. + * @returns A promise that resolves to the response containing the email content. + */ async getEmailContent( request: EmailFetchRequest ): Promise> { @@ -178,4 +230,38 @@ export class AutoApi { this.callsInFlight -= 1; } } + + /** + * Uploads an asset for the specified test result. + * @param resultId - The ID of the test result. + * @param file - The file to upload as an asset. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The GUID of the provider session. + * @param assetType - The type of the asset. + * @returns A promise that resolves to the response indicating the asset was uploaded. + */ + async uploadAsset( + resultId: number, + file: Buffer, + assetName: string, + providerSessionGuid: string, + assetType: AssetType + ): Promise> { + this.callsInFlight += 1; + + try { + // this filters out falsy values (null, undefined, 0) + return await this.client.postForm( + `/api/v1.0/test-result/${resultId}/upload`, + { + file, + assetName, + providerSessionGuid, + assetType, + } + ); + } finally { + this.callsInFlight -= 1; + } + } } diff --git a/src/config.ts b/src/config.ts index 2b6705a..d0c3e3a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,6 @@ +/** + * Represents the configuration options for the Applause Reporter. + */ import { existsSync, readFileSync } from 'fs'; import { TestRailOptions } from './dto.ts'; import path from 'path'; @@ -5,22 +8,37 @@ import path from 'path'; import Validator from 'validator'; const validator = Validator.default; +/** + * Represents the configuration object for the Applause Reporter. + */ export interface ApplauseConfig { readonly baseUrl: string; readonly apiKey: string; readonly productId: number; readonly testRailOptions?: TestRailOptions; readonly applauseTestCycleId?: number; + readonly logLevel?: string; } +/** + * The default base URL for the Applause API. + */ export const DEFAULT_URL = 'https://prod-auto-api.cloud.applause.com/'; +/** + * Represents the properties for loading the configuration. + */ export interface ConfigLoadProperties { configFile?: string; properties?: Partial; } -// Loads the configuration +/** + * Loads the configuration for the Applause Reporter. + * @param loadOptions - The options for loading the configuration. + * @returns The loaded Applause configuration. + * @throws Error if the configuration is not complete or invalid. + */ export function loadConfig(loadOptions?: ConfigLoadProperties): ApplauseConfig { // Setup the initial config with any default properties let config: Partial = { @@ -55,6 +73,12 @@ export function loadConfig(loadOptions?: ConfigLoadProperties): ApplauseConfig { return finalConfig; } +/** + * Overrides the configuration with the provided overrides. + * @param config - The base configuration. + * @param overrides - The overrides to apply. + * @returns The overridden configuration. + */ export function overrideConfig( config: Partial, overrides?: Partial @@ -68,6 +92,11 @@ export function overrideConfig( ); } +/** + * Checks if the configuration is complete. + * @param config - The configuration to check. + * @returns True if the configuration is complete, false otherwise. + */ export function isComplete(config: Partial): boolean { return ( config.baseUrl !== undefined && @@ -76,6 +105,11 @@ export function isComplete(config: Partial): boolean { ); } +/** + * Loads the configuration from the specified file. + * @param configFile - The path to the configuration file. + * @returns The loaded configuration from the file. + */ export function loadConfigFromFile( configFile?: string ): Partial { @@ -83,10 +117,15 @@ export function loadConfigFromFile( if (!existsSync(configFilePath)) { return {}; } - const fileCotents = readFileSync(configFilePath, 'utf8'); - return JSON.parse(fileCotents) as Partial; + const fileContents = readFileSync(configFilePath, 'utf8'); + return JSON.parse(fileContents) as Partial; } +/** + * Validates the configuration. + * @param config - The configuration to validate. + * @throws Error if the configuration is invalid. + */ export function validateConfig(config: ApplauseConfig) { if (!Number.isInteger(config.productId) || config.productId <= 0) { throw new Error( @@ -107,7 +146,7 @@ export function validateConfig(config: ApplauseConfig) { }) ) { throw new Error( - `baseUrl is not valid HTTP/HTTPS URL, was: ${config.baseUrl}` + `baseUrl is not a valid HTTP/HTTPS URL, was: ${config.baseUrl}` ); } @@ -116,6 +155,11 @@ export function validateConfig(config: ApplauseConfig) { } } +/** + * Validates a partial configuration. + * @param config - The partial configuration to validate. + * @throws Error if the partial configuration is invalid. + */ export function validatePartialConfig(config: Partial) { if ( config.productId !== undefined && diff --git a/src/dto.ts b/src/dto.ts index edab69f..ca1a1ee 100644 --- a/src/dto.ts +++ b/src/dto.ts @@ -127,3 +127,25 @@ export interface EmailAddressResponse { export interface EmailFetchRequest { emailAddress: string; } + +export enum AssetType { + SCREENSHOT = 'SCREENSHOT', + FAILURE_SCREENSHOT = 'FAILURE_SCREENSHOT', + VIDEO = 'VIDEO', + NETWORK_HAR = 'NETWORK_HAR', + VITALS_LOG = 'VITALS_LOG', + CONSOLE_LOG = 'CONSOLE_LOG', + NETWORK_LOG = 'NETWORK_LOG', + DEVICE_LOG = 'DEVICE_LOG', + SELENIUM_LOG = 'SELENIUM_LOG', + SELENIUM_LOG_JSON = 'SELENIUM_LOG_JSON', + BROWSER_LOG = 'BROWSER_LOG', + FRAMEWORK_LOG = 'FRAMEWORK_LOG', + EMAIL = 'EMAIL', + PAGE_SOURCE = 'PAGE_SOURCE', + CODE_BUNDLE = 'CODE_BUNDLE', + RESULTS_ZIP = 'RESULTS_ZIP', + SESSION_DETAILS = 'SESSION_DETAILS', + DEVICE_DETAILS = 'DEVICE_DETAILS', + UNKNOWN = 'UNKNOWN', +} diff --git a/src/email-helper.ts b/src/email-helper.ts index b4bd23a..efb37d0 100644 --- a/src/email-helper.ts +++ b/src/email-helper.ts @@ -1,9 +1,18 @@ import { AutoApi } from './auto-api.ts'; import { Inbox } from './email/inbox.ts'; +/** + * Helper class for managing email functionality. + */ export class EmailHelper { constructor(private autoApi: AutoApi) {} + /** + * Retrieves the inbox for the specified email prefix. + * + * @param emailPrefix - The prefix used to generate the email address. + * @returns A Promise that resolves to an Inbox object. + */ async getInbox(emailPrefix: string): Promise { const generatedAddress: string = ( await this.autoApi.getEmailAddress(emailPrefix) diff --git a/src/email/attachment.ts b/src/email/attachment.ts index 1245418..734e9ad 100644 --- a/src/email/attachment.ts +++ b/src/email/attachment.ts @@ -1,4 +1,14 @@ +/** + * Represents an email attachment. + */ export interface Attachment { + /** + * The name of the file. + */ fileName: string; + + /** + * The content of the file as a Uint16Array. + */ context: Uint16Array; } diff --git a/src/email/inbox.ts b/src/email/inbox.ts index 8723663..0cfe0f8 100644 --- a/src/email/inbox.ts +++ b/src/email/inbox.ts @@ -1,12 +1,24 @@ import { AutoApi } from '../auto-api.ts'; import { ParsedMail, simpleParser } from 'mailparser'; +/** + * Represents an email inbox. + */ export class Inbox { + /** + * Creates an instance of Inbox. + * @param emailAddress - The email address associated with the inbox. + * @param autoApi - An instance of the AutoApi class. + */ constructor( public readonly emailAddress: string, private autoApi: AutoApi ) {} + /** + * Retrieves the content of an email from the inbox. + * @returns A Promise that resolves to the parsed email content. + */ async getEmail(): Promise { const res = await this.autoApi.getEmailContent({ emailAddress: this.emailAddress, diff --git a/src/heartbeat.ts b/src/heartbeat.ts index 9f2afe5..36bba46 100644 --- a/src/heartbeat.ts +++ b/src/heartbeat.ts @@ -1,23 +1,41 @@ +import winston from 'winston'; import { AutoApi } from './auto-api.ts'; +/** + * Represents a service for sending heartbeats during a test run. + */ export class TestRunHeartbeatService { private enabled = false; private nextHeartbeat?: Promise; + /** + * Creates an instance of TestRunHeartbeatService. + * @param testRunId - The ID of the test run. + * @param autoApi - The AutoApi instance used for sending heartbeats. + */ constructor( readonly testRunId: number, - readonly autoApi: AutoApi + readonly autoApi: AutoApi, + readonly logger: winston.Logger ) {} + /** + * Starts sending heartbeats. + * @returns A promise that resolves when the heartbeats are started. + */ async start(): Promise { // End the current heartbeat if it has started await this.end(); - // Set up va new interval + // Set up a new interval this.enabled = true; this.scheduleNextHeartbeat(); } + /** + * Checks if the heartbeats are enabled. + * @returns True if the heartbeats are enabled, false otherwise. + */ public isEnabled(): boolean { return this.enabled; } @@ -38,6 +56,10 @@ export class TestRunHeartbeatService { this.scheduleNextHeartbeat(); } + /** + * Ends the heartbeats. + * @returns A promise that resolves when the heartbeats are ended. + */ async end(): Promise { if (this.nextHeartbeat !== undefined) { this.enabled = false; diff --git a/src/logging.ts b/src/logging.ts new file mode 100644 index 0000000..409bdd3 --- /dev/null +++ b/src/logging.ts @@ -0,0 +1,64 @@ +import Transport from 'winston-transport'; +import { TransformableInfo } from 'logform'; + +/** + * A simple Class for storing and retrieving log messages. + */ +export class LoggingContainer { + private logs: string[] = []; + + /** + * Retrieves all logs stored in the container. + * + * @returns An array of log messages. + */ + public getLogs(): string[] { + return this.logs; + } + + /** + * Retrieves and clears all logs stored in the container. + * + * @returns An array of log messages. + */ + public drainLogs(): string[] { + const logs = this.logs; + this.clearLogs(); + return logs; + } + + /** + * Clears all logs stored in the container. + */ + public clearLogs(): void { + this.logs = []; + } + + /** + * Adds a log message to the container. + * + * @param log - The log message to add. + */ + public addLog(log: string): void { + this.logs.push(log); + } +} + +// Create a new Shared LoggingContainer to store logs +export const APPLAUSE_LOG_RECORDS: LoggingContainer = new LoggingContainer(); + +/** + * A Custom Winston Transport that sends logs to the Applause LoggingContainer + */ +export class ApplauseTransport extends Transport { + constructor() { + super({}); + } + + log(info: TransformableInfo, callback: () => void): void { + APPLAUSE_LOG_RECORDS.addLog(info.message as string); + + // Continue to the next transport + callback(); + } +} diff --git a/src/reporter.ts b/src/reporter.ts index 85095f9..78a7607 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -3,12 +3,21 @@ import { AutoApi } from './auto-api.ts'; import { AdditionalTestCaseParams, AdditionalTestCaseResultParams, + AssetType, TestResultStatus, } from './dto.ts'; import { TestRunHeartbeatService } from './heartbeat.ts'; import { join as pathJoin } from 'path'; import { ApplauseConfig } from './config.ts'; +import winston from 'winston'; +import { ApplauseTransport } from './logging.ts'; +const TEST_RAIL_CASE_ID_PREFIX: string = 'TestRail-'; +const APPLAUSE_CASE_ID_PREFIX: string = 'Applause-'; + +/** + * Represents an Applause reporter. + */ export class ApplauseReporter { private autoApi: AutoApi; private initializer: RunInitializer; @@ -16,48 +25,92 @@ export class ApplauseReporter { private runStarted: boolean = false; private runFinished: boolean = false; - constructor(config: ApplauseConfig) { + /** + * Creates an instance of ApplauseReporter. + * @param config - The Applause configuration. + */ + constructor( + config: ApplauseConfig, + private logger?: winston.Logger + ) { this.autoApi = new AutoApi(config); - this.initializer = new RunInitializer(this.autoApi); + this.initializer = new RunInitializer(this.autoApi, this.logger); + const runId = process.env['APPLAUSE_RUN_ID']; + if (runId !== undefined) { + const r = new RunReporter(this.autoApi, parseInt(runId)); + this.reporter = new Promise(resolve => resolve(r)); + this.runStarted = true; + } } - public runnerStart(tests?: string[]): void { + /** + * Starts the Applause runner. + * @param tests - Optional array of test names to run. + * @returns A promise that resolves to the test run ID. + * @throws Error if a run is already started or finished. + */ + public async runnerStart(tests?: string[]): Promise { + if (this.reporter !== undefined) { + throw new Error( + 'Cannot start a run - run already started or run already finished' + ); + } this.reporter = this.initializer.initializeRun(tests); - void this.reporter.then(() => { - this.runStarted = true; - }); + const initializedReporter = await this.reporter; + this.runStarted = true; + process.env['APPLAUSE_RUN_ID'] = initializedReporter.testRunId.toString(); + return initializedReporter.testRunId; } - public startTestCase( + /** + * Starts a test case. + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Optional additional parameters for the test case. + * @returns A promise that resolves to the test case ID. + * @throws Error if a run was never initialized. + */ + public async startTestCase( id: string, testCaseName: string, params?: AdditionalTestCaseParams - ): void { + ): Promise { if (this.reporter === undefined) { throw new Error( 'Cannot start a test case for a run that was never initialized' ); } - void this.reporter.then(reporter => - reporter.startTestCase(id, testCaseName, params) - ); + const reporter = await this.reporter; + return reporter.startTestCase(id, testCaseName, params); } - public submitTestCaseResult( + /** + * Submits a test case result. + * @param id - The ID of the test case. + * @param status - The status of the test case result. + * @param params - Optional additional parameters for the test case result. + * @returns A promise that resolves to the test case result ID. + * @throws Error if a run was never initialized. + */ + public async submitTestCaseResult( id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams - ): void { + ): Promise { if (this.reporter === undefined) { throw new Error( 'Cannot submit test case result for a run that was never initialized' ); } - void this.reporter.then(reporter => - reporter.submitTestCaseResult(id, status, params) - ); + const reporter = await this.reporter; + return reporter.submitTestCaseResult(id, status, params); } + /** + * Ends the Applause runner. + * @returns A promise that resolves when the runner is ended. + * @throws Error if a run was never initialized. + */ public async runnerEnd(): Promise { if (this.reporter === undefined) { throw new Error('Cannot end a run that was never initialized'); @@ -67,8 +120,44 @@ export class ApplauseReporter { .then(() => (this.runFinished = true)); } + /** + * Attaches an asset to a test case. + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset data as a Buffer. + * @returns A promise that resolves when the asset is attached. + * @throws Error if a run was never initialized. + */ + public async attachTestCaseAsset( + id: string, + assetName: string, + providerSessionGuid: string, + assetType: AssetType, + asset: Buffer + ): Promise { + if (this.reporter === undefined) { + throw new Error( + 'Cannot attach an asset for a run that was never initialized' + ); + } + return await this.reporter.then(reporter => + reporter.attachTestCaseAsset( + id, + assetName, + providerSessionGuid, + assetType, + asset + ) + ); + } + + /** + * Checks if the Applause runner is synchronized. + * @returns True if the runner is not yet started or has ended, and all calls made to the applause API have finished. + */ public isSynchronized(): boolean { - // Verify the run is not yet started or it has ended, and all calls made to the applause api have finished return ( (!this.runStarted || (this.runStarted && this.runFinished)) && this.autoApi.getCallsInFlight == 0 @@ -76,9 +165,29 @@ export class ApplauseReporter { } } +/** + * Represents a Run Initializer. + */ export class RunInitializer { - constructor(private autoApi: AutoApi) {} + private logger: winston.Logger; + + constructor( + private autoApi: AutoApi, + logger?: winston.Logger + ) { + this.logger = + logger || + winston.createLogger({ + transports: [new winston.transports.Console(), new ApplauseTransport()], + }); + } + /** + * Initializes a test run. + * @param tests - An optional array of test names to include in the run. + * @returns A promise that resolves to a RunReporter instance. + * @throws An error if unable to create the test run. + */ async initializeRun(tests?: string[]): Promise { const cleanedTests = tests ?.map(parseTestCaseName) @@ -93,30 +202,51 @@ export class RunInitializer { throw new Error('Unable to create test run'); } const runId = testRunCreateResponse.data.runId; - console.log('Test Run %d initialized', runId); - const heartbeatService = new TestRunHeartbeatService(runId, this.autoApi); + this.logger.info('Test Run %d initialized', runId); + const heartbeatService = new TestRunHeartbeatService( + runId, + this.autoApi, + this.logger + ); await heartbeatService.start(); return new RunReporter(this.autoApi, runId, heartbeatService); } } +/** + * Handles reporting test results to the Applause API. + */ export class RunReporter { private uidToResultIdMap: Record> = {}; - private resultSubmissionMap: Record> = {}; + private resultSubmissionMap: Record> = {}; + /** + * Creates a new instance of the Reporter class. + * @param autoApi - The AutoApi instance. + * @param testRunId - The ID of the test run. + * @param heartbeatService - (Optional) The TestRunHeartbeatService instance. + */ constructor( private autoApi: AutoApi, - private testRunId: number, - private heartbeatService: TestRunHeartbeatService + public readonly testRunId: number, + private heartbeatService?: TestRunHeartbeatService ) {} + /** + * Starts a test case and returns a promise that resolves to the test result ID. + * + * @param id - The ID of the test case. + * @param testCaseName - The name of the test case. + * @param params - Additional parameters for the test case. + * @returns A promise that resolves to the test result ID. + */ public startTestCase( id: string, testCaseName: string, params?: AdditionalTestCaseParams - ): void { + ): Promise { const parsedTestCase = parseTestCaseName(testCaseName); - this.uidToResultIdMap[id] = this.autoApi + const submission = this.autoApi .startTestCase({ testCaseName: parsedTestCase.testCaseName, testCaseId: parsedTestCase.testRailTestCaseId, @@ -131,22 +261,68 @@ export class RunReporter { .then(res => { return res.data.testResultId; }); + this.uidToResultIdMap[id] = submission; + return submission; } + /** + * Submits the result of a test case. + * + * @param id - The ID of the test case. + * @param status - The status of the test result. + * @param params - Additional parameters for the test result. + * @returns A promise that resolves to the result ID. + */ public submitTestCaseResult( id: string, status: TestResultStatus, params?: AdditionalTestCaseResultParams - ): void { - this.resultSubmissionMap[id] = this.uidToResultIdMap[id]?.then(resultId => - this.autoApi.submitTestCaseResult({ - status: status, - testResultId: resultId, - ...params, - }) + ): Promise { + const submission = this.uidToResultIdMap[id]?.then(resultId => + this.autoApi + .submitTestCaseResult({ + status: status, + testResultId: resultId, + ...params, + }) + .then(() => resultId) ); + this.resultSubmissionMap[id] = submission; + return submission; } + /** + * Attaches a test case asset to a result. + * + * @param id - The ID of the test case. + * @param assetName - The name of the asset. + * @param providerSessionGuid - The provider session GUID. + * @param assetType - The type of the asset. + * @param asset - The asset to attach. + * @returns A promise that resolves when the asset is attached. + */ + public async attachTestCaseAsset( + id: string, + assetName: string, + providerSessionGuid: string, + assetType: AssetType, + asset: Buffer + ): Promise { + await this.uidToResultIdMap[id]?.then(resultId => + this.autoApi.uploadAsset( + resultId, + asset, + assetName, + providerSessionGuid, + assetType + ) + ); + } + + /** + * Ends the test runner and performs necessary cleanup tasks. + * @returns A promise that resolves when the runner has ended. + */ public async runnerEnd(): Promise { // Wait for all results to be created const resultIds = @@ -156,7 +332,7 @@ export class RunReporter { void (await Promise.all(Object.values(this.resultSubmissionMap))); // Wait the heartbeat to be ended - void (await this.heartbeatService.end()); + void (await this.heartbeatService?.end()); void (await this.autoApi.endTestRun(this.testRunId)); // Fetch the provider session asset links and save them off to a file @@ -173,8 +349,6 @@ export class RunReporter { } } } -const TEST_RAIL_CASE_ID_PREFIX: string = 'TestRail-'; -const APPLAUSE_CASE_ID_PREFIX: string = 'Applause-'; function parseTestCaseName(testCaseName: string): ParsedTestCaseName { // Split the name on spaces. We will reassemble after parsing out the other ids diff --git a/src/version.ts b/src/version.ts index 3c76b58..37cd57f 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const API_VERSION = '1.0.0'; +export const API_VERSION = '1.1.0'; diff --git a/test/config.test.ts b/test/config.test.ts index 0d6704c..2b88536 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -45,7 +45,7 @@ describe('config loader', () => { loadConfig({ configFile: './test/resources/bad-url-applause.json', }) - ).toThrowError('baseUrl is not valid HTTP/HTTPS URL, was: notAUrl'); + ).toThrowError('baseUrl is not a valid HTTP/HTTPS URL, was: notAUrl'); }); it('should fail for an invalid productId', () => { diff --git a/test/logging.test.ts b/test/logging.test.ts new file mode 100644 index 0000000..8c9f684 --- /dev/null +++ b/test/logging.test.ts @@ -0,0 +1,46 @@ +import { LoggingContainer, ApplauseTransport, APPLAUSE_LOG_RECORDS } from '../src/logging.ts'; + +describe('LoggingContainer', () => { + let loggingContainer: LoggingContainer; + + beforeEach(() => { + loggingContainer = new LoggingContainer(); + }); + + it('should add logs', () => { + loggingContainer.addLog('Log 1'); + loggingContainer.addLog('Log 2'); + + expect(loggingContainer.getLogs()).toEqual(['Log 1', 'Log 2']); + }); + + it('should clear logs', () => { + loggingContainer.addLog('Log 1'); + loggingContainer.addLog('Log 2'); + loggingContainer.clearLogs(); + + expect(loggingContainer.getLogs()).toEqual([]); + }); +}); + +describe('ApplauseTransport', () => { + let applauseTransport: ApplauseTransport; + + beforeEach(() => { + applauseTransport = new ApplauseTransport(); + }); + + it('should log information', () => { + const logs: string[] = []; + + applauseTransport.log({message: 'Log 1', level: 'info'}, () => { + logs.push('Log 1'); + }); + + applauseTransport.log({message: 'Log 2', level: 'info'}, () => { + logs.push('Log 2'); + }); + + expect(logs).toEqual(APPLAUSE_LOG_RECORDS.getLogs()); + }); +}); diff --git a/yarn.lock b/yarn.lock index 0db9e7b..f2f3f32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -302,6 +302,11 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" @@ -309,6 +314,15 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -851,6 +865,11 @@ resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@types/validator@^13.11.1": version "13.11.1" resolved "https://registry.npmjs.org/@types/validator/-/validator-13.11.1.tgz" @@ -1087,6 +1106,11 @@ array-union@^2.1.0: resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" @@ -1305,7 +1329,7 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz" integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -1324,11 +1348,35 @@ color-name@1.1.3: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -1494,6 +1542,11 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + encoding-japanese@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz" @@ -1786,6 +1839,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -1830,6 +1888,11 @@ flatted@^3.2.7: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" @@ -2038,7 +2101,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2048,6 +2111,11 @@ is-arrayish@^0.2.1: resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz" @@ -2612,6 +2680,11 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + leac@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz" @@ -2696,6 +2769,18 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +logform@^2.3.2, logform@^2.4.0, logform@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" @@ -2812,6 +2897,11 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" @@ -2858,6 +2948,13 @@ once@^1.3.0: dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" @@ -3075,6 +3172,15 @@ react-is@^18.0.0: resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + regexpp@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" @@ -3158,11 +3264,16 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.1.0: +safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -3211,6 +3322,13 @@ signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" @@ -3252,6 +3370,11 @@ sprintf-js@~1.0.2: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" @@ -3276,6 +3399,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -3356,6 +3486,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -3388,6 +3523,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + ts-api-utils@^1.0.1: version "1.0.3" resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz" @@ -3495,6 +3635,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" @@ -3528,6 +3673,32 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"