use crate::common::TestEnvironment;
#[test]
fn test_alias_basic() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.bk = ["log", "-r", "@", "-T", "bookmarks"]"#);
work_dir
.run_jj(["bookmark", "create", "my-bookmark", "-r", "@"])
.success();
let output = work_dir.run_jj(["bk"]);
insta::assert_snapshot!(output, @r"
@ my-bookmark
│
~
[EOF]
");
}
#[test]
fn test_alias_bad_name() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
let output = work_dir.run_jj(["foo."]);
insta::assert_snapshot!(output, @r"
------- stderr -------
error: unrecognized subcommand 'foo.'
Usage: jj [OPTIONS] <COMMAND>
For more information, try '--help'.
[EOF]
[exit status: 2]
");
}
#[test]
fn test_alias_calls_empty_command() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(
r#"
aliases.empty = []
aliases.empty_command_with_opts = ["--no-pager"]
"#,
);
let output = work_dir.run_jj(["empty"]);
insta::assert_snapshot!(
output.normalize_stderr_with(|s| s.split_inclusive('\n').take(3).collect()), @r"
------- stderr -------
Jujutsu (An experimental VCS)
Usage: jj [OPTIONS] <COMMAND>
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["empty", "--no-pager"]);
insta::assert_snapshot!(
output.normalize_stderr_with(|s| s.split_inclusive('\n').take(1).collect()), @r"
------- stderr -------
error: 'jj' requires a subcommand but one was not provided
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["empty_command_with_opts"]);
insta::assert_snapshot!(
output.normalize_stderr_with(|s| s.split_inclusive('\n').take(1).collect()), @r"
------- stderr -------
error: 'jj' requires a subcommand but one was not provided
[EOF]
[exit status: 2]
");
}
#[test]
fn test_alias_calls_unknown_command() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.foo = ["nonexistent"]"#);
let output = work_dir.run_jj(["foo"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
error: unrecognized subcommand 'nonexistent'
tip: a similar subcommand exists: 'next'
Usage: jj [OPTIONS] <COMMAND>
For more information, try '--help'.
[EOF]
[exit status: 2]
");
}
#[test]
fn test_alias_calls_command_with_invalid_option() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.foo = ["log", "--nonexistent"]"#);
let output = work_dir.run_jj(["foo"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
error: unexpected argument '--nonexistent' found
tip: to pass '--nonexistent' as a value, use '-- --nonexistent'
Usage: jj log [OPTIONS] [FILESETS]...
For more information, try '--help'.
[EOF]
[exit status: 2]
");
}
#[test]
fn test_alias_calls_help() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.h = ["--help"]"#);
let output = work_dir.run_jj(&["h"]);
insta::assert_snapshot!(
output.normalize_stdout_with(|s| s.split_inclusive('\n').take(7).collect()), @r"
Jujutsu (An experimental VCS)
To get started, see the tutorial [`jj help -k tutorial`].
[`jj help -k tutorial`]: https://jj-vcs.github.io/jj/latest/tutorial/
Usage: jj [OPTIONS] <COMMAND>
[EOF]
");
}
#[test]
fn test_alias_cannot_override_builtin() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.log = ["rebase"]"#);
let output = work_dir.run_jj(["log", "-r", "root()"]);
insta::assert_snapshot!(output, @r"
â—† zzzzzzzz root() 00000000
[EOF]
------- stderr -------
Warning: Cannot define an alias that overrides the built-in command 'log'
[EOF]
");
}
#[test]
fn test_alias_recursive() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(
r#"[aliases]
foo = ["foo"]
bar = ["baz"]
baz = ["bar"]
"#,
);
let output = work_dir.run_jj(["foo"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
Error: Recursive alias definition involving `foo`
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["bar"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
Error: Recursive alias definition involving `bar`
[EOF]
[exit status: 1]
");
}
#[test]
fn test_alias_global_args_before_and_after() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.l = ["log", "-T", "commit_id", "-r", "all()"]"#);
let output = work_dir.run_jj(["l"]);
insta::assert_snapshot!(output, @r"
@ e8849ae12c709f2321908879bc724fdb2ab8a781
â—† 0000000000000000000000000000000000000000
[EOF]
");
let output = work_dir.run_jj(["l", "--at-op", "@-"]);
insta::assert_snapshot!(output, @r"
â—† 0000000000000000000000000000000000000000
[EOF]
");
let output = work_dir.run_jj(["--at-op", "@-", "l"]);
insta::assert_snapshot!(output, @r"
â—† 0000000000000000000000000000000000000000
[EOF]
");
let output = work_dir.run_jj(["--at-op", "abc123", "l", "--at-op", "@-"]);
insta::assert_snapshot!(output, @r"
â—† 0000000000000000000000000000000000000000
[EOF]
");
let output = work_dir.run_jj(["-R", "../nonexistent", "l", "-R", "."]);
insta::assert_snapshot!(output, @r"
@ e8849ae12c709f2321908879bc724fdb2ab8a781
â—† 0000000000000000000000000000000000000000
[EOF]
");
}
#[test]
fn test_alias_global_args_in_definition() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(
r#"aliases.l = ["log", "-T", "commit_id", "--at-op", "@-", "-r", "all()", "--color=always"]"#,
);
let output = work_dir.run_jj(["l"]);
insta::assert_snapshot!(output, @r"
[1m[38;5;14mâ—†[0m [38;5;4m0000000000000000000000000000000000000000[39m
[EOF]
");
}
#[test]
fn test_alias_invalid_definition() {
let test_env = TestEnvironment::default();
test_env.add_config(
r#"[aliases]
non-list = 5
non-string-list = [0]
"#,
);
let output = test_env.run_jj_in(".", ["non-list"]);
insta::assert_snapshot!(output.normalize_backslash(), @r"
------- stderr -------
Config error: Invalid type or value for aliases.non-list
Caused by: invalid type: integer `5`, expected a sequence
Hint: Check the config file: $TEST_ENV/config/config0002.toml
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
[EOF]
[exit status: 1]
");
let output = test_env.run_jj_in(".", ["non-string-list"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
Config error: Invalid type or value for aliases.non-string-list
Caused by: invalid type: integer `0`, expected a string
Hint: Check the config file: $TEST_ENV/config/config0002.toml
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
[EOF]
[exit status: 1]
");
}
#[test]
fn test_alias_in_repo_config() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo1"]).success();
let work_dir1 = test_env.work_dir("repo1");
work_dir1.create_dir("sub");
test_env.run_jj_in(".", ["git", "init", "repo2"]).success();
let work_dir2 = test_env.work_dir("repo2");
work_dir2.create_dir("sub");
test_env.add_config(r#"aliases.l = ['log', '-r@', '--no-graph', '-T"user alias\n"']"#);
work_dir1.write_file(
".jj/repo/config.toml",
r#"aliases.l = ['log', '-r@', '--no-graph', '-T"repo1 alias\n"']"#,
);
let output = test_env.run_jj_in(work_dir1.root().join("sub"), ["l"]);
insta::assert_snapshot!(output, @r"
repo1 alias
[EOF]
");
let output = work_dir2.run_jj(["l"]);
insta::assert_snapshot!(output, @r"
user alias
[EOF]
");
let output = work_dir2.run_jj(["l", "-R", work_dir1.root().to_str().unwrap()]);
insta::assert_snapshot!(output, @r"
user alias
[EOF]
------- stderr -------
Warning: Command aliases cannot be loaded from -R/--repository path or --config/--config-file arguments.
[EOF]
");
let output = work_dir1.run_jj(["l", "-R", work_dir2.root().to_str().unwrap()]);
insta::assert_snapshot!(output, @r"
repo1 alias
[EOF]
------- stderr -------
Warning: Command aliases cannot be loaded from -R/--repository path or --config/--config-file arguments.
[EOF]
");
let output = work_dir1.run_jj(["file", "list", "-R", work_dir2.root().to_str().unwrap()]);
insta::assert_snapshot!(output, @"");
let output = work_dir1.run_jj([
"config",
"list",
"aliases",
"-R",
work_dir2.root().to_str().unwrap(),
]);
insta::assert_snapshot!(output, @r#"
aliases.l = ['log', '-r@', '--no-graph', '-T"user alias\n"']
[EOF]
"#);
}
#[test]
fn test_alias_in_config_arg() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
test_env.add_config(r#"aliases.l = ['log', '-r@', '--no-graph', '-T"user alias\n"']"#);
let output = work_dir.run_jj(["l"]);
insta::assert_snapshot!(output, @r"
user alias
[EOF]
");
let alias_arg = r#"--config=aliases.l=['log', '-r@', '--no-graph', '-T"arg alias\n"']"#;
let output = work_dir.run_jj([alias_arg, "l"]);
insta::assert_snapshot!(output, @r"
user alias
[EOF]
------- stderr -------
Warning: Command aliases cannot be loaded from -R/--repository path or --config/--config-file arguments.
[EOF]
");
let alias_arg = r#"--config=aliases.this-command-not-exist=['log', '-r@', '--no-graph', '-T"arg alias\n"']"#;
let output = work_dir.run_jj([alias_arg, "this-command-not-exist"]);
insta::assert_snapshot!(output, @r"
------- stderr -------
Warning: Command aliases cannot be loaded from -R/--repository path or --config/--config-file arguments.
error: unrecognized subcommand 'this-command-not-exist'
Usage: jj [OPTIONS] <COMMAND>
For more information, try '--help'.
[EOF]
[exit status: 2]
");
}
#[test]
fn test_aliases_overriding_friendly_errors() {
let test_env = TestEnvironment::default();
let output = test_env.run_jj_in(".", ["--color=always", "init", "repo"]);
insta::assert_snapshot!(output, @r#"
------- stderr -------
[1m[31merror:[0m unrecognized subcommand '[33minit[0m'
For more information, try '[1m[32m--help[0m'.
[1m[38;5;6mHint: [0m[39mYou probably want `jj git init`. See also `jj help git`.[39m
[1m[38;5;6mHint: [0m[39mYou can configure `aliases.init = ["git", "init"]` if you want `jj init` to work and always use the Git backend.[39m
[EOF]
[exit status: 2]
"#);
let output = test_env.run_jj_in(".", ["init", "repo"]);
insta::assert_snapshot!(output, @r#"
------- stderr -------
error: unrecognized subcommand 'init'
For more information, try '--help'.
Hint: You probably want `jj git init`. See also `jj help git`.
Hint: You can configure `aliases.init = ["git", "init"]` if you want `jj init` to work and always use the Git backend.
[EOF]
[exit status: 2]
"#);
let output = test_env.run_jj_in(".", ["clone", "https://example.org/repo"]);
insta::assert_snapshot!(output, @r#"
------- stderr -------
error: unrecognized subcommand 'clone'
For more information, try '--help'.
Hint: You probably want `jj git clone`. See also `jj help git`.
Hint: You can configure `aliases.clone = ["git", "clone"]` if you want `jj clone` to work and always use the Git backend.
[EOF]
[exit status: 2]
"#);
let output = test_env.run_jj_in(".", ["init", "--help"]);
insta::assert_snapshot!(output, @r#"
------- stderr -------
error: unrecognized subcommand 'init'
For more information, try '--help'.
Hint: You probably want `jj git init`. See also `jj help git`.
Hint: You can configure `aliases.init = ["git", "init"]` if you want `jj init` to work and always use the Git backend.
[EOF]
[exit status: 2]
"#);
test_env.add_config(r#"aliases.init=["config", "get", "user.name"]"#);
let output = test_env.run_jj_in(".", ["init"]);
insta::assert_snapshot!(output, @r"
Test User
[EOF]
");
}