Refactored code to use regexes from regexes module
Extracted constants from the code for easier reusability in some cases. Change select queries for multiple elements to use the `,` operator instead of calling `chain`. Remove check for "null" in `fix_lazy_images`. This mitigates a JSOM issue so it doesn't affect the Rust code in any way.
This commit is contained in:
parent
75018894ae
commit
b661211f0f
1 changed files with 148 additions and 151 deletions
|
@ -8,15 +8,41 @@ use kuchiki::{
|
|||
traits::*,
|
||||
NodeData, NodeRef,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
const SHARE_ELEMENT_THRESHOLD: usize = 500;
|
||||
const READABILITY_SCORE: &'static str = "readability-score";
|
||||
const HTML_NS: &'static str = "http://www.w3.org/1999/xhtml";
|
||||
// TODO: Change to HashSet
|
||||
const PHRASING_ELEMS: [&str; 39] = [
|
||||
"abbr", "audio", "b", "bdo", "br", "button", "cite", "code", "data", "datalist", "dfn", "em",
|
||||
"embed", "i", "img", "input", "kbd", "label", "mark", "math", "meter", "noscript", "object",
|
||||
"output", "progress", "q", "ruby", "samp", "script", "select", "small", "span", "strong",
|
||||
"sub", "sup", "textarea", "time", "var", "wbr",
|
||||
];
|
||||
// TODO: Change to HashSet
|
||||
const DEFAULT_TAGS_TO_SCORE: [&str; 9] =
|
||||
["section", "h2", "h3", "h4", "h5", "h6", "p", "td", "pre"];
|
||||
// TODO: Change to HashSet
|
||||
const ALTER_TO_DIV_EXCEPTIONS: [&str; 4] = ["div", "article", "section", "p"];
|
||||
const PRESENTATIONAL_ATTRIBUTES: [&str; 12] = [
|
||||
"align",
|
||||
"background",
|
||||
"bgcolor",
|
||||
"border",
|
||||
"cellpadding",
|
||||
"cellspacing",
|
||||
"frame",
|
||||
"hspace",
|
||||
"rules",
|
||||
"style",
|
||||
"valign",
|
||||
"vspace",
|
||||
];
|
||||
|
||||
const DATA_TABLE_DESCENDANTS: [&str; 5] = ["col", "colgroup", "tfoot", "thead", "th"];
|
||||
// TODO: Change to HashSet
|
||||
const DEPRECATED_SIZE_ATTRIBUTE_ELEMS: [&str; 5] = ["table", "th", "td", "hr", "pre"];
|
||||
|
||||
mod regexes;
|
||||
|
||||
pub struct Readability {
|
||||
|
@ -85,15 +111,11 @@ impl Readability {
|
|||
imgs.filter(|img_node_ref| {
|
||||
let img_attrs = img_node_ref.attributes.borrow();
|
||||
!img_attrs.map.iter().any(|(name, attr)| {
|
||||
// TODO: Replace with regex
|
||||
&name.local == "src"
|
||||
|| &name.local == "srcset"
|
||||
|| &name.local == "data-src"
|
||||
|| &name.local == "data-srcset"
|
||||
|| attr.value.ends_with(".jpg")
|
||||
|| attr.value.ends_with(".jpeg")
|
||||
|| attr.value.ends_with(".png")
|
||||
|| attr.value.ends_with(".webp")
|
||||
|| regexes::is_match_img_ext(&attr.value)
|
||||
})
|
||||
})
|
||||
.for_each(|img_ref| img_ref.as_node().detach());
|
||||
|
@ -130,11 +152,7 @@ impl Readability {
|
|||
!val.value.trim().is_empty()
|
||||
&& (&attr.local == "src"
|
||||
|| &attr.local == "srcset"
|
||||
// TODO: Replace with regex
|
||||
|| val.value.ends_with(".jpg")
|
||||
|| val.value.ends_with(".jpeg")
|
||||
|| val.value.ends_with(".png")
|
||||
|| val.value.ends_with(".webp"))
|
||||
|| regexes::is_match_img_ext(&val.value))
|
||||
});
|
||||
for (prev_attr, prev_value) in prev_attrs {
|
||||
match new_img.attributes.borrow().get(&prev_attr.local) {
|
||||
|
@ -405,19 +423,16 @@ impl Readability {
|
|||
let elem_attrs = elem_data.attributes.borrow();
|
||||
let rel_attr = elem_attrs.get("rel");
|
||||
let itemprop_attr = elem_attrs.get("itemprop");
|
||||
let byline_regex = Regex::new(r"(?i)byline|author|dateline|writtenby|p-author")
|
||||
.expect("Unable to create byline_regex");
|
||||
let is_byline = (if rel_attr.is_some() {
|
||||
rel_attr.unwrap() == "author"
|
||||
} else if itemprop_attr.is_some() {
|
||||
itemprop_attr.unwrap().contains("author")
|
||||
} else {
|
||||
byline_regex.is_match(match_string)
|
||||
regexes::is_match_byline(match_string)
|
||||
}) && Self::is_valid_byline(&node_ref.text_contents());
|
||||
if is_byline {
|
||||
self.byline = Some(node_ref.text_contents().trim().to_owned());
|
||||
}
|
||||
dbg!(is_byline);
|
||||
is_byline
|
||||
} else {
|
||||
false
|
||||
|
@ -513,9 +528,7 @@ impl Readability {
|
|||
}
|
||||
!node_ref.children().any(|node| {
|
||||
node.as_text().is_some()
|
||||
&& Regex::new(r"\S$")
|
||||
.unwrap()
|
||||
.is_match(&node.text_contents().trim_end())
|
||||
&& regexes::is_match_has_content(&node.text_contents().trim_end())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -523,9 +536,8 @@ impl Readability {
|
|||
let will_normalize = normalize_spaces.unwrap_or(true);
|
||||
let text = node_ref.text_contents();
|
||||
let text = text.trim();
|
||||
let normalize_regex = Regex::new(r"\s{2,}").unwrap();
|
||||
if will_normalize {
|
||||
return normalize_regex.replace_all(&text, " ").to_string();
|
||||
return regexes::NORMALIZE_REGEX.replace_all(&text, " ").to_string();
|
||||
}
|
||||
text.to_owned()
|
||||
}
|
||||
|
@ -603,15 +615,13 @@ impl Readability {
|
|||
fn get_class_weight(node_ref: &NodeRef) -> i32 {
|
||||
//TODO: Add check for weighing classes
|
||||
let mut weight = 0;
|
||||
let positive_regex = Regex::new(r"(?i)article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story").unwrap();
|
||||
let negative_regex = Regex::new(r"(?i)hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget").unwrap();
|
||||
let node_elem = node_ref.as_element().unwrap();
|
||||
let node_attrs = node_elem.attributes.borrow();
|
||||
if let Some(id) = node_attrs.get("id") {
|
||||
if !id.trim().is_empty() {
|
||||
weight = if positive_regex.is_match(id) {
|
||||
weight = if regexes::is_match_positive(id) {
|
||||
weight + 25
|
||||
} else if negative_regex.is_match(id) {
|
||||
} else if regexes::is_match_negative(id) {
|
||||
weight - 25
|
||||
} else {
|
||||
weight
|
||||
|
@ -620,9 +630,9 @@ impl Readability {
|
|||
}
|
||||
if let Some(class) = node_attrs.get("class") {
|
||||
if !class.trim().is_empty() {
|
||||
weight = if positive_regex.is_match(class) {
|
||||
weight = if regexes::is_match_positive(class) {
|
||||
weight + 25
|
||||
} else if negative_regex.is_match(class) {
|
||||
} else if regexes::is_match_negative(class) {
|
||||
weight - 25
|
||||
} else {
|
||||
weight
|
||||
|
@ -641,8 +651,8 @@ impl Readability {
|
|||
// should not also be mutably borrowed
|
||||
score += Self::get_class_weight(node_ref);
|
||||
let mut elem_attrs = element.attributes.borrow_mut();
|
||||
elem_attrs.insert("readability-score", score.to_string());
|
||||
let readability = elem_attrs.get_mut("readability-score");
|
||||
elem_attrs.insert(READABILITY_SCORE, score.to_string());
|
||||
let readability = elem_attrs.get_mut(READABILITY_SCORE);
|
||||
match &*element.name.local {
|
||||
"div" => score += 5,
|
||||
"pre" | "td" | "blockquote" => score += 3,
|
||||
|
@ -717,8 +727,8 @@ impl Readability {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let data_table_descendants = vec!["col", "colgroup", "tfoot", "thead", "th"];
|
||||
if data_table_descendants
|
||||
|
||||
if DATA_TABLE_DESCENDANTS
|
||||
.iter()
|
||||
.any(|tag_name| table_node.select_first(tag_name).is_ok())
|
||||
{
|
||||
|
@ -750,35 +760,27 @@ impl Readability {
|
|||
|
||||
/// Convert images and figures that have properties like data-src into images that can be loaded without JS
|
||||
fn fix_lazy_images(node_ref: &mut NodeRef) {
|
||||
let imgs = node_ref.select("img").unwrap();
|
||||
let pictures = node_ref.select("picture").unwrap();
|
||||
let figures = node_ref.select("figure").unwrap();
|
||||
let regex = Regex::new(r"(?i)^data:\s*([^\s;,]+)\s*;\s*base64\s*").unwrap();
|
||||
let nodes = imgs.chain(pictures).chain(figures);
|
||||
let nodes = node_ref.select("img, picture, figure").unwrap();
|
||||
for node in nodes {
|
||||
let mut node_attr = node.attributes.borrow_mut();
|
||||
if let Some(src) = node_attr.get("src") {
|
||||
let src_captures = regex.captures(src);
|
||||
let src_captures = regexes::B64_DATA_URL_REGEX.captures(src);
|
||||
if src_captures.is_some() {
|
||||
let svg_capture = src_captures.unwrap().get(1);
|
||||
if svg_capture.is_some() && svg_capture.unwrap().as_str() == "image/svg+xml" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let svg_could_be_removed = node_attr
|
||||
let src_could_be_removed = node_attr
|
||||
.map
|
||||
.iter()
|
||||
.filter(|(name, _)| &name.local != "src")
|
||||
.filter(|(_, val)| {
|
||||
let regex = Regex::new(r"(?i)\.(jpg|jpeg|png|webp)").unwrap();
|
||||
regex.is_match(&val.value)
|
||||
})
|
||||
.filter(|(_, val)| regexes::is_match_img_ext(&val.value))
|
||||
.count()
|
||||
> 0;
|
||||
|
||||
if svg_could_be_removed {
|
||||
let base64_regex = Regex::new(r"(?i)base64\s*").unwrap();
|
||||
let b64_start = base64_regex.find(src).unwrap().start();
|
||||
if src_could_be_removed {
|
||||
let b64_start = regexes::BASE64_REGEX.find(src).unwrap().start();
|
||||
let b64_length = src.len() - b64_start;
|
||||
if b64_length < 133 {
|
||||
node_attr.remove("src");
|
||||
|
@ -789,7 +791,7 @@ impl Readability {
|
|||
let src = node_attr.get("src");
|
||||
let srcset = node_attr.get("srcset");
|
||||
let class = node_attr.get("class");
|
||||
if (src.is_some() || (srcset.is_some() && srcset.unwrap() != "null"))
|
||||
if (src.is_some() || srcset.is_some())
|
||||
&& class.is_some()
|
||||
&& !class.unwrap().contains("lazy")
|
||||
{
|
||||
|
@ -803,11 +805,9 @@ impl Readability {
|
|||
.filter(|(key, _)| !(&key.local == "src" || &key.local == "srcset"))
|
||||
.for_each(|(_, val)| {
|
||||
let mut copy_to = "";
|
||||
let srcset_regex = Regex::new(r"\.(jpg|jpeg|png|webp)\s+\d").unwrap();
|
||||
let src_regex = Regex::new(r"^\s*\S+\.(jpg|jpeg|png|webp)\S*\s*$").unwrap();
|
||||
if srcset_regex.is_match(&val.value) {
|
||||
if regexes::is_match_srcset(&val.value) {
|
||||
copy_to = "srcset";
|
||||
} else if src_regex.is_match(&val.value) {
|
||||
} else if regexes::is_match_src_regex(&val.value) {
|
||||
copy_to = "src";
|
||||
}
|
||||
if copy_to.len() > 0 {
|
||||
|
@ -818,9 +818,8 @@ impl Readability {
|
|||
}
|
||||
} else if tag_name == "figure" {
|
||||
let node_ref = node.as_node();
|
||||
let imgs = node_ref.select("img").unwrap();
|
||||
let pictures = node_ref.select("picture").unwrap();
|
||||
if imgs.chain(pictures).count() > 0 {
|
||||
let img_picture_nodes = node_ref.select("img, picture").unwrap();
|
||||
if img_picture_nodes.count() > 0 {
|
||||
let img = NodeRef::new_element(
|
||||
QualName::new(
|
||||
None,
|
||||
|
@ -872,43 +871,43 @@ impl Readability {
|
|||
Some(-1),
|
||||
Some(is_data_table),
|
||||
)
|
||||
})
|
||||
.map(|node_data_ref|{
|
||||
let weight = Self::get_class_weight(node_data_ref.as_node());
|
||||
(node_data_ref,weight)
|
||||
})
|
||||
.filter(|(_, weight)| weight < &0)
|
||||
.filter(|(node_data_ref,_)| get_char_count(node_data_ref.as_node()) < 10)
|
||||
.filter(|(node_data_ref,_)|{
|
||||
let embed_tags = vec!["object", "embed", "iframe"];
|
||||
});
|
||||
let mut next_node = nodes.next();
|
||||
while let Some(node_data_ref) = next_node {
|
||||
next_node = nodes.next();
|
||||
let node = node_data_ref.as_node();
|
||||
let weight = Self::get_class_weight(node);
|
||||
// Remove all elements with negative class weights
|
||||
if weight < 0 {
|
||||
node.detach();
|
||||
continue;
|
||||
}
|
||||
|
||||
if get_char_count(node) >= 10 {
|
||||
continue;
|
||||
}
|
||||
let mut embeds = node_data_ref
|
||||
.as_node()
|
||||
.select(embed_tags.join(",").as_str())
|
||||
.select("object, embed, iframe")
|
||||
.unwrap();
|
||||
if embed_tags.contains(&&*node_data_ref.name.local) {
|
||||
embeds.next();
|
||||
}
|
||||
let videos_regex = Regex::new(r"(?i)\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)").unwrap();
|
||||
!(embeds.any(|node| &node.name.local == "object") || embeds.any(|node_data_ref| {
|
||||
let can_skip_embed = embeds.any(|node_data_ref| {
|
||||
&node_data_ref.name.local == "object" || {
|
||||
let attrs = node_data_ref.attributes.borrow();
|
||||
!attrs.map.iter().any(|(key,_)|videos_regex.is_match(&key.local))
|
||||
}))
|
||||
})
|
||||
.for_each(|(node_data_ref, weight)| {
|
||||
let node = node_data_ref.as_node();
|
||||
|
||||
let mut p_nodes = node_data_ref.as_node().select("p").unwrap().count();
|
||||
let mut img_nodes = node_data_ref.as_node().select("img").unwrap().count();
|
||||
let mut li_nodes = node_data_ref.as_node().select("li").unwrap().count();
|
||||
let mut input_nodes = node_data_ref.as_node().select("input").unwrap().count();
|
||||
|
||||
match node_name.as_ref() {
|
||||
"p" => p_nodes -= 1,
|
||||
"img" =>img_nodes -= 1,
|
||||
"li" => li_nodes -= 1,
|
||||
"input" => input_nodes -= 1,
|
||||
_ => ()
|
||||
attrs
|
||||
.map
|
||||
.iter()
|
||||
.any(|(_, val)| regexes::is_match_videos(&val.value))
|
||||
}
|
||||
});
|
||||
if can_skip_embed {
|
||||
continue;
|
||||
}
|
||||
|
||||
let p_nodes = node_data_ref.as_node().select("p").unwrap().count();
|
||||
let img_nodes = node_data_ref.as_node().select("img").unwrap().count();
|
||||
let li_nodes = node_data_ref.as_node().select("li").unwrap().count() as i32 - 100;
|
||||
let input_nodes = node_data_ref.as_node().select("input").unwrap().count();
|
||||
|
||||
let p = p_nodes as f32;
|
||||
let img = img_nodes as f32;
|
||||
|
@ -917,79 +916,80 @@ impl Readability {
|
|||
let link_density = Self::get_link_density(node);
|
||||
let content_length = Self::get_inner_text(node, None).len();
|
||||
let has_figure_ancestor = Self::has_ancestor_tag(node, "figure", None, None);
|
||||
let have_to_remove = (img_nodes > 1 && p /img < 0.5 && !has_figure_ancestor) ||
|
||||
(!is_list && li_nodes > p_nodes) || (input_nodes > (p_nodes / 3)) ||
|
||||
(!is_list && content_length < 25 && (img_nodes == 0 || img_nodes > 2) && !has_figure_ancestor) ||
|
||||
(!is_list && weight < 25 && link_density > 0.2) || (weight >= 25 && link_density > 0.5) ||
|
||||
((embed_count == 1 && content_length < 75) || embed_count > 1);
|
||||
let have_to_remove = (img_nodes > 1 && p / img < 0.5 && !has_figure_ancestor)
|
||||
|| (!is_list && li_nodes > p_nodes as i32)
|
||||
|| (input_nodes > (p_nodes / 3))
|
||||
|| (!is_list
|
||||
&& content_length < 25
|
||||
&& (img_nodes == 0 || img_nodes > 2)
|
||||
&& !has_figure_ancestor)
|
||||
|| (!is_list && weight < 25 && link_density > 0.2)
|
||||
|| (weight >= 25 && link_density > 0.5)
|
||||
|| ((embed_count == 1 && content_length < 75) || embed_count > 1);
|
||||
if have_to_remove {
|
||||
node.detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean a node of all elements of type "tag". (Unless it's a YouTube or Vimeo video)
|
||||
fn clean(node_ref: &mut NodeRef, tag_name: &str) {
|
||||
// Can be changed to a HashSet
|
||||
let is_embed = vec!["object", "embed", "iframe"].contains(&tag_name);
|
||||
let mut nodes = node_ref.select(tag_name).unwrap();
|
||||
let videos_regex = Regex::new(r"(?i)\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)").unwrap();
|
||||
if &node_ref.as_element().unwrap().name.local == tag_name {
|
||||
if node_ref.as_element().is_some() && &node_ref.as_element().unwrap().name.local == tag_name
|
||||
{
|
||||
nodes.next();
|
||||
}
|
||||
nodes
|
||||
.filter(|node_data_ref| {
|
||||
let mut nodes = nodes.filter(|node_data_ref| {
|
||||
!is_embed
|
||||
|| {
|
||||
let attrs = node_data_ref.attributes.borrow();
|
||||
!attrs
|
||||
.map
|
||||
.iter()
|
||||
.any(|(key, _)| videos_regex.is_match(&key.local))
|
||||
.any(|(_, val)| regexes::is_match_videos(&val.value))
|
||||
}
|
||||
|| &node_data_ref.name.local == "object" // This currently does not check the innerHTML.
|
||||
});
|
||||
let mut node = nodes.next();
|
||||
while let Some(node_data_ref) = node {
|
||||
node = nodes.next();
|
||||
node_data_ref.as_node().detach()
|
||||
}
|
||||
|| &node_data_ref.name.local == "object"
|
||||
})
|
||||
.for_each(|node_data_ref| node_data_ref.as_node().detach());
|
||||
}
|
||||
|
||||
/// Clean out spurious headers from an Element. Checks things like classnames and link density.
|
||||
fn clean_headers(node_ref: &mut NodeRef) {
|
||||
let mut nodes = node_ref.select("h1,h2").unwrap();
|
||||
|
||||
if vec!["h1", "h2"].contains(&node_ref.as_element().unwrap().name.local.as_ref()) {
|
||||
let mut nodes = node_ref
|
||||
.select("h1, h2")
|
||||
.unwrap()
|
||||
.filter(|node_data_ref| Self::get_class_weight(node_data_ref.as_node()) < 0);
|
||||
if node_ref.as_element().is_some()
|
||||
&& vec!["h1", "h2"].contains(&node_ref.as_element().unwrap().name.local.as_ref())
|
||||
{
|
||||
nodes.next();
|
||||
}
|
||||
nodes
|
||||
.filter(|node_data_ref| Self::get_class_weight(node_data_ref.as_node()) < 0)
|
||||
.for_each(|node_data_ref| node_data_ref.as_node().detach());
|
||||
let mut node = nodes.next();
|
||||
|
||||
while let Some(node_data_ref) = node {
|
||||
node = nodes.next();
|
||||
node_data_ref.as_node().detach();
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the style attribute on every element and descendants.
|
||||
fn clean_styles(node_ref: &mut NodeRef) {
|
||||
let presentational_attributes = vec![
|
||||
"align",
|
||||
"background",
|
||||
"bgcolor",
|
||||
"border",
|
||||
"cellpadding",
|
||||
"cellspacing",
|
||||
"frame",
|
||||
"hspace",
|
||||
"rules",
|
||||
"style",
|
||||
"valign",
|
||||
"vspace",
|
||||
];
|
||||
let deprecated_size_attribute_elems = vec!["table", "th", "td", "hr", "pre"];
|
||||
node_ref
|
||||
.inclusive_descendants()
|
||||
.elements()
|
||||
.filter(|node| &node.name.local != "svg")
|
||||
.for_each(|node_data_ref| {
|
||||
let mut attrs = node_data_ref.attributes.borrow_mut();
|
||||
presentational_attributes.iter().for_each(|pres_attr| {
|
||||
PRESENTATIONAL_ATTRIBUTES.iter().for_each(|pres_attr| {
|
||||
attrs.remove(*pres_attr);
|
||||
});
|
||||
if deprecated_size_attribute_elems.contains(&node_data_ref.name.local.as_ref()) {
|
||||
if DEPRECATED_SIZE_ATTRIBUTE_ELEMS.contains(&node_data_ref.name.local.as_ref()) {
|
||||
attrs.remove("width");
|
||||
attrs.remove("height");
|
||||
}
|
||||
|
@ -1026,13 +1026,10 @@ impl Readability {
|
|||
Self::clean(node_ref, "link");
|
||||
Self::clean(node_ref, "aside");
|
||||
|
||||
// TODO: Extract as constant
|
||||
let share_element_threshold = 500;
|
||||
let regex = Regex::new(r"(\b|_)(share|sharedaddy)(\b|_)").unwrap();
|
||||
|
||||
node_ref.children().for_each(|mut node| {
|
||||
Self::clean_matched_nodes(&mut node, |node: &NodeRef, match_string| {
|
||||
regex.is_match(match_string) && node.text_contents().len() < share_element_threshold
|
||||
regexes::is_match_share_elems(match_string)
|
||||
&& node.text_contents().len() < SHARE_ELEMENT_THRESHOLD
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1554,7 +1551,7 @@ impl Readability {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{Readability, SizeInfo, HTML_NS};
|
||||
use super::{Readability, SizeInfo, HTML_NS, READABILITY_SCORE};
|
||||
use html5ever::{LocalName, Namespace, QualName};
|
||||
use kuchiki::traits::*;
|
||||
use kuchiki::NodeRef;
|
||||
|
@ -2348,31 +2345,31 @@ characters. For that reason, this <p> tag could not be a byline because it's too
|
|||
let mut node = target.as_node().clone();
|
||||
Readability::initialize_node(&mut node);
|
||||
let node_attrs = node.as_element().unwrap().attributes.borrow();
|
||||
assert_eq!(Some("55"), node_attrs.get("readability-score"));
|
||||
assert_eq!(Some("55"), node_attrs.get(READABILITY_SCORE));
|
||||
|
||||
target = doc.root_node.select_first("h1.hidden").unwrap();
|
||||
let mut node = target.as_node().clone();
|
||||
Readability::initialize_node(&mut node);
|
||||
let node_attrs = node.as_element().unwrap().attributes.borrow();
|
||||
assert_eq!(Some("-30"), node_attrs.get("readability-score"));
|
||||
assert_eq!(Some("-30"), node_attrs.get(READABILITY_SCORE));
|
||||
|
||||
target = doc.root_node.select_first("p#story").unwrap();
|
||||
let mut node = target.as_node().clone();
|
||||
Readability::initialize_node(&mut node);
|
||||
let node_attrs = node.as_element().unwrap().attributes.borrow();
|
||||
assert_eq!(Some("25"), node_attrs.get("readability-score"));
|
||||
assert_eq!(Some("25"), node_attrs.get(READABILITY_SCORE));
|
||||
|
||||
target = doc.root_node.select_first("div#comments").unwrap();
|
||||
let mut node = target.as_node().clone();
|
||||
Readability::initialize_node(&mut node);
|
||||
let node_attrs = node.as_element().unwrap().attributes.borrow();
|
||||
assert_eq!(Some("-20"), node_attrs.get("readability-score"));
|
||||
assert_eq!(Some("-20"), node_attrs.get(READABILITY_SCORE));
|
||||
|
||||
target = doc.root_node.select_first("pre.comment").unwrap();
|
||||
let mut node = target.as_node().clone();
|
||||
Readability::initialize_node(&mut node);
|
||||
let node_attrs = node.as_element().unwrap().attributes.borrow();
|
||||
assert_eq!(Some("-22"), node_attrs.get("readability-score"));
|
||||
assert_eq!(Some("-22"), node_attrs.get(READABILITY_SCORE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue