char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somsnapshot: embed shared main and worker startup in the CLI688e96e

main
7.6 KiB223 linesraw
1use std::io::Write;
2use std::process::{Command, Output, Stdio};
3
4fn avril(args: &[&str]) -> Output {
5    let dir = tempfile::tempdir().unwrap();
6    std::fs::write(dir.path().join("main.js"), "console.log('ran');").unwrap();
7    Command::new(env!("CARGO_BIN_EXE_avril"))
8        .current_dir(dir.path())
9        .args(args)
10        .output()
11        .unwrap()
12}
13
14fn stdout(args: &[&str]) -> String {
15    let output = avril(args);
16    assert!(
17        output.status.success(),
18        "{args:?}: {}",
19        String::from_utf8_lossy(&output.stderr)
20    );
21    String::from_utf8(output.stdout).unwrap()
22}
23
24fn stderr(args: &[&str]) -> String {
25    let output = avril(args);
26    assert!(!output.status.success(), "{args:?}");
27    assert!(output.stdout.is_empty(), "{args:?}");
28    String::from_utf8(output.stderr).unwrap()
29}
30
31#[test]
32fn global_help_lists_commands_and_command_help_lists_only_their_options() {
33    for args in [&["--help"][..], &["-h"], &["help"], &["-0", "--help"]] {
34        let help = stdout(args);
35        for (heading, expected) in [
36            (
37                "Main commands:\n",
38                &["repl", "run", "serve", "task", "test"][..],
39            ),
40            (
41                "Additional commands:\n",
42                &[
43                    "add", "bundle", "cache", "check", "info", "lsp", "update", "vendor",
44                ][..],
45            ),
46        ] {
47            let commands: Vec<_> = help
48                .split_once(heading)
49                .unwrap()
50                .1
51                .lines()
52                .take_while(|line| !line.is_empty())
53                .map(|line| line.split_whitespace().next().unwrap())
54                .collect();
55            assert_eq!(commands, expected, "{args:?}: {help}");
56        }
57        assert!(
58            help.find("Main commands:").unwrap() < help.find("Additional commands:").unwrap(),
59            "{args:?}: {help}"
60        );
61        assert!(!help.contains("--prune"), "{help}");
62    }
63    for args in [
64        &["help", "update"][..],
65        &["update", "--help"],
66        &["update", "-h", "main.js"],
67    ] {
68        let help = stdout(args);
69        assert!(help.contains("Usage: avril update"), "{help}");
70        assert!(help.contains("--prune"), "{help}");
71        assert!(
72            !help.contains("--frozen") && !help.contains("--config"),
73            "{help}"
74        );
75    }
76    let help = stdout(&["help", "task"]);
77    assert!(
78        help.contains("Usage: avril task [--watch] [<name> [args...]]"),
79        "{help}"
80    );
81    assert!(help.contains("--watch"), "{help}");
82    assert!(stderr(&["task", "--bogus"]).contains("Usage: avril task"));
83    let help = stdout(&["help", "add"]);
84    assert!(help.contains("\nAdd:\n  --config=PATH"), "{help}");
85    assert!(help.contains("\nPackages:\n  --offline"), "{help}");
86    assert!(
87        !help.contains("--allow-") && !help.contains("--lock"),
88        "{help}"
89    );
90    assert!(stderr(&["help", "nope"]).contains("Unknown command: nope"));
91    assert!(stderr(&["editor", "main.js"]).contains("Unknown command: editor"));
92    assert!(stderr(&["help", "editor"]).contains("Unknown command: editor"));
93}
94
95#[test]
96fn package_commands_expose_explicit_registry_options() {
97    for command in [
98        "run", "repl", "test", "serve", "cache", "info", "update", "vendor", "lsp", "add",
99    ] {
100        let help = stdout(&[command, "--help"]);
101        assert!(help.contains("--jsr-registry=URL"), "{command}: {help}");
102        assert!(help.contains("--npm-registry=URL"), "{command}: {help}");
103        assert!(!help.contains("--registry"), "{command}: {help}");
104        assert!(
105            stderr(&[command, "--registry=https://jsr.io/"]).contains("Unknown option: --registry"),
106            "{command}"
107        );
108    }
109}
110
111#[test]
112fn lsp_exposes_live_project_policy_and_rejects_retired_options() {
113    for args in [&["lsp", "--help"][..], &["help", "lsp"]] {
114        let help = stdout(args);
115        assert!(!help.contains("--experimental-project"));
116        assert!(help.contains("--typescript=PATH"));
117        assert!(help.contains("--project=DIR") && help.contains("--lock"));
118    }
119    assert!(stderr(&["lsp"]).contains("requires --typescript"));
120    assert!(
121        stderr(&[
122            "lsp",
123            "--typescript=missing",
124            "--project=.",
125            "--experimental-project=missing"
126        ])
127        .contains("Unknown option")
128    );
129    assert!(stderr(&["lsp", "--allow-all"]).contains("Unknown option"));
130    assert!(stderr(&["lsp", "--typescript=a", "--typescript=b"]).contains("Duplicate option"));
131}
132
133#[test]
134fn check_exposes_a_typescript_backend_and_requires_it() {
135    for args in [&["check", "--help"][..], &["help", "check"]] {
136        let help = stdout(args);
137        assert!(
138            help.contains("Usage: avril check --typescript=PATH"),
139            "{help}"
140        );
141        assert!(help.contains("--typescript=PATH"), "{help}");
142        assert!(help.contains("--jsr-registry=URL"), "{help}");
143        assert!(help.contains("--lock=PATH"), "{help}");
144    }
145    assert!(stderr(&["check", "main.js"]).contains("check requires --typescript"));
146    assert!(stderr(&["check"]).contains("check requires --typescript"));
147    assert!(stderr(&["check", "--typescript=backend"]).contains("Usage: avril check"));
148    assert!(
149        stderr(&["check", "--typescript=a", "--typescript=b", "main.js"])
150            .contains("Duplicate option")
151    );
152    assert!(
153        stderr(&["check", "--project=.", "main.js"])
154            .contains("--project is not an option of `avril check`")
155    );
156    assert!(
157        stderr(&["run", "--typescript=backend", "main.js"])
158            .contains("--typescript is not an option of `avril run`")
159    );
160}
161
162#[test]
163fn commands_reject_options_they_do_not_take() {
164    for (args, message) in [
165        (
166            &["run", "--pre", "main.js"][..],
167            "--pre is not an option of `avril run`",
168        ),
169        (&["main.js"], "Unknown command: main.js"),
170        (&["-0", "main.js"], "The REPL does not accept a module"),
171        (&["--prune"], "--prune is not an option of `avril repl`"),
172        (
173            &["repl", "--include=x"],
174            "--include is not an option of `avril repl`",
175        ),
176        (
177            &["update", "--frozen", "main.js"],
178            "--frozen is not an option of `avril update`",
179        ),
180        (
181            &["cache", "--out=x", "main.js"],
182            "--out is not an option of `avril cache`",
183        ),
184        (
185            &["add", "--lock=x", "jsr:@test/pkg@1"],
186            "--lock is not an option of `avril add`",
187        ),
188        (
189            &["add", "-0", "jsr:@test/pkg@1"],
190            "-0 is not an option of `avril add`",
191        ),
192        (&["run", "--bogus", "main.js"], "Unknown option: --bogus"),
193        (
194            &["run", "--offline=yes", "main.js"],
195            "--offline does not take a value",
196        ),
197        (
198            &["add", "--pre=yes", "jsr:@test/pkg"],
199            "--pre does not take a value",
200        ),
201        (&["cache"], "Usage: avril cache [OPTIONS] <module>"),
202    ] {
203        let error = stderr(args);
204        assert!(error.contains(message), "{args:?}: {error}");
205    }
206    assert_eq!(stdout(&["run", "--", "main.js"]), "ran\n");
207}
208
209#[test]
210fn without_a_command_avril_only_starts_the_repl() {
211    for args in [&[][..], &["-0"]] {
212        let mut child = Command::new(env!("CARGO_BIN_EXE_avril"))
213            .args(args)
214            .stdin(Stdio::piped())
215            .stdout(Stdio::piped())
216            .spawn()
217            .unwrap();
218        child.stdin.take().unwrap().write_all(b"6 * 7\n").unwrap();
219        let output = child.wait_with_output().unwrap();
220        assert!(output.status.success(), "{args:?}");
221        assert_eq!(output.stdout, b"42\n", "{args:?}");
222    }
223}