@devicecloud.dev/dcd
Version:
Better cloud maestro testing
189 lines (188 loc) • 7.54 kB
JavaScript
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;
;