servo/tests/unit/style/properties/serialization.rs
2018-01-22 14:58:05 -08:00

1044 lines
44 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use properties::{parse, parse_input};
use style::computed_values::display::T as Display;
use style::properties::{PropertyDeclaration, Importance};
use style::properties::declaration_block::PropertyDeclarationBlock;
use style::properties::parse_property_declaration_list;
use style::values::{CustomIdent, RGBA};
use style::values::generics::flex::FlexBasis;
use style::values::specified::{BorderStyle, BorderSideWidth, Color};
use style::values::specified::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::specified::NoCalcLength;
use style::values::specified::url::SpecifiedUrl;
use style_traits::ToCss;
use stylesheets::block_from;
trait ToCssString {
fn to_css_string(&self) -> String;
}
impl ToCssString for PropertyDeclarationBlock {
fn to_css_string(&self) -> String {
let mut css = String::new();
self.to_css(&mut css).unwrap();
css
}
}
#[test]
fn property_declaration_block_should_serialize_correctly() {
use style::properties::longhands::overflow_x::SpecifiedValue as OverflowValue;
let declarations = vec![
(PropertyDeclaration::Width(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32))),
Importance::Normal),
(PropertyDeclaration::MinHeight(
LengthOrPercentage::Length(NoCalcLength::from_px(20f32))),
Importance::Normal),
(PropertyDeclaration::Height(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Important),
(PropertyDeclaration::Display(Display::InlineBlock),
Importance::Normal),
(PropertyDeclaration::OverflowX(
OverflowValue::Auto),
Importance::Normal),
(PropertyDeclaration::OverflowY(
OverflowValue::Auto),
Importance::Normal),
];
let block = block_from(declarations);
let css_string = block.to_css_string();
assert_eq!(
css_string,
"width: 70px; min-height: 20px; height: 20px !important; display: inline-block; overflow: auto;"
);
}
mod shorthand_serialization {
pub use super::*;
pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
let block = block_from(properties.into_iter().map(|d| (d, Importance::Normal)));
block.to_css_string()
}
// Add Test to show error if a longhand property is missing!!!!!!
mod overflow {
pub use super::*;
use style::properties::longhands::overflow_x::SpecifiedValue as OverflowValue;
#[test]
fn equal_overflow_properties_should_serialize_to_single_value() {
let mut properties = Vec::new();
let overflow = OverflowValue::Auto;
properties.push(PropertyDeclaration::OverflowX(overflow));
properties.push(PropertyDeclaration::OverflowY(overflow));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "overflow: auto;");
}
#[test]
fn different_overflow_properties_should_serialize_to_two_values() {
let mut properties = Vec::new();
let overflow_x = OverflowValue::Scroll;
properties.push(PropertyDeclaration::OverflowX(overflow_x));
let overflow_y = OverflowValue::Auto;
properties.push(PropertyDeclaration::OverflowY(overflow_y));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "overflow-x: scroll; overflow-y: auto;");
}
}
mod four_sides_shorthands {
pub use super::*;
// we can use margin as a base to test out the different combinations
// but afterwards, we only need to to one test per "four sides shorthand"
#[test]
fn all_equal_properties_should_serialize_to_one_value() {
let mut properties = Vec::new();
let px_70 = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(70f32));
properties.push(PropertyDeclaration::MarginTop(px_70.clone()));
properties.push(PropertyDeclaration::MarginRight(px_70.clone()));
properties.push(PropertyDeclaration::MarginBottom(px_70.clone()));
properties.push(PropertyDeclaration::MarginLeft(px_70));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "margin: 70px;");
}
#[test]
fn equal_vertical_and_equal_horizontal_properties_should_serialize_to_two_value() {
let mut properties = Vec::new();
let vertical_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
let horizontal_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
properties.push(PropertyDeclaration::MarginTop(vertical_px.clone()));
properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
properties.push(PropertyDeclaration::MarginBottom(vertical_px));
properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "margin: 10px 5px;");
}
#[test]
fn different_vertical_and_equal_horizontal_properties_should_serialize_to_three_values() {
let mut properties = Vec::new();
let top_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
let bottom_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
let horizontal_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(5f32));
properties.push(PropertyDeclaration::MarginTop(top_px));
properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
properties.push(PropertyDeclaration::MarginBottom(bottom_px));
properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "margin: 8px 5px 10px;");
}
#[test]
fn different_properties_should_serialize_to_four_values() {
let mut properties = Vec::new();
let top_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(8f32));
let right_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(12f32));
let bottom_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(10f32));
let left_px = LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(14f32));
properties.push(PropertyDeclaration::MarginTop(top_px));
properties.push(PropertyDeclaration::MarginRight(right_px));
properties.push(PropertyDeclaration::MarginBottom(bottom_px));
properties.push(PropertyDeclaration::MarginLeft(left_px));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "margin: 8px 12px 10px 14px;");
}
#[test]
fn different_longhands_should_serialize_to_long_form() {
let mut properties = Vec::new();
let solid = BorderStyle::Solid;
properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
let px_10 = BorderSideWidth::Length(Length::from_px(10f32));
properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderLeftWidth(px_10.clone()));
let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization,
"border-style: solid; border-width: 30px 30px 30px 10px; border-color: rgb(0, 0, 255);");
}
#[test]
fn same_longhands_should_serialize_correctly() {
let mut properties = Vec::new();
let solid = BorderStyle::Solid;
properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderRightStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderBottomStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderLeftStyle(solid.clone()));
let px_30 = BorderSideWidth::Length(Length::from_px(30f32));
properties.push(PropertyDeclaration::BorderTopWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderRightWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderBottomWidth(px_30.clone()));
properties.push(PropertyDeclaration::BorderLeftWidth(px_30.clone()));
let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
properties.push(PropertyDeclaration::BorderRightColor(blue.clone()));
properties.push(PropertyDeclaration::BorderBottomColor(blue.clone()));
properties.push(PropertyDeclaration::BorderLeftColor(blue.clone()));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-style: solid; border-width: 30px; border-color: rgb(0, 0, 255);");
}
#[test]
fn padding_should_serialize_correctly() {
use style::values::specified::NonNegativeLengthOrPercentage;
let mut properties = Vec::new();
let px_10: NonNegativeLengthOrPercentage = NoCalcLength::from_px(10f32).into();
let px_15: NonNegativeLengthOrPercentage = NoCalcLength::from_px(15f32).into();
properties.push(PropertyDeclaration::PaddingTop(px_10.clone()));
properties.push(PropertyDeclaration::PaddingRight(px_15.clone()));
properties.push(PropertyDeclaration::PaddingBottom(px_10));
properties.push(PropertyDeclaration::PaddingLeft(px_15));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "padding: 10px 15px;");
}
#[test]
fn border_width_should_serialize_correctly() {
let mut properties = Vec::new();
let top_px = BorderSideWidth::Length(Length::from_px(10f32));
let bottom_px = BorderSideWidth::Length(Length::from_px(10f32));
let right_px = BorderSideWidth::Length(Length::from_px(15f32));
let left_px = BorderSideWidth::Length(Length::from_px(15f32));
properties.push(PropertyDeclaration::BorderTopWidth(top_px));
properties.push(PropertyDeclaration::BorderRightWidth(right_px));
properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-width: 10px 15px;");
}
#[test]
fn border_width_with_keywords_should_serialize_correctly() {
let mut properties = Vec::new();
let top_px = BorderSideWidth::Thin;
let right_px = BorderSideWidth::Medium;
let bottom_px = BorderSideWidth::Thick;
let left_px = BorderSideWidth::Length(Length::from_px(15f32));
properties.push(PropertyDeclaration::BorderTopWidth(top_px));
properties.push(PropertyDeclaration::BorderRightWidth(right_px));
properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-width: thin medium thick 15px;");
}
#[test]
fn border_color_should_serialize_correctly() {
let mut properties = Vec::new();
let red = Color::rgba(RGBA::new(255, 0, 0, 255));
let blue = Color::rgba(RGBA::new(0, 0, 255, 255));
properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
properties.push(PropertyDeclaration::BorderRightColor(red.clone()));
properties.push(PropertyDeclaration::BorderBottomColor(blue));
properties.push(PropertyDeclaration::BorderLeftColor(red));
let serialization = shorthand_properties_to_string(properties);
// TODO: Make the rgb test show border-color as blue red instead of below tuples
assert_eq!(serialization, "border-color: rgb(0, 0, 255) rgb(255, 0, 0);");
}
#[test]
fn border_style_should_serialize_correctly() {
let mut properties = Vec::new();
let solid = BorderStyle::Solid;
let dotted = BorderStyle::Dotted;
properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
properties.push(PropertyDeclaration::BorderRightStyle(dotted.clone()));
properties.push(PropertyDeclaration::BorderBottomStyle(solid));
properties.push(PropertyDeclaration::BorderLeftStyle(dotted));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-style: solid dotted;");
}
use style::values::specified::{BorderCornerRadius, Percentage};
#[test]
fn border_radius_should_serialize_correctly() {
let mut properties = Vec::new();
properties.push(PropertyDeclaration::BorderTopLeftRadius(Box::new(BorderCornerRadius::new(
Percentage::new(0.01).into(), Percentage::new(0.05).into()
))));
properties.push(PropertyDeclaration::BorderTopRightRadius(Box::new(BorderCornerRadius::new(
Percentage::new(0.02).into(), Percentage::new(0.06).into()
))));
properties.push(PropertyDeclaration::BorderBottomRightRadius(Box::new(BorderCornerRadius::new(
Percentage::new(0.03).into(), Percentage::new(0.07).into()
))));
properties.push(PropertyDeclaration::BorderBottomLeftRadius(Box::new(BorderCornerRadius::new(
Percentage::new(0.04).into(), Percentage::new(0.08).into()
))));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-radius: 1% 2% 3% 4% / 5% 6% 7% 8%;");
}
}
mod border_shorthands {
use super::*;
#[test]
fn border_top_and_color() {
let mut properties = Vec::new();
properties.push(PropertyDeclaration::BorderTopWidth(BorderSideWidth::Length(Length::from_px(1.))));
properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid));
let c = Color::Numeric {
parsed: RGBA::new(255, 0, 0, 255),
authored: Some("green".to_string().into_boxed_str())
};
properties.push(PropertyDeclaration::BorderTopColor(c));
let c = Color::Numeric {
parsed: RGBA::new(0, 255, 0, 255),
authored: Some("red".to_string().into_boxed_str())
};
properties.push(PropertyDeclaration::BorderTopColor(c.clone()));
properties.push(PropertyDeclaration::BorderBottomColor(c.clone()));
properties.push(PropertyDeclaration::BorderLeftColor(c.clone()));
properties.push(PropertyDeclaration::BorderRightColor(c.clone()));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-top: 1px solid red; border-color: red;");
}
#[test]
fn border_color_and_top() {
let mut properties = Vec::new();
let c = Color::Numeric {
parsed: RGBA::new(0, 255, 0, 255),
authored: Some("red".to_string().into_boxed_str())
};
properties.push(PropertyDeclaration::BorderTopColor(c.clone()));
properties.push(PropertyDeclaration::BorderBottomColor(c.clone()));
properties.push(PropertyDeclaration::BorderLeftColor(c.clone()));
properties.push(PropertyDeclaration::BorderRightColor(c.clone()));
properties.push(PropertyDeclaration::BorderTopWidth(BorderSideWidth::Length(Length::from_px(1.))));
properties.push(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid));
let c = Color::Numeric {
parsed: RGBA::new(255, 0, 0, 255),
authored: Some("green".to_string().into_boxed_str())
};
properties.push(PropertyDeclaration::BorderTopColor(c));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-color: green red red; border-top: 1px solid green;");
}
// we can use border-top as a base to test out the different combinations
// but afterwards, we only need to to one test per "directional border shorthand"
#[test]
fn directional_border_should_show_all_properties_when_values_are_set() {
let mut properties = Vec::new();
let width = BorderSideWidth::Length(Length::from_px(4f32));
let style = BorderStyle::Solid;
let color = RGBA::new(255, 0, 0, 255).into();
properties.push(PropertyDeclaration::BorderTopWidth(width));
properties.push(PropertyDeclaration::BorderTopStyle(style));
properties.push(PropertyDeclaration::BorderTopColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);");
}
fn get_border_property_values() -> (BorderSideWidth, BorderStyle, Color) {
(BorderSideWidth::Length(Length::from_px(4f32)),
BorderStyle::Solid,
Color::currentcolor())
}
#[test]
fn border_top_should_serialize_correctly() {
let mut properties = Vec::new();
let (width, style, color) = get_border_property_values();
properties.push(PropertyDeclaration::BorderTopWidth(width));
properties.push(PropertyDeclaration::BorderTopStyle(style));
properties.push(PropertyDeclaration::BorderTopColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-top: 4px solid;");
}
#[test]
fn border_right_should_serialize_correctly() {
let mut properties = Vec::new();
let (width, style, color) = get_border_property_values();
properties.push(PropertyDeclaration::BorderRightWidth(width));
properties.push(PropertyDeclaration::BorderRightStyle(style));
properties.push(PropertyDeclaration::BorderRightColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-right: 4px solid;");
}
#[test]
fn border_bottom_should_serialize_correctly() {
let mut properties = Vec::new();
let (width, style, color) = get_border_property_values();
properties.push(PropertyDeclaration::BorderBottomWidth(width));
properties.push(PropertyDeclaration::BorderBottomStyle(style));
properties.push(PropertyDeclaration::BorderBottomColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-bottom: 4px solid;");
}
#[test]
fn border_left_should_serialize_correctly() {
let mut properties = Vec::new();
let (width, style, color) = get_border_property_values();
properties.push(PropertyDeclaration::BorderLeftWidth(width));
properties.push(PropertyDeclaration::BorderLeftStyle(style));
properties.push(PropertyDeclaration::BorderLeftColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "border-left: 4px solid;");
}
#[test]
fn border_should_serialize_correctly() {
// According to https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands,
// the border shorthand resets border-image to its initial value. To verify the
// serialization of 'border' shorthand, we need to set 'border-image' as well.
let block_text = "\
border-top: 4px solid; \
border-right: 4px solid; \
border-bottom: 4px solid; \
border-left: 4px solid; \
border-image: none;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "border: 4px solid;");
}
}
mod list_style {
use style::properties::longhands::list_style_image::SpecifiedValue as ListStyleImage;
use style::properties::longhands::list_style_position::SpecifiedValue as ListStylePosition;
use style::properties::longhands::list_style_type::SpecifiedValue as ListStyleType;
use style::values::Either;
use super::*;
#[test]
fn list_style_should_show_all_properties_when_values_are_set() {
let mut properties = Vec::new();
let position = ListStylePosition::Inside;
let image =
ListStyleImage(Either::First(SpecifiedUrl::new_for_testing("http://servo/test.png")));
let style_type = ListStyleType::Disc;
properties.push(PropertyDeclaration::ListStylePosition(position));
#[cfg(feature = "gecko")]
properties.push(PropertyDeclaration::ListStyleImage(Box::new(image)));
#[cfg(not(feature = "gecko"))]
properties.push(PropertyDeclaration::ListStyleImage(image));
properties.push(PropertyDeclaration::ListStyleType(style_type));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "list-style: inside url(\"http://servo/test.png\") disc;");
}
}
mod outline {
use style::values::specified::outline::OutlineStyle;
use super::*;
#[test]
fn outline_should_show_all_properties_when_set() {
let mut properties = Vec::new();
let width = BorderSideWidth::Length(Length::from_px(4f32));
let style = OutlineStyle::Other(BorderStyle::Solid);
let color = RGBA::new(255, 0, 0, 255).into();
properties.push(PropertyDeclaration::OutlineWidth(width));
properties.push(PropertyDeclaration::OutlineStyle(style));
properties.push(PropertyDeclaration::OutlineColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "outline: 4px solid rgb(255, 0, 0);");
}
#[test]
fn outline_should_serialize_correctly_when_style_is_auto() {
let mut properties = Vec::new();
let width = BorderSideWidth::Length(Length::from_px(4f32));
let style = OutlineStyle::Auto;
let color = RGBA::new(255, 0, 0, 255).into();
properties.push(PropertyDeclaration::OutlineWidth(width));
properties.push(PropertyDeclaration::OutlineStyle(style));
properties.push(PropertyDeclaration::OutlineColor(color));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "outline: 4px auto rgb(255, 0, 0);");
}
}
#[test]
fn columns_should_serialize_correctly() {
use style::values::{Auto, Either};
let mut properties = Vec::new();
let width = Either::Second(Auto);
let count = Either::Second(Auto);
properties.push(PropertyDeclaration::ColumnWidth(width));
properties.push(PropertyDeclaration::ColumnCount(count));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "columns: auto auto;");
}
#[test]
fn flex_should_serialize_all_available_properties() {
use style::values::specified::{NonNegativeNumber, Percentage};
let mut properties = Vec::new();
let grow = NonNegativeNumber::new(2f32);
let shrink = NonNegativeNumber::new(3f32);
let basis =
FlexBasis::Length(Percentage::new(0.5f32).into());
properties.push(PropertyDeclaration::FlexGrow(grow));
properties.push(PropertyDeclaration::FlexShrink(shrink));
properties.push(PropertyDeclaration::FlexBasis(basis));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "flex: 2 3 50%;");
}
#[test]
fn flex_flow_should_serialize_all_available_properties() {
use style::properties::longhands::flex_direction::SpecifiedValue as FlexDirection;
use style::properties::longhands::flex_wrap::SpecifiedValue as FlexWrap;
let mut properties = Vec::new();
let direction = FlexDirection::Row;
let wrap = FlexWrap::Wrap;
properties.push(PropertyDeclaration::FlexDirection(direction));
properties.push(PropertyDeclaration::FlexWrap(wrap));
let serialization = shorthand_properties_to_string(properties);
assert_eq!(serialization, "flex-flow: row wrap;");
}
mod background {
use super::*;
#[test]
fn background_should_serialize_all_available_properties_when_specified() {
let block_text = "\
background-color: rgb(255, 0, 0); \
background-image: url(\"http://servo/test.png\"); \
background-repeat: repeat-x; \
background-attachment: scroll; \
background-size: 70px 50px; \
background-position-x: 7px; \
background-position-y: bottom 4px; \
background-origin: border-box; \
background-clip: padding-box;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(
serialization,
"background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
scroll left 7px bottom 4px / 70px 50px border-box padding-box;"
);
}
#[test]
fn background_should_combine_origin_and_clip_properties_when_equal() {
let block_text = "\
background-color: rgb(255, 0, 0); \
background-image: url(\"http://servo/test.png\"); \
background-repeat: repeat-x; \
background-attachment: scroll; \
background-size: 70px 50px; \
background-position-x: 7px; \
background-position-y: 4px; \
background-origin: padding-box; \
background-clip: padding-box;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(
serialization,
"background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
scroll 7px 4px / 70px 50px padding-box;"
);
}
#[test]
fn serialize_multiple_backgrounds() {
let block_text = "\
background-color: rgb(0, 0, 255); \
background-image: url(\"http://servo/test.png\"), none; \
background-repeat: repeat-x, repeat-y; \
background-attachment: scroll, scroll; \
background-size: 70px 50px, 20px 30px; \
background-position-x: 7px, 70px; \
background-position-y: 4px, 40px; \
background-origin: border-box, padding-box; \
background-clip: padding-box, padding-box;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(
serialization, "background: \
url(\"http://servo/test.png\") repeat-x scroll 7px 4px / 70px 50px border-box padding-box, \
rgb(0, 0, 255) none repeat-y scroll 70px 40px / 20px 30px padding-box;"
);
}
#[test]
fn serialize_multiple_backgrounds_unequal_property_lists() {
// When the lengths of property values are different, the shorthand serialization
// should not be used. Previously the implementation cycled values if the lists were
// uneven. This is incorrect, in that we should serialize to a shorthand only when the
// lists have the same length (this affects background, transition and animation).
// https://github.com/servo/servo/issues/15398 )
// With background, the color is one exception as it should only appear once for
// multiple backgrounds.
// Below background-origin only has one value.
let block_text = "\
background-color: rgb(0, 0, 255); \
background-image: url(\"http://servo/test.png\"), none; \
background-repeat: repeat-x, repeat-y; \
background-attachment: scroll, scroll; \
background-size: 70px 50px, 20px 30px; \
background-position: 7px 4px, 5px 6px; \
background-origin: border-box; \
background-clip: padding-box, padding-box;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, block_text);
}
#[test]
fn background_position_should_be_a_valid_form_its_longhands() {
// If there is any longhand consisted of both keyword and position,
// the shorthand result should be the 4-value format.
let block_text = "\
background-position-x: 30px;\
background-position-y: bottom 20px;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "background-position: left 30px bottom 20px;");
// If there is no longhand consisted of both keyword and position,
// the shorthand result should be the 2-value format.
let block_text = "\
background-position-x: center;\
background-position-y: 20px;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "background-position: center 20px;");
}
}
mod transform {
pub use super::*;
use style::values::generics::transform::TransformOperation;
use style::values::specified::{Angle, Number};
use style::values::specified::transform::TransformOperation as SpecifiedOperation;
#[test]
fn should_serialize_none_correctly() {
use style::properties::longhands::transform;
assert_roundtrip_with_context!(transform::parse, "none");
}
#[inline(always)]
fn validate_serialization(op: &SpecifiedOperation, expected_string: &'static str) {
let css_string = op.to_css_string();
assert_eq!(css_string, expected_string);
}
#[test]
fn transform_scale() {
validate_serialization(&TransformOperation::Scale(Number::new(1.3), None), "scale(1.3)");
validate_serialization(
&TransformOperation::Scale(Number::new(2.0), Some(Number::new(2.0))),
"scale(2, 2)");
validate_serialization(&TransformOperation::ScaleX(Number::new(42.0)), "scaleX(42)");
validate_serialization(&TransformOperation::ScaleY(Number::new(0.3)), "scaleY(0.3)");
validate_serialization(&TransformOperation::ScaleZ(Number::new(1.0)), "scaleZ(1)");
validate_serialization(
&TransformOperation::Scale3D(Number::new(4.0), Number::new(5.0), Number::new(6.0)),
"scale3d(4, 5, 6)");
}
#[test]
fn transform_skew() {
validate_serialization(
&TransformOperation::Skew(Angle::from_degrees(42.3, false), None),
"skew(42.3deg)");
validate_serialization(
&TransformOperation::Skew(Angle::from_gradians(-50.0, false), Some(Angle::from_turns(0.73, false))),
"skew(-50grad, 0.73turn)");
validate_serialization(
&TransformOperation::SkewX(Angle::from_radians(0.31, false)), "skewX(0.31rad)");
}
#[test]
fn transform_rotate() {
validate_serialization(
&TransformOperation::Rotate(Angle::from_turns(35.0, false)),
"rotate(35turn)"
)
}
}
mod quotes {
pub use super::*;
#[test]
fn should_serialize_none_correctly() {
use style::properties::longhands::quotes;
assert_roundtrip_with_context!(quotes::parse, "none");
}
}
mod animation {
pub use super::*;
#[test]
fn serialize_single_animation() {
let block_text = "\
animation-name: bounce;\
animation-duration: 1s;\
animation-timing-function: ease-in;\
animation-delay: 0s;\
animation-direction: normal;\
animation-fill-mode: forwards;\
animation-iteration-count: infinite;\
animation-play-state: paused;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "animation: 1s ease-in 0s infinite normal forwards paused bounce;")
}
#[test]
fn serialize_multiple_animations() {
let block_text = "\
animation-name: bounce, roll;\
animation-duration: 1s, 0.2s;\
animation-timing-function: ease-in, linear;\
animation-delay: 0s, 1s;\
animation-direction: normal, reverse;\
animation-fill-mode: forwards, backwards;\
animation-iteration-count: infinite, 2;\
animation-play-state: paused, running;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization,
"animation: 1s ease-in 0s infinite normal forwards paused bounce, \
0.2s linear 1s 2 reverse backwards running roll;");
}
#[test]
fn serialize_multiple_animations_unequal_property_lists() {
// When the lengths of property values are different, the shorthand serialization
// should not be used. Previously the implementation cycled values if the lists were
// uneven. This is incorrect, in that we should serialize to a shorthand only when the
// lists have the same length (this affects background, transition and animation).
// https://github.com/servo/servo/issues/15398 )
let block_text = "\
animation-name: bounce, roll, flip, jump; \
animation-duration: 1s, 0.2s; \
animation-timing-function: ease-in, linear; \
animation-delay: 0s, 1s, 0.5s; \
animation-direction: normal; \
animation-fill-mode: forwards, backwards; \
animation-iteration-count: infinite, 2; \
animation-play-state: paused, running;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, block_text);
}
#[test]
fn serialize_multiple_without_all_properties_returns_longhand() {
// timing function and direction are missing, so no shorthand is returned.
let block_text = "animation-name: bounce, roll; \
animation-duration: 1s, 0.2s; \
animation-delay: 0s, 1s; \
animation-fill-mode: forwards, backwards; \
animation-iteration-count: infinite, 2; \
animation-play-state: paused, running;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, block_text);
}
}
mod transition {
pub use super::*;
#[test]
fn transition_should_serialize_all_available_properties() {
let block_text = "transition-property: margin-left; \
transition-duration: 3s; \
transition-delay: 4s; \
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2);";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "transition: margin-left 3s cubic-bezier(0.2, 5, 0.5, 2) 4s;");
}
#[test]
fn serialize_multiple_transitions() {
let block_text = "transition-property: margin-left, width; \
transition-duration: 3s, 2s; \
transition-delay: 4s, 5s; \
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2), ease;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "transition: \
margin-left 3s cubic-bezier(0.2, 5, 0.5, 2) 4s, \
width 2s ease 5s;");
}
#[test]
fn serialize_multiple_transitions_unequal_property_lists() {
// When the lengths of property values are different, the shorthand serialization
// should not be used. Previously the implementation cycled values if the lists were
// uneven. This is incorrect, in that we should serialize to a shorthand only when the
// lists have the same length (this affects background, transition and animation).
// https://github.com/servo/servo/issues/15398 )
// The duration below has 1 extra value.
let block_text = "transition-property: margin-left, width; \
transition-duration: 3s, 2s, 4s; \
transition-delay: 4s, 5s; \
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2), ease;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, block_text);
}
#[test]
fn transition_should_serialize_acceptable_step_timing_function() {
let block_text = "transition-property: margin-left; \
transition-duration: 3s; \
transition-delay: 4s; \
transition-timing-function: steps(2, start);";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "transition: margin-left 3s steps(2, start) 4s;");
}
#[test]
fn transition_should_serialize_acceptable_frames_timing_function() {
let block_text = "transition-property: margin-left; \
transition-duration: 3s; \
transition-delay: 4s; \
transition-timing-function: frames(2);";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "transition: margin-left 3s frames(2) 4s;");
}
}
mod keywords {
pub use super::*;
#[test]
fn css_wide_keywords_should_be_parsed() {
let block_text = "--a:inherit;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, "--a: inherit;");
}
#[test]
fn non_keyword_custom_property_should_be_unparsed() {
let block_text = "--main-color: #06c;";
let block = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), block_text).unwrap();
let serialization = block.to_css_string();
assert_eq!(serialization, block_text);
}
}
mod effects {
pub use super::*;
pub use style::properties::longhands::box_shadow::SpecifiedValue as BoxShadowList;
pub use style::values::specified::effects::{BoxShadow, SimpleShadow};
#[test]
fn box_shadow_should_serialize_correctly() {
use style::values::specified::length::NonNegativeLength;
let mut properties = Vec::new();
let shadow_val = BoxShadow {
base: SimpleShadow {
color: None,
horizontal: Length::from_px(1f32),
vertical: Length::from_px(2f32),
blur: Some(NonNegativeLength::from_px(3f32)),
},
spread: Some(Length::from_px(4f32)),
inset: false,
};
let shadow_decl = BoxShadowList(vec![shadow_val]);
properties.push(PropertyDeclaration::BoxShadow(shadow_decl));
let shadow_css = "box-shadow: 1px 2px 3px 4px;";
let shadow = parse(|c, e, i| Ok(parse_property_declaration_list(c, e, i)), shadow_css).unwrap();
assert_eq!(shadow.to_css_string(), shadow_css);
}
}
mod counter_increment {
pub use super::*;
pub use style::properties::longhands::counter_increment::SpecifiedValue as CounterIncrement;
use style::values::specified::Integer;
#[test]
fn counter_increment_with_properties_should_serialize_correctly() {
let mut properties = Vec::new();
properties.push((CustomIdent("counter1".into()), Integer::new(1)));
properties.push((CustomIdent("counter2".into()), Integer::new(-4)));
let counter_increment = CounterIncrement(properties);
let counter_increment_css = "counter1 1 counter2 -4";
assert_eq!(counter_increment.to_css_string(), counter_increment_css);
}
#[test]
fn counter_increment_without_properties_should_serialize_correctly() {
let counter_increment = CounterIncrement(Vec::new());
let counter_increment_css = "none";
assert_eq!(counter_increment.to_css_string(), counter_increment_css);
}
}
}