mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(jupyter): support Deno.test() (#20778)
This commit is contained in:
parent
820e93e3e7
commit
ac464ead49
9 changed files with 239 additions and 13 deletions
|
@ -871,6 +871,48 @@ fn repl_with_quiet_flag() {
|
||||||
assert!(err.is_empty());
|
assert!(err.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repl_unit_tests() {
|
||||||
|
util::with_pty(&["repl"], |mut console| {
|
||||||
|
console.write_line(
|
||||||
|
"\
|
||||||
|
console.log('Hello from outside of test!'); \
|
||||||
|
Deno.test('test1', async (t) => { \
|
||||||
|
console.log('Hello from inside of test!'); \
|
||||||
|
await t.step('step1', () => {}); \
|
||||||
|
}); \
|
||||||
|
Deno.test('test2', () => { \
|
||||||
|
throw new Error('some message'); \
|
||||||
|
}); \
|
||||||
|
console.log('Hello again from outside of test!'); \
|
||||||
|
",
|
||||||
|
);
|
||||||
|
|
||||||
|
console.expect("Hello from outside of test!");
|
||||||
|
console.expect("Hello again from outside of test!");
|
||||||
|
// FIXME(nayeemrmn): REPL unit tests don't support output capturing.
|
||||||
|
console.expect("Hello from inside of test!");
|
||||||
|
console.expect("test1 ...");
|
||||||
|
console.expect(" step1 ... ok (");
|
||||||
|
console.expect("test1 ... ok (");
|
||||||
|
console.expect("test2 ... FAILED (");
|
||||||
|
console.expect(" ERRORS ");
|
||||||
|
console.expect("test2 => <anonymous>:7:6");
|
||||||
|
console.expect("error: Error: some message");
|
||||||
|
console.expect(" at <anonymous>:8:9");
|
||||||
|
console.expect(" FAILURES ");
|
||||||
|
console.expect("test2 => <anonymous>:7:6");
|
||||||
|
console.expect("FAILED | 1 passed (1 step) | 1 failed (");
|
||||||
|
console.expect("undefined");
|
||||||
|
|
||||||
|
console.write_line("Deno.test('test2', () => {});");
|
||||||
|
|
||||||
|
console.expect("test2 ... ok (");
|
||||||
|
console.expect("ok | 1 passed | 0 failed (");
|
||||||
|
console.expect("undefined");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn npm_packages() {
|
fn npm_packages() {
|
||||||
let mut env_vars = util::env_vars_for_npm_tests();
|
let mut env_vars = util::env_vars_for_npm_tests();
|
||||||
|
|
|
@ -628,6 +628,58 @@
|
||||||
"console.table([1, 2, 3])"
|
"console.table([1, 2, 3])"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "9f38f1eb",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Unit Tests With `Deno.test()`"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"id": "b33808fd",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"passing test ... \u001b[0m\u001b[32mok\u001b[0m \u001b[0m\u001b[38;5;245m(1ms)\u001b[0m\n",
|
||||||
|
"passing test with steps ...\n",
|
||||||
|
" step 1 ... \u001b[0m\u001b[32mok\u001b[0m \u001b[0m\u001b[38;5;245m(0ms)\u001b[0m\n",
|
||||||
|
" step 2 ... \u001b[0m\u001b[32mok\u001b[0m \u001b[0m\u001b[38;5;245m(0ms)\u001b[0m\n",
|
||||||
|
"passing test with steps ... \u001b[0m\u001b[32mok\u001b[0m \u001b[0m\u001b[38;5;245m(1ms)\u001b[0m\n",
|
||||||
|
"failing test ... \u001b[0m\u001b[31mFAILED\u001b[0m \u001b[0m\u001b[38;5;245m(1ms)\u001b[0m\n",
|
||||||
|
"\n",
|
||||||
|
"\u001b[0m\u001b[1m\u001b[37m\u001b[41m ERRORS \u001b[0m\n",
|
||||||
|
"\n",
|
||||||
|
"failing test \u001b[0m\u001b[38;5;245m=> <anonymous>:7:6\u001b[0m\n",
|
||||||
|
"\u001b[0m\u001b[1m\u001b[31merror\u001b[0m: Error: some message\n",
|
||||||
|
" at \u001b[0m\u001b[36m<anonymous>\u001b[0m:\u001b[0m\u001b[33m8\u001b[0m:\u001b[0m\u001b[33m9\u001b[0m\n",
|
||||||
|
"\n",
|
||||||
|
"\u001b[0m\u001b[1m\u001b[37m\u001b[41m FAILURES \u001b[0m\n",
|
||||||
|
"\n",
|
||||||
|
"failing test \u001b[0m\u001b[38;5;245m=> <anonymous>:7:6\u001b[0m\n",
|
||||||
|
"\n",
|
||||||
|
"\u001b[0m\u001b[31mFAILED\u001b[0m | 2 passed (2 steps) | 1 failed \u001b[0m\u001b[38;5;245m(0ms)\u001b[0m\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"Deno.test(\"passing test\", () => {});\n",
|
||||||
|
"\n",
|
||||||
|
"Deno.test(\"passing test with steps\", async (t) => {\n",
|
||||||
|
" await t.step(\"step 1\", () => {});\n",
|
||||||
|
" await t.step(\"step 2\", () => {});\n",
|
||||||
|
"});\n",
|
||||||
|
"\n",
|
||||||
|
"Deno.test(\"failing test\", () => {\n",
|
||||||
|
" throw new Error(\"some message\");\n",
|
||||||
|
"});\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "8822eed9-a801-4c1b-81c0-00e4ff180f40",
|
"id": "8822eed9-a801-4c1b-81c0-00e4ff180f40",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::args::Flags;
|
use crate::args::Flags;
|
||||||
use crate::args::JupyterFlags;
|
use crate::args::JupyterFlags;
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
|
use crate::tools::jupyter::server::StdioMsg;
|
||||||
use crate::tools::repl;
|
use crate::tools::repl;
|
||||||
use crate::util::logger;
|
use crate::util::logger;
|
||||||
use crate::CliFactory;
|
use crate::CliFactory;
|
||||||
|
@ -12,9 +13,17 @@ use deno_core::located_script_name;
|
||||||
use deno_core::resolve_url_or_path;
|
use deno_core::resolve_url_or_path;
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use deno_runtime::deno_io::Stdio;
|
||||||
|
use deno_runtime::deno_io::StdioPipe;
|
||||||
use deno_runtime::permissions::Permissions;
|
use deno_runtime::permissions::Permissions;
|
||||||
use deno_runtime::permissions::PermissionsContainer;
|
use deno_runtime::permissions::PermissionsContainer;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::mpsc::unbounded_channel;
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
|
use super::test::reporters::PrettyTestReporter;
|
||||||
|
use super::test::TestEvent;
|
||||||
|
use super::test::TestEventSender;
|
||||||
|
|
||||||
mod install;
|
mod install;
|
||||||
pub(crate) mod jupyter_msg;
|
pub(crate) mod jupyter_msg;
|
||||||
|
@ -71,13 +80,25 @@ pub async fn kernel(
|
||||||
connection_filepath
|
connection_filepath
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
let (test_event_sender, test_event_receiver) =
|
||||||
|
unbounded_channel::<TestEvent>();
|
||||||
|
let test_event_sender = TestEventSender::new(test_event_sender);
|
||||||
|
let stdout = StdioPipe::File(test_event_sender.stdout());
|
||||||
|
let stderr = StdioPipe::File(test_event_sender.stderr());
|
||||||
let mut worker = worker_factory
|
let mut worker = worker_factory
|
||||||
.create_custom_worker(
|
.create_custom_worker(
|
||||||
main_module.clone(),
|
main_module.clone(),
|
||||||
permissions,
|
permissions,
|
||||||
vec![ops::jupyter::deno_jupyter::init_ops(stdio_tx)],
|
vec![
|
||||||
Default::default(),
|
ops::jupyter::deno_jupyter::init_ops(stdio_tx.clone()),
|
||||||
|
ops::testing::deno_test::init_ops(test_event_sender.clone()),
|
||||||
|
],
|
||||||
|
// FIXME(nayeemrmn): Test output capturing currently doesn't work.
|
||||||
|
Stdio {
|
||||||
|
stdin: StdioPipe::Inherit,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
worker.setup_repl().await?;
|
worker.setup_repl().await?;
|
||||||
|
@ -86,9 +107,35 @@ pub async fn kernel(
|
||||||
"Deno[Deno.internal].enableJupyter();",
|
"Deno[Deno.internal].enableJupyter();",
|
||||||
)?;
|
)?;
|
||||||
let worker = worker.into_main_worker();
|
let worker = worker.into_main_worker();
|
||||||
let repl_session =
|
let mut repl_session = repl::ReplSession::initialize(
|
||||||
repl::ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
|
cli_options,
|
||||||
.await?;
|
npm_resolver,
|
||||||
|
resolver,
|
||||||
|
worker,
|
||||||
|
main_module,
|
||||||
|
test_event_sender,
|
||||||
|
test_event_receiver,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
struct TestWriter(UnboundedSender<StdioMsg>);
|
||||||
|
impl std::io::Write for TestWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
self
|
||||||
|
.0
|
||||||
|
.send(StdioMsg::Stdout(String::from_utf8_lossy(buf).into_owned()))
|
||||||
|
.ok();
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repl_session.set_test_reporter_factory(Box::new(move || {
|
||||||
|
Box::new(
|
||||||
|
PrettyTestReporter::new(false, true, false, true)
|
||||||
|
.with_writer(Box::new(TestWriter(stdio_tx.clone()))),
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
server::JupyterServer::start(spec, stdio_rx, repl_session).await?;
|
server::JupyterServer::start(spec, stdio_rx, repl_session).await?;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use deno_core::unsync::spawn_blocking;
|
||||||
use deno_runtime::permissions::Permissions;
|
use deno_runtime::permissions::Permissions;
|
||||||
use deno_runtime::permissions::PermissionsContainer;
|
use deno_runtime::permissions::PermissionsContainer;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
|
use tokio::sync::mpsc::unbounded_channel;
|
||||||
|
|
||||||
pub(crate) mod cdp;
|
pub(crate) mod cdp;
|
||||||
mod channel;
|
mod channel;
|
||||||
|
@ -28,6 +29,9 @@ pub use session::EvaluationOutput;
|
||||||
pub use session::ReplSession;
|
pub use session::ReplSession;
|
||||||
pub use session::REPL_INTERNALS_NAME;
|
pub use session::REPL_INTERNALS_NAME;
|
||||||
|
|
||||||
|
use super::test::TestEvent;
|
||||||
|
use super::test::TestEventSender;
|
||||||
|
|
||||||
#[allow(clippy::await_holding_refcell_ref)]
|
#[allow(clippy::await_holding_refcell_ref)]
|
||||||
async fn read_line_and_poll(
|
async fn read_line_and_poll(
|
||||||
repl_session: &mut ReplSession,
|
repl_session: &mut ReplSession,
|
||||||
|
@ -114,15 +118,31 @@ pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result<i32, AnyError> {
|
||||||
.deno_dir()
|
.deno_dir()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|dir| dir.repl_history_file_path());
|
.and_then(|dir| dir.repl_history_file_path());
|
||||||
|
let (test_event_sender, test_event_receiver) =
|
||||||
|
unbounded_channel::<TestEvent>();
|
||||||
|
let test_event_sender = TestEventSender::new(test_event_sender);
|
||||||
let mut worker = worker_factory
|
let mut worker = worker_factory
|
||||||
.create_main_worker(main_module, permissions)
|
.create_custom_worker(
|
||||||
|
main_module.clone(),
|
||||||
|
permissions,
|
||||||
|
vec![crate::ops::testing::deno_test::init_ops(
|
||||||
|
test_event_sender.clone(),
|
||||||
|
)],
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
worker.setup_repl().await?;
|
worker.setup_repl().await?;
|
||||||
let worker = worker.into_main_worker();
|
let worker = worker.into_main_worker();
|
||||||
let mut repl_session =
|
let mut repl_session = ReplSession::initialize(
|
||||||
ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
|
cli_options,
|
||||||
.await?;
|
npm_resolver,
|
||||||
|
resolver,
|
||||||
|
worker,
|
||||||
|
main_module,
|
||||||
|
test_event_sender,
|
||||||
|
test_event_receiver,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let mut rustyline_channel = rustyline_channel();
|
let mut rustyline_channel = rustyline_channel();
|
||||||
|
|
||||||
let helper = EditorHelper {
|
let helper = EditorHelper {
|
||||||
|
|
|
@ -9,6 +9,13 @@ use crate::colors;
|
||||||
use crate::lsp::ReplLanguageServer;
|
use crate::lsp::ReplLanguageServer;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::resolver::CliGraphResolver;
|
use crate::resolver::CliGraphResolver;
|
||||||
|
use crate::tools::test::report_tests;
|
||||||
|
use crate::tools::test::reporters::PrettyTestReporter;
|
||||||
|
use crate::tools::test::reporters::TestReporter;
|
||||||
|
use crate::tools::test::run_tests_for_worker;
|
||||||
|
use crate::tools::test::worker_has_tests;
|
||||||
|
use crate::tools::test::TestEvent;
|
||||||
|
use crate::tools::test::TestEventSender;
|
||||||
|
|
||||||
use deno_ast::swc::ast as swc_ast;
|
use deno_ast::swc::ast as swc_ast;
|
||||||
use deno_ast::swc::visit::noop_visit_type;
|
use deno_ast::swc::visit::noop_visit_type;
|
||||||
|
@ -23,6 +30,7 @@ use deno_core::futures::FutureExt;
|
||||||
use deno_core::futures::StreamExt;
|
use deno_core::futures::StreamExt;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
|
use deno_core::unsync::spawn;
|
||||||
use deno_core::LocalInspectorSession;
|
use deno_core::LocalInspectorSession;
|
||||||
use deno_graph::source::Resolver;
|
use deno_graph::source::Resolver;
|
||||||
use deno_runtime::worker::MainWorker;
|
use deno_runtime::worker::MainWorker;
|
||||||
|
@ -131,6 +139,11 @@ pub struct ReplSession {
|
||||||
pub language_server: ReplLanguageServer,
|
pub language_server: ReplLanguageServer,
|
||||||
pub notifications: Rc<RefCell<UnboundedReceiver<Value>>>,
|
pub notifications: Rc<RefCell<UnboundedReceiver<Value>>>,
|
||||||
referrer: ModuleSpecifier,
|
referrer: ModuleSpecifier,
|
||||||
|
main_module: ModuleSpecifier,
|
||||||
|
test_reporter_factory: Box<dyn Fn() -> Box<dyn TestReporter>>,
|
||||||
|
test_event_sender: TestEventSender,
|
||||||
|
/// This is only optional because it's temporarily taken when evaluating.
|
||||||
|
test_event_receiver: Option<tokio::sync::mpsc::UnboundedReceiver<TestEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReplSession {
|
impl ReplSession {
|
||||||
|
@ -139,6 +152,9 @@ impl ReplSession {
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
resolver: Arc<CliGraphResolver>,
|
resolver: Arc<CliGraphResolver>,
|
||||||
mut worker: MainWorker,
|
mut worker: MainWorker,
|
||||||
|
main_module: ModuleSpecifier,
|
||||||
|
test_event_sender: TestEventSender,
|
||||||
|
test_event_receiver: tokio::sync::mpsc::UnboundedReceiver<TestEvent>,
|
||||||
) -> Result<Self, AnyError> {
|
) -> Result<Self, AnyError> {
|
||||||
let language_server = ReplLanguageServer::new_initialized().await?;
|
let language_server = ReplLanguageServer::new_initialized().await?;
|
||||||
let mut session = worker.create_inspector_session().await;
|
let mut session = worker.create_inspector_session().await;
|
||||||
|
@ -189,6 +205,12 @@ impl ReplSession {
|
||||||
language_server,
|
language_server,
|
||||||
referrer,
|
referrer,
|
||||||
notifications: Rc::new(RefCell::new(notification_rx)),
|
notifications: Rc::new(RefCell::new(notification_rx)),
|
||||||
|
test_reporter_factory: Box::new(|| {
|
||||||
|
Box::new(PrettyTestReporter::new(false, true, false, true))
|
||||||
|
}),
|
||||||
|
main_module,
|
||||||
|
test_event_sender,
|
||||||
|
test_event_receiver: Some(test_event_receiver),
|
||||||
};
|
};
|
||||||
|
|
||||||
// inject prelude
|
// inject prelude
|
||||||
|
@ -197,6 +219,13 @@ impl ReplSession {
|
||||||
Ok(repl_session)
|
Ok(repl_session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_test_reporter_factory(
|
||||||
|
&mut self,
|
||||||
|
f: Box<dyn Fn() -> Box<dyn TestReporter>>,
|
||||||
|
) {
|
||||||
|
self.test_reporter_factory = f;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn closing(&mut self) -> Result<bool, AnyError> {
|
pub async fn closing(&mut self) -> Result<bool, AnyError> {
|
||||||
let closed = self
|
let closed = self
|
||||||
.evaluate_expression("(this.closed)")
|
.evaluate_expression("(this.closed)")
|
||||||
|
@ -325,7 +354,7 @@ impl ReplSession {
|
||||||
|
|
||||||
// If that fails, we retry it without wrapping in parens letting the error bubble up to the
|
// If that fails, we retry it without wrapping in parens letting the error bubble up to the
|
||||||
// user if it is still an error.
|
// user if it is still an error.
|
||||||
if wrapped_line != line
|
let result = if wrapped_line != line
|
||||||
&& (evaluate_response.is_err()
|
&& (evaluate_response.is_err()
|
||||||
|| evaluate_response
|
|| evaluate_response
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -337,7 +366,29 @@ impl ReplSession {
|
||||||
self.evaluate_ts_expression(line).await
|
self.evaluate_ts_expression(line).await
|
||||||
} else {
|
} else {
|
||||||
evaluate_response
|
evaluate_response
|
||||||
|
};
|
||||||
|
|
||||||
|
if worker_has_tests(&mut self.worker) {
|
||||||
|
let report_tests_handle = spawn(report_tests(
|
||||||
|
self.test_event_receiver.take().unwrap(),
|
||||||
|
(self.test_reporter_factory)(),
|
||||||
|
));
|
||||||
|
run_tests_for_worker(
|
||||||
|
&mut self.worker,
|
||||||
|
&self.main_module,
|
||||||
|
&Default::default(),
|
||||||
|
&Default::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
self
|
||||||
|
.test_event_sender
|
||||||
|
.send(TestEvent::ForceEndReport)
|
||||||
|
.unwrap();
|
||||||
|
self.test_event_receiver = Some(report_tests_handle.await.unwrap().1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_last_thrown_error(
|
async fn set_last_thrown_error(
|
||||||
|
|
|
@ -473,6 +473,12 @@ pub async fn test_specifier(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn worker_has_tests(worker: &mut MainWorker) -> bool {
|
||||||
|
let state_rc = worker.js_runtime.op_state();
|
||||||
|
let state = state_rc.borrow();
|
||||||
|
!state.borrow::<ops::testing::TestContainer>().0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run_tests_for_worker(
|
pub async fn run_tests_for_worker(
|
||||||
worker: &mut MainWorker,
|
worker: &mut MainWorker,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
|
|
@ -215,7 +215,7 @@ pub(super) fn report_summary(
|
||||||
|
|
||||||
writeln!(
|
writeln!(
|
||||||
writer,
|
writer,
|
||||||
"\n{} | {} {}\n",
|
"\n{} | {} {}",
|
||||||
status,
|
status,
|
||||||
summary_result,
|
summary_result,
|
||||||
colors::gray(format!("({})", display::human_elapsed(elapsed.as_millis()))),
|
colors::gray(format!("({})", display::human_elapsed(elapsed.as_millis()))),
|
||||||
|
|
|
@ -190,6 +190,7 @@ impl TestReporter for DotTestReporter {
|
||||||
&self.summary,
|
&self.summary,
|
||||||
elapsed,
|
elapsed,
|
||||||
);
|
);
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_sigint(
|
fn report_sigint(
|
||||||
|
|
|
@ -43,6 +43,10 @@ impl PrettyTestReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_writer(self, writer: Box<dyn std::io::Write>) -> Self {
|
||||||
|
Self { writer, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
fn force_report_wait(&mut self, description: &TestDescription) {
|
fn force_report_wait(&mut self, description: &TestDescription) {
|
||||||
if !self.in_new_line {
|
if !self.in_new_line {
|
||||||
writeln!(&mut self.writer).unwrap();
|
writeln!(&mut self.writer).unwrap();
|
||||||
|
@ -368,6 +372,9 @@ impl TestReporter for PrettyTestReporter {
|
||||||
_test_steps: &IndexMap<usize, TestStepDescription>,
|
_test_steps: &IndexMap<usize, TestStepDescription>,
|
||||||
) {
|
) {
|
||||||
common::report_summary(&mut self.writer, &self.cwd, &self.summary, elapsed);
|
common::report_summary(&mut self.writer, &self.cwd, &self.summary, elapsed);
|
||||||
|
if !self.repl {
|
||||||
|
writeln!(&mut self.writer).unwrap();
|
||||||
|
}
|
||||||
self.in_new_line = true;
|
self.in_new_line = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue