UNPKG

@devicecloud.dev/dcd

Version:

Better cloud maestro testing

189 lines (188 loc) 7.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processDependencies = exports.checkIfFilesExistInWorkspace = exports.readTestYamlFileAsJson = exports.readYamlFileAsJson = void 0; exports.getFlowsToRunInSequence = getFlowsToRunInSequence; exports.isFlowFile = isFlowFile; exports.readDirectory = readDirectory; /* eslint-disable unicorn/filename-case */ const yaml = require("js-yaml"); const fs = require("node:fs"); const path = require("node:path"); const commandsThatRequireFiles = new Set(['addMedia', 'runFlow', 'runScript']); function getFlowsToRunInSequence(paths, flowOrder) { if (flowOrder.length === 0) return []; const orderSet = new Set(flowOrder); const namesInOrder = Object.keys(paths).filter((key) => orderSet.has(key)); if (namesInOrder.length === 0) return []; const result = [...orderSet].filter((item) => namesInOrder.includes(item)); if (result.length === 0) { throw new Error(`Could not find flows needed for execution in order: ${[...orderSet] .filter((item) => !namesInOrder.includes(item)) .join(', ')}`); } else if (flowOrder .slice(0, result.length) .every((value, index) => value === result[index])) { return result.map((item) => paths[item]); } else { return []; } } function isFlowFile(filePath) { // Exclude files inside .app bundles // Check if any directory in the path ends with .app const pathParts = filePath.split(path.sep); for (const part of pathParts) { if (part.endsWith('.app')) { return false; } } return filePath.endsWith('.yaml') || filePath.endsWith('.yml'); } const readYamlFileAsJson = (filePath) => { try { const normalizedPath = path.normalize(filePath); const yamlText = fs.readFileSync(normalizedPath, 'utf8'); const result = yaml.load(yamlText); // Ensure includeTags and excludeTags are always arrays if present if (result && typeof result === 'object') { if ('includeTags' in result && !Array.isArray(result.includeTags)) { result.includeTags = result.includeTags ? [result.includeTags] : []; } if ('excludeTags' in result && !Array.isArray(result.excludeTags)) { result.excludeTags = result.excludeTags ? [result.excludeTags] : []; } } return result; } catch (error) { throw new Error(`Error parsing YAML file ${filePath}: ${error}`); } }; exports.readYamlFileAsJson = readYamlFileAsJson; const readTestYamlFileAsJson = (filePath) => { try { const normalizedPath = path.normalize(filePath); const yamlText = fs.readFileSync(normalizedPath, 'utf8'); const normalizedText = yamlText .replaceAll('\r\n', '\n') .replaceAll(/\n---[\t ]*\n/g, '\n---\n'); if (normalizedText.includes('\n---\n')) { const yamlTexts = normalizedText.split('\n---\n'); const config = yaml.load(yamlTexts[0]); const testSteps = yaml.load(yamlTexts[1]); if (Object.keys(config ?? {}).length > 0) { return { config, testSteps }; } } const testSteps = yaml.load(yamlText); if (Object.keys(testSteps).length > 0) { return { config: null, testSteps }; } return { config: null, testSteps }; } catch (error) { const message = `Error parsing YAML file ${filePath}: ${error}`; console.error(message); throw new Error(message); } }; exports.readTestYamlFileAsJson = readTestYamlFileAsJson; async function readDirectory(dir, filterFunction) { const readDirResult = await fs.promises.readdir(dir); const files = await Promise.all(readDirResult.map(async (file) => { const filePath = path.join(dir, file); const stats = await fs.promises.stat(filePath); if (stats.isFile()) if (filterFunction) { if (filterFunction(filePath)) return filePath; } else { return filePath; } })); return files.flat().filter(Boolean); } const checkIfFilesExistInWorkspace = (commandName, command, absoluteFilePath) => { const errors = []; const files = []; const directory = path.dirname(absoluteFilePath); const buildError = (error) => `Flow file "${absoluteFilePath}" has a command "${commandName}" that references a ${error} ${JSON.stringify(command)}`; const processFilePath = (relativePath) => { const absoluteFilePath = path.normalize(path.resolve(directory, relativePath)); const error = checkFile(absoluteFilePath); if (error) errors.push(buildError(error)); files.push(absoluteFilePath); }; // simple command if (typeof command === 'string') { processFilePath(path.normalize(path.join(directory, command))); } // array command if (Array.isArray(command)) { for (const file of command) { processFilePath(file); } } // object command const x = command; // prevent annoying ts error if (typeof command === 'object' && x?.file) processFilePath(x.file); return { errors, files }; }; exports.checkIfFilesExistInWorkspace = checkIfFilesExistInWorkspace; const checkFile = (filePath) => { if (!fs.existsSync(filePath)) return `non-existent file`; }; const checkStepsArray = (steps, absoluteFilePath) => { let errors = []; let files = []; for (const command of steps) { if (typeof command === 'string') continue; for (const [commandName, commandValue] of Object.entries(command)) { if (commandsThatRequireFiles.has(commandName)) { const { errors: newErrors, files: newFiles } = (0, exports.checkIfFilesExistInWorkspace)(commandName, commandValue, path.normalize(absoluteFilePath)); errors = [...errors, ...newErrors]; files = [...files, ...newFiles]; } const nestedCommands = typeof commandValue === 'object' && commandValue.commands; if (nestedCommands) { const { errors: newErrors, files: newFiles } = checkStepsArray(nestedCommands, absoluteFilePath); errors = [...errors, ...newErrors]; files = [...files, ...newFiles]; } } } return { errors, files }; }; const processDependencies = ({ config, input, testSteps, }) => { let allErrors = []; let allFiles = []; const { onFlowComplete, onFlowStart } = config ?? {}; const stepsArray = [testSteps]; if (onFlowStart) stepsArray.push(onFlowStart); if (onFlowComplete) stepsArray.push(onFlowComplete); for (const [index, steps] of stepsArray.entries()) { try { const { errors, files } = checkStepsArray(steps, input); allErrors = [...allErrors, ...errors]; allFiles = [...allFiles, ...files]; } catch { const location = ['Test Steps', 'On Flow Start', 'On Flow Complete'][index]; throw new Error(`Error processing dependencies for file ${input} in the ${location} section. Expected an array of steps but received: ${JSON.stringify(steps)}`); } } return { allErrors, allFiles }; }; exports.processDependencies = processDependencies;