eyeling 1.5.17 → 1.5.19
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/README.md +1 -1
- package/package.json +1 -1
- package/test/examples.test.js +104 -101
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ npm run test:packlist
|
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
- `test:api` runs an independent JS API test suite (does not rely on `examples/`).
|
|
103
|
-
- `test:examples` runs the examples in `examples` directory and compares against the golden outputs in `examples/output`.
|
|
103
|
+
- `test:examples` runs the examples in the `examples` directory and compares against the golden outputs in `examples/output`.
|
|
104
104
|
- `test:package` does a “real consumer” smoke test: `npm pack` → install tarball into a temp project → run API + CLI + examples.
|
|
105
105
|
- `test:packlist` sanity-checks what will be published in the npm tarball (and the CLI shebang/bin wiring).
|
|
106
106
|
|
package/package.json
CHANGED
package/test/examples.test.js
CHANGED
|
@@ -15,16 +15,6 @@ function ok(msg) { console.log(`${C.g}OK${C.n} ${msg}`); }
|
|
|
15
15
|
function fail(msg) { console.error(`${C.r}FAIL${C.n} ${msg}`); }
|
|
16
16
|
function info(msg) { console.log(`${C.y}==${C.n} ${msg}`); }
|
|
17
17
|
|
|
18
|
-
function padRight(s, n) {
|
|
19
|
-
s = String(s);
|
|
20
|
-
return s.length >= n ? s : (s + ' '.repeat(n - s.length));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function padLeft(s, n) {
|
|
24
|
-
s = String(s);
|
|
25
|
-
return s.length >= n ? s : (' '.repeat(n - s.length) + s);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
18
|
function run(cmd, args, opts = {}) {
|
|
29
19
|
return cp.spawnSync(cmd, args, {
|
|
30
20
|
encoding: 'utf8',
|
|
@@ -44,8 +34,8 @@ function inGitWorktree(cwd) {
|
|
|
44
34
|
return r.status === 0 && String(r.stdout).trim() === 'true';
|
|
45
35
|
}
|
|
46
36
|
|
|
47
|
-
// Expectation logic
|
|
48
|
-
// 1) If file contains
|
|
37
|
+
// Expectation logic:
|
|
38
|
+
// 1) If file contains: # expect-exit: N -> use N
|
|
49
39
|
// 2) Else, if it contains "=> false" -> expect exit 2
|
|
50
40
|
// 3) Else -> expect exit 0
|
|
51
41
|
function expectedExitCode(n3Text) {
|
|
@@ -57,25 +47,45 @@ function expectedExitCode(n3Text) {
|
|
|
57
47
|
|
|
58
48
|
function getEyelingVersion(nodePath, eyelingJsPath, cwd) {
|
|
59
49
|
const r = run(nodePath, [eyelingJsPath, '-v'], { cwd });
|
|
60
|
-
// eyeling prints version to stdout in your CLI
|
|
61
50
|
const s = (r.stdout || r.stderr || '').trim();
|
|
62
51
|
return s || 'eyeling (unknown version)';
|
|
63
52
|
}
|
|
64
53
|
|
|
65
|
-
function
|
|
54
|
+
function mkTmpDir() {
|
|
66
55
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-examples-'));
|
|
67
|
-
|
|
68
|
-
return { dir, file };
|
|
56
|
+
return dir;
|
|
69
57
|
}
|
|
70
58
|
|
|
71
59
|
function rmrf(p) {
|
|
72
60
|
try { fs.rmSync(p, { recursive: true, force: true }); } catch {}
|
|
73
61
|
}
|
|
74
62
|
|
|
63
|
+
function showDiff({ IN_GIT, examplesDir, expectedPath, generatedPath, relExpectedPosix }) {
|
|
64
|
+
if (hasGit()) {
|
|
65
|
+
if (IN_GIT) {
|
|
66
|
+
// Show repo diff for the overwritten golden file
|
|
67
|
+
const d = run('git', ['diff', '--', relExpectedPosix], { cwd: examplesDir });
|
|
68
|
+
if (d.stdout) process.stdout.write(d.stdout);
|
|
69
|
+
if (d.stderr) process.stderr.write(d.stderr);
|
|
70
|
+
} else {
|
|
71
|
+
// Show no-index diff between packaged golden and generated tmp
|
|
72
|
+
const d = run('git', ['diff', '--no-index', expectedPath, generatedPath], { cwd: examplesDir });
|
|
73
|
+
// Replace tmp path in output (nice UX)
|
|
74
|
+
if (d.stdout) process.stdout.write(String(d.stdout).replaceAll(generatedPath, 'generated'));
|
|
75
|
+
if (d.stderr) process.stderr.write(String(d.stderr).replaceAll(generatedPath, 'generated'));
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
// Fallback: diff -u
|
|
79
|
+
const d = run('diff', ['-u', expectedPath, generatedPath], { cwd: examplesDir });
|
|
80
|
+
if (d.stdout) process.stdout.write(d.stdout);
|
|
81
|
+
if (d.stderr) process.stderr.write(d.stderr);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
function main() {
|
|
76
86
|
const suiteStart = Date.now();
|
|
77
87
|
|
|
78
|
-
//
|
|
88
|
+
// test/examples.test.js -> repo root is one level up
|
|
79
89
|
const root = path.resolve(__dirname, '..');
|
|
80
90
|
const examplesDir = path.join(root, 'examples');
|
|
81
91
|
const outputDir = path.join(examplesDir, 'output');
|
|
@@ -83,107 +93,98 @@ function main() {
|
|
|
83
93
|
const nodePath = process.execPath;
|
|
84
94
|
|
|
85
95
|
if (!fs.existsSync(examplesDir)) {
|
|
86
|
-
fail(`
|
|
96
|
+
fail(`Cannot find examples directory: ${examplesDir}`);
|
|
87
97
|
process.exit(1);
|
|
88
98
|
}
|
|
89
99
|
if (!fs.existsSync(eyelingJsPath)) {
|
|
90
|
-
fail(`
|
|
100
|
+
fail(`Cannot find eyeling.js: ${eyelingJsPath}`);
|
|
91
101
|
process.exit(1);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
const IN_GIT = inGitWorktree(root);
|
|
95
105
|
|
|
96
|
-
// Header
|
|
97
|
-
console.log(`${C.y}-------------------------------------------------${C.n}`);
|
|
98
|
-
console.log(`${C.y}running eyeling examples${C.n}`);
|
|
99
|
-
console.log(
|
|
100
|
-
`${C.y}using ${getEyelingVersion(nodePath, eyelingJsPath, root)} and node ${process.version}${C.n}`
|
|
101
|
-
);
|
|
102
|
-
console.log(`${C.y}-------------------------------------------------${C.n}`);
|
|
103
|
-
console.log('');
|
|
104
|
-
|
|
105
|
-
// In maintainer mode we write expected outputs (tracked) to examples/output/
|
|
106
|
-
if (IN_GIT) fs.mkdirSync(outputDir, { recursive: true });
|
|
107
|
-
|
|
108
106
|
const files = fs.readdirSync(examplesDir)
|
|
109
107
|
.filter(f => f.endsWith('.n3'))
|
|
110
108
|
.sort((a, b) => a.localeCompare(b));
|
|
111
109
|
|
|
110
|
+
info(
|
|
111
|
+
`Running ${files.length} examples tests (${IN_GIT ? 'git worktree mode' : 'npm-installed mode'})`
|
|
112
|
+
);
|
|
113
|
+
console.log(`${C.dim}${getEyelingVersion(nodePath, eyelingJsPath, root)}; node ${process.version}${C.n}`);
|
|
114
|
+
|
|
112
115
|
if (files.length === 0) {
|
|
113
|
-
|
|
116
|
+
ok('No .n3 files found in examples/');
|
|
114
117
|
process.exit(0);
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
// In maintainer mode we overwrite tracked goldens in examples/output/
|
|
121
|
+
if (IN_GIT) fs.mkdirSync(outputDir, { recursive: true });
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
let passed = 0;
|
|
124
|
+
let failed = 0;
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < files.length; i++) {
|
|
127
|
+
const idx = String(i + 1).padStart(2, '0');
|
|
128
|
+
const file = files[i];
|
|
123
129
|
|
|
124
130
|
const start = Date.now();
|
|
125
131
|
|
|
126
|
-
|
|
132
|
+
const filePath = path.join(examplesDir, file);
|
|
133
|
+
const expectedPath = path.join(outputDir, file);
|
|
134
|
+
const relExpectedPosix = path.posix.join('output', file); // for git diff inside examplesDir
|
|
135
|
+
|
|
136
|
+
let n3Text;
|
|
127
137
|
try {
|
|
128
138
|
n3Text = fs.readFileSync(filePath, 'utf8');
|
|
129
139
|
} catch (e) {
|
|
130
140
|
const ms = Date.now() - start;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
DIFF++;
|
|
141
|
+
fail(`${idx} ${file} (${ms} ms)`);
|
|
142
|
+
fail(`Cannot read input: ${e.message}`);
|
|
143
|
+
failed++;
|
|
135
144
|
continue;
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
const expectedRc = expectedExitCode(n3Text);
|
|
139
148
|
|
|
140
|
-
// Decide where
|
|
141
|
-
let
|
|
149
|
+
// Decide where generated output goes
|
|
150
|
+
let tmpDir = null;
|
|
142
151
|
let generatedPath = expectedPath;
|
|
143
152
|
|
|
144
153
|
if (!IN_GIT) {
|
|
145
154
|
// npm-installed / no .git: never modify output/ in node_modules
|
|
146
155
|
if (!fs.existsSync(expectedPath)) {
|
|
147
156
|
const ms = Date.now() - start;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
DIFF++;
|
|
157
|
+
fail(`${idx} ${file} (${ms} ms)`);
|
|
158
|
+
fail(`Missing expected output/${file}`);
|
|
159
|
+
failed++;
|
|
152
160
|
continue;
|
|
153
161
|
}
|
|
154
|
-
|
|
155
|
-
generatedPath =
|
|
162
|
+
tmpDir = mkTmpDir();
|
|
163
|
+
generatedPath = path.join(tmpDir, 'generated.n3');
|
|
156
164
|
}
|
|
157
165
|
|
|
158
|
-
// Run eyeling
|
|
159
|
-
|
|
160
|
-
const r = run(nodePath, [eyelingJsPath, file], { cwd: examplesDir });
|
|
161
|
-
const rc = (r.status == null) ? 1 : r.status;
|
|
166
|
+
// Run eyeling on this file (cwd examplesDir so relative behavior matches old script)
|
|
167
|
+
const outFd = fs.openSync(generatedPath, 'w');
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
169
|
+
const r = cp.spawnSync(nodePath, [eyelingJsPath, file], {
|
|
170
|
+
cwd: examplesDir,
|
|
171
|
+
stdio: ['ignore', outFd, 'pipe'], // stdout -> file, stderr captured
|
|
172
|
+
maxBuffer: 200 * 1024 * 1024,
|
|
173
|
+
encoding: 'utf8'
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
fs.closeSync(outFd);
|
|
177
|
+
|
|
178
|
+
const rc = (r.status == null) ? 1 : r.status;
|
|
175
179
|
|
|
176
180
|
const ms = Date.now() - start;
|
|
177
181
|
|
|
178
|
-
// Compare
|
|
182
|
+
// Compare output
|
|
179
183
|
let diffOk = false;
|
|
180
|
-
|
|
181
184
|
if (IN_GIT) {
|
|
182
|
-
|
|
183
|
-
const d = run('git', ['diff', '--quiet', '--', path.posix.join('output', file)], { cwd: examplesDir });
|
|
185
|
+
const d = run('git', ['diff', '--quiet', '--', relExpectedPosix], { cwd: examplesDir });
|
|
184
186
|
diffOk = (d.status === 0);
|
|
185
187
|
} else {
|
|
186
|
-
// Compare expectedPath vs generatedPath without needing a repo
|
|
187
188
|
if (hasGit()) {
|
|
188
189
|
const d = run('git', ['diff', '--no-index', '--quiet', expectedPath, generatedPath], { cwd: examplesDir });
|
|
189
190
|
diffOk = (d.status === 0);
|
|
@@ -193,49 +194,51 @@ function main() {
|
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
process.stdout.write(padRight(file, 36));
|
|
198
|
-
process.stdout.write(`${C.y}${padLeft(`${ms} ms`, 10)}${C.n} `);
|
|
197
|
+
const rcOk = (rc === expectedRc);
|
|
199
198
|
|
|
200
|
-
if (diffOk &&
|
|
201
|
-
if (
|
|
202
|
-
|
|
199
|
+
if (diffOk && rcOk) {
|
|
200
|
+
if (expectedRc === 0) {
|
|
201
|
+
ok(`${idx} ${file} (${ms} ms)`);
|
|
203
202
|
} else {
|
|
204
|
-
|
|
203
|
+
ok(`${idx} ${file} (expected exit ${expectedRc}, ${ms} ms)`);
|
|
205
204
|
}
|
|
206
|
-
|
|
205
|
+
passed++;
|
|
207
206
|
} else {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
console.log(`${C.r}DIFF${C.n}`);
|
|
207
|
+
fail(`${idx} ${file} (${ms} ms)`);
|
|
208
|
+
if (!rcOk) {
|
|
209
|
+
fail(`Exit code ${rc}, expected ${expectedRc}`);
|
|
212
210
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// In npm mode, show a diff (nice UX) without modifying node_modules
|
|
216
|
-
if (!IN_GIT) {
|
|
217
|
-
if (hasGit()) {
|
|
218
|
-
const d = run('git', ['diff', '--no-index', expectedPath, generatedPath], { cwd: examplesDir });
|
|
219
|
-
if (d.stdout) process.stdout.write(d.stdout);
|
|
220
|
-
if (d.stderr) process.stderr.write(d.stderr);
|
|
221
|
-
} else {
|
|
222
|
-
const d = run('diff', ['-u', expectedPath, generatedPath], { cwd: examplesDir });
|
|
223
|
-
if (d.stdout) process.stdout.write(d.stdout);
|
|
224
|
-
if (d.stderr) process.stderr.write(d.stderr);
|
|
225
|
-
}
|
|
211
|
+
if (!diffOk) {
|
|
212
|
+
fail('Output differs');
|
|
226
213
|
}
|
|
214
|
+
|
|
215
|
+
// Show diffs (both modes), because this is a test runner
|
|
216
|
+
showDiff({
|
|
217
|
+
IN_GIT,
|
|
218
|
+
examplesDir,
|
|
219
|
+
expectedPath,
|
|
220
|
+
generatedPath,
|
|
221
|
+
relExpectedPosix,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
failed++;
|
|
227
225
|
}
|
|
228
226
|
|
|
229
|
-
|
|
230
|
-
if (tmp) rmrf(tmp.dir);
|
|
227
|
+
if (tmpDir) rmrf(tmpDir);
|
|
231
228
|
}
|
|
232
229
|
|
|
233
230
|
console.log('');
|
|
234
231
|
const suiteMs = Date.now() - suiteStart;
|
|
235
|
-
|
|
236
|
-
console.log(`${C.y}==${C.n} ${C.g}${OK} OK${C.n} ${C.r}${DIFF} DIFF${C.n}`);
|
|
232
|
+
info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
|
|
237
233
|
|
|
238
|
-
|
|
234
|
+
if (failed === 0) {
|
|
235
|
+
ok(`All examples tests passed (${passed}/${files.length})`);
|
|
236
|
+
process.exit(0);
|
|
237
|
+
} else {
|
|
238
|
+
fail(`Some examples tests failed (${passed}/${files.length})`);
|
|
239
|
+
// keep exit code 2 (matches historical behavior of examples/test)
|
|
240
|
+
process.exit(2);
|
|
241
|
+
}
|
|
239
242
|
}
|
|
240
243
|
|
|
241
244
|
main();
|