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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puty",
3
- "version": "0.0.2",
3
+ "version": "0.0.3-rc1",
4
4
  "description": "A tooling function to test javascript functions and classes.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
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 { traverseAllFiles } from "./utils.js";
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 yamlContent = fs.readFileSync(file, "utf8");
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 = (path) => {
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 (filePath) {
10
- const content = fs.readFileSync(join(path, "..", filePath), "utf8");
11
- return yaml.load(content); // you could recurse here
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
- return yaml.load(fs.readFileSync(path, "utf8"), { schema });
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
+ };