@nudojs/cli 0.2.1 → 0.3.1
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/{chunk-3HCPXPEW.js → chunk-YDM4HHQ4.js} +1783 -1421
- package/dist/evaluator-api.d.ts +12 -1
- package/dist/evaluator-api.js +52 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +366 -3572
- package/package.json +23 -11
- package/CHANGELOG.md +0 -41
- package/examples/real-world.js +0 -66
- package/examples/validate-real-world.ts +0 -160
- package/src/__tests__/builtin-advanced.test.ts +0 -292
- package/src/__tests__/builtin-api.test.ts +0 -308
- package/src/__tests__/builtin-functions.test.ts +0 -313
- package/src/__tests__/builtin-map.test.ts +0 -129
- package/src/__tests__/builtin-promise.test.ts +0 -150
- package/src/__tests__/builtin-regexp.test.ts +0 -133
- package/src/__tests__/builtin-set.test.ts +0 -163
- package/src/__tests__/builtin-url.test.ts +0 -256
- package/src/__tests__/builtin-web.test.ts +0 -340
- package/src/__tests__/combination-scenarios.test.ts +0 -384
- package/src/__tests__/edge-cases.test.ts +0 -749
- package/src/__tests__/evaluator-loops.test.ts +0 -283
- package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
- package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
- package/src/__tests__/evaluator-phase2.test.ts +0 -564
- package/src/__tests__/evaluator-phase3.test.ts +0 -864
- package/src/__tests__/evaluator-refined.test.ts +0 -121
- package/src/__tests__/evaluator-string-methods.test.ts +0 -151
- package/src/__tests__/evaluator.test.ts +0 -943
- package/src/__tests__/mock-arrow-function.test.ts +0 -169
- package/src/__tests__/mock-comparison.test.ts +0 -221
- package/src/__tests__/mock-comprehensive.test.ts +0 -250
- package/src/__tests__/mock-final-integration.test.ts +0 -263
- package/src/__tests__/mock-helpers.test.ts +0 -174
- package/src/__tests__/mock-realworld.test.ts +0 -191
- package/src/__tests__/mock-rest-debug.test.ts +0 -110
- package/src/__tests__/mock-rest-edge.test.ts +0 -176
- package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
- package/src/__tests__/narrowing-discriminated.test.ts +0 -35
- package/src/__tests__/narrowing-in-operator.test.ts +0 -56
- package/src/__tests__/narrowing-switch.test.ts +0 -56
- package/src/__tests__/narrowing-truthiness.test.ts +0 -52
- package/src/__tests__/narrowing.test.ts +0 -82
- package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
- package/src/__tests__/realworld-patterns.test.ts +0 -430
- package/src/__tests__/syntax-sugar.test.ts +0 -345
- package/src/builtins/builtin-intl.ts +0 -17
- package/src/builtins/builtin-map.ts +0 -174
- package/src/builtins/builtin-promise.ts +0 -116
- package/src/builtins/builtin-reflect.ts +0 -30
- package/src/builtins/builtin-regexp.ts +0 -34
- package/src/builtins/builtin-set.ts +0 -131
- package/src/builtins/builtin-symbol.ts +0 -16
- package/src/builtins/builtin-url.ts +0 -58
- package/src/builtins/builtin-weak.ts +0 -22
- package/src/builtins/builtin-web.ts +0 -111
- package/src/builtins/index.ts +0 -61
- package/src/config.ts +0 -32
- package/src/env-loader.ts +0 -174
- package/src/evaluator-api.ts +0 -36
- package/src/evaluator.ts +0 -5623
- package/src/index.ts +0 -931
- package/src/narrowing.ts +0 -537
- package/src/resolve-npm.ts +0 -63
- package/tsconfig.json +0 -9
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parse, extractDirectives, type CaseDirective } from "@nudojs/parser";
|
|
3
|
-
import { typeValueToString, createEnvironment } from "@nudojs/core";
|
|
4
|
-
import { evaluateFunctionFull } from "../evaluator.js";
|
|
5
|
-
|
|
6
|
-
function runTest(source: string): { name: string; caseName: string; result: string }[] {
|
|
7
|
-
const ast = parse(source);
|
|
8
|
-
const directives = extractDirectives(ast);
|
|
9
|
-
const env = createEnvironment();
|
|
10
|
-
const results: { name: string; caseName: string; result: string }[] = [];
|
|
11
|
-
|
|
12
|
-
for (const fn of directives) {
|
|
13
|
-
const caseDirectives = fn.directives.filter((d): d is CaseDirective => d.kind === "case");
|
|
14
|
-
for (const dir of caseDirectives) {
|
|
15
|
-
const result = evaluateFunctionFull(fn.node, dir.args, env);
|
|
16
|
-
results.push({
|
|
17
|
-
name: fn.name,
|
|
18
|
-
caseName: dir.name,
|
|
19
|
-
result: typeValueToString(result.value),
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return results;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe("Real-World Pattern Tests", () => {
|
|
27
|
-
describe("Data Processing Pipelines", () => {
|
|
28
|
-
it("user data transformation", () => {
|
|
29
|
-
const results = runTest(`
|
|
30
|
-
// @nudo:case "transform-users" ([{ id: 1, firstName: "Alice", lastName: "Smith", active: true, isAdmin: false }, { id: 2, firstName: "Bob", lastName: "Jones", active: true, isAdmin: true }])
|
|
31
|
-
function transformUsers(users) {
|
|
32
|
-
return users
|
|
33
|
-
.filter(u => u.active)
|
|
34
|
-
.map(u => ({
|
|
35
|
-
id: u.id,
|
|
36
|
-
displayName: u.firstName + " " + u.lastName,
|
|
37
|
-
role: u.isAdmin ? "admin" : "user"
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
`);
|
|
41
|
-
expect(results[0].result).toContain("[");
|
|
42
|
-
expect(results[0].result).toContain("displayName");
|
|
43
|
-
expect(results[0].result).toContain("role");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("group by key", () => {
|
|
47
|
-
const results = runTest(`
|
|
48
|
-
// @nudo:case "group-by" ([{ type: "a", value: 1 }, { type: "b", value: 2 }, { type: "a", value: 3 }])
|
|
49
|
-
function groupByType(items) {
|
|
50
|
-
const groups = new Map();
|
|
51
|
-
for (const item of items) {
|
|
52
|
-
if (!groups.has(item.type)) {
|
|
53
|
-
groups.set(item.type, []);
|
|
54
|
-
}
|
|
55
|
-
groups.get(item.type).push(item.value);
|
|
56
|
-
}
|
|
57
|
-
return groups;
|
|
58
|
-
}
|
|
59
|
-
`);
|
|
60
|
-
expect(results[0].result).toContain("Map");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("flatten and deduplicate", () => {
|
|
64
|
-
const results = runTest(`
|
|
65
|
-
// @nudo:case "flatten-dedup" ([[1, 2], [2, 3], [3, 4]])
|
|
66
|
-
function flattenDedup(arrays) {
|
|
67
|
-
const flat = arrays.reduce((acc, arr) => acc.concat(arr), []);
|
|
68
|
-
return Array.from(new Set(flat));
|
|
69
|
-
}
|
|
70
|
-
`);
|
|
71
|
-
expect(results[0].result).toContain("[");
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe("Error Handling Patterns", () => {
|
|
76
|
-
it("Result type pattern", () => {
|
|
77
|
-
const results = runTest(`
|
|
78
|
-
// @nudo:case "result-type" (10, 2)
|
|
79
|
-
function safeDivide(a, b) {
|
|
80
|
-
try {
|
|
81
|
-
if (b === 0) throw new Error("Division by zero");
|
|
82
|
-
return { ok: true, value: a / b };
|
|
83
|
-
} catch (e) {
|
|
84
|
-
return { ok: false, error: e.message };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
`);
|
|
88
|
-
expect(results[0].result).toContain("ok");
|
|
89
|
-
expect(results[0].result).toContain("value");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("retry pattern", () => {
|
|
93
|
-
const results = runTest(`
|
|
94
|
-
// @nudo:case "retry" ()
|
|
95
|
-
async function withRetry(fn, maxRetries) {
|
|
96
|
-
let lastError;
|
|
97
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
98
|
-
try {
|
|
99
|
-
return { success: true, data: await fn() };
|
|
100
|
-
} catch (e) {
|
|
101
|
-
lastError = e;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return { success: false, error: "max retries exceeded" };
|
|
105
|
-
}
|
|
106
|
-
`);
|
|
107
|
-
// Async function returns Promise
|
|
108
|
-
expect(results[0].result).toContain("Promise");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("validation pattern", () => {
|
|
112
|
-
const results = runTest(`
|
|
113
|
-
// @nudo:case "validate" ({ email: "test@example.com", age: 25 })
|
|
114
|
-
function validateUser(user) {
|
|
115
|
-
const errors = [];
|
|
116
|
-
if (!user.email || !user.email.includes("@")) {
|
|
117
|
-
errors.push("Invalid email");
|
|
118
|
-
}
|
|
119
|
-
if (user.age < 0 || user.age > 150) {
|
|
120
|
-
errors.push("Invalid age");
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
valid: errors.length === 0,
|
|
124
|
-
errors,
|
|
125
|
-
user: errors.length === 0 ? user : undefined
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
`);
|
|
129
|
-
expect(results[0].result).toContain("valid");
|
|
130
|
-
expect(results[0].result).toContain("errors");
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe("Event Emitter Pattern", () => {
|
|
135
|
-
it("basic event emitter", () => {
|
|
136
|
-
const results = runTest(`
|
|
137
|
-
// @nudo:case "emitter" ()
|
|
138
|
-
function createEmitter() {
|
|
139
|
-
const listeners = new Map();
|
|
140
|
-
return {
|
|
141
|
-
on(event, fn) {
|
|
142
|
-
if (!listeners.has(event)) {
|
|
143
|
-
listeners.set(event, []);
|
|
144
|
-
}
|
|
145
|
-
listeners.get(event).push(fn);
|
|
146
|
-
},
|
|
147
|
-
off(event, fn) {
|
|
148
|
-
const fns = listeners.get(event);
|
|
149
|
-
if (fns) {
|
|
150
|
-
const idx = fns.indexOf(fn);
|
|
151
|
-
if (idx >= 0) fns.splice(idx, 1);
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
emit(event, ...args) {
|
|
155
|
-
const fns = listeners.get(event);
|
|
156
|
-
if (fns) {
|
|
157
|
-
for (const fn of fns) fn(...args);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
`);
|
|
163
|
-
expect(results[0].result).toContain("on");
|
|
164
|
-
expect(results[0].result).toContain("off");
|
|
165
|
-
expect(results[0].result).toContain("emit");
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe("Configuration Patterns", () => {
|
|
170
|
-
it("config with defaults", () => {
|
|
171
|
-
const results = runTest(`
|
|
172
|
-
// @nudo:case "config" ({ host: "localhost" })
|
|
173
|
-
function createConfig(userConfig) {
|
|
174
|
-
const defaults = {
|
|
175
|
-
port: 3000,
|
|
176
|
-
debug: false,
|
|
177
|
-
logLevel: "info"
|
|
178
|
-
};
|
|
179
|
-
return { ...defaults, ...userConfig };
|
|
180
|
-
}
|
|
181
|
-
`);
|
|
182
|
-
expect(results[0].result).toContain("host");
|
|
183
|
-
expect(results[0].result).toContain("port");
|
|
184
|
-
expect(results[0].result).toContain("debug");
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("config validation", () => {
|
|
188
|
-
const results = runTest(`
|
|
189
|
-
// @nudo:case "validate-config" ({ host: "localhost", port: 3000 })
|
|
190
|
-
function validateConfig(config) {
|
|
191
|
-
const errors = [];
|
|
192
|
-
if (!config.host) errors.push("host is required");
|
|
193
|
-
if (config.port < 1 || config.port > 65535) errors.push("port must be 1-65535");
|
|
194
|
-
return {
|
|
195
|
-
valid: errors.length === 0,
|
|
196
|
-
errors,
|
|
197
|
-
config: errors.length === 0 ? config : undefined
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
`);
|
|
201
|
-
expect(results[0].result).toContain("valid");
|
|
202
|
-
expect(results[0].result).toContain("config");
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe("API Response Patterns", () => {
|
|
207
|
-
it("API response handler", () => {
|
|
208
|
-
const results = runTest(`
|
|
209
|
-
// @nudo:case "api-response" ({ statusCode: 200, body: { data: "test" }, headers: { "x-request-id": "123" } })
|
|
210
|
-
function handleResponse(response) {
|
|
211
|
-
if (!response) {
|
|
212
|
-
return { status: "error", message: "No response" };
|
|
213
|
-
}
|
|
214
|
-
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
215
|
-
return {
|
|
216
|
-
status: "success",
|
|
217
|
-
data: response.body,
|
|
218
|
-
meta: { requestId: response.headers["x-request-id"] }
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
if (response.statusCode >= 400) {
|
|
222
|
-
return {
|
|
223
|
-
status: "error",
|
|
224
|
-
code: response.statusCode,
|
|
225
|
-
message: response.body?.error || "Unknown error"
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
return { status: "unknown", code: response.statusCode };
|
|
229
|
-
}
|
|
230
|
-
`);
|
|
231
|
-
expect(results[0].result).toContain("success");
|
|
232
|
-
expect(results[0].result).toContain("data");
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("paginated response", () => {
|
|
236
|
-
const results = runTest(`
|
|
237
|
-
// @nudo:case "paginate" ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1, 3)
|
|
238
|
-
function paginate(items, page, pageSize) {
|
|
239
|
-
const start = (page - 1) * pageSize;
|
|
240
|
-
const end = start + pageSize;
|
|
241
|
-
const pageItems = items.slice(start, end);
|
|
242
|
-
return {
|
|
243
|
-
items: pageItems,
|
|
244
|
-
page,
|
|
245
|
-
pageSize,
|
|
246
|
-
total: items.length,
|
|
247
|
-
totalPages: Math.ceil(items.length / pageSize)
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
`);
|
|
251
|
-
expect(results[0].result).toContain("items");
|
|
252
|
-
expect(results[0].result).toContain("page");
|
|
253
|
-
expect(results[0].result).toContain("total");
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe("State Management Patterns", () => {
|
|
258
|
-
it("counter with actions", () => {
|
|
259
|
-
const results = runTest(`
|
|
260
|
-
// @nudo:case "counter" ()
|
|
261
|
-
function createCounter(initial = 0) {
|
|
262
|
-
let count = initial;
|
|
263
|
-
return {
|
|
264
|
-
increment() { return ++count; },
|
|
265
|
-
decrement() { return --count; },
|
|
266
|
-
reset() { count = initial; return count; },
|
|
267
|
-
get() { return count; }
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
`);
|
|
271
|
-
expect(results[0].result).toContain("increment");
|
|
272
|
-
expect(results[0].result).toContain("decrement");
|
|
273
|
-
expect(results[0].result).toContain("reset");
|
|
274
|
-
expect(results[0].result).toContain("get");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("todo list", () => {
|
|
278
|
-
const results = runTest(`
|
|
279
|
-
// @nudo:case "todo-list" ()
|
|
280
|
-
function createTodoList() {
|
|
281
|
-
const todos = [];
|
|
282
|
-
let nextId = 1;
|
|
283
|
-
return {
|
|
284
|
-
add(text) {
|
|
285
|
-
const todo = { id: nextId++, text, completed: false };
|
|
286
|
-
todos.push(todo);
|
|
287
|
-
return todo;
|
|
288
|
-
},
|
|
289
|
-
complete(id) {
|
|
290
|
-
const todo = todos.find(t => t.id === id);
|
|
291
|
-
if (todo) todo.completed = true;
|
|
292
|
-
return todo;
|
|
293
|
-
},
|
|
294
|
-
getAll() { return todos; },
|
|
295
|
-
getActive() { return todos.filter(t => !t.completed); }
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
`);
|
|
299
|
-
expect(results[0].result).toContain("add");
|
|
300
|
-
expect(results[0].result).toContain("complete");
|
|
301
|
-
expect(results[0].result).toContain("getAll");
|
|
302
|
-
expect(results[0].result).toContain("getActive");
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
describe("Utility Functions", () => {
|
|
307
|
-
it("deep clone", () => {
|
|
308
|
-
const results = runTest(`
|
|
309
|
-
// @nudo:case "deep-clone" ({ a: { b: { c: 1 } }, d: [1, 2, 3] })
|
|
310
|
-
function deepClone(obj) {
|
|
311
|
-
if (obj === null || typeof obj !== "object") return obj;
|
|
312
|
-
if (Array.isArray(obj)) return obj.map(deepClone);
|
|
313
|
-
const clone = {};
|
|
314
|
-
for (const key in obj) {
|
|
315
|
-
clone[key] = deepClone(obj[key]);
|
|
316
|
-
}
|
|
317
|
-
return clone;
|
|
318
|
-
}
|
|
319
|
-
`);
|
|
320
|
-
expect(results[0].result).toContain("a");
|
|
321
|
-
expect(results[0].result).toContain("b");
|
|
322
|
-
expect(results[0].result).toContain("d");
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("debounce", () => {
|
|
326
|
-
const results = runTest(`
|
|
327
|
-
// @nudo:case "debounce" ()
|
|
328
|
-
function createDebounce(fn, delay) {
|
|
329
|
-
let timeoutId;
|
|
330
|
-
return function(...args) {
|
|
331
|
-
clearTimeout(timeoutId);
|
|
332
|
-
timeoutId = setTimeout(() => fn(...args), delay);
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
`);
|
|
336
|
-
// Functions are displayed as arrow function syntax
|
|
337
|
-
expect(results[0].result).toContain("=>");
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it("memoize", () => {
|
|
341
|
-
const results = runTest(`
|
|
342
|
-
// @nudo:case "memoize" ()
|
|
343
|
-
function memoize(fn) {
|
|
344
|
-
const cache = new Map();
|
|
345
|
-
return function(...args) {
|
|
346
|
-
const key = JSON.stringify(args);
|
|
347
|
-
if (cache.has(key)) return cache.get(key);
|
|
348
|
-
const result = fn(...args);
|
|
349
|
-
cache.set(key, result);
|
|
350
|
-
return result;
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
`);
|
|
354
|
-
// Functions are displayed as arrow function syntax
|
|
355
|
-
expect(results[0].result).toContain("=>");
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("escape dispatch through a string-keyed method table", () => {
|
|
359
|
-
const results = runTest(`
|
|
360
|
-
// @nudo:case "table-dispatch" (T.string)
|
|
361
|
-
function escapeViaTable(char) {
|
|
362
|
-
const Strings = {
|
|
363
|
-
'<': () => 'LT',
|
|
364
|
-
'>': () => 'GT',
|
|
365
|
-
'&': () => 'AMP'
|
|
366
|
-
};
|
|
367
|
-
return Strings[char]();
|
|
368
|
-
}
|
|
369
|
-
`);
|
|
370
|
-
// Strings[char] unions the table's functions; calling the union runs
|
|
371
|
-
// each member, so the result is the union of their results.
|
|
372
|
-
const r = results[0].result;
|
|
373
|
-
expect(r).toContain("LT");
|
|
374
|
-
expect(r).toContain("GT");
|
|
375
|
-
expect(r).toContain("AMP");
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe("Named Function Callbacks through builtin HOFs", () => {
|
|
381
|
-
it("arr.map(double) with a named function declaration yields precise literals", () => {
|
|
382
|
-
const results = runTest(`
|
|
383
|
-
// @nudo:case "xs" ([1, 2, 3])
|
|
384
|
-
function doubleAll(xs) {
|
|
385
|
-
function double(x) { return x * 2; }
|
|
386
|
-
return xs.map(double);
|
|
387
|
-
}
|
|
388
|
-
`);
|
|
389
|
-
// 具名函数回调从其 TypeValue 闭包求值,而非降级 unknown
|
|
390
|
-
expect(results[0].result).toBe("[2, 4, 6]");
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it("arr.reduce(sum, 0) with a named reducer yields number", () => {
|
|
394
|
-
const results = runTest(`
|
|
395
|
-
// @nudo:case "xs" ([1, 2, 3])
|
|
396
|
-
function sumAll(xs) {
|
|
397
|
-
function sum(a, b) { return a + b; }
|
|
398
|
-
return xs.reduce(sum, 0);
|
|
399
|
-
}
|
|
400
|
-
`);
|
|
401
|
-
expect(results[0].result).toBe("6");
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it("arr.filter(big) with a named predicate keeps the element type", () => {
|
|
405
|
-
const results = runTest(`
|
|
406
|
-
// @nudo:case "xs" ([1, 2, 3, 4])
|
|
407
|
-
function keepBig(xs) {
|
|
408
|
-
function big(x) { return x > 2; }
|
|
409
|
-
return xs.filter(big);
|
|
410
|
-
}
|
|
411
|
-
`);
|
|
412
|
-
// 过滤结果元素类型不丢失:保留 3、4 字面量成员
|
|
413
|
-
expect(results[0].result).toContain("3");
|
|
414
|
-
expect(results[0].result).toContain("4");
|
|
415
|
-
expect(results[0].result).toContain("[");
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it("named callbacks over a union receiver distribute per member", () => {
|
|
419
|
-
const results = runTest(`
|
|
420
|
-
// @nudo:case "go" ()
|
|
421
|
-
function pickThenMap() {
|
|
422
|
-
const xs = Math.random() > 0.5 ? [1, 2] : [3, 4, 5];
|
|
423
|
-
function double(x) { return x * 2; }
|
|
424
|
-
return xs.map(double);
|
|
425
|
-
}
|
|
426
|
-
`);
|
|
427
|
-
// union 接收者:每个数组成员分别经回调求值后 union
|
|
428
|
-
expect(results[0].result).toBe("[2, 4] | [6, 8, 10]");
|
|
429
|
-
});
|
|
430
|
-
});
|