mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 20:59:10 +00:00

This commit adds "--terse" flag to "deno test" that makes testing reporter output one character per test case instead of one line per case. This is an unstable feature.
818 lines
21 KiB
Rust
818 lines
21 KiB
Rust
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use crate::ast;
|
|
use crate::colors;
|
|
use crate::create_main_worker;
|
|
use crate::file_fetcher::File;
|
|
use crate::fs_util::collect_files;
|
|
use crate::fs_util::normalize_path;
|
|
use crate::media_type::MediaType;
|
|
use crate::module_graph;
|
|
use crate::program_state::ProgramState;
|
|
use crate::tokio_util;
|
|
use crate::tools::coverage::CoverageCollector;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::futures::future;
|
|
use deno_core::futures::stream;
|
|
use deno_core::futures::FutureExt;
|
|
use deno_core::futures::StreamExt;
|
|
use deno_core::located_script_name;
|
|
use deno_core::serde_json::json;
|
|
use deno_core::url::Url;
|
|
use deno_core::ModuleSpecifier;
|
|
use deno_runtime::permissions::Permissions;
|
|
use rand::rngs::SmallRng;
|
|
use rand::seq::SliceRandom;
|
|
use rand::SeedableRng;
|
|
use regex::Regex;
|
|
use serde::Deserialize;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::sync::mpsc::channel;
|
|
use std::sync::mpsc::Sender;
|
|
use std::sync::Arc;
|
|
use std::time::Instant;
|
|
use swc_common::comments::CommentKind;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum TestResult {
|
|
Ok,
|
|
Ignored,
|
|
Failed(String),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
#[serde(tag = "kind", content = "data", rename_all = "camelCase")]
|
|
pub enum TestMessage {
|
|
Plan {
|
|
pending: usize,
|
|
filtered: usize,
|
|
only: bool,
|
|
},
|
|
Wait {
|
|
name: String,
|
|
},
|
|
Result {
|
|
name: String,
|
|
duration: usize,
|
|
result: TestResult,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct TestEvent {
|
|
pub origin: String,
|
|
pub message: TestMessage,
|
|
}
|
|
|
|
trait TestReporter {
|
|
fn visit_event(&mut self, event: TestEvent);
|
|
fn done(&mut self);
|
|
}
|
|
|
|
struct PrettyTestReporter {
|
|
time: Instant,
|
|
failed: usize,
|
|
filtered_out: usize,
|
|
ignored: usize,
|
|
passed: usize,
|
|
measured: usize,
|
|
pending: usize,
|
|
failures: Vec<(String, String)>,
|
|
concurrent: bool,
|
|
}
|
|
|
|
impl PrettyTestReporter {
|
|
fn new(concurrent: bool) -> PrettyTestReporter {
|
|
PrettyTestReporter {
|
|
time: Instant::now(),
|
|
failed: 0,
|
|
filtered_out: 0,
|
|
ignored: 0,
|
|
passed: 0,
|
|
measured: 0,
|
|
pending: 0,
|
|
failures: Vec::new(),
|
|
concurrent,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TestReporter for PrettyTestReporter {
|
|
fn visit_event(&mut self, event: TestEvent) {
|
|
match &event.message {
|
|
TestMessage::Plan {
|
|
pending,
|
|
filtered,
|
|
only: _,
|
|
} => {
|
|
if *pending == 1 {
|
|
println!("running {} test from {}", pending, event.origin);
|
|
} else {
|
|
println!("running {} tests from {}", pending, event.origin);
|
|
}
|
|
|
|
self.pending += pending;
|
|
self.filtered_out += filtered;
|
|
}
|
|
|
|
TestMessage::Wait { name } => {
|
|
if !self.concurrent {
|
|
print!("test {} ...", name);
|
|
}
|
|
}
|
|
|
|
TestMessage::Result {
|
|
name,
|
|
duration,
|
|
result,
|
|
} => {
|
|
self.pending -= 1;
|
|
|
|
if self.concurrent {
|
|
print!("test {} ...", name);
|
|
}
|
|
|
|
match result {
|
|
TestResult::Ok => {
|
|
println!(
|
|
" {} {}",
|
|
colors::green("ok"),
|
|
colors::gray(format!("({}ms)", duration))
|
|
);
|
|
|
|
self.passed += 1;
|
|
}
|
|
TestResult::Ignored => {
|
|
println!(
|
|
" {} {}",
|
|
colors::yellow("ignored"),
|
|
colors::gray(format!("({}ms)", duration))
|
|
);
|
|
|
|
self.ignored += 1;
|
|
}
|
|
TestResult::Failed(error) => {
|
|
println!(
|
|
" {} {}",
|
|
colors::red("FAILED"),
|
|
colors::gray(format!("({}ms)", duration))
|
|
);
|
|
|
|
self.failed += 1;
|
|
self.failures.push((name.to_string(), error.to_string()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn done(&mut self) {
|
|
if !self.failures.is_empty() {
|
|
println!("\nfailures:\n");
|
|
for (name, error) in &self.failures {
|
|
println!("{}", name);
|
|
println!("{}", error);
|
|
println!();
|
|
}
|
|
|
|
println!("failures:\n");
|
|
for (name, _) in &self.failures {
|
|
println!("\t{}", name);
|
|
}
|
|
}
|
|
|
|
let status = if self.pending > 0 || !self.failures.is_empty() {
|
|
colors::red("FAILED").to_string()
|
|
} else {
|
|
colors::green("ok").to_string()
|
|
};
|
|
|
|
println!(
|
|
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
|
|
status,
|
|
self.passed,
|
|
self.failed,
|
|
self.ignored,
|
|
self.measured,
|
|
self.filtered_out,
|
|
colors::gray(format!("({}ms)", self.time.elapsed().as_millis())),
|
|
);
|
|
}
|
|
}
|
|
|
|
struct TerseTestReporter {
|
|
time: Instant,
|
|
failed: usize,
|
|
filtered_out: usize,
|
|
ignored: usize,
|
|
passed: usize,
|
|
measured: usize,
|
|
pending: usize,
|
|
failures: Vec<(String, String)>,
|
|
}
|
|
|
|
impl TerseTestReporter {
|
|
fn new() -> TerseTestReporter {
|
|
TerseTestReporter {
|
|
time: Instant::now(),
|
|
failed: 0,
|
|
filtered_out: 0,
|
|
ignored: 0,
|
|
passed: 0,
|
|
measured: 0,
|
|
pending: 0,
|
|
failures: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TestReporter for TerseTestReporter {
|
|
fn visit_event(&mut self, event: TestEvent) {
|
|
match &event.message {
|
|
TestMessage::Plan {
|
|
pending,
|
|
filtered,
|
|
only: _,
|
|
} => {
|
|
if *pending == 1 {
|
|
println!("running {} test from {}", pending, event.origin);
|
|
} else {
|
|
println!("running {} tests from {}", pending, event.origin);
|
|
}
|
|
|
|
self.pending += pending;
|
|
self.filtered_out += filtered;
|
|
}
|
|
|
|
TestMessage::Result {
|
|
name,
|
|
duration: _,
|
|
result,
|
|
} => {
|
|
self.pending -= 1;
|
|
|
|
match result {
|
|
TestResult::Ok => {
|
|
print!("{}", colors::green("."),);
|
|
|
|
self.passed += 1;
|
|
}
|
|
TestResult::Ignored => {
|
|
print!("{}", colors::yellow("i"),);
|
|
|
|
self.ignored += 1;
|
|
}
|
|
TestResult::Failed(error) => {
|
|
print!("{}", colors::red("F"),);
|
|
|
|
self.failed += 1;
|
|
self.failures.push((name.to_string(), error.to_string()));
|
|
}
|
|
}
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn done(&mut self) {
|
|
if !self.failures.is_empty() {
|
|
println!("\nfailures:\n");
|
|
for (name, error) in &self.failures {
|
|
println!("{}", name);
|
|
println!("{}", error);
|
|
println!();
|
|
}
|
|
|
|
println!("failures:\n");
|
|
for (name, _) in &self.failures {
|
|
println!("\t{}", name);
|
|
}
|
|
}
|
|
|
|
let status = if self.pending > 0 || !self.failures.is_empty() {
|
|
colors::red("FAILED").to_string()
|
|
} else {
|
|
colors::green("ok").to_string()
|
|
};
|
|
|
|
println!(
|
|
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
|
|
status,
|
|
self.passed,
|
|
self.failed,
|
|
self.ignored,
|
|
self.measured,
|
|
self.filtered_out,
|
|
colors::gray(format!("({}ms)", self.time.elapsed().as_millis())),
|
|
);
|
|
}
|
|
}
|
|
|
|
enum TestReporterKind {
|
|
Pretty,
|
|
Terse,
|
|
}
|
|
|
|
fn create_reporter(
|
|
kind: TestReporterKind,
|
|
concurrent: bool,
|
|
) -> Box<dyn TestReporter + Send> {
|
|
match kind {
|
|
TestReporterKind::Pretty => Box::new(PrettyTestReporter::new(concurrent)),
|
|
TestReporterKind::Terse => Box::new(TerseTestReporter::new()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn is_supported(p: &Path) -> bool {
|
|
use std::path::Component;
|
|
if let Some(Component::Normal(basename_os_str)) = p.components().next_back() {
|
|
let basename = basename_os_str.to_string_lossy();
|
|
basename.ends_with("_test.ts")
|
|
|| basename.ends_with("_test.tsx")
|
|
|| basename.ends_with("_test.js")
|
|
|| basename.ends_with("_test.mjs")
|
|
|| basename.ends_with("_test.jsx")
|
|
|| basename.ends_with(".test.ts")
|
|
|| basename.ends_with(".test.tsx")
|
|
|| basename.ends_with(".test.js")
|
|
|| basename.ends_with(".test.mjs")
|
|
|| basename.ends_with(".test.jsx")
|
|
|| basename == "test.ts"
|
|
|| basename == "test.tsx"
|
|
|| basename == "test.js"
|
|
|| basename == "test.mjs"
|
|
|| basename == "test.jsx"
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn is_remote_url(module_url: &str) -> bool {
|
|
let lower = module_url.to_lowercase();
|
|
lower.starts_with("http://") || lower.starts_with("https://")
|
|
}
|
|
|
|
pub fn collect_test_module_specifiers<P>(
|
|
include: Vec<String>,
|
|
root_path: &Path,
|
|
predicate: P,
|
|
) -> Result<Vec<Url>, AnyError>
|
|
where
|
|
P: Fn(&Path) -> bool,
|
|
{
|
|
let (include_paths, include_urls): (Vec<String>, Vec<String>) =
|
|
include.into_iter().partition(|n| !is_remote_url(n));
|
|
let mut prepared = vec![];
|
|
|
|
for path in include_paths {
|
|
let p = normalize_path(&root_path.join(path));
|
|
if p.is_dir() {
|
|
let test_files = collect_files(&[p], &[], &predicate).unwrap();
|
|
let test_files_as_urls = test_files
|
|
.iter()
|
|
.map(|f| Url::from_file_path(f).unwrap())
|
|
.collect::<Vec<Url>>();
|
|
prepared.extend(test_files_as_urls);
|
|
} else {
|
|
let url = Url::from_file_path(p).unwrap();
|
|
prepared.push(url);
|
|
}
|
|
}
|
|
|
|
for remote_url in include_urls {
|
|
let url = Url::parse(&remote_url)?;
|
|
prepared.push(url);
|
|
}
|
|
|
|
Ok(prepared)
|
|
}
|
|
|
|
pub async fn run_test_file(
|
|
program_state: Arc<ProgramState>,
|
|
main_module: ModuleSpecifier,
|
|
test_module: ModuleSpecifier,
|
|
permissions: Permissions,
|
|
channel: Sender<TestEvent>,
|
|
) -> Result<(), AnyError> {
|
|
let mut worker =
|
|
create_main_worker(&program_state, main_module.clone(), permissions, true);
|
|
|
|
{
|
|
let js_runtime = &mut worker.js_runtime;
|
|
js_runtime
|
|
.op_state()
|
|
.borrow_mut()
|
|
.put::<Sender<TestEvent>>(channel.clone());
|
|
}
|
|
|
|
let mut maybe_coverage_collector = if let Some(ref coverage_dir) =
|
|
program_state.coverage_dir
|
|
{
|
|
let session = worker.create_inspector_session().await;
|
|
let coverage_dir = PathBuf::from(coverage_dir);
|
|
let mut coverage_collector = CoverageCollector::new(coverage_dir, session);
|
|
worker
|
|
.with_event_loop(coverage_collector.start_collecting().boxed_local())
|
|
.await?;
|
|
|
|
Some(coverage_collector)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let execute_result = worker.execute_module(&main_module).await;
|
|
execute_result?;
|
|
|
|
worker.execute_script(
|
|
&located_script_name!(),
|
|
"window.dispatchEvent(new Event('load'))",
|
|
)?;
|
|
|
|
let execute_result = worker.execute_module(&test_module).await;
|
|
execute_result?;
|
|
|
|
worker
|
|
.run_event_loop(maybe_coverage_collector.is_none())
|
|
.await?;
|
|
worker.execute_script(
|
|
&located_script_name!(),
|
|
"window.dispatchEvent(new Event('unload'))",
|
|
)?;
|
|
|
|
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
|
|
worker
|
|
.with_event_loop(coverage_collector.stop_collecting().boxed_local())
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Runs tests.
|
|
///
|
|
/// Returns a boolean indicating whether the tests failed.
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn run_tests(
|
|
program_state: Arc<ProgramState>,
|
|
permissions: Permissions,
|
|
lib: module_graph::TypeLib,
|
|
doc_modules: Vec<ModuleSpecifier>,
|
|
test_modules: Vec<ModuleSpecifier>,
|
|
no_run: bool,
|
|
fail_fast: bool,
|
|
quiet: bool,
|
|
terse: bool,
|
|
allow_none: bool,
|
|
filter: Option<String>,
|
|
shuffle: Option<u64>,
|
|
concurrent_jobs: usize,
|
|
) -> Result<bool, AnyError> {
|
|
let test_modules = if let Some(seed) = shuffle {
|
|
let mut rng = SmallRng::seed_from_u64(seed);
|
|
let mut test_modules = test_modules.clone();
|
|
test_modules.sort();
|
|
test_modules.shuffle(&mut rng);
|
|
test_modules
|
|
} else {
|
|
test_modules
|
|
};
|
|
|
|
if !doc_modules.is_empty() {
|
|
let mut test_programs = Vec::new();
|
|
|
|
let blocks_regex = Regex::new(r"```([^\n]*)\n([\S\s]*?)```")?;
|
|
let lines_regex = Regex::new(r"(?:\* ?)(?:\# ?)?(.*)")?;
|
|
|
|
for specifier in &doc_modules {
|
|
let mut fetch_permissions = Permissions::allow_all();
|
|
let file = program_state
|
|
.file_fetcher
|
|
.fetch(&specifier, &mut fetch_permissions)
|
|
.await?;
|
|
|
|
let parsed_module =
|
|
ast::parse(&file.specifier.as_str(), &file.source, &file.media_type)?;
|
|
|
|
let mut comments = parsed_module.get_comments();
|
|
comments.sort_by_key(|comment| {
|
|
let location = parsed_module.get_location(&comment.span);
|
|
location.line
|
|
});
|
|
|
|
for comment in comments {
|
|
if comment.kind != CommentKind::Block || !comment.text.starts_with('*')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for block in blocks_regex.captures_iter(&comment.text) {
|
|
let body = block.get(2).unwrap();
|
|
let text = body.as_str();
|
|
|
|
// TODO(caspervonb) generate an inline source map
|
|
let mut source = String::new();
|
|
for line in lines_regex.captures_iter(&text) {
|
|
let text = line.get(1).unwrap();
|
|
source.push_str(&format!("{}\n", text.as_str()));
|
|
}
|
|
|
|
source.push_str("export {};");
|
|
|
|
let element = block.get(0).unwrap();
|
|
let span = comment
|
|
.span
|
|
.from_inner_byte_pos(element.start(), element.end());
|
|
let location = parsed_module.get_location(&span);
|
|
|
|
let specifier = deno_core::resolve_url_or_path(&format!(
|
|
"{}${}-{}",
|
|
location.filename,
|
|
location.line,
|
|
location.line + element.as_str().split('\n').count(),
|
|
))?;
|
|
|
|
let file = File {
|
|
local: specifier.to_file_path().unwrap(),
|
|
maybe_types: None,
|
|
media_type: MediaType::TypeScript, // media_type.clone(),
|
|
source: source.clone(),
|
|
specifier: specifier.clone(),
|
|
};
|
|
|
|
program_state.file_fetcher.insert_cached(file.clone());
|
|
test_programs.push(file.specifier.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
program_state
|
|
.prepare_module_graph(
|
|
test_programs.clone(),
|
|
lib.clone(),
|
|
Permissions::allow_all(),
|
|
permissions.clone(),
|
|
program_state.maybe_import_map.clone(),
|
|
)
|
|
.await?;
|
|
} else if test_modules.is_empty() {
|
|
println!("No matching test modules found");
|
|
if !allow_none {
|
|
std::process::exit(1);
|
|
}
|
|
|
|
return Ok(false);
|
|
}
|
|
|
|
program_state
|
|
.prepare_module_graph(
|
|
test_modules.clone(),
|
|
lib.clone(),
|
|
Permissions::allow_all(),
|
|
permissions.clone(),
|
|
program_state.maybe_import_map.clone(),
|
|
)
|
|
.await?;
|
|
|
|
if no_run {
|
|
return Ok(false);
|
|
}
|
|
|
|
// Because scripts, and therefore worker.execute cannot detect unresolved promises at the moment
|
|
// we generate a module for the actual test execution.
|
|
let test_options = json!({
|
|
"disableLog": quiet,
|
|
"filter": filter,
|
|
"shuffle": shuffle,
|
|
});
|
|
|
|
let test_module = deno_core::resolve_path("$deno$test.js")?;
|
|
let test_source =
|
|
format!("await Deno[Deno.internal].runTests({});", test_options);
|
|
let test_file = File {
|
|
local: test_module.to_file_path().unwrap(),
|
|
maybe_types: None,
|
|
media_type: MediaType::JavaScript,
|
|
source: test_source.clone(),
|
|
specifier: test_module.clone(),
|
|
};
|
|
|
|
program_state.file_fetcher.insert_cached(test_file);
|
|
|
|
let (sender, receiver) = channel::<TestEvent>();
|
|
|
|
let join_handles = test_modules.iter().map(move |main_module| {
|
|
let program_state = program_state.clone();
|
|
let main_module = main_module.clone();
|
|
let test_module = test_module.clone();
|
|
let permissions = permissions.clone();
|
|
let sender = sender.clone();
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
let join_handle = std::thread::spawn(move || {
|
|
let future = run_test_file(
|
|
program_state,
|
|
main_module,
|
|
test_module,
|
|
permissions,
|
|
sender,
|
|
);
|
|
|
|
tokio_util::run_basic(future)
|
|
});
|
|
|
|
join_handle.join().unwrap()
|
|
})
|
|
});
|
|
|
|
let join_futures = stream::iter(join_handles)
|
|
.buffer_unordered(concurrent_jobs)
|
|
.collect::<Vec<Result<Result<(), AnyError>, tokio::task::JoinError>>>();
|
|
|
|
let reporter_kind = if terse {
|
|
TestReporterKind::Terse
|
|
} else {
|
|
TestReporterKind::Pretty
|
|
};
|
|
|
|
let mut reporter = create_reporter(reporter_kind, concurrent_jobs > 1);
|
|
let handler = {
|
|
tokio::task::spawn_blocking(move || {
|
|
let mut used_only = false;
|
|
let mut has_error = false;
|
|
let mut planned = 0;
|
|
let mut reported = 0;
|
|
|
|
for event in receiver.iter() {
|
|
match event.message.clone() {
|
|
TestMessage::Plan {
|
|
pending,
|
|
filtered: _,
|
|
only,
|
|
} => {
|
|
if only {
|
|
used_only = true;
|
|
}
|
|
|
|
planned += pending;
|
|
}
|
|
TestMessage::Result {
|
|
name: _,
|
|
duration: _,
|
|
result,
|
|
} => {
|
|
reported += 1;
|
|
|
|
if let TestResult::Failed(_) = result {
|
|
has_error = true;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
reporter.visit_event(event);
|
|
|
|
if has_error && fail_fast {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if planned > reported {
|
|
has_error = true;
|
|
}
|
|
|
|
reporter.done();
|
|
|
|
if planned > reported {
|
|
has_error = true;
|
|
}
|
|
|
|
if used_only {
|
|
println!(
|
|
"{} because the \"only\" option was used\n",
|
|
colors::red("FAILED")
|
|
);
|
|
|
|
has_error = true;
|
|
}
|
|
|
|
has_error
|
|
})
|
|
};
|
|
|
|
let (result, join_results) = future::join(handler, join_futures).await;
|
|
|
|
let mut join_errors = join_results.into_iter().filter_map(|join_result| {
|
|
join_result
|
|
.ok()
|
|
.map(|handle_result| handle_result.err())
|
|
.flatten()
|
|
});
|
|
|
|
if let Some(e) = join_errors.next() {
|
|
Err(e)
|
|
} else {
|
|
Ok(result.unwrap_or(false))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_collect_test_module_specifiers() {
|
|
let test_data_path = test_util::root_path().join("cli/tests/subdir");
|
|
let mut matched_urls = collect_test_module_specifiers(
|
|
vec![
|
|
"https://example.com/colors_test.ts".to_string(),
|
|
"./mod1.ts".to_string(),
|
|
"./mod3.js".to_string(),
|
|
"subdir2/mod2.ts".to_string(),
|
|
"http://example.com/printf_test.ts".to_string(),
|
|
],
|
|
&test_data_path,
|
|
is_supported,
|
|
)
|
|
.unwrap();
|
|
let test_data_url =
|
|
Url::from_file_path(test_data_path).unwrap().to_string();
|
|
|
|
let expected: Vec<Url> = vec![
|
|
format!("{}/mod1.ts", test_data_url),
|
|
format!("{}/mod3.js", test_data_url),
|
|
format!("{}/subdir2/mod2.ts", test_data_url),
|
|
"http://example.com/printf_test.ts".to_string(),
|
|
"https://example.com/colors_test.ts".to_string(),
|
|
]
|
|
.into_iter()
|
|
.map(|f| Url::parse(&f).unwrap())
|
|
.collect();
|
|
matched_urls.sort();
|
|
assert_eq!(matched_urls, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_supported() {
|
|
assert!(is_supported(Path::new("tests/subdir/foo_test.ts")));
|
|
assert!(is_supported(Path::new("tests/subdir/foo_test.tsx")));
|
|
assert!(is_supported(Path::new("tests/subdir/foo_test.js")));
|
|
assert!(is_supported(Path::new("tests/subdir/foo_test.jsx")));
|
|
assert!(is_supported(Path::new("bar/foo.test.ts")));
|
|
assert!(is_supported(Path::new("bar/foo.test.tsx")));
|
|
assert!(is_supported(Path::new("bar/foo.test.js")));
|
|
assert!(is_supported(Path::new("bar/foo.test.jsx")));
|
|
assert!(is_supported(Path::new("foo/bar/test.js")));
|
|
assert!(is_supported(Path::new("foo/bar/test.jsx")));
|
|
assert!(is_supported(Path::new("foo/bar/test.ts")));
|
|
assert!(is_supported(Path::new("foo/bar/test.tsx")));
|
|
assert!(!is_supported(Path::new("README.md")));
|
|
assert!(!is_supported(Path::new("lib/typescript.d.ts")));
|
|
assert!(!is_supported(Path::new("notatest.js")));
|
|
assert!(!is_supported(Path::new("NotAtest.ts")));
|
|
}
|
|
|
|
#[test]
|
|
fn supports_dirs() {
|
|
// TODO(caspervonb) generate some fixtures in a temporary directory instead, there's no need
|
|
// for this to rely on external fixtures.
|
|
let root = test_util::root_path()
|
|
.join("test_util")
|
|
.join("std")
|
|
.join("http");
|
|
println!("root {:?}", root);
|
|
let mut matched_urls = collect_test_module_specifiers(
|
|
vec![".".to_string()],
|
|
&root,
|
|
is_supported,
|
|
)
|
|
.unwrap();
|
|
matched_urls.sort();
|
|
let root_url = Url::from_file_path(root).unwrap().to_string();
|
|
println!("root_url {}", root_url);
|
|
let expected: Vec<Url> = vec![
|
|
format!("{}/_io_test.ts", root_url),
|
|
format!("{}/cookie_test.ts", root_url),
|
|
format!("{}/file_server_test.ts", root_url),
|
|
format!("{}/racing_server_test.ts", root_url),
|
|
format!("{}/server_test.ts", root_url),
|
|
format!("{}/test.ts", root_url),
|
|
]
|
|
.into_iter()
|
|
.map(|f| Url::parse(&f).unwrap())
|
|
.collect();
|
|
assert_eq!(matched_urls, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_remote_url() {
|
|
assert!(is_remote_url("https://deno.land/std/http/file_server.ts"));
|
|
assert!(is_remote_url("http://deno.land/std/http/file_server.ts"));
|
|
assert!(is_remote_url("HTTP://deno.land/std/http/file_server.ts"));
|
|
assert!(is_remote_url("HTTp://deno.land/std/http/file_server.ts"));
|
|
assert!(!is_remote_url("file:///dev/deno_std/http/file_server.ts"));
|
|
assert!(!is_remote_url("./dev/deno_std/http/file_server.ts"));
|
|
}
|
|
}
|