diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-02-10 13:22:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-10 20:22:13 +0000 |
commit | f5e46c9bf2f50d66a953fa133161fc829cecff06 (patch) | |
tree | 8faf2f5831c1c7b11d842cd9908d141082c869a5 /cli/tests/integration/watcher_tests.rs | |
parent | d2477f780630a812bfd65e3987b70c0d309385bb (diff) |
chore: move cli/tests/ -> tests/ (#22369)
This looks like a massive PR, but it's only a move from cli/tests ->
tests, and updates of relative paths for files.
This is the first step towards aggregate all of the integration test
files under tests/, which will lead to a set of integration tests that
can run without the CLI binary being built.
While we could leave these tests under `cli`, it would require us to
keep a more complex directory structure for the various test runners. In
addition, we have a lot of complexity to ignore various test files in
the `cli` project itself (cargo publish exclusion rules, autotests =
false, etc).
And finally, the `tests/` folder will eventually house the `test_ffi`,
`test_napi` and other testing code, reducing the size of the root repo
directory.
For easier review, the extremely large and noisy "move" is in the first
commit (with no changes -- just a move), while the remainder of the
changes to actual files is in the second commit.
Diffstat (limited to 'cli/tests/integration/watcher_tests.rs')
-rw-r--r-- | cli/tests/integration/watcher_tests.rs | 1864 |
1 files changed, 0 insertions, 1864 deletions
diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs deleted file mode 100644 index 6a2cab08a..000000000 --- a/cli/tests/integration/watcher_tests.rs +++ /dev/null @@ -1,1864 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use flaky_test::flaky_test; -use test_util as util; -use test_util::assert_contains; -use test_util::TempDir; -use tokio::io::AsyncBufReadExt; -use util::DenoChild; - -use util::assert_not_contains; - -const CLEAR_SCREEN: &str = r#"[2J"#; - -/// Logs to stderr every time next_line() is called -struct LoggingLines<R> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - pub lines: tokio::io::Lines<R>, - pub stream_name: String, -} - -impl<R> LoggingLines<R> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - pub async fn next_line(&mut self) -> tokio::io::Result<Option<String>> { - let line = self.lines.next_line().await; - eprintln!( - "{}: {}", - self.stream_name, - line.as_ref().unwrap().clone().unwrap() - ); - line - } -} - -// Helper function to skip watcher output that contains "Restarting" -// phrase. -async fn skip_restarting_line<R>(stderr_lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - loop { - let msg = next_line(stderr_lines).await.unwrap(); - if !msg.contains("Restarting") { - return msg; - } - } -} - -async fn read_all_lints<R>(stderr_lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let mut str = String::new(); - while let Some(t) = next_line(stderr_lines).await { - let t = util::strip_ansi_codes(&t); - if t.starts_with("Watcher File change detected") { - continue; - } - if t.starts_with("Watcher") { - break; - } - if t.starts_with("error[") { - str.push_str(&t); - str.push('\n'); - } - } - str -} - -async fn next_line<R>(lines: &mut LoggingLines<R>) -> Option<String> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout(timeout, lines.next_line()) - .await - .unwrap_or_else(|_| { - panic!( - "Output did not contain a new line after {} seconds", - timeout.as_secs() - ) - }) - .unwrap() -} - -/// Returns the matched line or None if there are no more lines in this stream -async fn wait_for<R>( - condition: impl Fn(&str) -> bool, - lines: &mut LoggingLines<R>, -) -> Option<String> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - while let Some(line) = lines.next_line().await.unwrap() { - if condition(line.as_str()) { - return Some(line); - } - } - - None -} - -async fn wait_contains<R>(s: &str, lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout(timeout, wait_for(|line| line.contains(s), lines)) - .await - .unwrap_or_else(|_| { - panic!( - "Output did not contain \"{}\" after {} seconds", - s, - timeout.as_secs() - ) - }) - .unwrap_or_else(|| panic!("Output ended without containing \"{}\"", s)) -} - -/// Before test cases touch files, they need to wait for the watcher to be -/// ready. Waiting for subcommand output is insufficient. -/// The file watcher takes a moment to start watching files due to -/// asynchronicity. It is possible for the watched subcommand to finish before -/// any files are being watched. -/// deno must be running with --log-level=debug -/// file_name should be the file name and, optionally, extension. file_name -/// may not be a full path, as it is not portable. -async fn wait_for_watcher<R>( - file_name: &str, - stderr_lines: &mut LoggingLines<R>, -) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout( - timeout, - wait_for( - |line| line.contains("Watching paths") && line.contains(file_name), - stderr_lines, - ), - ) - .await - .unwrap_or_else(|_| { - panic!( - "Watcher did not start for file \"{}\" after {} seconds", - file_name, - timeout.as_secs() - ) - }) - .unwrap_or_else(|| { - panic!( - "Output ended without before the watcher started watching file \"{}\"", - file_name - ) - }) -} - -fn check_alive_then_kill(mut child: DenoChild) { - assert!(child.try_wait().unwrap().is_none()); - child.kill().unwrap(); -} - -fn child_lines( - child: &mut std::process::Child, -) -> ( - LoggingLines<tokio::io::BufReader<tokio::process::ChildStdout>>, - LoggingLines<tokio::io::BufReader<tokio::process::ChildStderr>>, -) { - let stdout_lines = LoggingLines { - lines: tokio::io::BufReader::new( - tokio::process::ChildStdout::from_std(child.stdout.take().unwrap()) - .unwrap(), - ) - .lines(), - stream_name: "STDOUT".to_string(), - }; - let stderr_lines = LoggingLines { - lines: tokio::io::BufReader::new( - tokio::process::ChildStderr::from_std(child.stderr.take().unwrap()) - .unwrap(), - ) - .lines(), - stream_name: "STDERR".to_string(), - }; - (stdout_lines, stderr_lines) -} - -#[tokio::test] -async fn lint_watch_test() { - let t = TempDir::new(); - let badly_linted_original = - util::testdata_path().join("lint/watch/badly_linted.js"); - let badly_linted_output = - util::testdata_path().join("lint/watch/badly_linted.js.out"); - let badly_linted_fixed1 = - util::testdata_path().join("lint/watch/badly_linted_fixed1.js"); - let badly_linted_fixed1_output = - util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out"); - let badly_linted_fixed2 = - util::testdata_path().join("lint/watch/badly_linted_fixed2.js"); - let badly_linted_fixed2_output = - util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out"); - let badly_linted = t.path().join("badly_linted.js"); - - std::fs::copy(badly_linted_original, &badly_linted).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("lint") - .arg(&badly_linted) - .arg("--watch") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - let next_line = next_line(&mut stderr_lines).await.unwrap(); - - assert_contains!(&next_line, "Lint started"); - let mut output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_output).unwrap(); - assert_eq!(output, expected); - - // Change content of the file again to be badly-linted - std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap(); - std::thread::sleep(std::time::Duration::from_secs(1)); - - output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap(); - assert_eq!(output, expected); - - // Change content of the file again to be badly-linted - std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap(); - - output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap(); - assert_eq!(output, expected); - - // the watcher process is still alive - assert!(child.try_wait().unwrap().is_none()); - - child.kill().unwrap(); - drop(t); -} - -#[tokio::test] -async fn lint_watch_without_args_test() { - let t = TempDir::new(); - let badly_linted_original = - util::testdata_path().join("lint/watch/badly_linted.js"); - let badly_linted_output = - util::testdata_path().join("lint/watch/badly_linted.js.out"); - let badly_linted_fixed1 = - util::testdata_path().join("lint/watch/badly_linted_fixed1.js"); - let badly_linted_fixed1_output = - util::testdata_path().join("lint/watch/badly_linted_fixed1.js.out"); - let badly_linted_fixed2 = - util::testdata_path().join("lint/watch/badly_linted_fixed2.js"); - let badly_linted_fixed2_output = - util::testdata_path().join("lint/watch/badly_linted_fixed2.js.out"); - let badly_linted = t.path().join("badly_linted.js"); - - std::fs::copy(badly_linted_original, &badly_linted).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(t.path()) - .arg("lint") - .arg("--watch") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Lint started"); - let mut output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_output).unwrap(); - assert_eq!(output, expected); - - // Change content of the file again to be badly-linted - std::fs::copy(badly_linted_fixed1, &badly_linted).unwrap(); - - output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_fixed1_output).unwrap(); - assert_eq!(output, expected); - - // Change content of the file again to be badly-linted - std::fs::copy(badly_linted_fixed2, &badly_linted).unwrap(); - std::thread::sleep(std::time::Duration::from_secs(1)); - - output = read_all_lints(&mut stderr_lines).await; - let expected = std::fs::read_to_string(badly_linted_fixed2_output).unwrap(); - assert_eq!(output, expected); - - // the watcher process is still alive - assert!(child.try_wait().unwrap().is_none()); - - child.kill().unwrap(); - drop(t); -} - -#[tokio::test] -async fn lint_all_files_on_each_change_test() { - let t = TempDir::new(); - let badly_linted_fixed0 = - util::testdata_path().join("lint/watch/badly_linted.js"); - let badly_linted_fixed1 = - util::testdata_path().join("lint/watch/badly_linted_fixed1.js"); - let badly_linted_fixed2 = - util::testdata_path().join("lint/watch/badly_linted_fixed2.js"); - - let badly_linted_1 = t.path().join("badly_linted_1.js"); - let badly_linted_2 = t.path().join("badly_linted_2.js"); - std::fs::copy(badly_linted_fixed0, badly_linted_1).unwrap(); - std::fs::copy(badly_linted_fixed1, &badly_linted_2).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("lint") - .arg(t.path()) - .arg("--watch") - .arg("--unstable") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 2 files" - ); - - std::fs::copy(badly_linted_fixed2, badly_linted_2).unwrap(); - - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 2 files" - ); - - assert!(child.try_wait().unwrap().is_none()); - - child.kill().unwrap(); - drop(t); -} - -#[tokio::test] -async fn fmt_watch_test() { - let fmt_testdata_path = util::testdata_path().join("fmt"); - let t = TempDir::new(); - let fixed = fmt_testdata_path.join("badly_formatted_fixed.js"); - let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs"); - let badly_formatted = t.path().join("badly_formatted.js"); - std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(&fmt_testdata_path) - .arg("fmt") - .arg(&badly_formatted) - .arg("--watch") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Fmt started"); - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - wait_contains("Fmt finished", &mut stderr_lines).await; - - let expected = std::fs::read_to_string(fixed.clone()).unwrap(); - let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap(); - assert_eq!(actual, expected); - - // Change content of the file again to be badly formatted - std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap(); - - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - wait_contains("Fmt finished", &mut stderr_lines).await; - - // Check if file has been automatically formatted by watcher - let expected = std::fs::read_to_string(fixed).unwrap(); - let actual = std::fs::read_to_string(badly_formatted).unwrap(); - assert_eq!(actual, expected); - check_alive_then_kill(child); -} - -#[tokio::test] -async fn fmt_watch_without_args_test() { - let fmt_testdata_path = util::testdata_path().join("fmt"); - let t = TempDir::new(); - let fixed = fmt_testdata_path.join("badly_formatted_fixed.js"); - let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs"); - let badly_formatted = t.path().join("badly_formatted.js"); - std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(t.path()) - .arg("fmt") - .arg("--watch") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Fmt started"); - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - - let expected = std::fs::read_to_string(fixed.clone()).unwrap(); - let actual = std::fs::read_to_string(badly_formatted.clone()).unwrap(); - assert_eq!(actual, expected); - - // Change content of the file again to be badly formatted - std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap(); - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - - // Check if file has been automatically formatted by watcher - let expected = std::fs::read_to_string(fixed).unwrap(); - let actual = std::fs::read_to_string(badly_formatted).unwrap(); - assert_eq!(actual, expected); - check_alive_then_kill(child); -} - -#[ignore = "https://github.com/denoland/deno/issues/19629"] -#[tokio::test] -async fn fmt_check_all_files_on_each_change_test() { - let t = TempDir::new(); - let fmt_testdata_path = util::testdata_path().join("fmt"); - let badly_formatted_original = fmt_testdata_path.join("badly_formatted.mjs"); - let badly_formatted_1 = t.path().join("badly_formatted_1.js"); - let badly_formatted_2 = t.path().join("badly_formatted_2.js"); - std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap(); - std::fs::copy(&badly_formatted_original, badly_formatted_2).unwrap(); - - let mut child = util::deno_cmd() - .current_dir(&fmt_testdata_path) - .arg("fmt") - .arg(t.path()) - .arg("--watch") - .arg("--check") - .arg("--unstable") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - wait_contains("error", &mut stderr_lines).await, - "Found 2 not formatted files in 2 files" - ); - - // Change content of the file again to be badly formatted - std::fs::copy(&badly_formatted_original, &badly_formatted_1).unwrap(); - - assert_contains!( - wait_contains("error", &mut stderr_lines).await, - "Found 2 not formatted files in 2 files" - ); - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn bundle_js_watch() { - use std::path::PathBuf; - // Test strategy extends this of test bundle_js by adding watcher - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.ts"); - file_to_watch.write("console.log('Hello world');"); - assert!(file_to_watch.is_file()); - let t = TempDir::new(); - let bundle = t.path().join("mod6.bundle.js"); - let mut deno = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("bundle") - .arg(&file_to_watch) - .arg(&bundle) - .arg("--watch") - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - - let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Warning"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "deno_emit"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bundle started" - ); - let line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(line, "file_to_watch.ts"); - assert_contains!(line, "Check"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Bundle"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "mod6.bundle.js" - ); - let file = PathBuf::from(&bundle); - assert!(file.is_file()); - - wait_contains("Bundle finished", &mut stderr_lines).await; - - file_to_watch.write("console.log('Hello world2');"); - - let line = next_line(&mut stderr_lines).await.unwrap(); - // Should not clear screen, as we are in non-TTY environment - assert_not_contains!(&line, CLEAR_SCREEN); - assert_contains!(&line, "File change detected!"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Check"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "file_to_watch.ts" - ); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "mod6.bundle.js" - ); - let file = PathBuf::from(&bundle); - assert!(file.is_file()); - wait_contains("Bundle finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - file_to_watch.write("syntax error ^^"); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "File change detected!" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error: "); - wait_contains("Bundle failed", &mut stderr_lines).await; - check_alive_then_kill(deno); -} - -/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt -#[tokio::test] -async fn bundle_watch_not_exit() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.ts"); - file_to_watch.write("syntax error ^^"); - let target_file = t.path().join("target.js"); - - let mut deno = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("bundle") - .arg(&file_to_watch) - .arg(&target_file) - .arg("--watch") - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Warning"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "deno_emit"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bundle started" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bundle failed" - ); - // the target file hasn't been created yet - assert!(!target_file.is_file()); - - // Make sure the watcher actually restarts and works fine with the proper syntax - file_to_watch.write("console.log(42);"); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "File change detected" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Check"); - let line = next_line(&mut stderr_lines).await.unwrap(); - // Should not clear screen, as we are in non-TTY environment - assert_not_contains!(&line, CLEAR_SCREEN); - assert_contains!(line, "file_to_watch.ts"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "target.js"); - - wait_contains("Bundle finished", &mut stderr_lines).await; - - // bundled file is created - assert!(target_file.is_file()); - check_alive_then_kill(deno); -} - -#[tokio::test] -async fn run_watch_no_dynamic() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("console.log('Hello world');"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--unstable") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Hello world", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Change content of the file - file_to_watch.write("console.log('Hello world2');"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Hello world2", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Add dependency - let another_file = t.path().join("another_file.js"); - another_file.write("export const foo = 0;"); - file_to_watch - .write("import { foo } from './another_file.js'; console.log(foo);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("0", &mut stdout_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_file.write("export const foo = 42;"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - file_to_watch.write("syntax error ^^"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Then restore the file - file_to_watch - .write("import { foo } from './another_file.js'; console.log(foo);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Update the content of the imported file with invalid syntax - another_file.write("syntax error ^^"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Modify the imported file and make sure that restarting occurs - another_file.write("export const foo = 'modified!';"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("modified!", &mut stdout_lines).await; - wait_contains("Watching paths", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -// TODO(bartlomieju): this test became flaky on macOS runner; it is unclear -// if that's because of a bug in code or the runner itself. We should reenable -// it once we upgrade to XL runners for macOS. -#[cfg(not(target_os = "macos"))] -#[tokio::test] -async fn run_watch_external_watch_files() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("console.log('Hello world');"); - - let external_file_to_watch = t.path().join("external_file_to_watch.txt"); - external_file_to_watch.write("Hello world"); - - let mut watch_arg = "--watch=".to_owned(); - let external_file_to_watch_str = external_file_to_watch.to_string(); - watch_arg.push_str(&external_file_to_watch_str); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg(watch_arg) - .arg("-L") - .arg("debug") - .arg("--unstable") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("Hello world", &mut stdout_lines).await; - wait_for_watcher("external_file_to_watch.txt", &mut stderr_lines).await; - - // Change content of the external file - external_file_to_watch.write("Hello world2"); - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - // Again (https://github.com/denoland/deno/issues/17584) - external_file_to_watch.write("Hello world3"); - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_load_unload_events() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - setInterval(() => {}, 0); - window.addEventListener("load", () => { - console.log("load"); - }); - - window.addEventListener("unload", () => { - console.log("unload"); - }); - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--unstable") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - // Wait for the first load event to fire - wait_contains("load", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Change content of the file, this time without an interval to keep it alive. - file_to_watch.write( - r#" - window.addEventListener("load", () => { - console.log("load"); - }); - - window.addEventListener("unload", () => { - console.log("unload"); - }); - "#, - ); - - // Wait for the restart - wait_contains("Restarting", &mut stderr_lines).await; - - // Confirm that the unload event was dispatched from the first run - wait_contains("unload", &mut stdout_lines).await; - - // Followed by the load event of the second run - wait_contains("load", &mut stdout_lines).await; - - // Which is then unloaded as there is nothing keeping it alive. - wait_contains("unload", &mut stdout_lines).await; - check_alive_then_kill(child); -} - -/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt -#[tokio::test] -async fn run_watch_not_exit() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("syntax error ^^"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--unstable") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Make sure the watcher actually restarts and works fine with the proper syntax - file_to_watch.write("console.log(42);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_with_import_map_and_relative_paths() { - fn create_relative_tmp_file( - directory: &TempDir, - filename: &'static str, - filecontent: &'static str, - ) -> std::path::PathBuf { - let absolute_path = directory.path().join(filename); - absolute_path.write(filecontent); - let relative_path = absolute_path - .as_path() - .strip_prefix(directory.path()) - .unwrap() - .to_owned(); - assert!(relative_path.is_relative()); - relative_path - } - - let temp_directory = TempDir::new(); - let file_to_watch = create_relative_tmp_file( - &temp_directory, - "file_to_watch.js", - "console.log('Hello world');", - ); - let import_map_path = create_relative_tmp_file( - &temp_directory, - "import_map.json", - "{\"imports\": {}}", - ); - - let mut child = util::deno_cmd() - .current_dir(temp_directory.path()) - .arg("run") - .arg("--watch") - .arg("--import-map") - .arg(&import_map_path) - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - let line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line, "Process started"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Process finished" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "Hello world"); - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_with_ext_flag() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch"); - file_to_watch.write("interface I{}; console.log(42);"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--log-level") - .arg("debug") - .arg("--ext") - .arg("ts") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("42", &mut stdout_lines).await; - - // Make sure the watcher actually restarts and works fine with the proper language - wait_for_watcher("file_to_watch", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - file_to_watch.write("type Bear = 'polar' | 'grizzly'; console.log(123);"); - - wait_contains("Restarting!", &mut stderr_lines).await; - wait_contains("123", &mut stdout_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_error_messages() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch - .write("throw SyntaxError(`outer`, {cause: TypeError(`inner`)})"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Process started", &mut stderr_lines).await; - wait_contains( - "error: Uncaught (in promise) SyntaxError: outer", - &mut stderr_lines, - ) - .await; - wait_contains("Caused by: TypeError: inner", &mut stderr_lines).await; - wait_contains("Process failed", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn test_watch_basic() { - let t = TempDir::new(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("test") - .arg("--watch") - .arg("--unstable") - .arg("--no-check") - .arg(t.path()) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "0 passed | 0 failed" - ); - wait_contains("Test finished", &mut stderr_lines).await; - - let foo_file = t.path().join("foo.js"); - let bar_file = t.path().join("bar.js"); - let foo_test = t.path().join("foo_test.js"); - let bar_test = t.path().join("bar_test.js"); - foo_file.write("export default function foo() { 1 + 1 }"); - bar_file.write("export default function bar() { 2 + 2 }"); - foo_test.write("import foo from './foo.js'; Deno.test('foo', foo);"); - bar_test.write("import bar from './bar.js'; Deno.test('bar', bar);"); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo", "bar"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo", "bar"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Change content of the file - foo_test.write("import foo from './foo.js'; Deno.test('foobar', foo);"); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foobar"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Add test - let another_test = t.path().join("new_test.js"); - another_test.write("Deno.test('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_test.write("Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 2 tests" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "another another one" - ); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - another_test.write("syntax error ^^"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Test failed"); - - // Then restore the file - another_test.write("Deno.test('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and the test fails - // This also confirms that it restarts when dependencies change - foo_file - .write("export default function foo() { throw new Error('Whoops!'); }"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "FAILED"); - wait_contains("FAILED", &mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test failed", &mut stderr_lines).await; - - // Then restore the file - foo_file.write("export default function foo() { 1 + 1 }"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Test that circular dependencies work fine - foo_file.write("import './bar.js'; export default function foo() { 1 + 1 }"); - bar_file.write("import './foo.js'; export default function bar() { 2 + 2 }"); - check_alive_then_kill(child); -} - -#[flaky_test] -#[tokio::main] -async fn test_watch_doc() { - let t = TempDir::new(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("test") - .arg("--watch") - .arg("--doc") - .arg("--unstable") - .arg(t.path()) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "0 passed | 0 failed" - ); - wait_contains("Test finished", &mut stderr_lines).await; - - let foo_file = t.path().join("foo.ts"); - foo_file.write( - r#" - export default function foo() {} - "#, - ); - - foo_file.write( - r#" - /** - * ```ts - * import foo from "./foo.ts"; - * ``` - */ - export default function foo() {} - "#, - ); - - // We only need to scan for a Check file://.../foo.ts$3-6 line that - // corresponds to the documentation block being type-checked. - assert_contains!(skip_restarting_line(&mut stderr_lines).await, "foo.ts$3-6"); - check_alive_then_kill(child); -} - -#[tokio::test] -async fn test_watch_module_graph_error_referrer() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("import './nonexistent.js';"); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - let line1 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line1, "Process started"); - let line2 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line2, "error: Module not found"); - assert_contains!(&line2, "nonexistent.js"); - let line3 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line3, " at "); - assert_contains!(&line3, "file_to_watch.js"); - wait_contains("Process failed", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -// Regression test for https://github.com/denoland/deno/issues/15428. -#[tokio::test] -async fn test_watch_unload_handler_error_on_drop() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - addEventListener("unload", () => { - throw new Error("foo"); - }); - setTimeout(() => { - throw new Error("bar"); - }); - "#, - ); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("Uncaught Error: bar", &mut stderr_lines).await; - wait_contains("Process failed", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_blob_urls_reset() { - let _g = util::http_server(); - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - let file_content = r#" - const prevUrl = localStorage.getItem("url"); - if (prevUrl == null) { - console.log("first run, storing blob url"); - const url = URL.createObjectURL( - new Blob(["export {}"], { type: "application/javascript" }), - ); - await import(url); // this shouldn't insert into the fs module cache - localStorage.setItem("url", url); - } else { - await import(prevUrl) - .then(() => console.log("importing old blob url incorrectly works")) - .catch(() => console.log("importing old blob url correctly failed")); - } - "#; - file_to_watch.write(file_content); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("first run, storing blob url", &mut stdout_lines).await; - wait_contains("finished", &mut stderr_lines).await; - file_to_watch.write(file_content); - wait_contains("importing old blob url correctly failed", &mut stdout_lines) - .await; - wait_contains("finished", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[cfg(unix)] -#[tokio::test] -async fn test_watch_sigint() { - use nix::sys::signal; - use nix::sys::signal::Signal; - use nix::unistd::Pid; - use util::TestContext; - - let context = TestContext::default(); - let t = context.temp_dir(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write(r#"Deno.test("foo", () => {});"#); - let mut child = context - .new_command() - .args_vec(["test", "--watch", &file_to_watch.to_string_lossy()]) - .env("NO_COLOR", "1") - .spawn_with_piped_output(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Test started", &mut stderr_lines).await; - wait_contains("ok | 1 passed | 0 failed", &mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - signal::kill(Pid::from_raw(child.id() as i32), Signal::SIGINT).unwrap(); - let exit_status = child.wait().unwrap(); - assert_eq!(exit_status.code(), Some(130)); -} - -#[tokio::test] -async fn bench_watch_basic() { - let t = TempDir::new(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("bench") - .arg("--watch") - .arg("--no-check") - .arg(t.path()) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bench started" - ); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bench finished" - ); - - let foo_file = t.path().join("foo.js"); - let bar_file = t.path().join("bar.js"); - let foo_bench = t.path().join("foo_bench.js"); - let bar_bench = t.path().join("bar_bench.js"); - foo_file.write("export default function foo() { 1 + 1 }"); - bar_file.write("export default function bar() { 2 + 2 }"); - foo_bench.write("import foo from './foo.js'; Deno.bench('foo bench', foo);"); - bar_bench.write("import bar from './bar.js'; Deno.bench('bar bench', bar);"); - - wait_contains("bar_bench.js", &mut stdout_lines).await; - wait_contains("bar bench", &mut stdout_lines).await; - wait_contains("foo_bench.js", &mut stdout_lines).await; - wait_contains("foo bench", &mut stdout_lines).await; - wait_contains("Bench finished", &mut stderr_lines).await; - - // Change content of the file - foo_bench.write("import foo from './foo.js'; Deno.bench('foo asdf', foo);"); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - if line.contains("foo asdf") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Add bench - let another_test = t.path().join("new_bench.js"); - another_test.write("Deno.bench('another one', () => 3 + 3)"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_test.write("Deno.bench('another one', () => 3 + 3); Deno.bench('another another one', () => 4 + 4)"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - another_test.write("syntax error ^^"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Bench failed"); - - // Then restore the file - another_test.write("Deno.bench('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Test that circular dependencies work fine - foo_file.write("import './bar.js'; export default function foo() { 1 + 1 }"); - bar_file.write("import './foo.js'; export default function bar() { 2 + 2 }"); - check_alive_then_kill(child); -} - -// Regression test for https://github.com/denoland/deno/issues/15465. -#[tokio::test] -async fn run_watch_reload_once() { - let _g = util::http_server(); - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - let file_content = r#" - import { time } from "http://localhost:4545/dynamic_module.ts"; - console.log(time); - "#; - file_to_watch.write(file_content); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--reload") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("finished", &mut stderr_lines).await; - let first_output = next_line(&mut stdout_lines).await.unwrap(); - - file_to_watch.write(file_content); - // The remote dynamic module should not have been reloaded again. - - wait_contains("finished", &mut stderr_lines).await; - let second_output = next_line(&mut stdout_lines).await.unwrap(); - assert_eq!(second_output, first_output); - - check_alive_then_kill(child); -} - -/// Regression test for https://github.com/denoland/deno/issues/18960. Ensures that Deno.serve -/// operates properly after a watch restart. -#[tokio::test] -async fn test_watch_serve() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - let file_content = r#" - console.error("serving"); - await Deno.serve({port: 4600, handler: () => new Response("hello")}); - "#; - file_to_watch.write(file_content); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--allow-net") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Listening on", &mut stdout_lines).await; - // Note that we start serving very quickly, so we specifically want to wait for this message - wait_contains(r#"Watching paths: [""#, &mut stderr_lines).await; - - file_to_watch.write(file_content); - - wait_contains("serving", &mut stderr_lines).await; - wait_contains("Listening on", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_dynamic_imports() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - console.log("Hopefully dynamic import will be watched..."); - await import("./imported.js"); - "#, - ); - let file_to_watch2 = t.path().join("imported.js"); - file_to_watch2.write( - r#" - import "./imported2.js"; - console.log("I'm dynamically imported and I cause restarts!"); - "#, - ); - let file_to_watch3 = t.path().join("imported2.js"); - file_to_watch3.write( - r#" - console.log("I'm statically imported from the dynamic import"); - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--unstable") - .arg("--allow-read") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_contains( - "Hopefully dynamic import will be watched...", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm statically imported from the dynamic import", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm dynamically imported and I cause restarts!", - &mut stdout_lines, - ) - .await; - - wait_for_watcher("imported2.js", &mut stderr_lines).await; - wait_contains("finished", &mut stderr_lines).await; - - file_to_watch3.write( - r#" - console.log("I'm statically imported from the dynamic import and I've changed"); - "#, - ); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains( - "Hopefully dynamic import will be watched...", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm statically imported from the dynamic import and I've changed", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm dynamically imported and I cause restarts!", - &mut stdout_lines, - ) - .await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_inspect() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - console.log("hello world"); - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--inspect") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Debugger listening", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("hello world", &mut stdout_lines).await; - - file_to_watch.write( - r#" - console.log("updated file"); - "#, - ); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Debugger listening", &mut stderr_lines).await; - wait_contains("updated file", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_server() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -globalThis.state = { i: 0 }; - -function bar() { - globalThis.state.i = 0; - console.log("got request", globalThis.state.i); -} - -function handler(_req) { - bar(); - return new Response("Hello world!"); -} - -Deno.serve({ port: 11111 }, handler); -console.log("Listening...") - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("--allow-net") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("Listening...", &mut stdout_lines).await; - - file_to_watch.write( - r#" -globalThis.state = { i: 0 }; - -function bar() { - globalThis.state.i = 0; - console.log("got request1", globalThis.state.i); -} - -function handler(_req) { - bar(); - return new Response("Hello world!"); -} - -Deno.serve({ port: 11111 }, handler); -console.log("Listening...") - "#, - ); - - wait_contains("Failed to reload module", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_jsx() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - return `<h1>Hello</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("5 <h1>Hello</h1>", &mut stdout_lines).await; - - file_to_watch2.write( - r#" -export function foo() { - return `<h1>Hello world</h1>`; -} - "#, - ); - - wait_contains("Replaced changed module", &mut stderr_lines).await; - wait_contains("<h1>Hello world</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_uncaught_error() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - setTimeout(() => { - throw new Error("fail"); - }); - return `<h1>asd1</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("<h1>asd1</h1>", &mut stdout_lines).await; - wait_contains("fail", &mut stderr_lines).await; - - file_to_watch2.write( - r#" -export function foo() { - return `<h1>asd2</h1>`; -} - "#, - ); - - wait_contains("Process failed", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - wait_contains("<h1>asd2</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_unhandled_rejection() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -// deno-lint-ignore require-await -async function rejection() { - throw new Error("boom!"); -} - -let i = 0; -setInterval(() => { - if (i == 3) { - rejection(); - } - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - return `<h1>asd1</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("2 <h1>asd1</h1>", &mut stdout_lines).await; - wait_contains("boom", &mut stderr_lines).await; - - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); - "#, - ); - - wait_contains("Process failed", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - wait_contains("<h1>asd1</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} |