UNPKG

@devicecloud.dev/dcd

Version:

Better cloud maestro testing

140 lines (139 loc) 6.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestSubmissionService = void 0; const node_crypto_1 = require("node:crypto"); const path = require("node:path"); const methods_1 = require("../methods"); const mimeTypeLookupByExtension = { zip: 'application/zip', }; /** * Service for building test submission form data */ class TestSubmissionService { /** * Build FormData for test submission * @param config Test submission configuration * @returns FormData ready to be submitted to the API */ async buildTestFormData(config) { const { appBinaryId, flowFile, executionPlan, commonRoot, cliVersion, env = [], metadata = [], googlePlay = false, androidApiLevel, androidDevice, androidNoSnapshot, iOSVersion, iOSDevice, name, runnerType, maestroVersion, deviceLocale, orientation, mitmHost, mitmPath, retry, continueOnFailure = true, report, showCrosshairs, maestroChromeOnboarding, raw, debug = false, logger, } = config; const { allExcludeTags, allIncludeTags, flowMetadata, flowOverrides, flowsToRun: testFileNames, referencedFiles, sequence, workspaceConfig, } = executionPlan; const { flows: sequentialFlows = [] } = sequence ?? {}; const testFormData = new FormData(); const envObject = this.parseKeyValuePairs(env); const metadataObject = this.parseKeyValuePairs(metadata); if (Object.keys(envObject).length > 0) { this.logDebug(debug, logger, `[DEBUG] Environment variables: ${JSON.stringify(envObject)}`); } if (Object.keys(metadataObject).length > 0) { this.logDebug(debug, logger, `[DEBUG] User metadata: ${JSON.stringify(metadataObject)}`); } // Log non-YAML file assets being uploaded if (referencedFiles.length > 0) { const nonYamlFiles = referencedFiles.filter((file) => !file.endsWith('.yaml') && !file.endsWith('.yml')); if (nonYamlFiles.length > 0) { this.logDebug(debug, logger, `[DEBUG] Uploading ${nonYamlFiles.length} non-YAML file asset(s):`); for (const file of nonYamlFiles) { const normalizedPath = this.normalizeFilePath(file, commonRoot); this.logDebug(debug, logger, `[DEBUG] - ${normalizedPath}`); } } } this.logDebug(debug, logger, `[DEBUG] Compressing files from path: ${flowFile}`); const buffer = await (0, methods_1.compressFilesFromRelativePath)(flowFile?.endsWith('.yaml') || flowFile?.endsWith('.yml') ? path.dirname(flowFile) : flowFile, [ ...new Set([ ...referencedFiles, ...testFileNames, ...sequentialFlows, ]), ], commonRoot); this.logDebug(debug, logger, `[DEBUG] Compressed file size: ${buffer.length} bytes`); // Calculate SHA-256 hash of the flow ZIP const sha = (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex'); this.logDebug(debug, logger, `[DEBUG] Flow ZIP SHA-256: ${sha}`); const blob = new Blob([buffer], { type: mimeTypeLookupByExtension.zip, }); testFormData.set('file', blob, 'flowFile.zip'); testFormData.set('sha', sha); testFormData.set('appBinaryId', appBinaryId); testFormData.set('testFileNames', JSON.stringify(this.normalizePaths(testFileNames, commonRoot))); testFormData.set('flowMetadata', JSON.stringify(this.normalizePathMap(flowMetadata, commonRoot))); testFormData.set('testFileOverrides', JSON.stringify(this.normalizePathMap(flowOverrides, commonRoot))); testFormData.set('sequentialFlows', JSON.stringify(this.normalizePaths(sequentialFlows, commonRoot))); testFormData.set('env', JSON.stringify(envObject)); testFormData.set('googlePlay', googlePlay ? 'true' : 'false'); const configPayload = { allExcludeTags, allIncludeTags, androidNoSnapshot, autoRetriesRemaining: retry, continueOnFailure, deviceLocale, maestroVersion, mitmHost, mitmPath, orientation, raw: JSON.stringify(raw), report, showCrosshairs, maestroChromeOnboarding, version: cliVersion, }; testFormData.set('config', JSON.stringify(configPayload)); if (Object.keys(metadataObject).length > 0) { const metadataPayload = { userMetadata: metadataObject }; testFormData.set('metadata', JSON.stringify(metadataPayload)); this.logDebug(debug, logger, `[DEBUG] Sending metadata to API: ${JSON.stringify(metadataPayload)}`); } this.setOptionalFields(testFormData, { androidApiLevel, androidDevice, iOSDevice, iOSVersion, name, runnerType, }); if (workspaceConfig) { testFormData.set('workspaceConfig', JSON.stringify(workspaceConfig)); } return testFormData; } logDebug(debug, logger, message) { if (debug && logger) { logger(message); } } normalizeFilePath(filePath, commonRoot) { return filePath.replaceAll(commonRoot, '.').split(path.sep).join('/'); } normalizePathMap(map, commonRoot) { return Object.fromEntries(Object.entries(map).map(([key, value]) => [ this.normalizeFilePath(key, commonRoot), value, ])); } normalizePaths(paths, commonRoot) { return paths.map((p) => this.normalizeFilePath(p, commonRoot)); } parseKeyValuePairs(pairs) { // eslint-disable-next-line unicorn/no-array-reduce return pairs.reduce((acc, cur) => { const [key, ...value] = cur.split('='); // handle case where value includes an equals sign acc[key] = value.join('='); return acc; }, {}); } setOptionalFields(formData, fields) { for (const [key, value] of Object.entries(fields)) { if (value) { formData.set(key, value.toString()); } } } } exports.TestSubmissionService = TestSubmissionService;