Fix tests

This commit is contained in:
Minoru Mizutani 2025-04-29 10:11:22 +09:00
parent d448111da5
commit 43ca6dcf07
No known key found for this signature in database
6 changed files with 214 additions and 185 deletions

View file

@ -23,15 +23,16 @@
"test:coverage": "deno test --coverage=coverage --allow-net --allow-env --allow-read --allow-run --allow-sys tests/ && deno coverage coverage" "test:coverage": "deno test --coverage=coverage --allow-net --allow-env --allow-read --allow-run --allow-sys tests/ && deno coverage coverage"
}, },
"imports": { "imports": {
"std/": "https://deno.land/std@0.220.0/", "std/": "https://deno.land/std@0.224.0/",
"node/": "https://deno.land/std@0.220.0/node/", "node/": "https://deno.land/std@0.224.0/node/",
"@modelcontextprotocol/sdk/": "npm:@modelcontextprotocol/sdk/" "@modelcontextprotocol/sdk/": "npm:@modelcontextprotocol/sdk/"
}, },
"compilerOptions": { "compilerOptions": {
"strict": true, "strict": true,
"lib": [ "lib": [
"ES2022", "ES2022",
"DOM" "DOM",
"deno.ns"
] ]
} }
} }

97
deno.lock generated
View file

@ -1358,59 +1358,50 @@
} }
}, },
"remote": { "remote": {
"https://deno.land/std@0.218.2/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
"https://deno.land/std@0.218.2/assert/_diff.ts": "dcc63d94ca289aec80644030cf88ccbf7acaa6fbd7b0f22add93616b36593840", "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
"https://deno.land/std@0.218.2/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4", "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
"https://deno.land/std@0.218.2/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e", "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
"https://deno.land/std@0.218.2/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2", "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
"https://deno.land/std@0.218.2/assert/assert_rejects.ts": "5206ac37d883797d9504e3915a0c7b692df6efcdefff3889cc14bb5a325641dd", "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
"https://deno.land/std@0.218.2/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
"https://deno.land/std@0.218.2/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2", "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
"https://deno.land/std@0.218.2/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a", "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
"https://deno.land/std@0.218.2/testing/mock.ts": "dc9e58f88f7e746edd0c551443a39096c75895a0fdcd7db62777e47787be6226", "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
"https://deno.land/std@0.220.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
"https://deno.land/std@0.220.0/assert/_diff.ts": "4bf42969aa8b1a33aaf23eb8e478b011bfaa31b82d85d2ff4b5c4662d8780d2b", "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
"https://deno.land/std@0.220.0/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4", "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
"https://deno.land/std@0.220.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
"https://deno.land/std@0.220.0/assert/assert_almost_equals.ts": "8b96b7385cc117668b0720115eb6ee73d04c9bcb2f5d2344d674918c9113688f", "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
"https://deno.land/std@0.220.0/assert/assert_array_includes.ts": "1688d76317fd45b7e93ef9e2765f112fdf2b7c9821016cdfb380b9445374aed1", "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
"https://deno.land/std@0.220.0/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e", "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
"https://deno.land/std@0.220.0/assert/assert_exists.ts": "24a7bf965e634f909242cd09fbaf38bde6b791128ece08e33ab08586a7cc55c9", "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
"https://deno.land/std@0.220.0/assert/assert_false.ts": "6f382568e5128c0f855e5f7dbda8624c1ed9af4fcc33ef4a9afeeedcdce99769", "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
"https://deno.land/std@0.220.0/assert/assert_greater.ts": "4945cf5729f1a38874d7e589e0fe5cc5cd5abe5573ca2ddca9d3791aa891856c", "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
"https://deno.land/std@0.220.0/assert/assert_greater_or_equal.ts": "573ed8823283b8d94b7443eb69a849a3c369a8eb9666b2d1db50c33763a5d219", "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
"https://deno.land/std@0.220.0/assert/assert_instance_of.ts": "72dc1faff1e248692d873c89382fa1579dd7b53b56d52f37f9874a75b11ba444", "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
"https://deno.land/std@0.220.0/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2", "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
"https://deno.land/std@0.220.0/assert/assert_less.ts": "2b4b3fe7910f65f7be52212f19c3977ecb8ba5b2d6d0a296c83cde42920bb005", "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
"https://deno.land/std@0.220.0/assert/assert_less_or_equal.ts": "b93d212fe669fbde959e35b3437ac9a4468f2e6b77377e7b6ea2cfdd825d38a0", "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
"https://deno.land/std@0.220.0/assert/assert_match.ts": "ec2d9680ed3e7b9746ec57ec923a17eef6d476202f339ad91d22277d7f1d16e1", "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
"https://deno.land/std@0.220.0/assert/assert_not_equals.ts": "ac86413ab70ffb14fdfc41740ba579a983fe355ba0ce4a9ab685e6b8e7f6a250", "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
"https://deno.land/std@0.220.0/assert/assert_not_instance_of.ts": "8f720d92d83775c40b2542a8d76c60c2d4aeddaf8713c8d11df8984af2604931", "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
"https://deno.land/std@0.220.0/assert/assert_not_match.ts": "b4b7c77f146963e2b673c1ce4846473703409eb93f5ab0eb60f6e6f8aeffe39f", "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
"https://deno.land/std@0.220.0/assert/assert_not_strict_equals.ts": "da0b8ab60a45d5a9371088378e5313f624799470c3b54c76e8b8abeec40a77be", "https://deno.land/std@0.224.0/async/delay.ts": "f90dd685b97c2f142b8069082993e437b1602b8e2561134827eeb7c12b95c499",
"https://deno.land/std@0.220.0/assert/assert_object_match.ts": "e85e5eef62a56ce364c3afdd27978ccab979288a3e772e6855c270a7b118fa49", "https://deno.land/std@0.224.0/data_structures/_binary_search_node.ts": "ce1da11601fef0638df4d1e53c377f791f96913383277389286b390685d76c07",
"https://deno.land/std@0.220.0/assert/assert_rejects.ts": "5206ac37d883797d9504e3915a0c7b692df6efcdefff3889cc14bb5a325641dd", "https://deno.land/std@0.224.0/data_structures/_red_black_node.ts": "4af8d3c5ac5f119d8058269259c46ea22ead567246cacde04584a83e43a9d2ea",
"https://deno.land/std@0.220.0/assert/assert_strict_equals.ts": "0425a98f70badccb151644c902384c12771a93e65f8ff610244b8147b03a2366", "https://deno.land/std@0.224.0/data_structures/binary_search_tree.ts": "2dd43d97ce5f5a4bdba11b075eb458db33e9143f50997b0eebf02912cb44f5d5",
"https://deno.land/std@0.220.0/assert/assert_string_includes.ts": "dfb072a890167146f8e5bdd6fde887ce4657098e9f71f12716ef37f35fb6f4a7", "https://deno.land/std@0.224.0/data_structures/comparators.ts": "17dfa68bf1550edadbfdd453a06f9819290bcb534c9945b5cec4b30242cff475",
"https://deno.land/std@0.220.0/assert/assert_throws.ts": "31f3c061338aec2c2c33731973d58ccd4f14e42f355501541409ee958d2eb8e5", "https://deno.land/std@0.224.0/data_structures/red_black_tree.ts": "2222be0c46842fc932e2c8589a66dced9e6eae180914807c5c55d1aa4c8c1b9b",
"https://deno.land/std@0.220.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
"https://deno.land/std@0.220.0/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2", "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
"https://deno.land/std@0.220.0/assert/fail.ts": "f310e51992bac8e54f5fd8e44d098638434b2edb802383690e0d7a9be1979f1c", "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
"https://deno.land/std@0.220.0/assert/mod.ts": "7e41449e77a31fef91534379716971bebcfc12686e143d38ada5438e04d4a90e", "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
"https://deno.land/std@0.220.0/assert/unimplemented.ts": "47ca67d1c6dc53abd0bd729b71a31e0825fc452dbcd4fde4ca06789d5644e7fd", "https://deno.land/std@0.224.0/testing/_test_suite.ts": "f10a8a6338b60c403f07a76f3f46bdc9f1e1a820c0a1decddeb2949f7a8a0546",
"https://deno.land/std@0.220.0/assert/unreachable.ts": "3670816a4ab3214349acb6730e3e6f5299021234657eefe05b48092f3848c270", "https://deno.land/std@0.224.0/testing/_time.ts": "fefd1ff35b50a410db9b0e7227e05163e1b172c88afd0d2071df0125958c3ff3",
"https://deno.land/std@0.220.0/async/delay.ts": "8e1d18fe8b28ff95885e2bc54eccec1713f57f756053576d8228e6ca110793ad", "https://deno.land/std@0.224.0/testing/bdd.ts": "3e4de4ff6d8f348b5574661cef9501b442046a59079e201b849d0e74120d476b",
"https://deno.land/std@0.220.0/data_structures/_binary_search_node.ts": "ce1da11601fef0638df4d1e53c377f791f96913383277389286b390685d76c07", "https://deno.land/std@0.224.0/testing/mock.ts": "a963181c2860b6ba3eb60e08b62c164d33cf5da7cd445893499b2efda20074db",
"https://deno.land/std@0.220.0/data_structures/_red_black_node.ts": "4af8d3c5ac5f119d8058269259c46ea22ead567246cacde04584a83e43a9d2ea", "https://deno.land/std@0.224.0/testing/time.ts": "7119072a198e9913da0d21106b1f05a90a4c05b07075529770ff0e2a9eb5eaba"
"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"
}, },
"workspace": { "workspace": {
"dependencies": [ "dependencies": [

View file

@ -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: 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:

View file

@ -1,70 +1,120 @@
import { delay } from "std/async/delay.ts";
/** /**
* Opens a URL in the default browser. * Options for the open function.
* 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
*/ */
export default async function open(url: string): Promise<void> { export interface OpenOptions {
let command: string[]; /**
const isWindows = Deno.build.os === "windows"; * Specify the operating system ('windows', 'darwin', 'linux').
const isMac = Deno.build.os === "darwin"; * Defaults to Deno.build.os. Useful for testing.
const isLinux = Deno.build.os === "linux"; */
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<void> {
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) { if (isWindows) {
command = ["cmd", "/c", "start", "", url]; command = "cmd";
} else if (isMac) { args.push("/c", "start", '""'); // Use empty title
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
];
// Try each command in order until one succeeds if (options?.wait) {
for (const cmd of linuxCommands) { args.push("/wait");
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 we get here, none of the commands worked if (options?.app) {
throw new Error("Could not open browser on Linux. Please open URL manually."); args.push(options.app);
}
args.push(target.replace(/&/g, "^&")); // Escape ampersands for cmd
} else { } 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 { try {
const process = new Deno.Command(command[0], { const process = new Deno.Command(command, {
args: command.slice(1), args: args,
stdout: "null", // Use 'piped' or 'null' for testing to avoid interfering with test output
stderr: "null" // For actual use, 'inherit' might be desired, but makes testing harder.
}).spawn(); 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) { if (!success) {
throw new Error(`Failed to open ${url} in browser`); 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);
} }
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error); // Handle 'wait' specifically on macOS/Linux if needed, as process.output() already waits.
throw new Error(`Failed to open ${url} in browser: ${errorMessage}`); // 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));
} }
} }

View file

@ -57,6 +57,7 @@ describe("DenoHttpServer", () => {
// Verify the response // Verify the response
assertEquals(response.status, 404); assertEquals(response.status, 404);
await response.body?.cancel(); // Consume the body to prevent leaks
}); });
}); });

View file

@ -5,7 +5,12 @@ import open from "../src/lib/deno-open.ts";
// Define the expected structure returned by the mocked Deno.Command // Define the expected structure returned by the mocked Deno.Command
interface MockCommandOutput { interface MockCommandOutput {
spawn: () => { status: Promise<{ success: boolean; code: number }> }; output: () => Promise<{
success: boolean;
code: number;
stdout: Uint8Array;
stderr: Uint8Array;
}>;
} }
describe("deno-open", () => { describe("deno-open", () => {
@ -26,88 +31,69 @@ describe("deno-open", () => {
}); });
it("calls the correct command on macOS", async () => { it("calls the correct command on macOS", async () => {
// Save original OS detection // Mock Deno.Command implementation to return success
const originalOs = Deno.build.os; const mockOutput = {
success: true,
try { code: 0,
// Mock OS detection - pretend we're on macOS stdout: new Uint8Array(),
Object.defineProperty(Deno.build, "os", { value: "darwin", configurable: true }); stderr: new Uint8Array(),
};
// Mock Deno.Command implementation const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) });
const mockSpawn = { status: Promise.resolve({ success: true, code: 0 }) };
const mockCommandConstructor = () => ({ spawn: () => mockSpawn });
commandSpy = spy(mockCommandConstructor); commandSpy = spy(mockCommandConstructor);
(Deno.Command as unknown) = commandSpy; (Deno.Command as unknown) = commandSpy;
// Call open // Call open, specifying macOS in options
const url = "https://example.com"; const url = "https://example.com";
await open(url); await open(url, { os: "darwin" });
// Verify the spy was called with correct arguments // Verify the spy was called with correct arguments
assertSpyCalls(commandSpy, 1); assertSpyCalls(commandSpy, 1);
assertEquals(commandSpy.calls[0].args[0], "open"); assertEquals(commandSpy.calls[0].args[0], "open");
assertEquals((commandSpy.calls[0].args[1] as { args: string[] }).args[0], url); assertEquals(commandSpy.calls[0].args[1]?.args, [url]);
} finally {
// Restore original OS detection
Object.defineProperty(Deno.build, "os", { value: originalOs, configurable: true });
}
}); });
it("calls the correct command on Windows", async () => { it("calls the correct command on Windows", async () => {
// Save original OS detection // Mock Deno.Command implementation to return success
const originalOs = Deno.build.os; const mockOutput = {
success: true,
try { code: 0,
// Mock OS detection - pretend we're on Windows stdout: new Uint8Array(),
Object.defineProperty(Deno.build, "os", { value: "windows", configurable: true }); stderr: new Uint8Array(),
};
// Mock Deno.Command implementation const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) });
const mockSpawn = { status: Promise.resolve({ success: true, code: 0 }) };
const mockCommandConstructor = () => ({ spawn: () => mockSpawn });
commandSpy = spy(mockCommandConstructor); commandSpy = spy(mockCommandConstructor);
(Deno.Command as unknown) = commandSpy; (Deno.Command as unknown) = commandSpy;
// Call open // Call open, specifying windows in options
const url = "https://example.com"; const url = "https://example.com";
await open(url); await open(url, { os: "windows" });
// Verify the spy was called with correct arguments // Verify the spy was called with correct arguments
assertSpyCalls(commandSpy, 1); assertSpyCalls(commandSpy, 1);
assertEquals(commandSpy.calls[0].args[0], "cmd"); 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]?.args, ["/c", "start", '""', url]);
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 });
}
}); });
it("throws error on command failure", async () => { it("throws error on command failure", async () => {
// Save original OS detection
const originalOs = Deno.build.os;
try {
// Mock OS detection
Object.defineProperty(Deno.build, "os", { value: "darwin", configurable: true });
// Mock Deno.Command to return failure // Mock Deno.Command to return failure
const mockSpawn = { status: Promise.resolve({ success: false, code: 1 }) }; const stderrOutput = new TextEncoder().encode("Command failed error message");
const mockCommandConstructor = () => ({ spawn: () => mockSpawn }); const mockOutput = {
success: false,
code: 1,
stdout: new Uint8Array(),
stderr: stderrOutput,
};
const mockCommandConstructor = () => ({ output: () => Promise.resolve(mockOutput) });
commandSpy = spy(mockCommandConstructor); commandSpy = spy(mockCommandConstructor);
(Deno.Command as unknown) = commandSpy; (Deno.Command as unknown) = commandSpy;
// Call open and expect it to throw // Call open and expect it to throw
const url = "https://example.com";
await assertRejects( await assertRejects(
() => open("https://example.com"), () => open(url, { os: "darwin" }),
Error, Error,
"Failed to open" `Failed to open "${url}". Command "open ${url}" exited with code 1.\nStderr: Command failed error message`,
); );
assertSpyCalls(commandSpy, 1); assertSpyCalls(commandSpy, 1);
} finally {
// Restore original OS detection
Object.defineProperty(Deno.build, "os", { value: originalOs, configurable: true });
}
}); });
}); });