-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
non-HTTP(s) URLs now works with start (#14370)
# Description this PR should close #14315 This PR enhances the start command in Nushell to handle both files and URLs more effectively, including support for custom URL schemes. Previously, the start command only reliably opened HTTP and HTTPS URLs, and custom schemes like Spotify and Obsidian which were not handled earlier. 1. **Custom URL Schemes Support:** - Added support for opening custom URL schemes 2. **Detailed Error Messages:** - Improved error reporting for failed external commands. - Captures and displays error output from the system to aid in debugging. **Example** **Opening a custom URL scheme (e.g., Spotify):** ```bash start spotify:track:4PTG3Z6ehGkBFwjybzWkR8?si=f9b4cdfc1aa14831 ``` Opens the specified track in the Spotify application. **User-Facing Changes** - **New Feature:** The start command now supports opening URLs with custom schemes
- Loading branch information
Showing
2 changed files
with
180 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use super::*; | ||
use nu_protocol::{ | ||
ast::Call, | ||
engine::{EngineState, Stack, StateWorkingSet}, | ||
PipelineData, Span, Spanned, Type, Value, | ||
}; | ||
use nu_engine::test_help::{convert_single_value_to_cmd_args, eval_block_with_input}; | ||
use nu_engine::{current_dir, eval_expression}; | ||
use std::path::PathBuf; | ||
|
||
/// Create a minimal test engine state and stack to run commands against. | ||
fn create_test_context() -> (EngineState, Stack) { | ||
let mut engine_state = EngineState::new(); | ||
let mut stack = Stack::new(); | ||
|
||
// A working set is needed for storing definitions in the engine state. | ||
let _working_set = StateWorkingSet::new(&mut engine_state); | ||
|
||
// Add the `Start` command to the engine state so we can run it. | ||
let start_cmd = Start; | ||
engine_state.add_cmd(Box::new(start_cmd)); | ||
|
||
(engine_state, stack) | ||
} | ||
|
||
#[test] | ||
fn test_start_valid_url() { | ||
let (engine_state, mut stack) = create_test_context(); | ||
|
||
// For safety in tests, we won't actually open anything, | ||
// but we can still check that the command resolves as a URL | ||
// and attempts to run. Typically, you'd mock `open::commands` if needed. | ||
|
||
// Create call for: `start https://www.example.com` | ||
let path = "https://www.example.com".to_string(); | ||
let span = Span::test_data(); | ||
let call = Call::test( | ||
"start", | ||
// The arguments for `start` are just the path in this case | ||
vec![Value::string(path, span)], | ||
); | ||
|
||
let result = Start.run( | ||
&engine_state, | ||
&mut stack, | ||
&call, | ||
PipelineData::Empty, | ||
); | ||
|
||
assert!( | ||
result.is_ok(), | ||
"Expected successful run with a valid URL, got error: {:?}", | ||
result.err() | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_start_valid_local_path() { | ||
let (engine_state, mut stack) = create_test_context(); | ||
|
||
// Here we'll simulate opening the current directory (`.`). | ||
let path = ".".to_string(); | ||
let span = Span::test_data(); | ||
let call = Call::test( | ||
"start", | ||
vec![Value::string(path, span)], | ||
); | ||
|
||
let result = Start.run( | ||
&engine_state, | ||
&mut stack, | ||
&call, | ||
PipelineData::Empty, | ||
); | ||
|
||
// If the environment is correctly set, it should succeed. | ||
// If you're running in a CI environment or restricted environment | ||
// this might fail, so you may need to mock `open` calls. | ||
assert!( | ||
result.is_ok(), | ||
"Expected successful run opening current directory, got error: {:?}", | ||
result.err() | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_start_nonexistent_local_path() { | ||
let (engine_state, mut stack) = create_test_context(); | ||
|
||
// Create an obviously invalid path | ||
let path = "this_file_does_not_exist_hopefully.txt".to_string(); | ||
let span = Span::test_data(); | ||
let call = Call::test( | ||
"start", | ||
vec![Value::string(path, span)], | ||
); | ||
|
||
let result = Start.run( | ||
&engine_state, | ||
&mut stack, | ||
&call, | ||
PipelineData::Empty, | ||
); | ||
|
||
// We expect an error since the file does not exist | ||
assert!( | ||
result.is_err(), | ||
"Expected an error for a non-existent file path" | ||
); | ||
|
||
if let Err(ShellError::GenericError { error, .. }) = result { | ||
assert!( | ||
error.contains("Cannot find file or URL"), | ||
"Expected 'Cannot find file or URL' in error, found: {}", | ||
error | ||
); | ||
} else { | ||
panic!("Unexpected error type, expected ShellError::GenericError"); | ||
} | ||
} |