puty 0.0.2 → 0.0.3-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/puty.js +5 -4
- package/src/utils.js +135 -5
package/package.json
CHANGED
package/src/puty.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import fs from "node:fs";
|
|
3
2
|
import yaml from "js-yaml";
|
|
4
3
|
import { expect, test, describe } from "vitest";
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
traverseAllFiles,
|
|
7
|
+
parseYamlDocumentsWithIncludes as parseWithIncludes,
|
|
8
|
+
} from "./utils.js";
|
|
7
9
|
|
|
8
10
|
const extensions = [".test.yaml", ".test.yml", ".spec.yaml", ".spec.yml"];
|
|
9
11
|
|
|
@@ -227,8 +229,7 @@ export const injectFunctions = (module, originalTestConfig) => {
|
|
|
227
229
|
export const setupTestSuiteFromYaml = async (dirname) => {
|
|
228
230
|
const testYamlFiles = traverseAllFiles(dirname, extensions);
|
|
229
231
|
for (const file of testYamlFiles) {
|
|
230
|
-
const
|
|
231
|
-
const testConfig = parseYamlDocuments(yamlContent);
|
|
232
|
+
const testConfig = parseWithIncludes(file);
|
|
232
233
|
const filepathRelativeToSpecFile = path.join(
|
|
233
234
|
path.dirname(file),
|
|
234
235
|
testConfig.file,
|
package/src/utils.js
CHANGED
|
@@ -3,16 +3,56 @@ import path, { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import yaml from "js-yaml";
|
|
5
5
|
|
|
6
|
-
export const loadYamlWithPath = (
|
|
6
|
+
export const loadYamlWithPath = (filePath, visitedFiles = new Set()) => {
|
|
7
|
+
const absolutePath = path.resolve(filePath);
|
|
8
|
+
|
|
9
|
+
// Check for circular dependencies
|
|
10
|
+
if (visitedFiles.has(absolutePath)) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Circular dependency detected: ${absolutePath} is already being processed`,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Add current file to visited set
|
|
17
|
+
visitedFiles.add(absolutePath);
|
|
18
|
+
|
|
7
19
|
const includeType = new yaml.Type("!include", {
|
|
8
20
|
kind: "scalar",
|
|
9
|
-
construct: function (
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
construct: function (includePath) {
|
|
22
|
+
// Resolve include path relative to the current file's directory
|
|
23
|
+
const currentDir = path.dirname(absolutePath);
|
|
24
|
+
const resolvedIncludePath = path.resolve(currentDir, includePath);
|
|
25
|
+
|
|
26
|
+
// Check if included file exists
|
|
27
|
+
if (!fs.existsSync(resolvedIncludePath)) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Include file not found: ${resolvedIncludePath} (included from ${absolutePath})`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Recursively load the included file with the same visited files set
|
|
34
|
+
return loadYamlWithPath(resolvedIncludePath, new Set(visitedFiles));
|
|
12
35
|
},
|
|
13
36
|
});
|
|
37
|
+
|
|
14
38
|
const schema = yaml.DEFAULT_SCHEMA.extend([includeType]);
|
|
15
|
-
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
42
|
+
// Check if content contains document separators
|
|
43
|
+
if (content.includes("---")) {
|
|
44
|
+
return yaml.loadAll(content, { schema });
|
|
45
|
+
} else {
|
|
46
|
+
return yaml.load(content, { schema });
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Error loading YAML file ${absolutePath}: ${error.message}`,
|
|
51
|
+
);
|
|
52
|
+
} finally {
|
|
53
|
+
// Remove current file from visited set when done processing
|
|
54
|
+
visitedFiles.delete(absolutePath);
|
|
55
|
+
}
|
|
16
56
|
};
|
|
17
57
|
|
|
18
58
|
/**
|
|
@@ -33,3 +73,93 @@ export const traverseAllFiles = (startPath, extensions) => {
|
|
|
33
73
|
}
|
|
34
74
|
return results;
|
|
35
75
|
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Recursively flatten nested arrays of documents
|
|
79
|
+
*/
|
|
80
|
+
const flattenDocuments = (data) => {
|
|
81
|
+
if (!Array.isArray(data)) {
|
|
82
|
+
return [data];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const result = [];
|
|
86
|
+
for (const item of data) {
|
|
87
|
+
if (Array.isArray(item)) {
|
|
88
|
+
result.push(...flattenDocuments(item));
|
|
89
|
+
} else {
|
|
90
|
+
result.push(item);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Process an array of YAML documents into structured test config
|
|
98
|
+
*/
|
|
99
|
+
const processDocuments = (docs) => {
|
|
100
|
+
const config = {
|
|
101
|
+
file: null,
|
|
102
|
+
group: null,
|
|
103
|
+
suites: [],
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
let currentSuite = null;
|
|
107
|
+
|
|
108
|
+
for (const doc of docs) {
|
|
109
|
+
if (doc.file) {
|
|
110
|
+
config.file = doc.file;
|
|
111
|
+
config.group = doc.group || doc.name;
|
|
112
|
+
if (doc.suites) {
|
|
113
|
+
config.suiteNames = doc.suites;
|
|
114
|
+
}
|
|
115
|
+
} else if (doc.suite) {
|
|
116
|
+
if (currentSuite) {
|
|
117
|
+
config.suites.push(currentSuite);
|
|
118
|
+
}
|
|
119
|
+
currentSuite = {
|
|
120
|
+
name: doc.suite,
|
|
121
|
+
exportName: doc.exportName || doc.suite,
|
|
122
|
+
cases: [],
|
|
123
|
+
};
|
|
124
|
+
// Only add mode and constructorArgs if mode is explicitly 'class'
|
|
125
|
+
if (doc.mode === "class") {
|
|
126
|
+
currentSuite.mode = "class";
|
|
127
|
+
currentSuite.constructorArgs = doc.constructorArgs || [];
|
|
128
|
+
}
|
|
129
|
+
} else if (doc.case && currentSuite) {
|
|
130
|
+
const testCase = {
|
|
131
|
+
name: doc.case,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (currentSuite.mode === "class") {
|
|
135
|
+
testCase.executions = doc.executions || [];
|
|
136
|
+
} else {
|
|
137
|
+
testCase.in = doc.in || [];
|
|
138
|
+
testCase.out = doc.out;
|
|
139
|
+
if (doc.throws) {
|
|
140
|
+
testCase.throws = doc.throws;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
currentSuite.cases.push(testCase);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (currentSuite) {
|
|
149
|
+
config.suites.push(currentSuite);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return config;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Parse YAML file with !include support into structured test config
|
|
157
|
+
*/
|
|
158
|
+
export const parseYamlDocumentsWithIncludes = (filePath) => {
|
|
159
|
+
const yamlData = loadYamlWithPath(filePath);
|
|
160
|
+
|
|
161
|
+
// Flatten any nested arrays that come from multi-document includes
|
|
162
|
+
const flattenedDocs = flattenDocuments(yamlData);
|
|
163
|
+
|
|
164
|
+
return processDocuments(flattenedDocs);
|
|
165
|
+
};
|