visaljs 0.0.6 → 0.0.8
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 +63 -2
- package/THIRD_PARTY_LICENSES +1216 -0
- package/cli.js +4414 -325
- package/node/include/js_node.h +57 -1
- package/node/include/js_uv.h +8 -1
- package/node/prelude/_stream_duplex.js +4 -0
- package/node/prelude/_stream_passthrough.js +4 -0
- package/node/prelude/_stream_readable.js +4 -0
- package/node/prelude/_stream_transform.js +4 -0
- package/node/prelude/_stream_writable.js +4 -0
- package/node/prelude/assert.js +1334 -0
- package/node/prelude/assert_strict.js +12 -0
- package/node/prelude/async_hooks.js +1 -1
- package/node/prelude/buffer.js +43 -3
- package/node/prelude/child_process.js +1098 -0
- package/node/prelude/cluster.js +52 -0
- package/node/prelude/console.js +328 -0
- package/node/prelude/constants.js +10 -0
- package/node/prelude/crypto.js +1 -1
- package/node/prelude/diagnostics_channel.js +1 -1
- package/node/prelude/dns.js +1 -1
- package/node/prelude/domain.js +115 -0
- package/node/prelude/events.js +144 -16
- package/node/prelude/fs.js +1138 -151
- package/node/prelude/fs_promises.js +117 -11
- package/node/prelude/http.js +104 -27
- package/node/prelude/http2.js +1 -1
- package/node/prelude/https.js +148 -0
- package/node/prelude/inspector.js +27 -0
- package/node/prelude/inspector_promises.js +4 -0
- package/node/prelude/internal/async_context.js +1 -1
- package/node/prelude/internal/buffer_transcode.js +44 -0
- package/node/prelude/internal/comparisons.js +916 -0
- package/node/prelude/internal/compression_streams.js +153 -0
- package/node/prelude/internal/crypto/asym.js +27 -14
- package/node/prelude/internal/crypto/cipher.js +16 -5
- package/node/prelude/internal/crypto/diffiehellman.js +3 -2
- package/node/prelude/internal/crypto/hash.js +2 -1
- package/node/prelude/internal/crypto/kdf.js +14 -9
- package/node/prelude/internal/crypto/keys.js +63 -50
- package/node/prelude/internal/crypto/random.js +2 -1
- package/node/prelude/internal/crypto/util.js +4 -4
- package/node/prelude/internal/dotenv.js +98 -0
- package/node/prelude/internal/errors.js +21 -5
- package/node/prelude/internal/events_extras.js +116 -0
- package/node/prelude/internal/events_on.js +100 -0
- package/node/prelude/internal/features.js +55 -0
- package/node/prelude/internal/fetch.js +364 -0
- package/node/prelude/internal/form_data.js +376 -0
- package/node/prelude/internal/fs_bigint_stats.js +41 -0
- package/node/prelude/internal/fs_blob.js +19 -0
- package/node/prelude/internal/fs_cp.js +709 -0
- package/node/prelude/internal/fs_dir.js +166 -0
- package/node/prelude/internal/fs_filehandle.js +287 -0
- package/node/prelude/internal/fs_glob.js +296 -0
- package/node/prelude/internal/fs_statfs.js +42 -0
- package/node/prelude/internal/fs_stats.js +89 -0
- package/node/prelude/internal/fs_streams.js +541 -0
- package/node/prelude/internal/fs_utils.js +286 -0
- package/node/prelude/internal/fs_watch.js +580 -0
- package/node/prelude/internal/http_agent.js +26 -0
- package/node/prelude/internal/mime.js +249 -0
- package/node/prelude/internal/net.js +1 -0
- package/node/prelude/internal/parse_args.js +288 -0
- package/node/prelude/internal/perf_histogram.js +435 -0
- package/node/prelude/internal/perf_observe.js +119 -0
- package/node/prelude/internal/performance_global.js +19 -0
- package/node/prelude/internal/process_events.js +49 -1
- package/node/prelude/internal/process_memory.js +14 -0
- package/node/prelude/internal/process_more.js +414 -0
- package/node/prelude/internal/process_signals.js +2 -1
- package/node/prelude/internal/process_stdin.js +52 -0
- package/node/prelude/internal/process_stdio.js +36 -0
- package/node/prelude/internal/querystring.js +0 -0
- package/node/prelude/internal/readline.js +338 -0
- package/node/prelude/internal/socketaddress.js +8 -4
- package/node/prelude/internal/source_map.js +122 -0
- package/node/prelude/internal/stream_compose.js +74 -0
- package/node/prelude/internal/stream_from.js +6 -0
- package/node/prelude/internal/stream_operators.js +23 -0
- package/node/prelude/internal/stream_pipeline.js +7 -0
- package/node/prelude/internal/stream_transform.js +10 -0
- package/node/prelude/internal/stream_web_adapters.js +143 -0
- package/node/prelude/internal/streams/add_abort_signal.js +58 -0
- package/node/prelude/internal/streams/compose.js +244 -0
- package/node/prelude/internal/streams/destroy.js +358 -0
- package/node/prelude/internal/streams/duplex.js +199 -0
- package/node/prelude/internal/streams/duplexify.js +378 -0
- package/node/prelude/internal/streams/end_of_stream.js +387 -0
- package/node/prelude/internal/streams/errors.js +216 -0
- package/node/prelude/internal/streams/from.js +211 -0
- package/node/prelude/internal/streams/legacy.js +120 -0
- package/node/prelude/internal/streams/operators.js +375 -0
- package/node/prelude/internal/streams/passthrough.js +45 -0
- package/node/prelude/internal/streams/pipeline.js +468 -0
- package/node/prelude/internal/streams/readable.js +1835 -0
- package/node/prelude/internal/streams/state.js +48 -0
- package/node/prelude/internal/streams/transform.js +203 -0
- package/node/prelude/internal/streams/utils.js +355 -0
- package/node/prelude/internal/streams/validators.js +108 -0
- package/node/prelude/internal/streams/writable.js +1160 -0
- package/node/prelude/internal/structured_clone.js +238 -0
- package/node/prelude/internal/system_errors.js +37 -0
- package/node/prelude/internal/test_runner/mock.js +431 -0
- package/node/prelude/internal/test_runner/reporters.js +404 -0
- package/node/prelude/internal/test_runner/test.js +1571 -0
- package/node/prelude/internal/timers.js +38 -7
- package/node/prelude/internal/timers_global.js +1 -1
- package/node/prelude/internal/tls/common.js +8 -2
- package/node/prelude/internal/tls/wrap.js +3 -15
- package/node/prelude/internal/tls/x509.js +1 -1
- package/node/prelude/internal/tty_read_stream.js +39 -0
- package/node/prelude/internal/tty_streams.js +3 -0
- package/node/prelude/internal/util_debuglog.js +68 -0
- package/node/prelude/internal/util_diff.js +63 -0
- package/node/prelude/internal/util_more.js +111 -0
- package/node/prelude/internal/util_styles.js +76 -0
- package/node/prelude/internal/zlib_engines.js +1 -1
- package/node/prelude/module.js +194 -7
- package/node/prelude/net.js +2 -1
- package/node/prelude/os.js +88 -11
- package/node/prelude/path.js +11 -7
- package/node/prelude/path_posix.js +1 -1
- package/node/prelude/path_win32.js +1 -1
- package/node/prelude/perf_hooks.js +147 -0
- package/node/prelude/process.js +36 -1
- package/node/prelude/punycode.js +188 -0
- package/node/prelude/querystring.js +1 -1
- package/node/prelude/readline.js +8 -0
- package/node/prelude/readline_promises.js +88 -0
- package/node/prelude/repl.js +38 -0
- package/node/prelude/sea.js +23 -0
- package/node/prelude/stream.js +70 -1332
- package/node/prelude/stream_consumers.js +34 -0
- package/node/prelude/stream_promises.js +1 -1
- package/node/prelude/stream_web.js +15 -2
- package/node/prelude/string_decoder.js +1 -1
- package/node/prelude/sys.js +7 -0
- package/node/prelude/test.js +56 -0
- package/node/prelude/test_reporters.js +269 -0
- package/node/prelude/timers.js +7 -84
- package/node/prelude/timers_promises.js +1 -1
- package/node/prelude/tls.js +2 -1
- package/node/prelude/trace_events.js +52 -0
- package/node/prelude/tty.js +10 -4
- package/node/prelude/url.js +88 -27
- package/node/prelude/util.js +121 -5
- package/node/prelude/util_types.js +4 -3
- package/node/prelude/v8.js +814 -0
- package/node/prelude/vm.js +86 -0
- package/node/prelude/wasi.js +10 -0
- package/node/prelude/worker_threads.js +56 -0
- package/node/prelude/zlib.js +58 -25
- package/node/src/host_brotli.c +59 -11
- package/node/src/host_fs.c +342 -11
- package/node/src/host_fswatch.c +170 -0
- package/node/src/host_http.c +143 -20
- package/node/src/host_loop.c +25 -2
- package/node/src/host_net.c +55 -7
- package/node/src/host_os.c +55 -1
- package/node/src/host_proc.c +291 -0
- package/node/src/host_process.c +488 -0
- package/node/src/host_sys.c +64 -2
- package/node/src/host_zlib.c +27 -7
- package/node/src/host_zstd.c +41 -6
- package/node/third_party/hdrhistogram/LICENSE +42 -0
- package/node/third_party/hdrhistogram/README.md +5 -0
- package/node/third_party/libuv/LICENSE +0 -0
- package/node/third_party/libuv/LICENSE-extra +36 -0
- package/node/third_party/libuv/include/uv/aix.h +0 -0
- package/node/third_party/libuv/include/uv/bsd.h +0 -0
- package/node/third_party/libuv/include/uv/darwin.h +0 -0
- package/node/third_party/libuv/include/uv/errno.h +0 -0
- package/node/third_party/libuv/include/uv/linux.h +0 -0
- package/node/third_party/libuv/include/uv/os390.h +0 -0
- package/node/third_party/libuv/include/uv/posix.h +0 -0
- package/node/third_party/libuv/include/uv/sunos.h +0 -0
- package/node/third_party/libuv/include/uv/threadpool.h +0 -0
- package/node/third_party/libuv/include/uv/tree.h +0 -0
- package/node/third_party/libuv/include/uv/unix.h +0 -0
- package/node/third_party/libuv/include/uv/version.h +0 -0
- package/node/third_party/libuv/include/uv/win.h +0 -0
- package/node/third_party/libuv/include/uv.h +0 -0
- package/node/third_party/libuv/src/fs-poll.c +0 -0
- package/node/third_party/libuv/src/heap-inl.h +0 -0
- package/node/third_party/libuv/src/idna.c +0 -0
- package/node/third_party/libuv/src/idna.h +0 -0
- package/node/third_party/libuv/src/inet.c +0 -0
- package/node/third_party/libuv/src/queue.h +0 -0
- package/node/third_party/libuv/src/random.c +0 -0
- package/node/third_party/libuv/src/strscpy.c +0 -0
- package/node/third_party/libuv/src/strscpy.h +0 -0
- package/node/third_party/libuv/src/strtok.c +0 -0
- package/node/third_party/libuv/src/strtok.h +0 -0
- package/node/third_party/libuv/src/thread-common.c +0 -0
- package/node/third_party/libuv/src/threadpool.c +0 -0
- package/node/third_party/libuv/src/timer.c +0 -0
- package/node/third_party/libuv/src/unix/async.c +0 -0
- package/node/third_party/libuv/src/unix/bsd-ifaddrs.c +0 -0
- package/node/third_party/libuv/src/unix/core.c +0 -0
- package/node/third_party/libuv/src/unix/darwin-proctitle.c +0 -0
- package/node/third_party/libuv/src/unix/darwin-stub.h +0 -0
- package/node/third_party/libuv/src/unix/darwin-syscalls.h +0 -0
- package/node/third_party/libuv/src/unix/darwin.c +0 -0
- package/node/third_party/libuv/src/unix/dl.c +0 -0
- package/node/third_party/libuv/src/unix/fs.c +0 -0
- package/node/third_party/libuv/src/unix/fsevents.c +0 -0
- package/node/third_party/libuv/src/unix/getaddrinfo.c +0 -0
- package/node/third_party/libuv/src/unix/getnameinfo.c +0 -0
- package/node/third_party/libuv/src/unix/internal.h +0 -0
- package/node/third_party/libuv/src/unix/kqueue.c +0 -0
- package/node/third_party/libuv/src/unix/linux.c +0 -0
- package/node/third_party/libuv/src/unix/loop-watcher.c +0 -0
- package/node/third_party/libuv/src/unix/loop.c +0 -0
- package/node/third_party/libuv/src/unix/pipe.c +0 -0
- package/node/third_party/libuv/src/unix/poll.c +0 -0
- package/node/third_party/libuv/src/unix/process.c +0 -0
- package/node/third_party/libuv/src/unix/procfs-exepath.c +0 -0
- package/node/third_party/libuv/src/unix/proctitle.c +0 -0
- package/node/third_party/libuv/src/unix/random-devurandom.c +0 -0
- package/node/third_party/libuv/src/unix/random-getentropy.c +0 -0
- package/node/third_party/libuv/src/unix/random-getrandom.c +0 -0
- package/node/third_party/libuv/src/unix/random-sysctl-linux.c +0 -0
- package/node/third_party/libuv/src/unix/signal.c +0 -0
- package/node/third_party/libuv/src/unix/stream.c +0 -0
- package/node/third_party/libuv/src/unix/tcp.c +0 -0
- package/node/third_party/libuv/src/unix/thread.c +0 -0
- package/node/third_party/libuv/src/unix/tty.c +0 -0
- package/node/third_party/libuv/src/unix/udp.c +0 -0
- package/node/third_party/libuv/src/uv-common.c +0 -0
- package/node/third_party/libuv/src/uv-common.h +0 -0
- package/node/third_party/libuv/src/uv-data-getter-setters.c +0 -0
- package/node/third_party/libuv/src/version.c +0 -0
- package/node/third_party/llhttp/LICENSE +0 -0
- package/node/third_party/llhttp/api.c +0 -0
- package/node/third_party/llhttp/http.c +0 -0
- package/node/third_party/llhttp/llhttp.c +0 -0
- package/node/third_party/llhttp/llhttp.h +0 -0
- package/node/third_party/nodejs/LICENSE +47 -0
- package/node/third_party/nodejs/README.md +5 -0
- package/node/third_party/parseargs/LICENSE +21 -0
- package/node/third_party/parseargs/README.md +5 -0
- package/node/third_party/punycode/LICENSE +20 -0
- package/node/third_party/punycode/README.md +5 -0
- package/node/third_party/zlib/LICENSE.chromium +27 -0
- package/package.json +1 -5
- package/prebuilt/darwin-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/darwin-arm64/brotli-7995bbdacc52e51c.a +0 -0
- package/prebuilt/darwin-arm64/uv-72ab666eae197cd8.a +0 -0
- package/prebuilt/darwin-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/darwin-arm64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/darwin-x64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/darwin-x64/brotli-7995bbdacc52e51c.a +0 -0
- package/prebuilt/darwin-x64/uv-72ab666eae197cd8.a +0 -0
- package/prebuilt/darwin-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/darwin-x64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-arm64/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-arm64/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-arm64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-arm64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-arm64-musl/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-arm64-musl/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-arm64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-arm64-musl/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-x64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-x64/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-x64/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-x64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-x64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-x64-musl/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-x64-musl/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-x64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-x64-musl/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_atoms.gen.h +0 -0
- package/runtime/include/js_atoms.h +1 -1
- package/runtime/include/js_bigint.h +1 -1
- package/runtime/include/js_buffer.h +4 -4
- package/runtime/include/js_builtins.h +41 -3
- package/runtime/include/js_console.h +9 -4
- package/runtime/include/js_convert.h +1 -1
- package/runtime/include/js_core.def +5 -4
- package/runtime/include/js_crypto.h +1 -1
- package/runtime/include/js_error.h +1 -1
- package/runtime/include/js_exception.h +21 -1
- package/runtime/include/js_function.h +33 -9
- package/runtime/include/js_gc.h +172 -30
- package/runtime/include/js_generator.h +2 -2
- package/runtime/include/js_host.h +19 -1
- package/runtime/include/js_jobs.h +5 -5
- package/runtime/include/js_module.h +1 -1
- package/runtime/include/js_object.h +142 -15
- package/runtime/include/js_ops.h +107 -5
- package/runtime/include/js_promise.h +3 -3
- package/runtime/include/js_regexp.h +1 -1
- package/runtime/include/js_runtime.h +0 -0
- package/runtime/include/js_symbol.h +1 -1
- package/runtime/include/js_web.h +2 -1
- package/runtime/include/jscell.h +22 -15
- package/runtime/include/jscontext.h +74 -39
- package/runtime/include/jsnum.h +1 -1
- package/runtime/include/jsstring.h +19 -6
- package/runtime/include/jsvalue.h +7 -3
- package/runtime/src/arguments.c +2 -2
- package/runtime/src/array.c +4 -1
- package/runtime/src/arraybuffer.c +19 -7
- package/runtime/src/async_iter.c +4 -3
- package/runtime/src/atoms.c +1 -1
- package/runtime/src/base64.c +2 -2
- package/runtime/src/bigint.c +1 -1
- package/runtime/src/blob.c +22 -9
- package/runtime/src/boringssl_support.cc +1 -1
- package/runtime/src/buffer.c +299 -91
- package/runtime/src/builtin_table.c +1 -1
- package/runtime/src/builtins.c +30 -4
- package/runtime/src/builtins_util.h +0 -0
- package/runtime/src/collections.c +18 -3
- package/runtime/src/components.c +1 -1
- package/runtime/src/console.c +14 -7
- package/runtime/src/convert.c +0 -0
- package/runtime/src/core_all.c +1 -1
- package/runtime/src/crypto.c +1 -1
- package/runtime/src/crypto_asym.c +1 -1
- package/runtime/src/crypto_bn.c +1 -1
- package/runtime/src/crypto_cipher.c +10 -3
- package/runtime/src/crypto_hash.c +1 -1
- package/runtime/src/crypto_host.c +1 -1
- package/runtime/src/crypto_internal.h +1 -1
- package/runtime/src/crypto_kdf.c +1 -1
- package/runtime/src/crypto_key.c +14 -1
- package/runtime/src/crypto_pkey.c +1 -1
- package/runtime/src/crypto_subtle.c +142 -58
- package/runtime/src/crypto_tls.c +1 -1
- package/runtime/src/date.c +5 -2
- package/runtime/src/domexception.c +2 -2
- package/runtime/src/elements.c +56 -17
- package/runtime/src/elements.h +7 -2
- package/runtime/src/encoding.c +1 -1
- package/runtime/src/error.c +4 -4
- package/runtime/src/events.c +23 -14
- package/runtime/src/exception.c +1 -1
- package/runtime/src/fetch.c +241 -17
- package/runtime/src/function.c +31 -19
- package/runtime/src/gc.c +1716 -207
- package/runtime/src/gc_visit.h +87 -0
- package/runtime/src/generator.c +5 -3
- package/runtime/src/global.c +14 -6
- package/runtime/src/headers.c +10 -10
- package/runtime/src/inspect.c +76 -17
- package/runtime/src/internal.h +5 -2
- package/runtime/src/iterate.c +94 -2
- package/runtime/src/iterator.c +1 -1
- package/runtime/src/jobs.c +11 -5
- package/runtime/src/jsnum.c +2 -2
- package/runtime/src/json.c +1 -1
- package/runtime/src/math.c +9 -1
- package/runtime/src/module.c +3 -3
- package/runtime/src/number.c +0 -0
- package/runtime/src/object.c +107 -74
- package/runtime/src/ops.c +21 -3
- package/runtime/src/os.c +192 -1
- package/runtime/src/os.h +33 -2
- package/runtime/src/process.c +73 -8
- package/runtime/src/promise.c +4 -4
- package/runtime/src/proxy.c +1 -1
- package/runtime/src/reflect.c +1 -1
- package/runtime/src/regexp.c +134 -2
- package/runtime/src/regexp_anchor.h +1 -1
- package/runtime/src/regexp_message.h +1 -1
- package/runtime/src/shape.c +10 -19
- package/runtime/src/shape.h +0 -0
- package/runtime/src/streams.c +22 -8
- package/runtime/src/streams.h +0 -0
- package/runtime/src/string.c +64 -25
- package/runtime/src/string_builtins.c +7 -7
- package/runtime/src/symbol.c +2 -7
- package/runtime/src/test_host.c +3 -3
- package/runtime/src/tls_roots.gen.h +102 -620
- package/runtime/src/typedarray.c +8 -3
- package/runtime/src/tznames.gen.h +2 -1
- package/runtime/src/uri.c +1 -1
- package/runtime/src/url.c +42 -39
- package/runtime/src/weak.c +15 -12
- package/runtime/src/web.c +21 -3
- package/runtime/src/wrapper.c +0 -0
- package/runtime/src/writable.c +7 -6
- package/runtime/third_party/ada-idna/LICENSE-MIT +0 -0
- package/runtime/third_party/ada-idna/README.md +7 -7
- package/runtime/third_party/ada-idna/convert.mjs +89 -27
- package/runtime/third_party/ada-idna/idna.c +30 -31
- package/runtime/third_party/ada-idna/idna.h +0 -0
- package/runtime/third_party/ada-idna/idna_tables.h +2272 -1952
- package/runtime/third_party/boringssl/third_party/fiat/LICENSE +15 -0
- package/runtime/third_party/mozilla-ca/LICENSE +373 -0
- package/runtime/third_party/mozilla-ca/README.md +9 -0
- package/runtime/third_party/nodejs/LICENSE +47 -0
- package/runtime/third_party/nodejs/README.md +7 -0
- package/runtime/third_party/quickjs-dtoa/LICENSE +0 -0
- package/runtime/third_party/quickjs-dtoa/README.md +1 -1
- package/runtime/third_party/quickjs-dtoa/dtoa.c +0 -0
- package/runtime/third_party/quickjs-dtoa/dtoa.h +0 -0
- package/runtime/third_party/quickjs-dtoa/dtoa_compat.h +0 -0
- package/runtime/third_party/quickjs-regexp/LICENSE +0 -0
- package/runtime/third_party/quickjs-regexp/README.md +2 -2
- package/runtime/third_party/quickjs-regexp/cutils.c +0 -0
- package/runtime/third_party/quickjs-regexp/cutils.h +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp-opcode.h +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp.c +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp.h +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode-table.h +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode.c +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode.h +0 -0
- package/runtime/third_party/unicode/LICENSE +39 -0
- package/runtime/third_party/unicode/README.md +9 -0
- package/runtime/third_party/v8-ieee754/LICENSE +0 -0
- package/runtime/third_party/v8-ieee754/LICENSE.fdlibm +10 -0
- package/runtime/third_party/v8-ieee754/README.md +3 -2
- package/runtime/third_party/v8-ieee754/convert.py +0 -0
- package/runtime/third_party/v8-ieee754/ieee754.c +0 -0
- package/runtime/third_party/v8-ieee754/ieee754.h +0 -0
- package/runtime/tools/regexp_compile.c +2 -2
|
@@ -0,0 +1,1571 @@
|
|
|
1
|
+
// node:test's runner: Node's lib/internal/test_runner/test.js, harness.js and
|
|
2
|
+
// tests_stream.js (MIT; Node v22.22.2), ported. Tests, suites and hooks run as Node runs them: a
|
|
3
|
+
// suite's tests are collected first, tests run one after another (or `concurrency` at a time),
|
|
4
|
+
// hooks in Node's order, results reported in declaration order, and the summary when the event
|
|
5
|
+
// loop is done ('beforeExit'). What differs:
|
|
6
|
+
// - The current test is the AsyncLocalStorage store the test runs in (visaljs carries it through
|
|
7
|
+
// promises, timers and I/O), where Node maps async_hooks ids to tests.
|
|
8
|
+
// - A test has no location (Node's file:line:column of the test() call): a compiled program
|
|
9
|
+
// doesn't know it.
|
|
10
|
+
// - The reporter is the default one, tap (spec when stdout is a terminal): a compiled program has
|
|
11
|
+
// no node --test-* options. Its text goes to stdout as the events come, a turn of the event loop
|
|
12
|
+
// later, as Node's reporter stream writes it.
|
|
13
|
+
// - run(), the test runner that starts a process per test file, isn't provided.
|
|
14
|
+
// Ported from Node.js (Copyright Node.js contributors, MIT): node/third_party/nodejs/LICENSE.
|
|
15
|
+
import { AsyncLocalStorage } from "../../async_hooks.js";
|
|
16
|
+
import assert from "../../assert.js";
|
|
17
|
+
import { EventEmitter } from "../../events.js";
|
|
18
|
+
import { codeError, invalidArgType, invalidArgValue, outOfRange, validateFunction } from "../errors.js";
|
|
19
|
+
import { MockTracker } from "./mock.js";
|
|
20
|
+
import { SpecReporter } from "./reporters.js";
|
|
21
|
+
import { isPromise } from "../../util_types.js";
|
|
22
|
+
import { utilInspect as inspect } from "visaljs:host";
|
|
23
|
+
|
|
24
|
+
const kCallbackAndPromisePresent = "callbackAndPromisePresent";
|
|
25
|
+
export const kCancelledByParent = "cancelledByParent";
|
|
26
|
+
const kAborted = "testAborted";
|
|
27
|
+
const kParentAlreadyFinished = "parentAlreadyFinished";
|
|
28
|
+
const kSubtestsFailed = "subtestsFailed";
|
|
29
|
+
const kTestCodeFailure = "testCodeFailure";
|
|
30
|
+
const kTestTimeoutFailure = "testTimeoutFailure";
|
|
31
|
+
const kHookFailure = "hookFailed";
|
|
32
|
+
const kMultipleCallbackInvocations = "multipleCallbackInvocations";
|
|
33
|
+
const kDefaultTimeout = null;
|
|
34
|
+
const noop = () => {};
|
|
35
|
+
const kShouldAbort = Symbol("kShouldAbort");
|
|
36
|
+
const kHookNames = Object.seal(["before", "after", "beforeEach", "afterEach"]);
|
|
37
|
+
const kDispose = Symbol("kDispose");
|
|
38
|
+
const kIsTestFailure = Symbol("kIsTestFailure");
|
|
39
|
+
const TIMEOUT_MAX = 2 ** 31 - 1;
|
|
40
|
+
const kGenericUserError = 1;
|
|
41
|
+
|
|
42
|
+
/* the test (or hook) whose code is running: Node's executionAsyncId() → test map */
|
|
43
|
+
const testStorage = new AsyncLocalStorage();
|
|
44
|
+
|
|
45
|
+
/* milliseconds, as a high-resolution time */
|
|
46
|
+
function hrtime() {
|
|
47
|
+
const [s, ns] = process.hrtime();
|
|
48
|
+
return s * 1e3 + ns / 1e6;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ---- Node's errors and validators ---------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/** ERR_TEST_FAILURE: an error (or a message) and why the test failed. */
|
|
54
|
+
class TestFailure extends Error {
|
|
55
|
+
constructor(error, failureType) {
|
|
56
|
+
let msg = error?.message ?? error;
|
|
57
|
+
if (typeof msg !== "string") {
|
|
58
|
+
msg = inspectWithNoCustomRetry(msg);
|
|
59
|
+
}
|
|
60
|
+
super(msg);
|
|
61
|
+
this.failureType = failureType;
|
|
62
|
+
this.cause = error;
|
|
63
|
+
Object.defineProperty(this, "code", { value: "ERR_TEST_FAILURE", enumerable: true, writable: true, configurable: true });
|
|
64
|
+
Object.defineProperty(this, kIsTestFailure, { value: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toString() {
|
|
68
|
+
return `Error [ERR_TEST_FAILURE]: ${this.message}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function inspectWithNoCustomRetry(obj, options) {
|
|
73
|
+
try {
|
|
74
|
+
return inspect(obj, options);
|
|
75
|
+
} catch {
|
|
76
|
+
return inspect(obj, { ...options, customInspect: false });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isTestFailureError(err) {
|
|
81
|
+
return err?.code === "ERR_TEST_FAILURE" && err?.[kIsTestFailure] === true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class AbortError extends Error {
|
|
85
|
+
constructor(message = "The operation was aborted", options = undefined) {
|
|
86
|
+
super(message, options);
|
|
87
|
+
this.code = "ABORT_ERR";
|
|
88
|
+
this.name = "AbortError";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validateObject(value, name) {
|
|
93
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw invalidArgType(name, "of type object", value);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function validateNumber(value, name, min, max) {
|
|
97
|
+
if (typeof value !== "number") throw invalidArgType(name, "of type number", value);
|
|
98
|
+
if (value < min || value > max || Number.isNaN(value)) throw outOfRange(name, `>= ${min} && <= ${max}`, value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function validateUint32(value, name, positive = false) {
|
|
102
|
+
if (typeof value !== "number") throw invalidArgType(name, "of type number", value);
|
|
103
|
+
if (!Number.isInteger(value)) throw outOfRange(name, "an integer", value);
|
|
104
|
+
const min = positive ? 1 : 0;
|
|
105
|
+
if (value < min || value > 4294967295) throw outOfRange(name, `>= ${min} && <= 4294967295`, value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function validateOneOf(value, name, oneOf) {
|
|
109
|
+
if (!oneOf.includes(value)) {
|
|
110
|
+
throw invalidArgValue(name, `must be one of: ${oneOf.map((v) => (typeof v === "string" ? `'${v}'` : String(v))).join(", ")}`, value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function validateAbortSignal(signal, name) {
|
|
115
|
+
if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) {
|
|
116
|
+
throw invalidArgType(name, "an instance of AbortSignal", signal);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Node's internal/util once(), preserving the first call's return value. */
|
|
121
|
+
function runOnce(fn) {
|
|
122
|
+
let called = false;
|
|
123
|
+
let value;
|
|
124
|
+
return function (...args) {
|
|
125
|
+
if (called) return value;
|
|
126
|
+
called = true;
|
|
127
|
+
value = Reflect.apply(fn, this, args);
|
|
128
|
+
return value;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function createDeferredCallback() {
|
|
133
|
+
let calledCount = 0;
|
|
134
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
135
|
+
const cb = (err) => {
|
|
136
|
+
calledCount++;
|
|
137
|
+
|
|
138
|
+
// If the callback is called a second time, let the user know, but
|
|
139
|
+
// don't let them know more than once.
|
|
140
|
+
if (calledCount > 1) {
|
|
141
|
+
if (calledCount === 2) {
|
|
142
|
+
throw new TestFailure("callback invoked multiple times", kMultipleCallbackInvocations);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (err) {
|
|
149
|
+
return reject(err);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
resolve();
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return { __proto__: null, promise, cb };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function countCompletedTest(test, harness = test.root.harness) {
|
|
159
|
+
if (test.nesting === 0) {
|
|
160
|
+
harness.counters.topLevel++;
|
|
161
|
+
}
|
|
162
|
+
if (test.reportedType === "suite") {
|
|
163
|
+
harness.counters.suites++;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Check SKIP and TODO tests first, as those should not be counted as
|
|
167
|
+
// failures.
|
|
168
|
+
if (test.skipped) {
|
|
169
|
+
harness.counters.skipped++;
|
|
170
|
+
} else if (test.isTodo) {
|
|
171
|
+
harness.counters.todo++;
|
|
172
|
+
} else if (test.cancelled) {
|
|
173
|
+
harness.counters.cancelled++;
|
|
174
|
+
harness.success = false;
|
|
175
|
+
} else if (!test.passed) {
|
|
176
|
+
harness.counters.failed++;
|
|
177
|
+
harness.success = false;
|
|
178
|
+
} else {
|
|
179
|
+
harness.counters.passed++;
|
|
180
|
+
}
|
|
181
|
+
harness.counters.tests++;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---- The event stream (lib/internal/test_runner/tests_stream.js) --------------------------
|
|
185
|
+
|
|
186
|
+
/** The tests' events, as Node's TestsStream emits them; a reporter writes them to stdout. */
|
|
187
|
+
class TestsStream extends EventEmitter {
|
|
188
|
+
#reporter = null;
|
|
189
|
+
#pending = [];
|
|
190
|
+
#scheduled = false;
|
|
191
|
+
#ended = false;
|
|
192
|
+
|
|
193
|
+
/** Starts writing the events with a reporter: tap, or spec. */
|
|
194
|
+
attach(reporter) {
|
|
195
|
+
this.#reporter = reporter;
|
|
196
|
+
const head = reporter.start();
|
|
197
|
+
if (head) this.#pending.push(head);
|
|
198
|
+
this.#schedule();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#schedule() {
|
|
202
|
+
if (this.#scheduled || this.#reporter === null) return;
|
|
203
|
+
this.#scheduled = true;
|
|
204
|
+
setImmediate(() => this.#flush());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#flush() {
|
|
208
|
+
this.#scheduled = false;
|
|
209
|
+
const text = this.#pending.join("");
|
|
210
|
+
this.#pending = [];
|
|
211
|
+
if (text) process.stdout.write(text);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#emitMessage(type, data) {
|
|
215
|
+
this.emit(type, data);
|
|
216
|
+
if (this.#reporter === null || this.#ended) return;
|
|
217
|
+
const text = this.#reporter.handle({ type, data });
|
|
218
|
+
if (text) {
|
|
219
|
+
this.#pending.push(text);
|
|
220
|
+
this.#schedule();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fail(nesting, loc, testNumber, name, details, directive) {
|
|
225
|
+
this.#emitMessage("test:fail", { __proto__: null, name, nesting, testNumber, details, ...loc, ...directive });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
ok(nesting, loc, testNumber, name, details, directive) {
|
|
229
|
+
this.#emitMessage("test:pass", { __proto__: null, name, nesting, testNumber, details, ...loc, ...directive });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
complete(nesting, loc, testNumber, name, details, directive) {
|
|
233
|
+
this.#emitMessage("test:complete", { __proto__: null, name, nesting, testNumber, details, ...loc, ...directive });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
plan(nesting, loc, count) {
|
|
237
|
+
this.#emitMessage("test:plan", { __proto__: null, nesting, count, ...loc });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
getSkip(reason = undefined) {
|
|
241
|
+
return { __proto__: null, skip: reason ?? true };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
getTodo(reason = undefined) {
|
|
245
|
+
return { __proto__: null, todo: reason ?? true };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
enqueue(nesting, loc, name, type) {
|
|
249
|
+
this.#emitMessage("test:enqueue", { __proto__: null, nesting, name, type, ...loc });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
dequeue(nesting, loc, name, type) {
|
|
253
|
+
this.#emitMessage("test:dequeue", { __proto__: null, nesting, name, type, ...loc });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
start(nesting, loc, name) {
|
|
257
|
+
this.#emitMessage("test:start", { __proto__: null, nesting, name, ...loc });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
diagnostic(nesting, loc, message, level = "info") {
|
|
261
|
+
this.#emitMessage("test:diagnostic", { __proto__: null, nesting, message, level, ...loc });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
summary(nesting, file, success, counts, duration_ms) {
|
|
265
|
+
this.#emitMessage("test:summary", { __proto__: null, success, counts, duration_ms, file });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
end() {
|
|
269
|
+
if (this.#reporter !== null) {
|
|
270
|
+
const tail = this.#reporter.flush();
|
|
271
|
+
if (tail) this.#pending.push(tail);
|
|
272
|
+
this.#schedule();
|
|
273
|
+
}
|
|
274
|
+
this.#ended = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* a filtered test's reporter: its events go nowhere */
|
|
279
|
+
let noopTestStream;
|
|
280
|
+
|
|
281
|
+
// ---- Tests (lib/internal/test_runner/test.js) ---------------------------------------------
|
|
282
|
+
|
|
283
|
+
function stopTest(timeout, signal) {
|
|
284
|
+
const deferred = Promise.withResolvers();
|
|
285
|
+
const onAbort = () => deferred.resolve();
|
|
286
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
287
|
+
let timer;
|
|
288
|
+
let disposeFunction;
|
|
289
|
+
|
|
290
|
+
if (timeout === kDefaultTimeout) {
|
|
291
|
+
disposeFunction = () => signal.removeEventListener("abort", onAbort);
|
|
292
|
+
} else {
|
|
293
|
+
timer = setTimeout(() => deferred.resolve(), timeout);
|
|
294
|
+
timer?.unref?.();
|
|
295
|
+
|
|
296
|
+
deferred.promise = deferred.promise.then(() => {
|
|
297
|
+
throw new TestFailure(`test timed out after ${timeout}ms`, kTestTimeoutFailure);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
disposeFunction = () => {
|
|
301
|
+
signal.removeEventListener("abort", onAbort);
|
|
302
|
+
clearTimeout(timer);
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
deferred.promise[kDispose] = disposeFunction;
|
|
307
|
+
return deferred.promise;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
class TestPlan {
|
|
311
|
+
#waitIndefinitely = false;
|
|
312
|
+
#planPromise = null;
|
|
313
|
+
#timeoutId = null;
|
|
314
|
+
|
|
315
|
+
constructor(count, options = {}) {
|
|
316
|
+
validateUint32(count, "count");
|
|
317
|
+
validateObject(options, "options");
|
|
318
|
+
this.expected = count;
|
|
319
|
+
this.actual = 0;
|
|
320
|
+
|
|
321
|
+
const { wait } = options;
|
|
322
|
+
if (typeof wait === "boolean") {
|
|
323
|
+
this.wait = wait;
|
|
324
|
+
this.#waitIndefinitely = wait;
|
|
325
|
+
} else if (typeof wait === "number") {
|
|
326
|
+
validateNumber(wait, "options.wait", 0, TIMEOUT_MAX);
|
|
327
|
+
this.wait = wait;
|
|
328
|
+
} else if (wait !== undefined) {
|
|
329
|
+
throw invalidArgType("options.wait", "of type boolean or number", wait);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#planMet() {
|
|
334
|
+
return this.actual === this.expected;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#createTimeout(reject) {
|
|
338
|
+
return setTimeout(() => {
|
|
339
|
+
const err = new TestFailure(
|
|
340
|
+
`plan timed out after ${this.wait}ms with ${this.actual} assertions when expecting ${this.expected}`,
|
|
341
|
+
kTestTimeoutFailure,
|
|
342
|
+
);
|
|
343
|
+
reject(err);
|
|
344
|
+
}, this.wait);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
check() {
|
|
348
|
+
if (this.#planMet()) {
|
|
349
|
+
if (this.#timeoutId) {
|
|
350
|
+
clearTimeout(this.#timeoutId);
|
|
351
|
+
this.#timeoutId = null;
|
|
352
|
+
}
|
|
353
|
+
if (this.#planPromise) {
|
|
354
|
+
const { resolve } = this.#planPromise;
|
|
355
|
+
resolve();
|
|
356
|
+
this.#planPromise = null;
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!this.#shouldWait()) {
|
|
362
|
+
throw new TestFailure(`plan expected ${this.expected} assertions but received ${this.actual}`, kTestCodeFailure);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!this.#planPromise) {
|
|
366
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
367
|
+
this.#planPromise = { __proto__: null, promise, resolve, reject };
|
|
368
|
+
|
|
369
|
+
if (!this.#waitIndefinitely) {
|
|
370
|
+
this.#timeoutId = this.#createTimeout(reject);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return this.#planPromise.promise;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
count() {
|
|
378
|
+
this.actual++;
|
|
379
|
+
if (this.#planPromise) {
|
|
380
|
+
this.check();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#shouldWait() {
|
|
385
|
+
return this.wait !== undefined && this.wait !== false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* t.assert's methods: node:assert's (internal/test_runner/assert.js), and register()'s */
|
|
390
|
+
const assertMap = new Map([
|
|
391
|
+
"deepEqual", "deepStrictEqual", "doesNotMatch", "doesNotReject", "doesNotThrow", "equal", "fail", "ifError",
|
|
392
|
+
"match", "notDeepEqual", "notDeepStrictEqual", "notEqual", "notStrictEqual", "partialDeepStrictEqual",
|
|
393
|
+
"rejects", "strictEqual", "throws",
|
|
394
|
+
].map((name) => [name, assert[name]]));
|
|
395
|
+
|
|
396
|
+
/** test.assert.register(name, fn): a method every test context's t.assert has. */
|
|
397
|
+
export function registerAssertion(name, fn) {
|
|
398
|
+
if (typeof name !== "string") throw invalidArgType("name", "of type string", name);
|
|
399
|
+
validateFunction(fn, "fn");
|
|
400
|
+
assertMap.set(name, fn);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class TestContext {
|
|
404
|
+
#assert;
|
|
405
|
+
#test;
|
|
406
|
+
|
|
407
|
+
constructor(test) {
|
|
408
|
+
this.#test = test;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
get signal() {
|
|
412
|
+
return this.#test.signal;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
get name() {
|
|
416
|
+
return this.#test.name;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
get filePath() {
|
|
420
|
+
return this.#test.entryFile;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
get fullName() {
|
|
424
|
+
return getFullName(this.#test);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
get error() {
|
|
428
|
+
return this.#test.error;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
get passed() {
|
|
432
|
+
return this.#test.passed;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
diagnostic(message) {
|
|
436
|
+
this.#test.diagnostic(message);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
plan(count, options = {}) {
|
|
440
|
+
if (this.#test.plan !== null) {
|
|
441
|
+
throw new TestFailure("cannot set plan more than once", kTestCodeFailure);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this.#test.plan = new TestPlan(count, options);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
get assert() {
|
|
448
|
+
if (this.#assert === undefined) {
|
|
449
|
+
const { plan } = this.#test;
|
|
450
|
+
const assertObj = { __proto__: null };
|
|
451
|
+
|
|
452
|
+
this.#assert = assertObj;
|
|
453
|
+
assertMap.forEach((method, name) => {
|
|
454
|
+
assertObj[name] = (...args) => {
|
|
455
|
+
if (plan !== null) {
|
|
456
|
+
plan.count();
|
|
457
|
+
}
|
|
458
|
+
return Reflect.apply(method, this, args);
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
if (!assertMap.has("ok")) {
|
|
463
|
+
assertObj.ok = (...args) => {
|
|
464
|
+
if (plan !== null) {
|
|
465
|
+
plan.count();
|
|
466
|
+
}
|
|
467
|
+
Reflect.apply(assert.ok, undefined, args);
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return this.#assert;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
get mock() {
|
|
475
|
+
this.#test.mock ??= new MockTracker();
|
|
476
|
+
return this.#test.mock;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
runOnly(value) {
|
|
480
|
+
this.#test.runOnlySubtests = !!value;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
skip(message) {
|
|
484
|
+
this.#test.skip(message);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
todo(message) {
|
|
488
|
+
this.#test.todo(message);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
test(name, options, fn) {
|
|
492
|
+
const { plan } = this.#test;
|
|
493
|
+
if (plan !== null) {
|
|
494
|
+
plan.count();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const subtest = this.#test.createSubtest(Test, name, options, fn, { __proto__: null });
|
|
498
|
+
|
|
499
|
+
return subtest.start();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
before(fn, options) {
|
|
503
|
+
this.#test.createHook("before", fn, { __proto__: null, ...options, parent: this.#test, hookType: "before" });
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
after(fn, options) {
|
|
507
|
+
this.#test.createHook("after", fn, { __proto__: null, ...options, parent: this.#test, hookType: "after" });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
beforeEach(fn, options) {
|
|
511
|
+
this.#test.createHook("beforeEach", fn, { __proto__: null, ...options, parent: this.#test, hookType: "beforeEach" });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
afterEach(fn, options) {
|
|
515
|
+
this.#test.createHook("afterEach", fn, { __proto__: null, ...options, parent: this.#test, hookType: "afterEach" });
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
waitFor(condition, options = {}) {
|
|
519
|
+
validateFunction(condition, "condition");
|
|
520
|
+
validateObject(options, "options");
|
|
521
|
+
|
|
522
|
+
const { interval = 50, timeout = 1000 } = options;
|
|
523
|
+
|
|
524
|
+
validateNumber(interval, "options.interval", 0, TIMEOUT_MAX);
|
|
525
|
+
validateNumber(timeout, "options.timeout", 0, TIMEOUT_MAX);
|
|
526
|
+
|
|
527
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
528
|
+
const noError = Symbol();
|
|
529
|
+
let cause = noError;
|
|
530
|
+
let pollerId;
|
|
531
|
+
let timeoutId = undefined;
|
|
532
|
+
const done = (err, result) => {
|
|
533
|
+
clearTimeout(pollerId);
|
|
534
|
+
clearTimeout(timeoutId);
|
|
535
|
+
|
|
536
|
+
if (err === noError) {
|
|
537
|
+
resolve(result);
|
|
538
|
+
} else {
|
|
539
|
+
reject(err);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
timeoutId = setTimeout(() => {
|
|
544
|
+
const err = new Error("waitFor() timed out");
|
|
545
|
+
|
|
546
|
+
if (cause !== noError) {
|
|
547
|
+
err.cause = cause;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
done(err);
|
|
551
|
+
}, timeout);
|
|
552
|
+
|
|
553
|
+
const poller = async () => {
|
|
554
|
+
try {
|
|
555
|
+
const result = await condition();
|
|
556
|
+
|
|
557
|
+
done(noError, result);
|
|
558
|
+
} catch (err) {
|
|
559
|
+
cause = err;
|
|
560
|
+
pollerId = setTimeout(poller, interval);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
poller();
|
|
565
|
+
return promise;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
class SuiteContext {
|
|
570
|
+
#suite;
|
|
571
|
+
|
|
572
|
+
constructor(suite) {
|
|
573
|
+
this.#suite = suite;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
get signal() {
|
|
577
|
+
return this.#suite.signal;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
get name() {
|
|
581
|
+
return this.#suite.name;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
get filePath() {
|
|
585
|
+
return this.#suite.entryFile;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
get fullName() {
|
|
589
|
+
return getFullName(this.#suite);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export class Test {
|
|
594
|
+
reportedType = "test";
|
|
595
|
+
abortController;
|
|
596
|
+
outerSignal;
|
|
597
|
+
#reportedSubtest;
|
|
598
|
+
|
|
599
|
+
constructor(options) {
|
|
600
|
+
let { fn, name, parent } = options;
|
|
601
|
+
const { concurrency, entryFile, loc, only, timeout, todo, skip, signal, plan } = options;
|
|
602
|
+
|
|
603
|
+
if (typeof fn !== "function") {
|
|
604
|
+
fn = noop;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (typeof name !== "string" || name === "") {
|
|
608
|
+
name = fn.name || "<anonymous>";
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (!(parent instanceof Test)) {
|
|
612
|
+
parent = null;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
this.name = name;
|
|
616
|
+
this.parent = parent;
|
|
617
|
+
this.testNumber = 0;
|
|
618
|
+
this.outputSubtestCount = 0;
|
|
619
|
+
this.diagnostics = [];
|
|
620
|
+
this.filtered = false;
|
|
621
|
+
this.filteredByName = false;
|
|
622
|
+
this.hasOnlyTests = false;
|
|
623
|
+
|
|
624
|
+
if (parent === null) {
|
|
625
|
+
this.root = this;
|
|
626
|
+
this.harness = options.harness;
|
|
627
|
+
this.config = this.harness.config;
|
|
628
|
+
this.concurrency = 1;
|
|
629
|
+
this.nesting = 0;
|
|
630
|
+
this.only = this.config.only;
|
|
631
|
+
this.reporter = new TestsStream();
|
|
632
|
+
this.runOnlySubtests = this.only;
|
|
633
|
+
this.childNumber = 0;
|
|
634
|
+
this.timeout = kDefaultTimeout;
|
|
635
|
+
this.entryFile = entryFile;
|
|
636
|
+
} else {
|
|
637
|
+
const nesting = parent.parent === null ? parent.nesting : parent.nesting + 1;
|
|
638
|
+
|
|
639
|
+
this.root = parent.root;
|
|
640
|
+
this.harness = null;
|
|
641
|
+
this.config = this.root.harness.config;
|
|
642
|
+
this.concurrency = parent.concurrency;
|
|
643
|
+
this.nesting = nesting;
|
|
644
|
+
this.only = only;
|
|
645
|
+
this.reporter = parent.reporter;
|
|
646
|
+
this.runOnlySubtests = false;
|
|
647
|
+
this.childNumber = parent.subtests.length + 1;
|
|
648
|
+
this.timeout = parent.timeout;
|
|
649
|
+
this.entryFile = parent.entryFile;
|
|
650
|
+
|
|
651
|
+
/* Node 24: a file run directly filters by `only` (no --test-only needed) */
|
|
652
|
+
if (this.root.harness.isFilteringByOnly) {
|
|
653
|
+
if (this.only) {
|
|
654
|
+
// If filtering impacts the tests within a suite, then the suite only
|
|
655
|
+
// runs those tests. If filtering does not impact the tests within a
|
|
656
|
+
// suite, then all tests are run.
|
|
657
|
+
this.parent.runOnlySubtests = true;
|
|
658
|
+
|
|
659
|
+
if (this.parent === this.root || this.parent.startTime === null) {
|
|
660
|
+
for (let t = this.parent; t !== null && !t.hasOnlyTests; t = t.parent) {
|
|
661
|
+
t.hasOnlyTests = true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} else if (this.only === false) {
|
|
665
|
+
fn = noop;
|
|
666
|
+
}
|
|
667
|
+
} else if (only || this.parent.runOnlySubtests) {
|
|
668
|
+
const warning = "'only' and 'runOnly' require the --test-only command-line option.";
|
|
669
|
+
this.diagnostic(warning);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
switch (typeof concurrency) {
|
|
674
|
+
case "number":
|
|
675
|
+
validateUint32(concurrency, "options.concurrency", true);
|
|
676
|
+
this.concurrency = concurrency;
|
|
677
|
+
break;
|
|
678
|
+
|
|
679
|
+
case "boolean":
|
|
680
|
+
if (concurrency) {
|
|
681
|
+
this.concurrency = parent === null ? Math.max((globalThis.navigator?.hardwareConcurrency ?? 1) - 1, 1) : Infinity;
|
|
682
|
+
} else {
|
|
683
|
+
this.concurrency = 1;
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
|
|
687
|
+
default:
|
|
688
|
+
if (concurrency != null) throw invalidArgType("options.concurrency", "of type boolean or number", concurrency);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (timeout != null && timeout !== Infinity) {
|
|
692
|
+
validateNumber(timeout, "options.timeout", 0, TIMEOUT_MAX);
|
|
693
|
+
this.timeout = timeout;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (skip) {
|
|
697
|
+
fn = noop;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
this.abortController = new AbortController();
|
|
701
|
+
this.outerSignal = signal;
|
|
702
|
+
this.signal = this.abortController.signal;
|
|
703
|
+
|
|
704
|
+
validateAbortSignal(signal, "options.signal");
|
|
705
|
+
|
|
706
|
+
this.outerSignal?.addEventListener("abort", this.#abortHandler);
|
|
707
|
+
|
|
708
|
+
this.fn = fn;
|
|
709
|
+
this.mock = null;
|
|
710
|
+
this.plan = null;
|
|
711
|
+
this.expectedAssertions = plan;
|
|
712
|
+
this.cancelled = false;
|
|
713
|
+
this.skipped = skip !== undefined && skip !== false;
|
|
714
|
+
this.isTodo = (todo !== undefined && todo !== false) || this.parent?.isTodo;
|
|
715
|
+
this.startTime = null;
|
|
716
|
+
this.endTime = null;
|
|
717
|
+
this.passed = false;
|
|
718
|
+
this.error = null;
|
|
719
|
+
this.message = typeof skip === "string" ? skip : typeof todo === "string" ? todo : null;
|
|
720
|
+
this.activeSubtests = 0;
|
|
721
|
+
this.pendingSubtests = [];
|
|
722
|
+
this.readySubtests = new Map();
|
|
723
|
+
this.subtests = [];
|
|
724
|
+
this.waitingOn = 0;
|
|
725
|
+
this.finished = false;
|
|
726
|
+
this.hooks = {
|
|
727
|
+
__proto__: null,
|
|
728
|
+
before: [],
|
|
729
|
+
after: [],
|
|
730
|
+
beforeEach: [],
|
|
731
|
+
afterEach: [],
|
|
732
|
+
ownAfterEachCount: 0,
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
this.loc = loc;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** Runs fn(...args) with this test as the current one (Node's AsyncResource.runInAsyncScope). */
|
|
739
|
+
runInAsyncScope(fn, thisArg, ...args) {
|
|
740
|
+
return testStorage.run(this, () => Reflect.apply(fn, thisArg, args));
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** Node 24's: a test outside the `only` ones, when filtering by them, isn't run or reported. */
|
|
744
|
+
applyFilters() {
|
|
745
|
+
if (this.error) return; /* errors are never filtered */
|
|
746
|
+
if (this.root.harness.isFilteringByOnly && !this.only && !this.hasOnlyTests) {
|
|
747
|
+
if (this.parent.runOnlySubtests || this.parent.hasOnlyTests || this.only === false) {
|
|
748
|
+
this.filtered = true;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** The name of the test after its ancestors' names, each after a space. */
|
|
754
|
+
getTestNameWithAncestors() {
|
|
755
|
+
if (!this.parent) return "";
|
|
756
|
+
|
|
757
|
+
return `${this.parent.getTestNameWithAncestors()} ${this.name}`;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
hasConcurrency() {
|
|
761
|
+
return this.concurrency > this.activeSubtests;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
addPendingSubtest(deferred) {
|
|
765
|
+
this.pendingSubtests.push(deferred);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async processPendingSubtests() {
|
|
769
|
+
while (this.pendingSubtests.length > 0 && this.hasConcurrency()) {
|
|
770
|
+
const deferred = this.pendingSubtests.shift();
|
|
771
|
+
const test = deferred.test;
|
|
772
|
+
test.reporter.dequeue(test.nesting, test.loc, test.name, this.reportedType);
|
|
773
|
+
await test.run();
|
|
774
|
+
deferred.resolve();
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
addReadySubtest(subtest) {
|
|
779
|
+
this.readySubtests.set(subtest.childNumber, subtest);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
processReadySubtestRange(canSend) {
|
|
783
|
+
const start = this.waitingOn;
|
|
784
|
+
const end = start + this.readySubtests.size;
|
|
785
|
+
|
|
786
|
+
for (let i = start; i < end; i++) {
|
|
787
|
+
const subtest = this.readySubtests.get(i);
|
|
788
|
+
|
|
789
|
+
// Check if the specified subtest is in the map. If it is not, return
|
|
790
|
+
// early to avoid trying to process any more tests since they would be
|
|
791
|
+
// out of order.
|
|
792
|
+
if (subtest === undefined) {
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Call isClearToSend() in the loop so that it is:
|
|
797
|
+
// - Only called if there are results to report in the correct order.
|
|
798
|
+
// - Guaranteed to only be called a maximum of once per call to
|
|
799
|
+
// processReadySubtestRange().
|
|
800
|
+
canSend ||= this.isClearToSend();
|
|
801
|
+
|
|
802
|
+
if (!canSend) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Report the subtest's results and remove it from the ready map.
|
|
807
|
+
subtest.finalize();
|
|
808
|
+
this.readySubtests.delete(i);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
createSubtest(Factory, name, options, fn, overrides) {
|
|
813
|
+
if (typeof name === "function") {
|
|
814
|
+
fn = name;
|
|
815
|
+
} else if (name !== null && typeof name === "object") {
|
|
816
|
+
fn = options;
|
|
817
|
+
options = name;
|
|
818
|
+
} else if (typeof options === "function") {
|
|
819
|
+
fn = options;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (options === null || typeof options !== "object") {
|
|
823
|
+
options = {};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// If this test has already ended, attach this test to the root test so
|
|
827
|
+
// that the error can be properly reported.
|
|
828
|
+
const preventAddingSubtests = this.finished || this.buildPhaseFinished;
|
|
829
|
+
const parent = preventAddingSubtests ? this.root : this;
|
|
830
|
+
|
|
831
|
+
const test = new Factory({ __proto__: null, fn, name, parent, ...options, ...overrides });
|
|
832
|
+
|
|
833
|
+
if (parent.waitingOn === 0) {
|
|
834
|
+
parent.waitingOn = test.childNumber;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (preventAddingSubtests) {
|
|
838
|
+
test.fail(new TestFailure("test could not be started because its parent finished", kParentAlreadyFinished));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
parent.subtests.push(test);
|
|
842
|
+
return test;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
#abortHandler = () => {
|
|
846
|
+
const error = this.outerSignal?.reason || new AbortError("The test was aborted");
|
|
847
|
+
error.failureType = kAborted;
|
|
848
|
+
this.#cancel(error);
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
#cancel(error) {
|
|
852
|
+
if (this.endTime !== null || this.error !== null) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
this.fail(error || new TestFailure("test did not finish before its parent and was cancelled", kCancelledByParent));
|
|
857
|
+
this.cancelled = true;
|
|
858
|
+
this.abortController.abort();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
computeInheritedHooks() {
|
|
862
|
+
if (this.parent.hooks.beforeEach.length > 0) {
|
|
863
|
+
this.hooks.beforeEach.unshift(...this.parent.hooks.beforeEach.slice());
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (this.parent.hooks.afterEach.length > 0) {
|
|
867
|
+
this.hooks.afterEach.push(...this.parent.hooks.afterEach.slice());
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
createHook(name, fn, options) {
|
|
872
|
+
validateOneOf(name, "hook name", kHookNames);
|
|
873
|
+
const hook = new TestHook(fn, options);
|
|
874
|
+
if (name === "before" || name === "after") {
|
|
875
|
+
hook.run = runOnce(hook.run);
|
|
876
|
+
}
|
|
877
|
+
if (name === "before" && this.startTime !== null) {
|
|
878
|
+
// Test has already started, run the hook immediately
|
|
879
|
+
hook.run(this.getRunArgs()).then(() => {
|
|
880
|
+
if (hook.error != null) {
|
|
881
|
+
this.fail(hook.error);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
if (name === "afterEach") {
|
|
886
|
+
// afterEach hooks for the current test should run in the order that they
|
|
887
|
+
// are created. However, the current test's afterEach hooks should run
|
|
888
|
+
// prior to any ancestor afterEach hooks.
|
|
889
|
+
this.hooks[name].splice(this.hooks.ownAfterEachCount, 0, hook);
|
|
890
|
+
this.hooks.ownAfterEachCount++;
|
|
891
|
+
} else {
|
|
892
|
+
this.hooks[name].push(hook);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
fail(err) {
|
|
897
|
+
if (this.error !== null) {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
this.passed = false;
|
|
902
|
+
this.error = err;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
pass() {
|
|
906
|
+
if (this.error !== null) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
this.passed = true;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
skip(message) {
|
|
914
|
+
this.skipped = true;
|
|
915
|
+
this.message = message;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
todo(message) {
|
|
919
|
+
this.isTodo = true;
|
|
920
|
+
this.message = message;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
diagnostic(message) {
|
|
924
|
+
this.diagnostics.push(message);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
start() {
|
|
928
|
+
this.applyFilters();
|
|
929
|
+
|
|
930
|
+
if (this.filtered) {
|
|
931
|
+
noopTestStream ??= new TestsStream();
|
|
932
|
+
this.reporter = noopTestStream;
|
|
933
|
+
this.run = this.filteredRun;
|
|
934
|
+
} else {
|
|
935
|
+
this.testNumber = ++this.parent.outputSubtestCount;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// If there is enough available concurrency to run the test now, then do
|
|
939
|
+
// it. Otherwise, return a Promise to the caller and mark the test as
|
|
940
|
+
// pending for later execution.
|
|
941
|
+
this.reporter.enqueue(this.nesting, this.loc, this.name, this.reportedType);
|
|
942
|
+
if (this.root.harness.buildPromise || !this.parent.hasConcurrency()) {
|
|
943
|
+
const deferred = Promise.withResolvers();
|
|
944
|
+
|
|
945
|
+
deferred.test = this;
|
|
946
|
+
this.parent.addPendingSubtest(deferred);
|
|
947
|
+
return deferred.promise;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
this.reporter.dequeue(this.nesting, this.loc, this.name, this.reportedType);
|
|
951
|
+
return this.run();
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
[kShouldAbort]() {
|
|
955
|
+
if (this.signal.aborted || this.outerSignal?.aborted) {
|
|
956
|
+
this.#abortHandler();
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
getRunArgs() {
|
|
962
|
+
const ctx = new TestContext(this);
|
|
963
|
+
return { __proto__: null, ctx, args: [ctx] };
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
async runHook(hook, args) {
|
|
967
|
+
validateOneOf(hook, "hook name", kHookNames);
|
|
968
|
+
try {
|
|
969
|
+
const hooks = this.hooks[hook];
|
|
970
|
+
for (let i = 0; i < hooks.length; ++i) {
|
|
971
|
+
const hook = hooks[i];
|
|
972
|
+
await hook.run(args);
|
|
973
|
+
if (hook.error) {
|
|
974
|
+
throw hook.error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
} catch (err) {
|
|
978
|
+
const error = new TestFailure(`failed running ${hook} hook`, kHookFailure);
|
|
979
|
+
error.cause = isTestFailureError(err) ? err.cause : err;
|
|
980
|
+
throw error;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
async filteredRun() {
|
|
985
|
+
this.pass();
|
|
986
|
+
this.subtests = [];
|
|
987
|
+
this.report = noop;
|
|
988
|
+
queueMicrotask(() => this.postRun());
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
async run() {
|
|
992
|
+
if (this.parent !== null) {
|
|
993
|
+
this.parent.activeSubtests++;
|
|
994
|
+
this.computeInheritedHooks();
|
|
995
|
+
}
|
|
996
|
+
this.startTime ??= hrtime();
|
|
997
|
+
|
|
998
|
+
if (this[kShouldAbort]()) {
|
|
999
|
+
this.postRun();
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const hookArgs = this.getRunArgs();
|
|
1004
|
+
const { args, ctx } = hookArgs;
|
|
1005
|
+
|
|
1006
|
+
if (this.plan === null && this.expectedAssertions) {
|
|
1007
|
+
ctx.plan(this.expectedAssertions);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
const after = async () => {
|
|
1011
|
+
if (this.hooks.after.length > 0) {
|
|
1012
|
+
await this.runHook("after", hookArgs);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
const afterEach = runOnce(async () => {
|
|
1016
|
+
if (this.parent?.hooks.afterEach.length > 0 && !this.skipped) {
|
|
1017
|
+
await this.parent.runHook("afterEach", hookArgs);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
let stopPromise;
|
|
1022
|
+
|
|
1023
|
+
try {
|
|
1024
|
+
if (this.parent?.hooks.before.length > 0) {
|
|
1025
|
+
// This hook usually runs immediately, we need to wait for it to finish
|
|
1026
|
+
await this.parent.runHook("before", this.parent.getRunArgs());
|
|
1027
|
+
}
|
|
1028
|
+
if (this.parent?.hooks.beforeEach.length > 0 && !this.skipped) {
|
|
1029
|
+
await this.parent.runHook("beforeEach", hookArgs);
|
|
1030
|
+
}
|
|
1031
|
+
stopPromise = stopTest(this.timeout, this.signal);
|
|
1032
|
+
const runArgs = args.slice();
|
|
1033
|
+
runArgs.unshift(this.fn, ctx);
|
|
1034
|
+
|
|
1035
|
+
const promises = [];
|
|
1036
|
+
if (this.fn.length === runArgs.length - 1) {
|
|
1037
|
+
// This test is using legacy Node.js error-first callbacks.
|
|
1038
|
+
const { promise, cb } = createDeferredCallback();
|
|
1039
|
+
runArgs.push(cb);
|
|
1040
|
+
|
|
1041
|
+
const ret = Reflect.apply(this.runInAsyncScope, this, runArgs);
|
|
1042
|
+
|
|
1043
|
+
if (isPromise(ret)) {
|
|
1044
|
+
this.fail(new TestFailure("passed a callback but also returned a Promise", kCallbackAndPromisePresent));
|
|
1045
|
+
promises.push(ret);
|
|
1046
|
+
} else {
|
|
1047
|
+
promises.push(Promise.resolve(promise));
|
|
1048
|
+
}
|
|
1049
|
+
} else {
|
|
1050
|
+
// This test is synchronous or using Promises.
|
|
1051
|
+
const promise = Reflect.apply(this.runInAsyncScope, this, runArgs);
|
|
1052
|
+
promises.push(Promise.resolve(promise));
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
promises.push(stopPromise);
|
|
1056
|
+
|
|
1057
|
+
// Wait for the race to finish
|
|
1058
|
+
await Promise.race(promises);
|
|
1059
|
+
|
|
1060
|
+
this[kShouldAbort]();
|
|
1061
|
+
this.plan?.check();
|
|
1062
|
+
|
|
1063
|
+
if (this.plan !== null) {
|
|
1064
|
+
const planPromise = this.plan?.check();
|
|
1065
|
+
// If the plan returns a promise, it means that it is waiting for more assertions to be made before
|
|
1066
|
+
// continuing.
|
|
1067
|
+
if (planPromise) {
|
|
1068
|
+
await Promise.race([planPromise, stopPromise]);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
this.pass();
|
|
1073
|
+
await afterEach();
|
|
1074
|
+
await after();
|
|
1075
|
+
} catch (err) {
|
|
1076
|
+
if (isTestFailureError(err)) {
|
|
1077
|
+
if (err.failureType === kTestTimeoutFailure) {
|
|
1078
|
+
this.#cancel(err);
|
|
1079
|
+
} else {
|
|
1080
|
+
this.fail(err);
|
|
1081
|
+
}
|
|
1082
|
+
} else {
|
|
1083
|
+
this.fail(new TestFailure(err, kTestCodeFailure));
|
|
1084
|
+
}
|
|
1085
|
+
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
|
|
1086
|
+
try { await after(); } catch { /* Ignore error. */ }
|
|
1087
|
+
} finally {
|
|
1088
|
+
stopPromise?.[kDispose]();
|
|
1089
|
+
|
|
1090
|
+
// Do not abort hooks and the root test as hooks instance are shared between tests suite so aborting them will
|
|
1091
|
+
// cause them to not run for further tests.
|
|
1092
|
+
if (this.parent !== null) {
|
|
1093
|
+
this.abortController.abort();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (this.parent !== null || typeof this.hookType === "string") {
|
|
1098
|
+
// Clean up the test. Then, try to report the results and execute any
|
|
1099
|
+
// tests that were pending due to available concurrency.
|
|
1100
|
+
//
|
|
1101
|
+
// The root test is skipped here because it is a special case. Its
|
|
1102
|
+
// postRun() method is called when the process is getting ready to exit.
|
|
1103
|
+
// This helps catch any asynchronous activity that occurs after the tests
|
|
1104
|
+
// have finished executing.
|
|
1105
|
+
this.postRun();
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
postRun(pendingSubtestsError) {
|
|
1110
|
+
// If the test was cancelled before it started, then the start and end
|
|
1111
|
+
// times need to be corrected.
|
|
1112
|
+
this.endTime ??= hrtime();
|
|
1113
|
+
this.startTime ??= this.endTime;
|
|
1114
|
+
|
|
1115
|
+
// The test has run, so recursively cancel any outstanding subtests and
|
|
1116
|
+
// mark this test as failed if any subtests failed.
|
|
1117
|
+
this.pendingSubtests = [];
|
|
1118
|
+
let failed = 0;
|
|
1119
|
+
for (let i = 0; i < this.subtests.length; i++) {
|
|
1120
|
+
const subtest = this.subtests[i];
|
|
1121
|
+
|
|
1122
|
+
if (!subtest.finished) {
|
|
1123
|
+
subtest.#cancel(pendingSubtestsError);
|
|
1124
|
+
subtest.postRun(pendingSubtestsError);
|
|
1125
|
+
}
|
|
1126
|
+
if (!subtest.passed && !subtest.isTodo) {
|
|
1127
|
+
failed++;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if ((this.passed || this.parent === null) && failed > 0) {
|
|
1132
|
+
const subtestString = `subtest${failed > 1 ? "s" : ""}`;
|
|
1133
|
+
const msg = `${failed} ${subtestString} failed`;
|
|
1134
|
+
|
|
1135
|
+
this.fail(new TestFailure(msg, kSubtestsFailed));
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
this.outerSignal?.removeEventListener("abort", this.#abortHandler);
|
|
1139
|
+
this.mock?.reset();
|
|
1140
|
+
|
|
1141
|
+
if (this.parent !== null) {
|
|
1142
|
+
if (!this.filtered) {
|
|
1143
|
+
const report = this.getReportDetails();
|
|
1144
|
+
report.details.passed = this.passed;
|
|
1145
|
+
this.testNumber ||= ++this.parent.outputSubtestCount;
|
|
1146
|
+
this.reporter.complete(this.nesting, this.loc, this.testNumber, this.name, report.details, report.directive);
|
|
1147
|
+
this.parent.activeSubtests--;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
this.parent.addReadySubtest(this);
|
|
1151
|
+
this.parent.processReadySubtestRange(false);
|
|
1152
|
+
this.parent.processPendingSubtests();
|
|
1153
|
+
} else if (!this.reported) {
|
|
1154
|
+
const { diagnostics, harness, loc, nesting, reporter } = this;
|
|
1155
|
+
|
|
1156
|
+
this.reported = true;
|
|
1157
|
+
reporter.plan(nesting, loc, harness.counters.topLevel);
|
|
1158
|
+
|
|
1159
|
+
for (let i = 0; i < diagnostics.length; i++) {
|
|
1160
|
+
reporter.diagnostic(nesting, loc, diagnostics[i]);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const duration = this.duration();
|
|
1164
|
+
reporter.diagnostic(nesting, loc, `tests ${harness.counters.tests}`);
|
|
1165
|
+
reporter.diagnostic(nesting, loc, `suites ${harness.counters.suites}`);
|
|
1166
|
+
reporter.diagnostic(nesting, loc, `pass ${harness.counters.passed}`);
|
|
1167
|
+
reporter.diagnostic(nesting, loc, `fail ${harness.counters.failed}`);
|
|
1168
|
+
reporter.diagnostic(nesting, loc, `cancelled ${harness.counters.cancelled}`);
|
|
1169
|
+
reporter.diagnostic(nesting, loc, `skipped ${harness.counters.skipped}`);
|
|
1170
|
+
reporter.diagnostic(nesting, loc, `todo ${harness.counters.todo}`);
|
|
1171
|
+
reporter.diagnostic(nesting, loc, `duration_ms ${duration}`);
|
|
1172
|
+
|
|
1173
|
+
reporter.summary(nesting, loc?.file, harness.success, harness.counters, duration);
|
|
1174
|
+
reporter.end();
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
isClearToSend() {
|
|
1179
|
+
return this.parent === null || (this.parent.waitingOn === this.childNumber && this.parent.isClearToSend());
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
finalize() {
|
|
1183
|
+
// By the time this function is called, the following can be relied on:
|
|
1184
|
+
// - The current test has completed or been cancelled.
|
|
1185
|
+
// - All of this test's subtests have completed or been cancelled.
|
|
1186
|
+
// - It is the current test's turn to report its results.
|
|
1187
|
+
|
|
1188
|
+
// Report any subtests that have not been reported yet. Since all of the
|
|
1189
|
+
// subtests have finished, it's safe to pass true to
|
|
1190
|
+
// processReadySubtestRange(), which will finalize all remaining subtests.
|
|
1191
|
+
this.processReadySubtestRange(true);
|
|
1192
|
+
|
|
1193
|
+
// Output this test's results and update the parent's waiting counter.
|
|
1194
|
+
this.report();
|
|
1195
|
+
this.parent.waitingOn++;
|
|
1196
|
+
this.finished = true;
|
|
1197
|
+
|
|
1198
|
+
if (this.parent === this.root && this.root.waitingOn > this.root.subtests.length) {
|
|
1199
|
+
// At this point all of the tests have finished running. However, there
|
|
1200
|
+
// might be ref'ed handles keeping the event loop alive. This gives the
|
|
1201
|
+
// global after() hook a chance to clean them up.
|
|
1202
|
+
this.root.run();
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
duration() {
|
|
1207
|
+
return this.endTime - this.startTime;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
getReportDetails() {
|
|
1211
|
+
let directive;
|
|
1212
|
+
const details = { __proto__: null, duration_ms: this.duration() };
|
|
1213
|
+
|
|
1214
|
+
if (this.skipped) {
|
|
1215
|
+
directive = this.reporter.getSkip(this.message);
|
|
1216
|
+
} else if (this.isTodo) {
|
|
1217
|
+
directive = this.reporter.getTodo(this.message);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
if (this.reportedType) {
|
|
1221
|
+
details.type = this.reportedType;
|
|
1222
|
+
}
|
|
1223
|
+
if (!this.passed) {
|
|
1224
|
+
details.error = this.error;
|
|
1225
|
+
}
|
|
1226
|
+
return { __proto__: null, details, directive };
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
report() {
|
|
1230
|
+
countCompletedTest(this);
|
|
1231
|
+
if (this.outputSubtestCount > 0) {
|
|
1232
|
+
this.reporter.plan(this.subtests[0].nesting, this.loc, this.outputSubtestCount);
|
|
1233
|
+
} else {
|
|
1234
|
+
this.reportStarted();
|
|
1235
|
+
}
|
|
1236
|
+
const report = this.getReportDetails();
|
|
1237
|
+
|
|
1238
|
+
if (this.passed) {
|
|
1239
|
+
this.reporter.ok(this.nesting, this.loc, this.testNumber, this.name, report.details, report.directive);
|
|
1240
|
+
} else {
|
|
1241
|
+
this.reporter.fail(this.nesting, this.loc, this.testNumber, this.name, report.details, report.directive);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
for (let i = 0; i < this.diagnostics.length; i++) {
|
|
1245
|
+
this.reporter.diagnostic(this.nesting, this.loc, this.diagnostics[i]);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
reportStarted() {
|
|
1250
|
+
if (this.#reportedSubtest || this.parent === null) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
this.#reportedSubtest = true;
|
|
1254
|
+
this.parent.reportStarted();
|
|
1255
|
+
this.reporter.start(this.nesting, this.loc, this.name);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
class TestHook extends Test {
|
|
1260
|
+
reportedType = "hook";
|
|
1261
|
+
#args;
|
|
1262
|
+
constructor(fn, options) {
|
|
1263
|
+
const { hookType, loc, parent, timeout, signal } = options;
|
|
1264
|
+
super({ __proto__: null, fn, loc, timeout, signal, harness: parent.root.harness });
|
|
1265
|
+
this.parentTest = parent;
|
|
1266
|
+
this.hookType = hookType;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
run(args) {
|
|
1270
|
+
if (this.error && !this.outerSignal?.aborted) {
|
|
1271
|
+
this.passed = false;
|
|
1272
|
+
this.error = null;
|
|
1273
|
+
this.abortController.abort();
|
|
1274
|
+
this.abortController = new AbortController();
|
|
1275
|
+
this.signal = this.abortController.signal;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
this.#args = args;
|
|
1279
|
+
return super.run();
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
getRunArgs() {
|
|
1283
|
+
return this.#args;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
postRun() {
|
|
1287
|
+
const { error, loc, parentTest: parent } = this;
|
|
1288
|
+
|
|
1289
|
+
// Report failures in the root test's after() hook.
|
|
1290
|
+
if (error && parent === parent.root && this.hookType === "after") {
|
|
1291
|
+
if (isTestFailureError(error)) {
|
|
1292
|
+
error.failureType = kHookFailure;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
this.endTime ??= hrtime();
|
|
1296
|
+
parent.reporter.fail(0, loc, parent.subtests.length + 1, loc?.file ?? parent.entryFile, {
|
|
1297
|
+
__proto__: null,
|
|
1298
|
+
duration_ms: this.duration(),
|
|
1299
|
+
error,
|
|
1300
|
+
}, undefined);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
export class Suite extends Test {
|
|
1306
|
+
reportedType = "suite";
|
|
1307
|
+
constructor(options) {
|
|
1308
|
+
super(options);
|
|
1309
|
+
|
|
1310
|
+
this.buildSuite = this.createBuild();
|
|
1311
|
+
this.fn = noop;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
async createBuild() {
|
|
1315
|
+
try {
|
|
1316
|
+
const { ctx, args } = this.getRunArgs();
|
|
1317
|
+
const runArgs = [this.fn, ctx];
|
|
1318
|
+
runArgs.push(...args);
|
|
1319
|
+
await Reflect.apply(this.runInAsyncScope, this, runArgs);
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
this.fail(new TestFailure(err, kTestCodeFailure));
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
this.buildPhaseFinished = true;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
getRunArgs() {
|
|
1328
|
+
const ctx = new SuiteContext(this);
|
|
1329
|
+
return { __proto__: null, ctx, args: [ctx] };
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
async run() {
|
|
1333
|
+
this.computeInheritedHooks();
|
|
1334
|
+
const hookArgs = this.getRunArgs();
|
|
1335
|
+
|
|
1336
|
+
let stopPromise;
|
|
1337
|
+
const after = runOnce(() => this.runHook("after", hookArgs));
|
|
1338
|
+
try {
|
|
1339
|
+
this.parent.activeSubtests++;
|
|
1340
|
+
await this.buildSuite;
|
|
1341
|
+
this.startTime = hrtime();
|
|
1342
|
+
|
|
1343
|
+
if (this[kShouldAbort]()) {
|
|
1344
|
+
this.subtests = [];
|
|
1345
|
+
this.postRun();
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if (this.parent.hooks.before.length > 0) {
|
|
1350
|
+
await this.parent.runHook("before", this.parent.getRunArgs());
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
await this.runHook("before", hookArgs);
|
|
1354
|
+
|
|
1355
|
+
stopPromise = stopTest(this.timeout, this.signal);
|
|
1356
|
+
const subtests = this.skipped || this.error ? [] : this.subtests;
|
|
1357
|
+
const promise = Promise.all(subtests.map((subtest) => subtest.start()));
|
|
1358
|
+
|
|
1359
|
+
await Promise.race([promise, stopPromise]);
|
|
1360
|
+
await after();
|
|
1361
|
+
|
|
1362
|
+
this.pass();
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
try { await after(); } catch { /* suite is already failing */ }
|
|
1365
|
+
if (isTestFailureError(err)) {
|
|
1366
|
+
this.fail(err);
|
|
1367
|
+
} else {
|
|
1368
|
+
this.fail(new TestFailure(err, kTestCodeFailure));
|
|
1369
|
+
}
|
|
1370
|
+
} finally {
|
|
1371
|
+
stopPromise?.[kDispose]();
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
this.postRun();
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
function getFullName(test) {
|
|
1379
|
+
if (test === test.root) return test.name;
|
|
1380
|
+
|
|
1381
|
+
let fullName = test.name;
|
|
1382
|
+
|
|
1383
|
+
for (let t = test.parent; t !== t.root; t = t.parent) {
|
|
1384
|
+
fullName = `${t.name} > ${fullName}`;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return fullName;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// ---- The harness (lib/internal/test_runner/harness.js) ------------------------------------
|
|
1391
|
+
|
|
1392
|
+
let globalRoot;
|
|
1393
|
+
|
|
1394
|
+
function createTestTree(rootTestOptions, globalOptions) {
|
|
1395
|
+
const buildPhaseDeferred = Promise.withResolvers();
|
|
1396
|
+
const harness = {
|
|
1397
|
+
__proto__: null,
|
|
1398
|
+
buildPromise: buildPhaseDeferred.promise,
|
|
1399
|
+
buildSuites: [],
|
|
1400
|
+
isWaitingForBuildPhase: false,
|
|
1401
|
+
bootstrapPromise: Promise.resolve(),
|
|
1402
|
+
config: globalOptions,
|
|
1403
|
+
/* Node 24: unless tests run in their own processes (--test), always by `only` */
|
|
1404
|
+
isFilteringByOnly: globalOptions.isolation === "process" || process.env.NODE_TEST_CONTEXT ? globalOptions.only : true,
|
|
1405
|
+
resetCounters() {
|
|
1406
|
+
harness.counters = {
|
|
1407
|
+
__proto__: null,
|
|
1408
|
+
tests: 0,
|
|
1409
|
+
failed: 0,
|
|
1410
|
+
passed: 0,
|
|
1411
|
+
cancelled: 0,
|
|
1412
|
+
skipped: 0,
|
|
1413
|
+
todo: 0,
|
|
1414
|
+
topLevel: 0,
|
|
1415
|
+
suites: 0,
|
|
1416
|
+
};
|
|
1417
|
+
},
|
|
1418
|
+
success: true,
|
|
1419
|
+
counters: null,
|
|
1420
|
+
teardown: null,
|
|
1421
|
+
async waitForBuildPhase() {
|
|
1422
|
+
if (harness.buildSuites.length > 0) {
|
|
1423
|
+
await Promise.all(harness.buildSuites);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
buildPhaseDeferred.resolve();
|
|
1427
|
+
},
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
harness.resetCounters();
|
|
1431
|
+
globalRoot = new Test({ __proto__: null, ...rootTestOptions, harness, name: "<root>" });
|
|
1432
|
+
setupProcessState(globalRoot, globalOptions, harness);
|
|
1433
|
+
globalRoot.startTime = hrtime();
|
|
1434
|
+
return globalRoot;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function createProcessEventHandler(eventName, rootTest) {
|
|
1438
|
+
return (err) => {
|
|
1439
|
+
if (rootTest.harness.bootstrapPromise) {
|
|
1440
|
+
// Something went wrong during the asynchronous portion of bootstrapping
|
|
1441
|
+
// the test runner. Since the test runner is not setup properly, we can't
|
|
1442
|
+
// do anything but throw the error.
|
|
1443
|
+
throw err;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
const test = testStorage.getStore();
|
|
1447
|
+
|
|
1448
|
+
// Check if this error is coming from a test or test hook. If it is, fail the test.
|
|
1449
|
+
if (!test || test.finished || test.hookType) {
|
|
1450
|
+
// If the test is already finished or the resource that created the error
|
|
1451
|
+
// is not mapped to a Test, report this as a top level diagnostic.
|
|
1452
|
+
let msg;
|
|
1453
|
+
|
|
1454
|
+
if (test) {
|
|
1455
|
+
const name = test.hookType ? `Test hook "${test.hookType}"` : `Test "${test.name}"`;
|
|
1456
|
+
msg = `Error: ${name} generated asynchronous ` +
|
|
1457
|
+
"activity after the test ended. This activity created the error " +
|
|
1458
|
+
`"${err}" and would have caused the test to fail, but instead ` +
|
|
1459
|
+
`triggered an ${eventName} event.`;
|
|
1460
|
+
} else {
|
|
1461
|
+
msg = "Error: A resource generated asynchronous activity after " +
|
|
1462
|
+
`the test ended. This activity created the error "${err}" which ` +
|
|
1463
|
+
`triggered an ${eventName} event, caught by the test runner.`;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
rootTest.diagnostic(msg);
|
|
1467
|
+
rootTest.harness.success = false;
|
|
1468
|
+
process.exitCode = kGenericUserError;
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
test.fail(new TestFailure(err, eventName));
|
|
1473
|
+
test.abortController.abort();
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function setupProcessState(root) {
|
|
1478
|
+
const exceptionHandler = createProcessEventHandler("uncaughtException", root);
|
|
1479
|
+
const rejectionHandler = createProcessEventHandler("unhandledRejection", root);
|
|
1480
|
+
const exitHandler = async () => {
|
|
1481
|
+
if (root.subtests.length === 0 && (root.hooks.before.length > 0 || root.hooks.after.length > 0)) {
|
|
1482
|
+
// Run global before/after hooks in case there are no tests
|
|
1483
|
+
await root.run();
|
|
1484
|
+
}
|
|
1485
|
+
root.postRun(new TestFailure("Promise resolution is still pending but the event loop has already resolved", kCancelledByParent));
|
|
1486
|
+
|
|
1487
|
+
process.removeListener("uncaughtException", exceptionHandler);
|
|
1488
|
+
process.removeListener("unhandledRejection", rejectionHandler);
|
|
1489
|
+
process.removeListener("beforeExit", exitHandler);
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
process.on("uncaughtException", exceptionHandler);
|
|
1493
|
+
process.on("unhandledRejection", rejectionHandler);
|
|
1494
|
+
process.on("beforeExit", exitHandler);
|
|
1495
|
+
|
|
1496
|
+
root.harness.teardown = exitHandler;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function lazyBootstrapRoot() {
|
|
1500
|
+
if (!globalRoot) {
|
|
1501
|
+
// This is where the test runner is bootstrapped when node:test is used
|
|
1502
|
+
// without the --test flag or the run() API.
|
|
1503
|
+
const entryFile = process.argv?.[1];
|
|
1504
|
+
const globalOptions = { __proto__: null, only: false };
|
|
1505
|
+
createTestTree({ __proto__: null, entryFile }, globalOptions);
|
|
1506
|
+
globalRoot.reporter.on("test:summary", (data) => {
|
|
1507
|
+
if (!data.success) {
|
|
1508
|
+
process.exitCode = kGenericUserError;
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
globalRoot.reporter.attach(new SpecReporter()); /* Node 24's default, a terminal or not */
|
|
1512
|
+
}
|
|
1513
|
+
return globalRoot;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
async function startSubtestAfterBootstrap(subtest) {
|
|
1517
|
+
if (subtest.root.harness.buildPromise) {
|
|
1518
|
+
if (subtest.root.harness.bootstrapPromise) {
|
|
1519
|
+
await subtest.root.harness.bootstrapPromise;
|
|
1520
|
+
subtest.root.harness.bootstrapPromise = null;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
if (subtest.buildSuite) {
|
|
1524
|
+
subtest.root.harness.buildSuites.push(subtest.buildSuite);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (!subtest.root.harness.isWaitingForBuildPhase) {
|
|
1528
|
+
subtest.root.harness.isWaitingForBuildPhase = true;
|
|
1529
|
+
queueMicrotask(() => {
|
|
1530
|
+
subtest.root.harness.waitForBuildPhase();
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
await subtest.root.harness.buildPromise;
|
|
1535
|
+
subtest.root.harness.buildPromise = null;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
await subtest.start();
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/** test() or describe(): a test of the current test (or suite), or a top-level one. */
|
|
1542
|
+
export function runInParentContext(Factory) {
|
|
1543
|
+
function run(name, options, fn, overrides) {
|
|
1544
|
+
const parent = testStorage.getStore() || lazyBootstrapRoot();
|
|
1545
|
+
const subtest = parent.createSubtest(Factory, name, options, fn, overrides);
|
|
1546
|
+
if (parent instanceof Suite) {
|
|
1547
|
+
return Promise.resolve();
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
return startSubtestAfterBootstrap(subtest);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
const test = (name, options, fn) => run(name, options, fn, { __proto__: null });
|
|
1554
|
+
for (const keyword of ["skip", "todo", "only"]) {
|
|
1555
|
+
test[keyword] = (name, options, fn) => run(name, options, fn, { __proto__: null, [keyword]: true });
|
|
1556
|
+
}
|
|
1557
|
+
return test;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/** before(), after(), beforeEach() or afterEach() of the current test (or suite), or the root. */
|
|
1561
|
+
export function hook(hook) {
|
|
1562
|
+
return (fn, options) => {
|
|
1563
|
+
const parent = testStorage.getStore() || lazyBootstrapRoot();
|
|
1564
|
+
parent.createHook(hook, fn, { __proto__: null, ...options, parent, hookType: hook });
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/** run(): Node's runner of test files, each in its own process. */
|
|
1569
|
+
export function run() {
|
|
1570
|
+
throw codeError(Error, "ERR_VISALJS_UNSUPPORTED", "run() is not supported by visaljs yet: a compiled program runs its own tests");
|
|
1571
|
+
}
|