From 43ca6dcf07b28cfc6d79586a5fb6677f2b4de309 Mon Sep 17 00:00:00 2001 From: Minoru Mizutani Date: Tue, 29 Apr 2025 10:11:22 +0900 Subject: [PATCH] Fix tests --- deno.json | 7 +- deno.lock | 97 +++++------ ...entation_plan.md => implementation_plan.md | 2 +- src/lib/deno-open.ts | 160 ++++++++++++------ tests/deno-http-server_test.ts | 1 + tests/deno-open_test.ts | 132 +++++++-------- 6 files changed, 214 insertions(+), 185 deletions(-) rename implmentation_plan.md => implementation_plan.md (99%) diff --git a/deno.json b/deno.json index 737c551..98b2a68 100644 --- a/deno.json +++ b/deno.json @@ -23,15 +23,16 @@ "test:coverage": "deno test --coverage=coverage --allow-net --allow-env --allow-read --allow-run --allow-sys tests/ && deno coverage coverage" }, "imports": { - "std/": "https://deno.land/std@0.220.0/", - "node/": "https://deno.land/std@0.220.0/node/", + "std/": "https://deno.land/std@0.224.0/", + "node/": "https://deno.land/std@0.224.0/node/", "@modelcontextprotocol/sdk/": "npm:@modelcontextprotocol/sdk/" }, "compilerOptions": { "strict": true, "lib": [ "ES2022", - "DOM" + "DOM", + "deno.ns" ] } } diff --git a/deno.lock b/deno.lock index 141499b..68ba07c 100644 --- a/deno.lock +++ b/deno.lock @@ -1358,59 +1358,50 @@ } }, "remote": { - "https://deno.land/std@0.218.2/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", - "https://deno.land/std@0.218.2/assert/_diff.ts": "dcc63d94ca289aec80644030cf88ccbf7acaa6fbd7b0f22add93616b36593840", - "https://deno.land/std@0.218.2/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4", - "https://deno.land/std@0.218.2/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e", - "https://deno.land/std@0.218.2/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2", - "https://deno.land/std@0.218.2/assert/assert_rejects.ts": "5206ac37d883797d9504e3915a0c7b692df6efcdefff3889cc14bb5a325641dd", - "https://deno.land/std@0.218.2/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", - "https://deno.land/std@0.218.2/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2", - "https://deno.land/std@0.218.2/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a", - "https://deno.land/std@0.218.2/testing/mock.ts": "dc9e58f88f7e746edd0c551443a39096c75895a0fdcd7db62777e47787be6226", - "https://deno.land/std@0.220.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", - "https://deno.land/std@0.220.0/assert/_diff.ts": "4bf42969aa8b1a33aaf23eb8e478b011bfaa31b82d85d2ff4b5c4662d8780d2b", - "https://deno.land/std@0.220.0/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4", - "https://deno.land/std@0.220.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", - "https://deno.land/std@0.220.0/assert/assert_almost_equals.ts": "8b96b7385cc117668b0720115eb6ee73d04c9bcb2f5d2344d674918c9113688f", - "https://deno.land/std@0.220.0/assert/assert_array_includes.ts": "1688d76317fd45b7e93ef9e2765f112fdf2b7c9821016cdfb380b9445374aed1", - "https://deno.land/std@0.220.0/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e", - "https://deno.land/std@0.220.0/assert/assert_exists.ts": "24a7bf965e634f909242cd09fbaf38bde6b791128ece08e33ab08586a7cc55c9", - "https://deno.land/std@0.220.0/assert/assert_false.ts": "6f382568e5128c0f855e5f7dbda8624c1ed9af4fcc33ef4a9afeeedcdce99769", - "https://deno.land/std@0.220.0/assert/assert_greater.ts": "4945cf5729f1a38874d7e589e0fe5cc5cd5abe5573ca2ddca9d3791aa891856c", - "https://deno.land/std@0.220.0/assert/assert_greater_or_equal.ts": "573ed8823283b8d94b7443eb69a849a3c369a8eb9666b2d1db50c33763a5d219", - "https://deno.land/std@0.220.0/assert/assert_instance_of.ts": "72dc1faff1e248692d873c89382fa1579dd7b53b56d52f37f9874a75b11ba444", - "https://deno.land/std@0.220.0/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2", - "https://deno.land/std@0.220.0/assert/assert_less.ts": "2b4b3fe7910f65f7be52212f19c3977ecb8ba5b2d6d0a296c83cde42920bb005", - "https://deno.land/std@0.220.0/assert/assert_less_or_equal.ts": "b93d212fe669fbde959e35b3437ac9a4468f2e6b77377e7b6ea2cfdd825d38a0", - "https://deno.land/std@0.220.0/assert/assert_match.ts": "ec2d9680ed3e7b9746ec57ec923a17eef6d476202f339ad91d22277d7f1d16e1", - "https://deno.land/std@0.220.0/assert/assert_not_equals.ts": "ac86413ab70ffb14fdfc41740ba579a983fe355ba0ce4a9ab685e6b8e7f6a250", - "https://deno.land/std@0.220.0/assert/assert_not_instance_of.ts": "8f720d92d83775c40b2542a8d76c60c2d4aeddaf8713c8d11df8984af2604931", - "https://deno.land/std@0.220.0/assert/assert_not_match.ts": "b4b7c77f146963e2b673c1ce4846473703409eb93f5ab0eb60f6e6f8aeffe39f", - "https://deno.land/std@0.220.0/assert/assert_not_strict_equals.ts": "da0b8ab60a45d5a9371088378e5313f624799470c3b54c76e8b8abeec40a77be", - "https://deno.land/std@0.220.0/assert/assert_object_match.ts": "e85e5eef62a56ce364c3afdd27978ccab979288a3e772e6855c270a7b118fa49", - "https://deno.land/std@0.220.0/assert/assert_rejects.ts": "5206ac37d883797d9504e3915a0c7b692df6efcdefff3889cc14bb5a325641dd", - "https://deno.land/std@0.220.0/assert/assert_strict_equals.ts": "0425a98f70badccb151644c902384c12771a93e65f8ff610244b8147b03a2366", - "https://deno.land/std@0.220.0/assert/assert_string_includes.ts": "dfb072a890167146f8e5bdd6fde887ce4657098e9f71f12716ef37f35fb6f4a7", - "https://deno.land/std@0.220.0/assert/assert_throws.ts": "31f3c061338aec2c2c33731973d58ccd4f14e42f355501541409ee958d2eb8e5", - "https://deno.land/std@0.220.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", - "https://deno.land/std@0.220.0/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2", - "https://deno.land/std@0.220.0/assert/fail.ts": "f310e51992bac8e54f5fd8e44d098638434b2edb802383690e0d7a9be1979f1c", - "https://deno.land/std@0.220.0/assert/mod.ts": "7e41449e77a31fef91534379716971bebcfc12686e143d38ada5438e04d4a90e", - "https://deno.land/std@0.220.0/assert/unimplemented.ts": "47ca67d1c6dc53abd0bd729b71a31e0825fc452dbcd4fde4ca06789d5644e7fd", - "https://deno.land/std@0.220.0/assert/unreachable.ts": "3670816a4ab3214349acb6730e3e6f5299021234657eefe05b48092f3848c270", - "https://deno.land/std@0.220.0/async/delay.ts": "8e1d18fe8b28ff95885e2bc54eccec1713f57f756053576d8228e6ca110793ad", - "https://deno.land/std@0.220.0/data_structures/_binary_search_node.ts": "ce1da11601fef0638df4d1e53c377f791f96913383277389286b390685d76c07", - "https://deno.land/std@0.220.0/data_structures/_red_black_node.ts": "4af8d3c5ac5f119d8058269259c46ea22ead567246cacde04584a83e43a9d2ea", - "https://deno.land/std@0.220.0/data_structures/binary_search_tree.ts": "2dd43d97ce5f5a4bdba11b075eb458db33e9143f50997b0eebf02912cb44f5d5", - "https://deno.land/std@0.220.0/data_structures/comparators.ts": "74e64752f005f03614d9bd4912ea64c58d2e663b5d8c9dba6e2e2997260f51eb", - "https://deno.land/std@0.220.0/data_structures/red_black_tree.ts": "2222be0c46842fc932e2c8589a66dced9e6eae180914807c5c55d1aa4c8c1b9b", - "https://deno.land/std@0.220.0/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a", - "https://deno.land/std@0.220.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546", - "https://deno.land/std@0.220.0/testing/_time.ts": "fefd1ff35b50a410db9b0e7227e05163e1b172c88afd0d2071df0125958c3ff3", - "https://deno.land/std@0.220.0/testing/bdd.ts": "7a8ac58eded80e6fefa7cf7538927e88781cf5f247c04b35261c3213316e2dd0", - "https://deno.land/std@0.220.0/testing/mock.ts": "a963181c2860b6ba3eb60e08b62c164d33cf5da7cd445893499b2efda20074db", - "https://deno.land/std@0.220.0/testing/time.ts": "0d25e0f15eded2d66c9ed37d16c3188b16cc1aefa58be4a4753afb7750e72cb0" + "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", + "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", + "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", + "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", + "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", + "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", + "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", + "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", + "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", + "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", + "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", + "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", + "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", + "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", + "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", + "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", + "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", + "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", + "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", + "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", + "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", + "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", + "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", + "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", + "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", + "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", + "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", + "https://deno.land/std@0.224.0/async/delay.ts": "f90dd685b97c2f142b8069082993e437b1602b8e2561134827eeb7c12b95c499", + "https://deno.land/std@0.224.0/data_structures/_binary_search_node.ts": "ce1da11601fef0638df4d1e53c377f791f96913383277389286b390685d76c07", + "https://deno.land/std@0.224.0/data_structures/_red_black_node.ts": "4af8d3c5ac5f119d8058269259c46ea22ead567246cacde04584a83e43a9d2ea", + "https://deno.land/std@0.224.0/data_structures/binary_search_tree.ts": "2dd43d97ce5f5a4bdba11b075eb458db33e9143f50997b0eebf02912cb44f5d5", + "https://deno.land/std@0.224.0/data_structures/comparators.ts": "17dfa68bf1550edadbfdd453a06f9819290bcb534c9945b5cec4b30242cff475", + "https://deno.land/std@0.224.0/data_structures/red_black_tree.ts": "2222be0c46842fc932e2c8589a66dced9e6eae180914807c5c55d1aa4c8c1b9b", + "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", + "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", + "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", + "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", + "https://deno.land/std@0.224.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546", + "https://deno.land/std@0.224.0/testing/_time.ts": "fefd1ff35b50a410db9b0e7227e05163e1b172c88afd0d2071df0125958c3ff3", + "https://deno.land/std@0.224.0/testing/bdd.ts": "3e4de4ff6d8f348b5574661cef9501b442046a59079e201b849d0e74120d476b", + "https://deno.land/std@0.224.0/testing/mock.ts": "a963181c2860b6ba3eb60e08b62c164d33cf5da7cd445893499b2efda20074db", + "https://deno.land/std@0.224.0/testing/time.ts": "7119072a198e9913da0d21106b1f05a90a4c05b07075529770ff0e2a9eb5eaba" }, "workspace": { "dependencies": [ diff --git a/implmentation_plan.md b/implementation_plan.md similarity index 99% rename from implmentation_plan.md rename to implementation_plan.md index 50f2c0f..b372e30 100644 --- a/implmentation_plan.md +++ b/implementation_plan.md @@ -1,4 +1,4 @@ -# Implmentation Plan +# Implementation Plan Here is a plan to transform your Node.js CLI package into a Deno CLI project, focusing on reusing the existing TypeScript code in the `src/` directory: diff --git a/src/lib/deno-open.ts b/src/lib/deno-open.ts index 5212ad0..fa23ab4 100644 --- a/src/lib/deno-open.ts +++ b/src/lib/deno-open.ts @@ -1,70 +1,120 @@ +import { delay } from "std/async/delay.ts"; + /** - * Opens a URL in the default browser. - * This is a cross-platform implementation using Deno subprocess API - * @param url The URL to open - * @returns A promise that resolves when the command has been executed + * Options for the open function. */ -export default async function open(url: string): Promise { - let command: string[]; - const isWindows = Deno.build.os === "windows"; - const isMac = Deno.build.os === "darwin"; - const isLinux = Deno.build.os === "linux"; +export interface OpenOptions { + /** + * Specify the operating system ('windows', 'darwin', 'linux'). + * Defaults to Deno.build.os. Useful for testing. + */ + os?: string; + /** + * Specify an application to open the URL/file with. + */ + app?: string; + /** + * Wait for the opened application to exit before resolving the promise. + * If the application closes instantly, wait for 1 second. + */ + wait?: boolean; + /** + * Use 'background' on macOS to open the application in the background. + */ + background?: boolean; // Relevant for macOS 'open' command +} + +/** + * Opens the given URL or file path using the default application, + * or a specified application. + * + * @param target The URL or file path to open. + * @param options Optional configuration for opening the target. + */ +export default async function open( + target: string, + options?: OpenOptions, +): Promise { + const currentOs = options?.os ?? Deno.build.os; + const isWindows = currentOs === "windows"; + const isMac = currentOs === "darwin"; + const isLinux = currentOs === "linux"; // Handle Linux as well + + let command: string; + const args: string[] = []; if (isWindows) { - command = ["cmd", "/c", "start", "", url]; - } else if (isMac) { - command = ["open", url]; - } else if (isLinux) { - // On Linux, try several common browser-opener commands - const linuxCommands = [ - ["xdg-open", url], - ["gnome-open", url], - ["kde-open", url], - ["wslview", url] // For Windows Subsystem for Linux - ]; + command = "cmd"; + args.push("/c", "start", '""'); // Use empty title - // Try each command in order until one succeeds - for (const cmd of linuxCommands) { - try { - const process = new Deno.Command(cmd[0], { - args: cmd.slice(1), - stdout: "null", - stderr: "null" - }).spawn(); - - const status = await process.status; - - if (status.success) { - return; // Command succeeded, so exit the function - } - } catch { - // If this command fails, try the next one - } + if (options?.wait) { + args.push("/wait"); } - // If we get here, none of the commands worked - throw new Error("Could not open browser on Linux. Please open URL manually."); + if (options?.app) { + args.push(options.app); + } + + args.push(target.replace(/&/g, "^&")); // Escape ampersands for cmd } else { - throw new Error(`Unsupported platform: ${Deno.build.os}`); + // Common logic for macOS and Linux, potentially differing commands + if (isMac) { + command = "open"; + if (options?.wait) { + args.push("-W"); + } + if (options?.background) { + args.push("-g"); // Use -g for background on macOS + } + if (options?.app) { + args.push("-a", options.app); + } + } else if (isLinux) { + // Try common Linux commands - may need adjustment based on distribution + command = options?.app ? options.app : "xdg-open"; // Use specific app or xdg-open + // Note: Wait behavior might be inconsistent on Linux with xdg-open + } else { + throw new Error(`Unsupported platform: ${currentOs}`); + } + args.push(target); } - // For Windows and Mac, execute the chosen command - if (isWindows || isMac) { - try { - const process = new Deno.Command(command[0], { - args: command.slice(1), - stdout: "null", - stderr: "null" - }).spawn(); + try { + const process = new Deno.Command(command, { + args: args, + // Use 'piped' or 'null' for testing to avoid interfering with test output + // For actual use, 'inherit' might be desired, but makes testing harder. + stdout: "piped", // Capture stdout + stderr: "piped", // Capture stderr + }); - const status = await process.status; + // Use output() to wait for the command and get status/output + const { success, code, stdout, stderr } = await process.output(); - if (!status.success) { - throw new Error(`Failed to open ${url} in browser`); - } - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to open ${url} in browser: ${errorMessage}`); + if (!success) { + const errorDetails = new TextDecoder().decode(stderr).trim(); + const stdoutDetails = new TextDecoder().decode(stdout).trim(); + let errorMessage = `Failed to open "${target}". Command "${command} ${args.join(" ")}" exited with code ${code}.`; + if (errorDetails) errorMessage += `\nStderr: ${errorDetails}`; + if (stdoutDetails) errorMessage += `\nStdout: ${stdoutDetails}`; // Include stdout too + throw new Error(errorMessage); } + + // Handle 'wait' specifically on macOS/Linux if needed, as process.output() already waits. + // The 'wait' option for cmd /c start /wait is handled by the command itself. + // For macOS 'open -W', output() correctly waits. + // For Linux, if wait is true and we used xdg-open, true waiting might not be possible easily. + // We might need a fallback sleep if 'wait' is requested on Linux without a specific app. + if (options?.wait && isLinux && !options?.app) { + // xdg-open often returns immediately. Add a small delay as a basic wait. + await delay(1000); // Wait 1 second (adjust as necessary) + } + + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + throw new Error(`Failed to open "${target}": Command not found: ${command}`); + } + // Re-throw other errors or wrap them + throw error instanceof Error ? error : new Error(String(error)); } } diff --git a/tests/deno-http-server_test.ts b/tests/deno-http-server_test.ts index cc589c8..5d08a77 100644 --- a/tests/deno-http-server_test.ts +++ b/tests/deno-http-server_test.ts @@ -57,6 +57,7 @@ describe("DenoHttpServer", () => { // Verify the response assertEquals(response.status, 404); + await response.body?.cancel(); // Consume the body to prevent leaks }); }); diff --git a/tests/deno-open_test.ts b/tests/deno-open_test.ts index 59156e4..d553fe0 100644 --- a/tests/deno-open_test.ts +++ b/tests/deno-open_test.ts @@ -5,7 +5,12 @@ import open from "../src/lib/deno-open.ts"; // Define the expected structure returned by the mocked Deno.Command interface MockCommandOutput { - spawn: () => { status: Promise<{ success: boolean; code: number }> }; + output: () => Promise<{ + success: boolean; + code: number; + stdout: Uint8Array; + stderr: Uint8Array; + }>; } describe("deno-open", () => { @@ -26,88 +31,69 @@ describe("deno-open", () => { }); it("calls the correct command on macOS", async () => { - // Save original OS detection - const originalOs = Deno.build.os; + // Mock Deno.Command implementation to return success + const mockOutput = { + success: true, + code: 0, + stdout: new Uint8Array(), + stderr: new Uint8Array(), + }; + const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) }); + commandSpy = spy(mockCommandConstructor); + (Deno.Command as unknown) = commandSpy; - try { - // Mock OS detection - pretend we're on macOS - Object.defineProperty(Deno.build, "os", { value: "darwin", configurable: true }); + // Call open, specifying macOS in options + const url = "https://example.com"; + await open(url, { os: "darwin" }); - // Mock Deno.Command implementation - const mockSpawn = { status: Promise.resolve({ success: true, code: 0 }) }; - const mockCommandConstructor = () => ({ spawn: () => mockSpawn }); - commandSpy = spy(mockCommandConstructor); - (Deno.Command as unknown) = commandSpy; - - // Call open - const url = "https://example.com"; - await open(url); - - // Verify the spy was called with correct arguments - assertSpyCalls(commandSpy, 1); - assertEquals(commandSpy.calls[0].args[0], "open"); - assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[0], url); - } finally { - // Restore original OS detection - Object.defineProperty(Deno.build, "os", { value: originalOs, configurable: true }); - } + // Verify the spy was called with correct arguments + assertSpyCalls(commandSpy, 1); + assertEquals(commandSpy.calls[0].args[0], "open"); + assertEquals(commandSpy.calls[0].args[1]?.args, [url]); }); it("calls the correct command on Windows", async () => { - // Save original OS detection - const originalOs = Deno.build.os; + // Mock Deno.Command implementation to return success + const mockOutput = { + success: true, + code: 0, + stdout: new Uint8Array(), + stderr: new Uint8Array(), + }; + const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) }); + commandSpy = spy(mockCommandConstructor); + (Deno.Command as unknown) = commandSpy; - try { - // Mock OS detection - pretend we're on Windows - Object.defineProperty(Deno.build, "os", { value: "windows", configurable: true }); + // Call open, specifying windows in options + const url = "https://example.com"; + await open(url, { os: "windows" }); - // Mock Deno.Command implementation - const mockSpawn = { status: Promise.resolve({ success: true, code: 0 }) }; - const mockCommandConstructor = () => ({ spawn: () => mockSpawn }); - commandSpy = spy(mockCommandConstructor); - (Deno.Command as unknown) = commandSpy; - - // Call open - const url = "https://example.com"; - await open(url); - - // Verify the spy was called with correct arguments - assertSpyCalls(commandSpy, 1); - assertEquals(commandSpy.calls[0].args[0], "cmd"); - assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[0], "/c"); - assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[1], "start"); - assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[2], ""); - assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[3], url); - } finally { - // Restore original OS detection - Object.defineProperty(Deno.build, "os", { value: originalOs, configurable: true }); - } + // Verify the spy was called with correct arguments + assertSpyCalls(commandSpy, 1); + assertEquals(commandSpy.calls[0].args[0], "cmd"); + assertEquals(commandSpy.calls[0].args[1]?.args, ["/c", "start", '""', url]); }); it("throws error on command failure", async () => { - // Save original OS detection - const originalOs = Deno.build.os; + // Mock Deno.Command to return failure + const stderrOutput = new TextEncoder().encode("Command failed error message"); + const mockOutput = { + success: false, + code: 1, + stdout: new Uint8Array(), + stderr: stderrOutput, + }; + const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) }); + commandSpy = spy(mockCommandConstructor); + (Deno.Command as unknown) = commandSpy; - try { - // Mock OS detection - Object.defineProperty(Deno.build, "os", { value: "darwin", configurable: true }); - - // Mock Deno.Command to return failure - const mockSpawn = { status: Promise.resolve({ success: false, code: 1 }) }; - const mockCommandConstructor = () => ({ spawn: () => mockSpawn }); - commandSpy = spy(mockCommandConstructor); - (Deno.Command as unknown) = commandSpy; - - // Call open and expect it to throw - await assertRejects( - () => open("https://example.com"), - Error, - "Failed to open" - ); - assertSpyCalls(commandSpy, 1); - } finally { - // Restore original OS detection - Object.defineProperty(Deno.build, "os", { value: originalOs, configurable: true }); - } + // Call open and expect it to throw + const url = "https://example.com"; + await assertRejects( + () => open(url, { os: "darwin" }), + Error, + `Failed to open "${url}". Command "open ${url}" exited with code 1.\nStderr: Command failed error message`, + ); + assertSpyCalls(commandSpy, 1); }); });