@telorun/test 0.1.8 → 0.1.9
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/dist/suite.js +42 -22
- package/package.json +3 -3
- package/src/suite.ts +43 -24
package/dist/suite.js
CHANGED
|
@@ -48,17 +48,37 @@ function discoverTests(baseDir, include, exclude, filter) {
|
|
|
48
48
|
const includeRe = include.map(globToRegex);
|
|
49
49
|
const excludeRe = exclude.map(globToRegex);
|
|
50
50
|
const results = [];
|
|
51
|
+
// Dedupe by realpath: pnpm symlinks workspace packages into multiple
|
|
52
|
+
// node_modules locations, so the same test file can be reached via
|
|
53
|
+
// many paths. Without dedupe, recursive traversal yields the same yaml
|
|
54
|
+
// dozens of times under different prefixes.
|
|
55
|
+
const seen = new Set();
|
|
51
56
|
for (const entry of entries) {
|
|
52
|
-
const
|
|
53
|
-
|
|
57
|
+
const rel = entry.replace(/\\/g, "/");
|
|
58
|
+
// Hard-skip node_modules: those are always symlinked workspace dupes
|
|
59
|
+
// (or vendored copies that shouldn't run as workspace tests). The
|
|
60
|
+
// user-facing `exclude` defaults to `__fixtures__` only, but
|
|
61
|
+
// node_modules is a hard architectural skip.
|
|
62
|
+
if (rel.split("/").includes("node_modules"))
|
|
54
63
|
continue;
|
|
55
|
-
if (!includeRe.some((re) => re.test(
|
|
64
|
+
if (!includeRe.some((re) => re.test(rel)))
|
|
56
65
|
continue;
|
|
57
|
-
if (excludeRe.some((re) => re.test(
|
|
66
|
+
if (excludeRe.some((re) => re.test(rel)))
|
|
58
67
|
continue;
|
|
59
|
-
if (filter && !
|
|
68
|
+
if (filter && !rel.includes(filter))
|
|
60
69
|
continue;
|
|
61
|
-
|
|
70
|
+
const abs = path.resolve(baseDir, rel);
|
|
71
|
+
let real;
|
|
72
|
+
try {
|
|
73
|
+
real = fs.realpathSync(abs);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
real = abs;
|
|
77
|
+
}
|
|
78
|
+
if (seen.has(real))
|
|
79
|
+
continue;
|
|
80
|
+
seen.add(real);
|
|
81
|
+
results.push(abs);
|
|
62
82
|
}
|
|
63
83
|
results.sort();
|
|
64
84
|
return results;
|
|
@@ -66,13 +86,19 @@ function discoverTests(baseDir, include, exclude, filter) {
|
|
|
66
86
|
function labelFor(testPath, baseDir) {
|
|
67
87
|
return path.relative(baseDir, testPath);
|
|
68
88
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
89
|
+
function tryReadFile(filePath) {
|
|
90
|
+
try {
|
|
91
|
+
return fs.readFileSync(filePath, "utf8");
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
73
97
|
function parseEnvFile(content) {
|
|
98
|
+
if (!content)
|
|
99
|
+
return {};
|
|
74
100
|
const result = {};
|
|
75
|
-
for (const line of content.split(
|
|
101
|
+
for (const line of content.split(/\r?\n/)) {
|
|
76
102
|
const trimmed = line.trim();
|
|
77
103
|
if (!trimmed || trimmed.startsWith("#"))
|
|
78
104
|
continue;
|
|
@@ -81,24 +107,18 @@ function parseEnvFile(content) {
|
|
|
81
107
|
continue;
|
|
82
108
|
const key = trimmed.slice(0, eq).trim();
|
|
83
109
|
let value = trimmed.slice(eq + 1).trim();
|
|
84
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
110
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
111
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
85
112
|
value = value.slice(1, -1);
|
|
86
113
|
}
|
|
87
114
|
result[key] = value;
|
|
88
115
|
}
|
|
89
116
|
return result;
|
|
90
117
|
}
|
|
91
|
-
function tryReadFile(filePath) {
|
|
92
|
-
try {
|
|
93
|
-
return fs.readFileSync(filePath, "utf8");
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
118
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
119
|
+
* Loads .env and .env.local files (in that order) from the directory of
|
|
120
|
+
* the manifest, layered under (and overridden by) process.env.
|
|
121
|
+
*
|
|
102
122
|
* Keys already present in process.env take precedence (same as CLI behaviour).
|
|
103
123
|
*/
|
|
104
124
|
function buildEnvForManifest(manifestPath) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/test",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Telo Test module - Test runner for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@sinclair/typebox": "^0.34.48",
|
|
36
|
-
"@telorun/kernel": "0.
|
|
37
|
-
"@telorun/sdk": "0.
|
|
36
|
+
"@telorun/kernel": "0.5.0",
|
|
37
|
+
"@telorun/sdk": "0.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^20.0.0",
|
package/src/suite.ts
CHANGED
|
@@ -78,15 +78,33 @@ function discoverTests(
|
|
|
78
78
|
const excludeRe = exclude.map(globToRegex);
|
|
79
79
|
|
|
80
80
|
const results: string[] = [];
|
|
81
|
+
// Dedupe by realpath: pnpm symlinks workspace packages into multiple
|
|
82
|
+
// node_modules locations, so the same test file can be reached via
|
|
83
|
+
// many paths. Without dedupe, recursive traversal yields the same yaml
|
|
84
|
+
// dozens of times under different prefixes.
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
|
|
81
87
|
for (const entry of entries) {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
const rel = entry.replace(/\\/g, "/");
|
|
89
|
+
// Hard-skip node_modules: those are always symlinked workspace dupes
|
|
90
|
+
// (or vendored copies that shouldn't run as workspace tests). The
|
|
91
|
+
// user-facing `exclude` defaults to `__fixtures__` only, but
|
|
92
|
+
// node_modules is a hard architectural skip.
|
|
93
|
+
if (rel.split("/").includes("node_modules")) continue;
|
|
94
|
+
if (!includeRe.some((re) => re.test(rel))) continue;
|
|
95
|
+
if (excludeRe.some((re) => re.test(rel))) continue;
|
|
96
|
+
if (filter && !rel.includes(filter)) continue;
|
|
97
|
+
const abs = path.resolve(baseDir, rel);
|
|
98
|
+
let real: string;
|
|
99
|
+
try {
|
|
100
|
+
real = fs.realpathSync(abs);
|
|
101
|
+
} catch {
|
|
102
|
+
real = abs;
|
|
103
|
+
}
|
|
104
|
+
if (seen.has(real)) continue;
|
|
105
|
+
seen.add(real);
|
|
106
|
+
results.push(abs);
|
|
88
107
|
}
|
|
89
|
-
|
|
90
108
|
results.sort();
|
|
91
109
|
return results;
|
|
92
110
|
}
|
|
@@ -95,20 +113,28 @@ function labelFor(testPath: string, baseDir: string): string {
|
|
|
95
113
|
return path.relative(baseDir, testPath);
|
|
96
114
|
}
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
116
|
+
function tryReadFile(filePath: string): string | null {
|
|
117
|
+
try {
|
|
118
|
+
return fs.readFileSync(filePath, "utf8");
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseEnvFile(content: string | null): Record<string, string> {
|
|
125
|
+
if (!content) return {};
|
|
103
126
|
const result: Record<string, string> = {};
|
|
104
|
-
for (const line of content.split(
|
|
127
|
+
for (const line of content.split(/\r?\n/)) {
|
|
105
128
|
const trimmed = line.trim();
|
|
106
129
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
107
130
|
const eq = trimmed.indexOf("=");
|
|
108
131
|
if (eq === -1) continue;
|
|
109
132
|
const key = trimmed.slice(0, eq).trim();
|
|
110
133
|
let value = trimmed.slice(eq + 1).trim();
|
|
111
|
-
if (
|
|
134
|
+
if (
|
|
135
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
136
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
137
|
+
) {
|
|
112
138
|
value = value.slice(1, -1);
|
|
113
139
|
}
|
|
114
140
|
result[key] = value;
|
|
@@ -116,17 +142,10 @@ function parseEnvFile(content: string): Record<string, string> {
|
|
|
116
142
|
return result;
|
|
117
143
|
}
|
|
118
144
|
|
|
119
|
-
function tryReadFile(filePath: string): string {
|
|
120
|
-
try {
|
|
121
|
-
return fs.readFileSync(filePath, "utf8");
|
|
122
|
-
} catch {
|
|
123
|
-
return "";
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
145
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
146
|
+
* Loads .env and .env.local files (in that order) from the directory of
|
|
147
|
+
* the manifest, layered under (and overridden by) process.env.
|
|
148
|
+
*
|
|
130
149
|
* Keys already present in process.env take precedence (same as CLI behaviour).
|
|
131
150
|
*/
|
|
132
151
|
function buildEnvForManifest(manifestPath: string): Record<string, string | undefined> {
|