Compare commits
No commits in common. "ec65b6d03f13d88dbf874b72b086eec369d94475" and "608a9f68f4e756bf65c0084fbf654c8e40d4762c" have entirely different histories.
ec65b6d03f
...
608a9f68f4
20 changed files with 81 additions and 14426 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
|||
/target
|
||||
/dist
|
||||
/.env
|
||||
*.snap.new
|
||||
|
|
80
Cargo.lock
generated
80
Cargo.lock
generated
|
@ -156,7 +156,6 @@ dependencies = [
|
|||
"hex",
|
||||
"http",
|
||||
"humansize",
|
||||
"junit-parser",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"once_cell",
|
||||
|
@ -164,7 +163,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project",
|
||||
"proptest",
|
||||
"quick-xml",
|
||||
"quick_cache",
|
||||
"rand",
|
||||
"regex",
|
||||
|
@ -499,18 +497,6 @@ dependencies = [
|
|||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
|
@ -634,7 +620,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -710,12 +695,6 @@ version = "1.0.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "entities"
|
||||
version = "1.0.1"
|
||||
|
@ -1238,19 +1217,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"serde",
|
||||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
|
@ -1287,19 +1253,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "junit-parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"insta",
|
||||
"once_cell",
|
||||
"path_macro",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -1318,12 +1271,6 @@ version = "0.2.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
|
@ -1758,15 +1705,6 @@ version = "1.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick_cache"
|
||||
version = "0.5.1"
|
||||
|
@ -2244,12 +2182,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -2424,12 +2356,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2438,16 +2368,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
|
|
@ -39,14 +39,12 @@ headers = "0.4.0"
|
|||
hex = "0.4.3"
|
||||
http = "1.1.0"
|
||||
humansize = "2.1.3"
|
||||
junit-parser = { path = "crates/junit-parser" }
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0.4"
|
||||
once_cell = "1.19.0"
|
||||
path_macro = "1.0.0"
|
||||
percent-encoding = "2.3.1"
|
||||
pin-project = "1.1.5"
|
||||
quick-xml = { version = "0.31.0", features = ["escape-html"] }
|
||||
quick_cache = "0.5.1"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.4"
|
||||
|
|
|
@ -89,7 +89,6 @@ Artifactview is configured using environment variables.
|
|||
| `REPO_BLACKLIST` | - | List of sites/users/repos that can NOT be accessed. The blacklist takes precedence over the whitelist (repos included in both lists cannot be accessed)<br />Example: `github.com/evil-corp/world-destruction;codeberg.org/blackhat;example.org` |
|
||||
| `REPO_WHITELIST` | - | List of sites/users/repos that can ONLY be accessed. If the whitelist is empty, it will be ignored and any repository can be accessed. Uses the same syntax as `REPO_BLACLIST`. |
|
||||
| `SITE_ALIASES` | - | Aliases for sites to make URLs shorter<br />Example: `gh => github.com;cb => codeberg.org` |
|
||||
| `VIEWER_MAX_SIZE` | 500000 | Maximum file size to be displayed using the viewer |
|
||||
|
||||
## Technical details
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "junit-parser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Boris Faure <boris@fau.re>"]
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/borisfaure/junit-parser"
|
||||
|
||||
[dependencies]
|
||||
quick-xml = { version = "0.31.0", features = ["escape-html"] }
|
||||
thiserror = "1.0.61"
|
||||
time = { version = "0.3.36", features = ["parsing", "serde-well-known"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.39.0", features = ["json"] }
|
||||
once_cell = "1.19.0"
|
||||
path_macro = "1.0.0"
|
|
@ -1,40 +0,0 @@
|
|||
#![warn(missing_docs)]
|
||||
use thiserror::Error;
|
||||
|
||||
/// Error enumerates all possible errors returned by this library.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Error while parsing XML
|
||||
#[error("Error while parsing XML: {0}")]
|
||||
Xml(#[from] ::quick_xml::Error),
|
||||
/// Error while converting f64 attribute
|
||||
#[error("Error while converting f64 attribute: {0}")]
|
||||
ParseFloat(#[from] std::num::ParseFloatError),
|
||||
/// Error while converting u64 attribute
|
||||
#[error("Error while converting u64 attribute: {0}")]
|
||||
ParseInt(#[from] std::num::ParseIntError),
|
||||
/// Error while converting bytes to Utf8
|
||||
#[error("Error while converting bytes to Utf8: {0}")]
|
||||
ParseUt8(#[from] std::str::Utf8Error),
|
||||
#[error("Error while parsing timestamp: {0}")]
|
||||
ParseTimestamp(#[from] time::error::Parse),
|
||||
/// Error parsing the `property` element: missing `name`
|
||||
#[error("Missing `name` attribute in property")]
|
||||
MissingPropertyName,
|
||||
}
|
||||
|
||||
impl From<::quick_xml::events::attributes::AttrError> for Error {
|
||||
#[inline]
|
||||
/// Convert [`::quick_xml::events::attributes`] into [`Error::XMLError`]
|
||||
fn from(err: ::quick_xml::events::attributes::AttrError) -> Error {
|
||||
Error::Xml(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::quick_xml::escape::EscapeError> for Error {
|
||||
#[inline]
|
||||
/// Convert [`::quick_xml::escape::EscapeError`] into [`Error::XMLError`]
|
||||
fn from(err: ::quick_xml::escape::EscapeError) -> Error {
|
||||
Error::Xml(err.into())
|
||||
}
|
||||
}
|
|
@ -1,564 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
use std::io::BufRead;
|
||||
|
||||
use quick_xml::escape::unescape;
|
||||
use quick_xml::events::BytesStart as XMLBytesStart;
|
||||
use quick_xml::events::Event as XMLEvent;
|
||||
use quick_xml::name::QName;
|
||||
use quick_xml::Error as XMLError;
|
||||
use quick_xml::Reader as XMLReader;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
mod errors;
|
||||
|
||||
use errors::Error;
|
||||
|
||||
/// Struct representing a JUnit report, containing test suites
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct TestSuites {
|
||||
/// Name of the test suites, from the `name` attribute
|
||||
pub name: String,
|
||||
/// List of tests suites represented by [`TestSuite`]
|
||||
pub suites: Vec<TestSuite>,
|
||||
/// How long the test suites took to run, from the `time` attribute
|
||||
pub time: f64,
|
||||
/// Number of tests in the test suites, from the `tests` attribute
|
||||
pub tests: u64,
|
||||
/// Number of tests in error in the test suites, from the `errors` attribute
|
||||
pub errors: u64,
|
||||
/// Number of tests in failure in the test suites, from the `failures` attribute
|
||||
pub failures: u64,
|
||||
/// Number of tests skipped in the test suites, from the `skipped` attribute
|
||||
pub skipped: u64,
|
||||
/// Number of tests that passed after failed attempts
|
||||
pub flaky: u64,
|
||||
}
|
||||
|
||||
/// A test suite, containing test cases [`TestCase`]
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct TestSuite {
|
||||
/// Name of the test suite, from the `name` attribute
|
||||
pub name: String,
|
||||
/// Timestamp when the test suite was run, from the `timestamp` attribute
|
||||
#[serde(with = "time::serde::rfc3339::option")]
|
||||
pub timestamp: Option<OffsetDateTime>,
|
||||
/// List of status of tests represented by [`TestCase`]
|
||||
pub cases: Vec<TestCase>,
|
||||
/// How long the test suite took to run, from the `time` attribute
|
||||
pub time: f64,
|
||||
/// Number of tests in the test suite, from the `tests` attribute
|
||||
pub tests: u64,
|
||||
/// Number of tests in error in the test suite, from the `errors` attribute
|
||||
pub errors: u64,
|
||||
/// Number of tests in failure in the test suite, from the `failures` attribute
|
||||
pub failures: u64,
|
||||
/// Number of tests skipped in the test suites, from the `skipped` attribute
|
||||
pub skipped: u64,
|
||||
/// Number of tests that passed after failed attempts
|
||||
pub flaky: u64,
|
||||
}
|
||||
|
||||
/// A test case
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct TestCase {
|
||||
/// Name of the test case, from the `name` attribute
|
||||
pub name: String,
|
||||
/// Original name, from the `name` attribute
|
||||
pub original_name: String,
|
||||
/// Class name, from the `classname` attribute
|
||||
pub classname: Option<String>,
|
||||
/// Timestamp when the test case was run
|
||||
#[serde(with = "time::serde::rfc3339::option")]
|
||||
pub timestamp: Option<OffsetDateTime>,
|
||||
/// Run time in seconds
|
||||
pub time: f64,
|
||||
/// Status of the test case
|
||||
pub status: TestStatus,
|
||||
/// stdout output from the `system-out` element
|
||||
pub system_out: Option<String>,
|
||||
/// stderr output from the `system-err` element
|
||||
pub system_err: Option<String>,
|
||||
/// Previous test attempts, from `rerunFailure` and `flakyFailure` element
|
||||
pub retries: Vec<Retry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub enum TestStatus {
|
||||
#[default]
|
||||
Success,
|
||||
Error(Message),
|
||||
Failure(Message),
|
||||
Flaky,
|
||||
Skipped,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Message {
|
||||
pub message: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Retry {
|
||||
/// Timestamp when the retry was run
|
||||
#[serde(with = "time::serde::rfc3339::option")]
|
||||
pub timestamp: Option<OffsetDateTime>,
|
||||
/// Run time in seconds
|
||||
pub time: f64,
|
||||
/// Status of the retry
|
||||
pub status: TestStatus,
|
||||
/// stdout output from the `system-out` element
|
||||
pub system_out: Option<String>,
|
||||
/// stderr output from the `system-err` element
|
||||
pub system_err: Option<String>,
|
||||
}
|
||||
|
||||
impl TestSuites {
|
||||
/// Fill up `self` with attributes from the XML tag
|
||||
fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
|
||||
for a in e.attributes() {
|
||||
let a = a?;
|
||||
match a.key {
|
||||
QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
|
||||
QName(b"tests") => self.tests = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"errors") => self.errors = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"failures") => self.failures = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"skipped") => self.skipped = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"name") => self.name = try_from_attribute_value_string(a.value)?,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// New [`TestSuites`] from empty XML tag
|
||||
fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
|
||||
let mut ts = Self::default();
|
||||
ts.parse_attributes(e)?;
|
||||
Ok(ts)
|
||||
}
|
||||
|
||||
/// New [`TestSuites`] from XML tree
|
||||
fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
|
||||
let mut ts = Self::default();
|
||||
ts.parse_attributes(e)?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testsuites") => break,
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testrun") => break,
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuite") => {
|
||||
ts.suites.push(TestSuite::from_reader(e, r)?);
|
||||
}
|
||||
Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuite") => {
|
||||
ts.suites.push(TestSuite::new_empty(e)?);
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof("testsuites".to_string()).into())
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
ts.flaky = ts.suites.iter().map(|s| s.flaky).sum();
|
||||
|
||||
Ok(ts)
|
||||
}
|
||||
}
|
||||
|
||||
impl TestSuite {
|
||||
/// Fill up `self` with attributes from the XML tag
|
||||
fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
|
||||
for a in e.attributes() {
|
||||
let a = a?;
|
||||
match a.key {
|
||||
QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
|
||||
QName(b"tests") => self.tests = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"errors") => self.errors = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"failures") => self.failures = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"skipped") => self.skipped = try_from_attribute_value_u64(a.value)?,
|
||||
QName(b"name") => self.name = try_from_attribute_value_string(a.value)?,
|
||||
QName(b"timestamp") => {
|
||||
self.timestamp = Some(try_from_attribute_value_timestamp(a.value)?)
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// New [`TestSuite`] from empty XML tag
|
||||
fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
|
||||
let mut ts = Self::default();
|
||||
ts.parse_attributes(e)?;
|
||||
Ok(ts)
|
||||
}
|
||||
|
||||
/// New [`TestSuite`] from XML tree
|
||||
fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
|
||||
let mut ts = Self::default();
|
||||
ts.parse_attributes(e)?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == QName(b"testsuite") => break,
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testcase") => {
|
||||
ts.cases.push(TestCase::from_reader(e, r)?);
|
||||
}
|
||||
Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testcase") => {
|
||||
ts.cases.push(TestCase::new_empty(e)?);
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof("testsuite".to_string()).into())
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
ts.flaky = ts
|
||||
.cases
|
||||
.iter()
|
||||
.filter(|c| matches!(c.status, TestStatus::Flaky))
|
||||
.count() as u64;
|
||||
|
||||
Ok(ts)
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
/// Fill up `self` with attributes from the XML tag
|
||||
fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
|
||||
for a in e.attributes() {
|
||||
let a = a?;
|
||||
match a.key {
|
||||
QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
|
||||
QName(b"timestamp") => {
|
||||
self.timestamp = Some(try_from_attribute_value_timestamp(a.value)?)
|
||||
}
|
||||
QName(b"name") => self.original_name = try_from_attribute_value_string(a.value)?,
|
||||
QName(b"classname") => {
|
||||
self.classname = Some(try_from_attribute_value_string(a.value)?)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(cn) = self.classname.as_ref() {
|
||||
self.name = format!("{}::{}", cn, self.original_name);
|
||||
} else {
|
||||
self.name.clone_from(&self.original_name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// New [`TestCase`] from empty XML tag
|
||||
fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
|
||||
let mut tc = Self::default();
|
||||
tc.parse_attributes(e)?;
|
||||
Ok(tc)
|
||||
}
|
||||
|
||||
/// New [`TestCase`] from XML tree
|
||||
fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
|
||||
let mut tc = Self::default();
|
||||
tc.parse_attributes(e)?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf)? {
|
||||
XMLEvent::End(ref e) if e.name() == QName(b"testcase") => break,
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"skipped") => {
|
||||
tc.status = TestStatus::Skipped;
|
||||
}
|
||||
XMLEvent::Empty(ref e) if e.name() == QName(b"skipped") => {
|
||||
tc.status = TestStatus::Skipped;
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"failure") => {
|
||||
let msg = Message::from_reader(e, r)?;
|
||||
tc.status = TestStatus::Failure(msg);
|
||||
}
|
||||
XMLEvent::Empty(ref e) if e.name() == QName(b"failure") => {
|
||||
let msg = Message::new_empty(e)?;
|
||||
tc.status = TestStatus::Failure(msg);
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"error") => {
|
||||
let msg = Message::from_reader(e, r)?;
|
||||
tc.status = TestStatus::Error(msg);
|
||||
}
|
||||
XMLEvent::Empty(ref e) if e.name() == QName(b"error") => {
|
||||
let msg = Message::new_empty(e)?;
|
||||
tc.status = TestStatus::Error(msg);
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"system-out") => {
|
||||
tc.system_out = parse_system(e, r)?;
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"system-err") => {
|
||||
tc.system_err = parse_system(e, r)?;
|
||||
}
|
||||
XMLEvent::Empty(ref e) if e.name() == QName(b"rerunFailure") => {
|
||||
tc.retries.push(Retry::new_empty(e)?);
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"rerunFailure") => {
|
||||
tc.retries.push(Retry::from_reader(e, r)?);
|
||||
}
|
||||
XMLEvent::Empty(ref e) if e.name() == QName(b"flakyFailure") => {
|
||||
tc.status = TestStatus::Flaky;
|
||||
tc.retries.push(Retry::new_empty(e)?);
|
||||
}
|
||||
XMLEvent::Start(ref e) if e.name() == QName(b"flakyFailure") => {
|
||||
tc.status = TestStatus::Flaky;
|
||||
tc.retries.push(Retry::from_reader(e, r)?);
|
||||
}
|
||||
XMLEvent::Eof => return Err(XMLError::UnexpectedEof("testcase".to_string()).into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(tc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Fill up `self` with attributes from the XML tag
|
||||
fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
|
||||
for a in e.attributes() {
|
||||
let a = a?;
|
||||
if let QName(b"message") = a.key {
|
||||
self.message = try_from_attribute_value_string(a.value)?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// New [`Message`] from empty XML tag
|
||||
fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
|
||||
let mut tf = Self::default();
|
||||
tf.parse_attributes(e)?;
|
||||
Ok(tf)
|
||||
}
|
||||
|
||||
/// New [`Message`] from XML tree
|
||||
fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
|
||||
let name = e.name();
|
||||
let mut msg = Self::default();
|
||||
msg.parse_attributes(e)?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == name => break,
|
||||
Ok(XMLEvent::Text(e)) => {
|
||||
msg.text += e.unescape()?.trim();
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof("failure".to_string()).into())
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Retry {
|
||||
/// Fill up `self` with attributes from the XML tag
|
||||
fn parse_attributes(&mut self, e: &XMLBytesStart) -> Result<(), Error> {
|
||||
for a in e.attributes() {
|
||||
let a = a?;
|
||||
match a.key {
|
||||
QName(b"time") => self.time = try_from_attribute_value_f64(a.value)?,
|
||||
QName(b"timestamp") => {
|
||||
self.timestamp = Some(try_from_attribute_value_timestamp(a.value)?)
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_empty(e: &XMLBytesStart) -> Result<Self, Error> {
|
||||
let mut rt = Self::default();
|
||||
rt.parse_attributes(e)?;
|
||||
Ok(rt)
|
||||
}
|
||||
|
||||
fn from_reader<B: BufRead>(e: &XMLBytesStart, r: &mut XMLReader<B>) -> Result<Self, Error> {
|
||||
let name = e.name();
|
||||
let mut rt = Self::default();
|
||||
rt.parse_attributes(e)?;
|
||||
|
||||
let mut msg = Message::new_empty(e)?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == name => break,
|
||||
Ok(XMLEvent::Text(e)) => {
|
||||
msg.text += e.unescape()?.trim();
|
||||
}
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-out") => {
|
||||
rt.system_out = parse_system(e, r)?;
|
||||
}
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"system-err") => {
|
||||
rt.system_err = parse_system(e, r)?;
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof("failure".to_string()).into())
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
rt.status = TestStatus::Failure(msg);
|
||||
Ok(rt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to decode attribute value as [`f64`]
|
||||
fn try_from_attribute_value_f64(value: Cow<[u8]>) -> Result<f64, Error> {
|
||||
match str::from_utf8(&value)? {
|
||||
"" => Ok(f64::default()),
|
||||
s => Ok(s.parse::<f64>()?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to decode attribute value as [`u64`]
|
||||
fn try_from_attribute_value_u64(value: Cow<[u8]>) -> Result<u64, Error> {
|
||||
match str::from_utf8(&value)? {
|
||||
"" => Ok(u64::default()),
|
||||
s => Ok(s.parse::<u64>()?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to decode and unescape attribute value as [`String`]
|
||||
fn try_from_attribute_value_string(value: Cow<[u8]>) -> Result<String, Error> {
|
||||
let s = str::from_utf8(&value)?;
|
||||
let u = unescape(s)?;
|
||||
Ok(u.to_string())
|
||||
}
|
||||
|
||||
/// Try to decode and unescape attribute value as [`String`]
|
||||
fn try_from_attribute_value_timestamp(value: Cow<[u8]>) -> Result<OffsetDateTime, Error> {
|
||||
let s = str::from_utf8(&value)?;
|
||||
let t = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)?;
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
/// Parse a chunk of xml as system-out or system-err
|
||||
fn parse_system<B: BufRead>(
|
||||
orig: &XMLBytesStart,
|
||||
r: &mut XMLReader<B>,
|
||||
) -> Result<Option<String>, Error> {
|
||||
let mut buf = Vec::new();
|
||||
let mut res = None;
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::End(ref e)) if e.name() == orig.name() => break,
|
||||
Ok(XMLEvent::Text(e)) => {
|
||||
res = Some(e.unescape()?.to_string());
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof(format!("{:?}", orig.name())).into());
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Creates a [`TestSuites`](struct.TestSuites.html) structure from a JUnit XML data read from `reader`
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// let xml = r#"
|
||||
/// <testsuite tests="3" failures="1">
|
||||
/// <testcase classname="foo1" name="ASuccessfulTest"/>
|
||||
/// <testcase classname="foo2" name="AnotherSuccessfulTest"/>
|
||||
/// <testcase classname="foo3" name="AFailingTest">
|
||||
/// <failure type="NotEnoughFoo"> details about failure </failure>
|
||||
/// </testcase>
|
||||
/// </testsuite>
|
||||
/// "#;
|
||||
/// let cursor = Cursor::new(xml);
|
||||
/// let r = junit_parser::from_reader(cursor);
|
||||
/// assert!(r.is_ok());
|
||||
/// ```
|
||||
pub fn from_reader<B: BufRead>(reader: B) -> Result<TestSuites, Error> {
|
||||
let mut r = XMLReader::from_reader(reader);
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
match r.read_event_into(&mut buf) {
|
||||
Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuites") => {
|
||||
return TestSuites::new_empty(e);
|
||||
}
|
||||
Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testrun") => {
|
||||
return TestSuites::new_empty(e);
|
||||
}
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuites") => {
|
||||
return TestSuites::from_reader(e, &mut r);
|
||||
}
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testrun") => {
|
||||
return TestSuites::from_reader(e, &mut r);
|
||||
}
|
||||
Ok(XMLEvent::Empty(ref e)) if e.name() == QName(b"testsuite") => {
|
||||
let ts = TestSuite::new_empty(e)?;
|
||||
let mut suites = TestSuites::default();
|
||||
suites.suites.push(ts);
|
||||
return Ok(suites);
|
||||
}
|
||||
Ok(XMLEvent::Start(ref e)) if e.name() == QName(b"testsuite") => {
|
||||
let ts = TestSuite::from_reader(e, &mut r)?;
|
||||
let mut suites = TestSuites::default();
|
||||
suites.suites.push(ts);
|
||||
return Ok(suites);
|
||||
}
|
||||
Ok(XMLEvent::Eof) => {
|
||||
return Err(XMLError::UnexpectedEof("testsuites".to_string()).into())
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`TestSuites`](struct.TestSuites.html) structure from a JUnit XML data read from a string
|
||||
pub fn from_str(s: &str) -> Result<TestSuites, Error> {
|
||||
from_reader(s.as_bytes())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fs::File, io::BufReader, path::PathBuf};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use path_macro::path;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub static TESTFILES: Lazy<PathBuf> =
|
||||
Lazy::new(|| path!(env!("CARGO_MANIFEST_DIR") / "testfiles"));
|
||||
|
||||
fn parse_test(n: &str) {
|
||||
let file = File::open(path!(*TESTFILES / format!("{n}.junit.xml"))).unwrap();
|
||||
let suites = from_reader(BufReader::new(file)).unwrap();
|
||||
insta::assert_json_snapshot!(format!("parse_{n}"), suites);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_simple() {
|
||||
parse_test("simple")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_vite() {
|
||||
parse_test("vite")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_retry() {
|
||||
parse_test("retry")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,824 +0,0 @@
|
|||
---
|
||||
source: crates/junit-parser/src/lib.rs
|
||||
expression: suites
|
||||
---
|
||||
{
|
||||
"name": "vitest tests",
|
||||
"suites": [
|
||||
{
|
||||
"name": "src/lib/server/query/util.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.788Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/server/query/util.test.ts::query builder",
|
||||
"original_name": "query builder",
|
||||
"classname": "src/lib/server/query/util.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.002,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/server/query/util.test.ts::parse search query",
|
||||
"original_name": "parse search query",
|
||||
"classname": "src/lib/server/query/util.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/server/query/util.test.ts::mapSortFields",
|
||||
"original_name": "mapSortFields",
|
||||
"classname": "src/lib/server/query/util.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.006,
|
||||
"tests": 3,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/model/validation.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.789Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/shared/model/validation.test.ts::date string",
|
||||
"original_name": "date string",
|
||||
"classname": "src/lib/shared/model/validation.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.002,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/model/validation.test.ts::filter data",
|
||||
"original_name": "filter data",
|
||||
"classname": "src/lib/shared/model/validation.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.002,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.004,
|
||||
"tests": 2,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.79Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > colorToHex",
|
||||
"original_name": "color conversion > colorToHex",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/colors.test.ts::color conversion > hexToColor",
|
||||
"original_name": "color conversion > hexToColor",
|
||||
"classname": "src/lib/shared/util/colors.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.005,
|
||||
"tests": 20,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.792Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::formatDate",
|
||||
"original_name": "formatDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.014,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::dateFromYMD",
|
||||
"original_name": "dateFromYMD",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::dateFromYMD",
|
||||
"original_name": "dateFromYMD",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::utcDateToYMD",
|
||||
"original_name": "utcDateToYMD",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::dateToYMD",
|
||||
"original_name": "dateToYMD",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.002,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::humanDate",
|
||||
"original_name": "humanDate",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange ''",
|
||||
"original_name": "parse daterange ''",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange '..'",
|
||||
"original_name": "parse daterange '..'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange 'foo..bar'",
|
||||
"original_name": "parse daterange 'foo..bar'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange '2024-04-15'",
|
||||
"original_name": "parse daterange '2024-04-15'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange '2024-04-13..2024-04-20'",
|
||||
"original_name": "parse daterange '2024-04-13..2024-04-20'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange '2024-04-13..'",
|
||||
"original_name": "parse daterange '2024-04-13..'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::parse daterange '..2024-04-20'",
|
||||
"original_name": "parse daterange '..2024-04-20'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '..2024-04-14'",
|
||||
"original_name": "shiftDateRange '..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '..2024-04-14'",
|
||||
"original_name": "shiftDateRange '..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..'",
|
||||
"original_name": "shiftDateRange '2024-04-08..'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..'",
|
||||
"original_name": "shiftDateRange '2024-04-08..'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"original_name": "shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"original_name": "shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-13..2024-04-16'",
|
||||
"original_name": "shiftDateRange '2024-04-13..2024-04-16'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-13..2024-04-16'",
|
||||
"original_name": "shiftDateRange '2024-04-13..2024-04-16'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-13..2024-04-13'",
|
||||
"original_name": "shiftDateRange '2024-04-13..2024-04-13'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-13..2024-04-13'",
|
||||
"original_name": "shiftDateRange '2024-04-13..2024-04-13'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"original_name": "shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"original_name": "shiftDateRange '2024-04-08..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::shiftDateRange '..2024-04-14'",
|
||||
"original_name": "shiftDateRange '..2024-04-14'",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.001,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/date.test.ts::dateFromHuman",
|
||||
"original_name": "dateFromHuman",
|
||||
"classname": "src/lib/shared/util/date.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.027,
|
||||
"tests": 39,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/diff.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.795Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/shared/util/diff.test.ts::versions diff",
|
||||
"original_name": "versions diff",
|
||||
"classname": "src/lib/shared/util/diff.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.003,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.003,
|
||||
"tests": 1,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/util.test.ts",
|
||||
"timestamp": "2024-06-04T11:43:17.795Z",
|
||||
"cases": [
|
||||
{
|
||||
"name": "src/lib/shared/util/util.test.ts::getQueryUrl",
|
||||
"original_name": "getQueryUrl",
|
||||
"classname": "src/lib/shared/util/util.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.005,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
},
|
||||
{
|
||||
"name": "src/lib/shared/util/util.test.ts::normalizeLineEndings",
|
||||
"original_name": "normalizeLineEndings",
|
||||
"classname": "src/lib/shared/util/util.test.ts",
|
||||
"timestamp": null,
|
||||
"time": 0.0,
|
||||
"status": "Success",
|
||||
"system_out": null,
|
||||
"system_err": null,
|
||||
"retries": []
|
||||
}
|
||||
],
|
||||
"time": 0.005,
|
||||
"tests": 2,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
}
|
||||
],
|
||||
"time": 1.371,
|
||||
"tests": 67,
|
||||
"errors": 0,
|
||||
"failures": 0,
|
||||
"skipped": 0,
|
||||
"flaky": 0
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
.viewer > pre {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
|
@ -10,7 +9,7 @@ pre, code {
|
|||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
.prose {
|
||||
.markup {
|
||||
margin: 20px 20px 0 20px;
|
||||
max-width: 800px;
|
||||
word-wrap: break-word;
|
||||
|
@ -18,170 +17,169 @@ pre, code {
|
|||
font-size: 16px;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
.prose > :first-child {
|
||||
.markup > :first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.prose > :last-child {
|
||||
.markup > :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.prose h1,
|
||||
.prose h2,
|
||||
.prose h3,
|
||||
.prose h4,
|
||||
.prose h5,
|
||||
.prose h6 {
|
||||
.markup h1,
|
||||
.markup h2,
|
||||
.markup h3,
|
||||
.markup h4,
|
||||
.markup h5,
|
||||
.markup h6 {
|
||||
font-weight: 600;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.prose h1 tt,
|
||||
.prose h1 code,
|
||||
.prose h2 tt,
|
||||
.prose h2 code,
|
||||
.prose h3 tt,
|
||||
.prose h3 code,
|
||||
.prose h4 tt,
|
||||
.prose h4 code,
|
||||
.prose h5 tt,
|
||||
.prose h5 code,
|
||||
.prose h6 tt,
|
||||
.prose h6 code {
|
||||
.markup h1 tt,
|
||||
.markup h1 code,
|
||||
.markup h2 tt,
|
||||
.markup h2 code,
|
||||
.markup h3 tt,
|
||||
.markup h3 code,
|
||||
.markup h4 tt,
|
||||
.markup h4 code,
|
||||
.markup h5 tt,
|
||||
.markup h5 code,
|
||||
.markup h6 tt,
|
||||
.markup h6 code {
|
||||
font-size: inherit;
|
||||
}
|
||||
.prose h1 {
|
||||
.markup h1 {
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 2em;
|
||||
}
|
||||
.prose h2 {
|
||||
.markup h2 {
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.prose h3 {
|
||||
.markup h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
.prose h4 {
|
||||
.markup h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.prose h5 {
|
||||
.markup h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.prose h6 {
|
||||
.markup h6 {
|
||||
color: var(--color-text-light);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.prose p,
|
||||
.prose blockquote,
|
||||
.prose details,
|
||||
.prose ul,
|
||||
.prose ol,
|
||||
.prose dl,
|
||||
.prose table,
|
||||
.prose pre {
|
||||
.markup p,
|
||||
.markup blockquote,
|
||||
.markup details,
|
||||
.markup ul,
|
||||
.markup ol,
|
||||
.markup dl,
|
||||
.markup table,
|
||||
.markup pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.prose hr {
|
||||
.markup hr {
|
||||
background-color: var(--color-secondary);
|
||||
border: 0;
|
||||
height: 4px;
|
||||
margin: 16px 0;
|
||||
padding: 0;
|
||||
}
|
||||
.prose ul,
|
||||
.prose ol {
|
||||
.markup ul,
|
||||
.markup ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.prose ul ul,
|
||||
.prose ul ol,
|
||||
.prose ol ol,
|
||||
.prose ol ul {
|
||||
.markup ul ul,
|
||||
.markup ul ol,
|
||||
.markup ol ol,
|
||||
.markup ol ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.prose ol ol,
|
||||
.prose ul ol {
|
||||
.markup ol ol,
|
||||
.markup ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
.prose li > p {
|
||||
.markup li > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.prose li + li {
|
||||
.markup li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.prose dl {
|
||||
.markup dl {
|
||||
padding: 0;
|
||||
}
|
||||
.prose dl dt {
|
||||
.markup dl dt {
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
margin-top: 16px;
|
||||
padding: 0;
|
||||
}
|
||||
.prose dl dd {
|
||||
.markup dl dd {
|
||||
margin-bottom: 16px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.prose blockquote {
|
||||
.markup blockquote {
|
||||
color: var(--color-text-light);
|
||||
border-left: 4px solid var(--color-secondary);
|
||||
margin-left: 0;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.prose blockquote > :first-child {
|
||||
.markup blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.prose blockquote > :last-child {
|
||||
.markup blockquote > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.prose table {
|
||||
.markup table {
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.prose table th {
|
||||
.markup table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
.prose table th,
|
||||
.prose table td {
|
||||
.markup table th,
|
||||
.markup table td {
|
||||
border: 1px solid var(--color-secondary) !important;
|
||||
padding: 6px 13px !important;
|
||||
}
|
||||
.prose table tr {
|
||||
.markup table tr {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
.prose table tr:nth-child(2n) {
|
||||
.markup table tr:nth-child(2n) {
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
.prose img,
|
||||
.prose video {
|
||||
.markup img,
|
||||
.markup video {
|
||||
box-sizing: initial;
|
||||
max-width: 100%;
|
||||
}
|
||||
.prose img[align="right"],
|
||||
.prose video[align="right"] {
|
||||
.markup img[align="right"],
|
||||
.markup video[align="right"] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.prose img[align="left"],
|
||||
.prose video[align="left"] {
|
||||
.markup img[align="left"],
|
||||
.markup video[align="left"] {
|
||||
padding-right: 28px;
|
||||
}
|
||||
.prose code {
|
||||
.markup code {
|
||||
white-space: break-spaces;
|
||||
border-radius: 4px;
|
||||
margin: 0;
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 85%;
|
||||
}
|
||||
.prose code br {
|
||||
.markup code br {
|
||||
display: none;
|
||||
}
|
||||
.prose pre {
|
||||
.markup pre {
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
line-height: 1.45;
|
||||
|
@ -189,16 +187,13 @@ pre, code {
|
|||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
}
|
||||
.prose pre code {
|
||||
padding: 0;
|
||||
}
|
||||
.prose pre code:before,
|
||||
.prose pre code:after {
|
||||
.markup pre code:before,
|
||||
.markup pre code:after {
|
||||
content: normal;
|
||||
}
|
||||
.prose .ui.list .list,
|
||||
.prose ol.ui.list ol,
|
||||
.prose ul.ui.list ul {
|
||||
.markup .ui.list .list,
|
||||
.markup ol.ui.list ol,
|
||||
.markup ul.ui.list ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ pub(crate) const STYLE_CONTENT_PATH: &str = "/content1.css";
|
|||
|
||||
const FAVICON_BYTES: &[u8; 268] = include_bytes!("../resources/favicon.ico");
|
||||
const STYLE_MAIN_BYTES: &[u8; 4057] = include_bytes!("../resources/style.css");
|
||||
const STYLE_CONTENT_BYTES: &[u8; 10079] = include_bytes!("../resources/content.css");
|
||||
const STYLE_CONTENT_BYTES: &[u8; 10063] = include_bytes!("../resources/content.css");
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
|
|
10
src/cache.rs
10
src/cache.rs
|
@ -249,12 +249,8 @@ impl CacheEntry {
|
|||
.entries()
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let name = entry.filename().as_str().ok()?;
|
||||
if name.ends_with('/') {
|
||||
return None;
|
||||
}
|
||||
Some((
|
||||
name.to_owned(),
|
||||
entry.filename().as_str().ok()?.to_owned(),
|
||||
FileEntry {
|
||||
header_offset: entry.header_offset().try_into().ok()?,
|
||||
uncompressed_size: entry.uncompressed_size().try_into().ok()?,
|
||||
|
@ -273,7 +269,7 @@ impl CacheEntry {
|
|||
}
|
||||
|
||||
pub fn get_file(&self, path: &str, url_query: &str) -> Result<GetFileResult> {
|
||||
let path = path.trim_matches('/');
|
||||
let path = path.trim_start_matches('/');
|
||||
let mut index_path: Option<Cow<str>> = None;
|
||||
|
||||
if path.is_empty() {
|
||||
|
@ -308,7 +304,7 @@ impl CacheEntry {
|
|||
}
|
||||
|
||||
// Directory listing
|
||||
let path_as_dir: Cow<str> = if path.is_empty() {
|
||||
let path_as_dir: Cow<str> = if path.is_empty() || path.ends_with('/') {
|
||||
path.into()
|
||||
} else {
|
||||
format!("{path}/").into()
|
||||
|
|
|
@ -90,7 +90,7 @@ impl Default for ConfigData {
|
|||
repo_blacklist: QueryFilterList::default(),
|
||||
repo_whitelist: QueryFilterList::default(),
|
||||
site_aliases: HashMap::new(),
|
||||
viewer_max_size: Some(NonZeroU32::new(500_000).unwrap()),
|
||||
viewer_max_size: Some(NonZeroU32::new(100_000).unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,17 +269,10 @@ impl IntoResponse for ErrorJson {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod tests {
|
||||
use http::{header, HeaderMap};
|
||||
use once_cell::sync::Lazy;
|
||||
use path_macro::path;
|
||||
use rstest::rstest;
|
||||
|
||||
pub static TESTFILES: Lazy<PathBuf> =
|
||||
Lazy::new(|| path!(env!("CARGO_MANIFEST_DIR") / "tests" / "testfiles"));
|
||||
|
||||
#[rstest]
|
||||
#[case("", false)]
|
||||
#[case("br", false)]
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use crate::error::Error;
|
||||
|
||||
use super::Viewer;
|
||||
|
||||
/// JUnit format documentation: https://llg.cubic.org/docs/junit/
|
||||
pub struct JunitViewer;
|
||||
|
||||
impl Viewer for JunitViewer {
|
||||
fn id(&self) -> &'static str {
|
||||
"junit"
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"JUnit"
|
||||
}
|
||||
|
||||
fn is_applicable(&self, filename: &str, ext: &str) -> bool {
|
||||
ext == "xml" && filename.contains("junit")
|
||||
}
|
||||
|
||||
fn try_render(&self, filename: &str, _ext: &str, data: &str) -> Result<String, Error> {
|
||||
let suites = junit_parser::from_str(data).map_err(|e| {
|
||||
tracing::error!("could not parse junit report {filename}: {e}");
|
||||
Error::ViewerNotApplicable
|
||||
})?;
|
||||
dbg!(&suites);
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use path_macro::path;
|
||||
|
||||
use crate::{util::tests::TESTFILES, viewer::Viewer};
|
||||
|
||||
use super::JunitViewer;
|
||||
|
||||
#[test]
|
||||
fn t1() {
|
||||
let data =
|
||||
std::fs::read_to_string(path!(*TESTFILES / "junit" / "simple.junit.xml")).unwrap();
|
||||
let html = JunitViewer.try_render("", "", &data).unwrap();
|
||||
println!("{html}");
|
||||
}
|
||||
}
|
|
@ -37,20 +37,13 @@ impl Viewer for MarkdownViewer {
|
|||
}
|
||||
|
||||
fn try_render(&self, _filename: &str, _ext: &str, data: &str) -> Result<String, Error> {
|
||||
let mut options = comrak::Options::default();
|
||||
options.extension.autolink = true;
|
||||
options.extension.table = true;
|
||||
options.extension.tasklist = true;
|
||||
options.extension.strikethrough = true;
|
||||
options.extension.multiline_block_quotes = true;
|
||||
options.extension.superscript = true;
|
||||
|
||||
let options = comrak::Options::default();
|
||||
let mut plugins = comrak::Plugins::default();
|
||||
plugins.render.codefence_syntax_highlighter = Some(&self.adapter);
|
||||
|
||||
let html = comrak::markdown_to_html_with_plugins(data, &options, &plugins);
|
||||
|
||||
Ok(format!("<div class=\"prose\">{html}</div>"))
|
||||
Ok(format!("<div class=\"markup\">{html}</div>"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ use syntect::parsing::SyntaxSet;
|
|||
use crate::{error::Error, templates::ViewerLink};
|
||||
|
||||
mod code;
|
||||
mod junit;
|
||||
mod markdown;
|
||||
|
||||
pub trait Viewer: Sync + Send {
|
||||
|
@ -17,7 +16,7 @@ pub trait Viewer: Sync + Send {
|
|||
}
|
||||
|
||||
pub struct Viewers {
|
||||
viewers: [Box<dyn Viewer>; 3],
|
||||
viewers: [Box<dyn Viewer>; 2],
|
||||
}
|
||||
|
||||
pub struct RenderRes {
|
||||
|
@ -30,7 +29,6 @@ impl Viewers {
|
|||
let ss = Arc::new(SyntaxSet::load_defaults_newlines());
|
||||
Self {
|
||||
viewers: [
|
||||
Box::new(junit::JunitViewer),
|
||||
Box::new(markdown::MarkdownViewer::new(ss.clone())),
|
||||
Box::new(code::CodeViewer::new(ss)),
|
||||
],
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue