+ read_rows(&b"foo"[..]).unwrap(),
+ vec![Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::new(),
+ })]
+ );
+ assert_eq!(
+ read_rows(&b"bar"[..]).unwrap(),
+ vec![Rowlike::Row(Row {
+ label: "bar".to_owned(),
+ entries: HashMap::new(),
+ })]
+ );
+ assert_eq!(
+ read_rows(&b"foo\nbar\n"[..]).unwrap(),
+ vec![
+ Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::new(),
+ }),
+ Rowlike::Row(Row {
+ label: "bar".to_owned(),
+ entries: HashMap::new(),
+ })
+ ]
+ );
+ assert_eq!(
+ read_rows(&b"foo\n bar\n"[..]).unwrap(),
+ vec![Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::from([("bar".to_owned(), vec![None])]),
+ })]
+ );
+ assert_eq!(
+ read_rows(&b"foo\n bar\n baz\n"[..]).unwrap(),
+ vec![Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::from([
+ ("bar".to_owned(), vec![None]),
+ ("baz".to_owned(), vec![None])
+ ]),
+ })]
+ );
+ assert_eq!(
+ read_rows(&b"foo\n\nbar\n"[..]).unwrap(),
+ vec![
+ Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::new(),
+ }),
+ Rowlike::Row(Row {
+ label: "bar".to_owned(),
+ entries: HashMap::new(),
+ })
+ ]
+ );
+ assert_eq!(
+ read_rows(&b"foo\n\n\nbar\n"[..]).unwrap(),
+ vec![
+ Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::new(),
+ }),
+ Rowlike::Spacer,
+ Rowlike::Row(Row {
+ label: "bar".to_owned(),
+ entries: HashMap::new(),
+ })
+ ]
+ );
+ assert_eq!(
+ read_rows(&b"foo\n \nbar\n"[..]).unwrap(),
+ vec![
+ Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::new(),
+ }),
+ Rowlike::Row(Row {
+ label: "bar".to_owned(),
+ entries: HashMap::new(),
+ })
+ ]
+ );
+ assert_eq!(
+ read_rows(&b"foo \n bar \n"[..]).unwrap(),
+ vec![Rowlike::Row(Row {
+ label: "foo".to_owned(),
+ entries: HashMap::from([("bar".to_owned(), vec![None])]),
+ })]
+ );
+
+ let bad = read_rows(&b" foo"[..]);
+ assert!(bad.is_err());
+ assert!(format!("{bad:?}").contains("line 1: Entry with no header"));
+
+ let bad2 = read_rows(&b"foo\n\n bar"[..]);
+ assert!(bad2.is_err());
+ assert!(format!("{bad2:?}").contains("line 3: Entry with no header"));
+ }
+
+ #[test]
+ fn test_read_config() {
+ assert_eq!(
+ read_config(&b"!col_threshold 10"[..])
+ .unwrap()
+ .column_threshold,
+ 10
+ );
+ assert_eq!(
+ read_config(&b"!col foo"[..]).unwrap().static_columns,
+ vec![Some("foo".to_owned())]
+ );
+
+ let bad_command = read_config(&b"!no such command"[..]);
+ assert!(bad_command.is_err());
+ assert!(format!("{bad_command:?}").contains("line 1: Unknown command"));
+
+ let bad_num = read_config(&b"!col_threshold foo"[..]);
+ assert!(bad_num.is_err());
+ assert!(format!("{bad_num:?}").contains("line 1: col_threshold must be numeric"));
+ }
+
+ #[test]
+ fn test_column_counts() {
+ assert_eq!(
+ column_counts(&read_rows(&b"foo\n bar\n baz\n"[..]).unwrap()),
+ vec![(1, String::from("bar")), (1, String::from("baz"))]
+ );
+ assert_eq!(
+ column_counts(&read_rows(&b"foo\n bar\n baz\nquux\n baz"[..]).unwrap()),
+ vec![(2, String::from("baz")), (1, String::from("bar"))]
+ );
+ assert_eq!(
+ column_counts(&read_rows(&b"foo\n bar\n bar\n baz\n bar\nquux\n baz"[..]).unwrap()),
+ vec![(2, String::from("baz")), (1, String::from("bar"))]
+ );
+ assert_eq!(
+ column_counts(
+ &read_rows(&b"foo\n bar: 1\n bar: 2\n baz\n bar\nquux\n baz"[..]).unwrap()
+ ),
+ vec![(2, String::from("baz")), (1, String::from("bar"))]
+ );
+ }
+
+ #[test]
+ fn test_render_cell() {
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::new(),
+ }
+ ),
+ HTML::from(
+ r#"<td class="" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')"></td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("bar".to_owned(), vec![None])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')"></td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("foo".to_owned(), vec![None])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')"></td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("foo".to_owned(), vec![None, None])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')">2</td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([(
+ "foo".to_owned(),
+ vec![Some("5".to_owned()), Some("10".to_owned())]
+ )]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')">5 10</td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("foo".to_owned(), vec![Some("5".to_owned()), None])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')">5 ✓</td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "heart",
+ &mut Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("heart".to_owned(), vec![Some("<3".to_owned())])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('nope','heart')" onmouseout="ch2('nope','heart')"><3</td>"#
+ )
+ );
+ assert_eq!(
+ render_cell(
+ "foo",
+ &mut Row {
+ label: "bob's".to_owned(),
+ entries: HashMap::from([("foo".to_owned(), vec![None])]),
+ }
+ ),
+ HTML::from(
+ r#"<td class="yes" onmouseover="h2('bob's','foo')" onmouseout="ch2('bob's','foo')"></td>"#
+ )
+ );
+ let mut r = Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([
+ ("foo".to_owned(), vec![None]),
+ ("baz".to_owned(), vec![None]),
+ ]),
+ };
+ assert_eq!(r.entries.len(), 2);
+ render_cell("foo", &mut r);
+ assert_eq!(r.entries.len(), 1);
+ render_cell("bar", &mut r);
+ assert_eq!(r.entries.len(), 1);
+ render_cell("baz", &mut r);
+ assert_eq!(r.entries.len(), 0);
+ }
+
+ #[test]
+ fn test_render_leftovers() {
+ assert_eq!(
+ render_all_leftovers(&Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("foo".to_owned(), vec![None])]),
+ }),
+ HTML::from("foo")
+ );
+ assert_eq!(
+ render_all_leftovers(&Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([
+ ("foo".to_owned(), vec![None]),
+ ("bar".to_owned(), vec![None])
+ ]),
+ }),
+ HTML::from("bar, foo")
+ );
+ assert_eq!(
+ render_all_leftovers(&Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([
+ ("foo".to_owned(), vec![None]),
+ ("bar".to_owned(), vec![None, None])
+ ]),
+ }),
+ HTML::from("bar: 2, foo")
+ );
+ }
+
+ #[test]
+ fn test_render_row() {
+ assert_eq!(
+ render_row(
+ &DEFAULT_CONFIG,
+ &["foo".to_owned()],
+ &mut Rowlike::Row(Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([("bar".to_owned(), vec![None])]),
+ })
+ ),
+ HTML::from(
+ r#"<tr><th id="nope">nope</th><td class="" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')"></td><td class="leftover" onmouseover="highlight('nope')" onmouseout="clear_highlight('nope')">bar</td></tr>
+"#
+ )
+ );
+ assert_eq!(
+ render_row(
+ &Config {
+ column_threshold: 0,
+ static_columns: vec![Some("foo".to_owned()), Some("bar".to_owned())],
+ },
+ &["baz".to_owned()],
+ &mut Rowlike::Row(Row {
+ label: "nope".to_owned(),
+ entries: HashMap::from([
+ ("bar".to_owned(), vec![Some("r".to_owned())]),
+ ("baz".to_owned(), vec![Some("z".to_owned())]),
+ ("foo".to_owned(), vec![Some("f".to_owned())]),
+ ]),
+ })
+ ),
+ HTML::from(
+ r#"<tr><th id="nope">nope</th><td class="yes" onmouseover="h2('nope','foo')" onmouseout="ch2('nope','foo')">f</td><td class="yes" onmouseover="h2('nope','bar')" onmouseout="ch2('nope','bar')">r</td><td class="yes" onmouseover="h2('nope','baz')" onmouseout="ch2('nope','baz')">z</td><td class="leftover" onmouseover="highlight('nope')" onmouseout="clear_highlight('nope')"></td></tr>
+"#
+ )