Simplify tooltip tests

This commit is contained in:
Laurenz 2024-11-09 12:18:24 +01:00
parent 5d003fb1b2
commit 3d1cad2bec
3 changed files with 82 additions and 24 deletions

View File

@ -1390,9 +1390,10 @@ mod tests {
use std::collections::BTreeSet; use std::collections::BTreeSet;
use typst::model::Document; use typst::model::Document;
use typst::syntax::Source;
use super::{autocomplete, Completion}; use super::{autocomplete, Completion};
use crate::tests::TestWorld; use crate::tests::{SourceExt, TestWorld};
type Response = Option<(usize, Vec<Completion>)>; type Response = Option<(usize, Vec<Completion>)>;
@ -1465,21 +1466,17 @@ mod tests {
#[track_caller] #[track_caller]
fn test_with_world(world: &TestWorld, cursor: isize) -> Response { fn test_with_world(world: &TestWorld, cursor: isize) -> Response {
let doc = typst::compile(&world).output.ok(); let doc = typst::compile(&world).output.ok();
test_with_world_and_doc(world, doc.as_ref(), cursor) test_full(world, &world.main, doc.as_ref(), cursor)
} }
#[track_caller] #[track_caller]
fn test_with_world_and_doc( fn test_full(
world: &TestWorld, world: &TestWorld,
source: &Source,
doc: Option<&Document>, doc: Option<&Document>,
cursor: isize, cursor: isize,
) -> Response { ) -> Response {
let cursor = if cursor < 0 { autocomplete(&world, doc, source, source.cursor(cursor), true)
world.main.len_bytes().checked_add_signed(cursor).unwrap()
} else {
cursor as usize
};
autocomplete(&world, doc, &world.main, cursor, true)
} }
#[test] #[test]
@ -1523,7 +1520,7 @@ mod tests {
let end = world.main.len_bytes(); let end = world.main.len_bytes();
world.main.edit(end..end, " #cite()"); world.main.edit(end..end, " #cite()");
test_with_world_and_doc(&world, doc.as_ref(), -1) test_full(&world, &world.main, doc.as_ref(), -1)
.must_include(["netwok", "glacier-melt", "supplement"]) .must_include(["netwok", "glacier-melt", "supplement"])
.must_exclude(["bib"]); .must_exclude(["bib"]);
} }

View File

@ -111,6 +111,7 @@ mod tests {
pub struct TestWorld { pub struct TestWorld {
pub main: Source, pub main: Source,
assets: HashMap<FileId, Bytes>, assets: HashMap<FileId, Bytes>,
sources: HashMap<FileId, Source>,
base: &'static TestBase, base: &'static TestBase,
} }
@ -124,11 +125,12 @@ mod tests {
Self { Self {
main, main,
assets: HashMap::new(), assets: HashMap::new(),
sources: HashMap::new(),
base: singleton!(TestBase, TestBase::default()), base: singleton!(TestBase, TestBase::default()),
} }
} }
/// Add an additional file to the test world. /// Add an additional asset file to the test world.
#[track_caller] #[track_caller]
pub fn with_asset_by_name(mut self, filename: &str) -> Self { pub fn with_asset_by_name(mut self, filename: &str) -> Self {
let id = FileId::new(None, VirtualPath::new(filename)); let id = FileId::new(None, VirtualPath::new(filename));
@ -138,6 +140,14 @@ mod tests {
self self
} }
/// Add an additional source file to the test world.
pub fn with_source(mut self, path: &str, text: &str) -> Self {
let id = FileId::new(None, VirtualPath::new(path));
let source = Source::new(id, text.into());
self.sources.insert(id, source);
self
}
/// The ID of the main file in a `TestWorld`. /// The ID of the main file in a `TestWorld`.
pub fn main_id() -> FileId { pub fn main_id() -> FileId {
*singleton!(FileId, FileId::new(None, VirtualPath::new("main.typ"))) *singleton!(FileId, FileId::new(None, VirtualPath::new("main.typ")))
@ -160,6 +170,8 @@ mod tests {
fn source(&self, id: FileId) -> FileResult<Source> { fn source(&self, id: FileId) -> FileResult<Source> {
if id == self.main.id() { if id == self.main.id() {
Ok(self.main.clone()) Ok(self.main.clone())
} else if let Some(source) = self.sources.get(&id) {
Ok(source.clone())
} else { } else {
Err(FileError::NotFound(id.vpath().as_rootless_path().into())) Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
} }
@ -181,6 +193,22 @@ mod tests {
} }
} }
/// Extra methods for [`Source`].
pub trait SourceExt {
/// Negative cursors index from the back.
fn cursor(&self, cursor: isize) -> usize;
}
impl SourceExt for Source {
fn cursor(&self, cursor: isize) -> usize {
if cursor < 0 {
self.len_bytes().checked_add_signed(cursor).unwrap()
} else {
cursor as usize
}
}
}
/// Shared foundation of all test worlds. /// Shared foundation of all test worlds.
struct TestBase { struct TestBase {
library: LazyHash<Library>, library: LazyHash<Library>,

View File

@ -134,7 +134,7 @@ fn closure_tooltip(leaf: &LinkedNode) -> Option<Tooltip> {
names.sort(); names.sort();
let tooltip = repr::separated_list(&names, "and"); let tooltip = repr::separated_list(&names, "and");
Some(Tooltip::Text(eco_format!("This closure captures {tooltip}."))) Some(Tooltip::Text(eco_format!("This closure captures {tooltip}")))
} }
/// Tooltip text for a hovered length. /// Tooltip text for a hovered length.
@ -256,32 +256,65 @@ mod tests {
use typst::syntax::Side; use typst::syntax::Side;
use super::{tooltip, Tooltip}; use super::{tooltip, Tooltip};
use crate::tests::TestWorld; use crate::tests::{SourceExt, TestWorld};
fn text(text: &str) -> Option<Tooltip> { type Response = Option<Tooltip>;
Some(Tooltip::Text(text.into()))
trait ResponseExt {
fn must_be_none(&self) -> &Self;
fn must_be_text(&self, text: &str) -> &Self;
fn must_be_code(&self, code: &str) -> &Self;
} }
fn code(code: &str) -> Option<Tooltip> { impl ResponseExt for Response {
Some(Tooltip::Code(code.into())) #[track_caller]
fn must_be_none(&self) -> &Self {
assert_eq!(*self, None);
self
} }
#[track_caller] #[track_caller]
fn test(text: &str, cursor: usize, side: Side, expected: Option<Tooltip>) { fn must_be_text(&self, text: &str) -> &Self {
assert_eq!(*self, Some(Tooltip::Text(text.into())));
self
}
#[track_caller]
fn must_be_code(&self, code: &str) -> &Self {
assert_eq!(*self, Some(Tooltip::Code(code.into())));
self
}
}
#[track_caller]
fn test(text: &str, cursor: isize, side: Side) -> Response {
let world = TestWorld::new(text); let world = TestWorld::new(text);
test_with_world(&world, cursor, side)
}
#[track_caller]
fn test_with_world(world: &TestWorld, cursor: isize, side: Side) -> Response {
let source = &world.main;
let doc = typst::compile(&world).output.ok(); let doc = typst::compile(&world).output.ok();
assert_eq!(tooltip(&world, doc.as_ref(), &world.main, cursor, side), expected); tooltip(&world, doc.as_ref(), source, source.cursor(cursor), side)
} }
#[test] #[test]
fn test_tooltip() { fn test_tooltip() {
test("#let x = 1 + 2", 5, Side::After, code("3")); test("#let x = 1 + 2", 14, Side::After).must_be_none();
test("#let x = 1 + 2", 6, Side::Before, code("3")); test("#let x = 1 + 2", 5, Side::After).must_be_code("3");
test("#let f(x) = x + y", 11, Side::Before, text("This closure captures `y`.")); test("#let x = 1 + 2", 6, Side::Before).must_be_code("3");
test("#let x = 1 + 2", 6, Side::Before).must_be_code("3");
} }
#[test] #[test]
fn test_empty_contextual() { fn test_tooltip_empty_contextual() {
test("#{context}", 10, Side::Before, code("context()")); test("#{context}", 10, Side::Before).must_be_code("context()");
}
#[test]
fn test_tooltip_closure() {
test("#let f(x) = x + y", 11, Side::Before)
.must_be_text("This closure captures `y`");
} }
} }