Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 34 additions & 45 deletions test/test_effects.ml
Original file line number Diff line number Diff line change
Expand Up @@ -256,17 +256,15 @@ let test_inset_shadow_theme_override () =
Tw.Scheme.with_overrides Tw.Scheme.default
[ ("inset-shadow-sm", "inset 0 1px 1px rgb(0 0 0 / 0.05)") ]
in
let css =
Tw.to_css ~theme ~base:false
[ Result.get_ok (Tw.of_string ~theme "inset-shadow-sm") ]
|> Tw.Css.to_string ~minify:true
in
Alcotest.(check bool)
"inset-shadow-sm @theme override flows to [inset 0 1px 1px]" true
(Astring.String.is_infix ~affix:"inset 0 1px 1px" css);
Alcotest.(check bool)
"inset-shadow-sm @theme override drops the default [inset 0 2px 4px]" false
(Astring.String.is_infix ~affix:"inset 0 2px 4px" css)
(* The whole list, which is where "drops the default" is said: it used to be a
second search for a spelling that must not appear, and a sheet with no rule
in it satisfies that too. *)
Test_helpers.check_declarations ~theme "inset-shadow-sm"
[
"--tw-inset-shadow:inset 0 1px 1px var(--tw-inset-shadow-color,rgb(0 0 \
0/.05))";
composes_box_shadow;
]

(* The override is read through the CSS shadow grammar, so it carries whatever
that grammar allows: a length in any unit, a fourth length for the spread,
Expand All @@ -275,28 +273,26 @@ let test_inset_shadow_theme_override () =
the rest, which put the built-in [inset 0 2px 4px] in the sheet and lost the
override without saying so. *)
let test_inset_shadow_theme_override_grammar () =
let sheet override =
(* The whole list per override. "The default is not substituted" was a search
for a spelling that must not appear; the list says it by construction, and
also that the colour the project wrote reached the fallback slot. *)
let holds ~override body =
let theme =
Tw.Scheme.with_overrides Tw.Scheme.default
[ ("inset-shadow-sm", override) ]
in
Tw.to_css ~theme ~base:false
[ Result.get_ok (Tw.of_string ~theme "inset-shadow-sm") ]
|> Tw.Css.to_string ~minify:true
in
let holds ~name ~override affix =
Alcotest.(check bool)
name true
(Astring.String.is_infix ~affix (sheet override))
Test_helpers.check_declarations ~theme "inset-shadow-sm"
[
"--tw-inset-shadow:" ^ body
^ " var(--tw-inset-shadow-color,rgb(0 0 0/.05))";
composes_box_shadow;
]
in
holds ~name:"an em override keeps its unit"
~override:"inset 0 0.125em 0.25em rgb(0 0 0 / 0.05)" "inset 0 .125em .25em";
holds ~name:"a fourth length is the spread"
~override:"inset 0 1px 2px 3px rgb(0 0 0 / 0.05)" "inset 0 1px 2px 3px";
Alcotest.(check bool)
"the default is not substituted" false
(Astring.String.is_infix ~affix:"inset 0 2px 4px"
(sheet "inset 0 0.125em 0.25em rgb(0 0 0 / 0.05)"))
(* an em override keeps its unit *)
holds ~override:"inset 0 0.125em 0.25em rgb(0 0 0 / 0.05)"
"inset 0 .125em .25em";
(* a fourth length is the spread *)
holds ~override:"inset 0 1px 2px 3px rgb(0 0 0 / 0.05)" "inset 0 1px 2px 3px"

(* A shadeless colour has no shade segment, so shadow-white never reached the
colour parse: the size cases claimed the segment and rejected it. The class
Expand Down Expand Up @@ -609,14 +605,9 @@ let test_bracket_hex_opacity_var () =
comes from a separate, correctly-scaled computation, so --tw-shadow-alpha
here is a plain, unconverted echo of what the class wrote. *)
let test_shadow_bracket_alpha_tracking () =
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string ~minify:true
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
let lacks cls affix =
Alcotest.(check bool) cls false (Astring.String.is_infix ~affix (css cls))
in
(* The [lacks] checks that used to sit beside each list here - "no
--tw-shadow-alpha:2500%" - said nothing the list does not already say, and
passed on a sheet with no rule in it. *)
let shadow_lg alpha painted =
[
"--tw-shadow-alpha:" ^ alpha;
Expand All @@ -627,7 +618,6 @@ let test_shadow_bracket_alpha_tracking () =
in
Test_helpers.check_declarations "shadow-lg/[25]"
(shadow_lg "25" "oklab(0%0 0/25)");
lacks "shadow-lg/[25]" "--tw-shadow-alpha:2500%";
Test_helpers.check_declarations "shadow-[0_1px_2px_#000]/[25]"
[
"--tw-shadow-alpha:25";
Expand All @@ -641,7 +631,6 @@ let test_shadow_bracket_alpha_tracking () =
0/25))";
composes_box_shadow;
];
lacks "inset-shadow-sm/[25]" "--tw-inset-shadow-alpha:2500%";
(* A bracket alpha that does carry a [%] sign, or the plain percent form, both
keep behaving as a percentage. *)
Test_helpers.check_declarations "shadow-lg/[25%]"
Expand Down Expand Up @@ -784,13 +773,13 @@ let test_project_shadow_tokens () =
("inset-shadow-dent", "inset 0 1px 2px #000");
]
in
let css cls =
match Tw.of_string ~theme cls with
| Ok u -> Tw.to_css ~theme ~base:false [ u ] |> Tw.Css.to_string
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
let emits affix cls =
Alcotest.(check bool) cls true (Astring.String.is_infix ~affix (css cls))
let emits decl cls =
Test_helpers.check_declarations ~theme ~minify:false cls
[
decl;
"box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), \
var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)";
]
in
emits "--tw-shadow: 0 0 8px var(--tw-shadow-color, #f00)" "shadow-halo";
emits "--tw-inset-shadow: inset 0 1px 2px var(--tw-inset-shadow-color, #000)"
Expand Down
98 changes: 33 additions & 65 deletions test/test_masks.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,13 @@ let test_typed () =

(* [mask-[<image>]] takes any background-image, not only a linear-gradient. *)
let test_bracket_image () =
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string ~minify:true
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
Alcotest.(check bool)
"radial-gradient reaches mask-image" true
(Astring.String.is_infix ~affix:"mask-image:radial-gradient(white,black)"
(css "mask-[radial-gradient(white,black)]"));
(* The whole list. A mask utility aliases its property for WebKit, so the
affix this replaces was satisfied by the unaliased arm alone. *)
Test_helpers.check_declarations "mask-[radial-gradient(white,black)]"
[
"-webkit-mask-image:radial-gradient(white,black)";
"mask-image:radial-gradient(white,black)";
];
check "mask-[radial-gradient(white,black)]";
check "mask-[conic-gradient(white,black)]";
check "mask-[linear-gradient(white,black)]"
Expand All @@ -69,19 +67,12 @@ let test_bracket_layer_list () =
[30% 50%] compacts to [30%50%] - still two components, still valid, and it
round-trips - while a different minifier folds the pair to its [x] alone.
Pinning either spelling pins the minifier instead of the reading. *)
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
let both cls prop value =
Test_helpers.check_declarations ~minify:false cls
[ "-webkit-" ^ prop ^ ": " ^ value; prop ^ ": " ^ value ]
in
Alcotest.(check bool)
"two url layers stay two" true
(Astring.String.is_infix ~affix:"mask-image: url(/a.png), url(/b.png)"
(css "mask-[url(/a.png),url(/b.png)]"));
Alcotest.(check bool)
"two positions stay two" true
(Astring.String.is_infix ~affix:"mask-position: 30% 50%, 70% 50%"
(css "mask-position-[30%_50%,70%_50%]"))
both "mask-[url(/a.png),url(/b.png)]" "mask-image" "url(/a.png), url(/b.png)";
both "mask-position-[30%_50%,70%_50%]" "mask-position" "30% 50%, 70% 50%"

(* A bracket that would end the declaration or swallow what follows it is the
one thing no mask utility can hold, and Tailwind writes nothing for it
Expand Down Expand Up @@ -168,17 +159,13 @@ let test_bracket_falls_through_to_a_longhand () =
rem before; every CSS length unit does now, matching real Tailwind's
mask-size-[2em]. *)
let test_bracket_size_units () =
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string ~minify:true
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
let has cls affix =
Alcotest.(check bool) cls true (Astring.String.is_infix ~affix (css cls))
let has cls =
Test_helpers.check_declarations cls
[ "-webkit-mask-size:2em"; "mask-size:2em" ]
in
has "mask-size-[2em]" "mask-size:2em";
has "mask-[size:2em]" "mask-size:2em";
has "mask-[length:2em]" "mask-size:2em"
has "mask-size-[2em]";
has "mask-[size:2em]";
has "mask-[length:2em]"

(* A mask-position bracket takes the whole CSS grammar, the same as
background-position: a single edge keyword and the edge/offset form. Both
Expand All @@ -187,20 +174,16 @@ let test_bracket_position_grammar () =
(* Unminified, so the assertions read as the grammar they are about. Compact
spelling belongs to the minifier: [10px 20px] keeps its space while [30%
50%] loses it, because a [%] already ends the token. *)
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
let has cls affix =
Alcotest.(check bool) cls true (Astring.String.is_infix ~affix (css cls))
let has cls value =
Test_helpers.check_declarations ~minify:false cls
[ "-webkit-mask-position: " ^ value; "mask-position: " ^ value ]
in
has "mask-[position:top]" "mask-position: top";
has "mask-position-[top]" "mask-position: top";
has "mask-[top]" "mask-position: top";
has "mask-[position:top]" "top";
has "mask-position-[top]" "top";
has "mask-[top]" "top";
(* the lengths and layer-list forms are unchanged *)
has "mask-[position:10px_20px]" "mask-position: 10px 20px";
has "mask-position-[30%_50%,70%_50%]" "mask-position: 30% 50%, 70% 50%"
has "mask-[position:10px_20px]" "10px 20px";
has "mask-position-[30%_50%,70%_50%]" "30% 50%, 70% 50%"

(* Masks sit between the backgrounds and fill/stroke, and the mask-gradient
utilities lead them. Sharing padding's slot interleaved the two families with
Expand Down Expand Up @@ -228,15 +211,8 @@ let order_matches_tailwind () =
(* A mask image is an arbitrary value, so [_] is a space and [\_] a literal
underscore: a file name carrying one is written with the escape. *)
let test_bracket_image_underscore_escape () =
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
Alcotest.(check bool)
"an escaped underscore stays in the file name" true
(Astring.String.is_infix ~affix:"mask-image: url('a_b.png')"
(css {|mask-[url('a\_b.png')]|}))
Test_helpers.check_declarations ~minify:false {|mask-[url('a\_b.png')]|}
[ "-webkit-mask-image: url('a_b.png')"; "mask-image: url('a_b.png')" ]

(* Tailwind leaves a bare [_] alone inside [url()], where it is part of the file
name rather than an encoded space, so [mask-[url('a_b.png')]] names
Expand All @@ -252,22 +228,14 @@ let test_bracket_url_keeps_underscores () =
name too. Only the [_] outside the url is a space, which [image-set()] is the
case that tells the two apart. *)
let test_bracket_url_underscore () =
let css cls =
match Tw.of_string cls with
| Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string
| Error (`Msg m) -> Alcotest.failf "%s: %s" cls m
in
let has cls affix =
Alcotest.(check bool)
(cls ^ " emits " ^ affix)
true
(Astring.String.is_infix ~affix (css cls))
let has cls value =
Test_helpers.check_declarations ~minify:false cls
[ "-webkit-mask-image: " ^ value; "mask-image: " ^ value ]
in
has "mask-[url('a_b.png')]" "mask-image: url('a_b.png')";
has "mask-[url('a_b.png')]" "url('a_b.png')";
(* Tailwind writes the inner url quoted. The quoting is cascade's canonical
spelling of the same URL; the underscore is the point. *)
has "mask-[image-set(url('a_b.png')_1x)]"
"mask-image: image-set(url(a_b.png) 1x)"
has "mask-[image-set(url('a_b.png')_1x)]" "image-set(url(a_b.png) 1x)"

(* A [url()] carrying a position is no [url()] token, so the typed reading has
no answer for it and the whole bracket goes out verbatim instead. What must
Expand Down
53 changes: 29 additions & 24 deletions test/test_prose.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,29 @@ let test_combinations () =
Alcotest.(check string)
"complex prose" "prose prose-xl prose-gray mx-auto" complex

(* [prose] is a component, so its 77 rules are not worth spelling out; what
these check is that the right selectors and the right declarations are among
them. Membership in the lists is exact where a substring was not: the affix
[.prose] matched every descendant rule as well as the root one, and the h1
affix stopped mid-selector. *)
let test_css_generation () =
(* Test that prose generates CSS rules *)
let css = to_css [ prose ] in
let css_string = Css.to_string css in

(* Check that CSS variables are included *)
let utility =
match Tw.of_string "prose" with
| Ok u -> u
| Error (`Msg m) -> Alcotest.failf "prose: %s" m
in
let selectors = Test_helpers.selectors_of_utility utility in
let has_selector sel =
Alcotest.(check bool) ("has " ^ sel) true (List.mem sel selectors)
in
has_selector ".prose";
has_selector
{|.prose :where(h1):not(:where([class~="not-prose"], [class~="not-prose"] *))|};
(* The root rule reads the body variable rather than inlining a colour. *)
Alcotest.(check bool)
"has prose body variable" true
(Astring.String.is_infix ~affix:"--tw-prose-body" css_string);

(* Check that prose class is generated *)
Alcotest.(check bool)
"has prose class" true
(Astring.String.is_infix ~affix:".prose" css_string);

(* Check that descendant selectors are generated *)
Alcotest.(check bool)
"has prose h1 selector" true
(Astring.String.is_infix
~affix:".prose :where(h1):not(:where([class~=\"not-prose\"]" css_string)
(List.mem "color:var(--tw-prose-body)"
(Test_helpers.declarations_of_class "prose"))

let test_inline_styles () =
(* Prose utilities can generate inline styles from their rules, but CSS
Expand All @@ -63,15 +66,17 @@ let test_inline_styles () =
(* prose-invert remaps the palette to the inverted vars; prose-orange overrides
the link accent colours. Both used to be no-ops / unknown. *)
let test_color_variants () =
let invert = Css.to_string (to_css ~base:false [ prose_invert ]) in
Alcotest.(check bool)
"prose-invert remaps body to the invert var" true
(Astring.String.is_infix ~affix:"var(--tw-prose-invert-body)" invert);
let orange = Css.to_string (to_css ~base:false [ prose_orange ]) in
Alcotest.(check bool)
"prose-orange sets the link accent" true
(Astring.String.is_infix ~affix:"--tw-prose-links" orange
&& Astring.String.is_infix ~affix:"--tw-prose-invert-links" orange)
(List.mem "--tw-prose-body:var(--tw-prose-invert-body)"
(Test_helpers.declarations_of_class "prose-invert"));
(* A colour variant sets only the accents it names, which the pair of searches
this replaces could not say. *)
Test_helpers.check_declarations "prose-orange"
[
"--tw-prose-links:oklch(64.6%.222 41.116)";
"--tw-prose-invert-links:oklch(70.5%.213 47.604)";
]

(* The colours [prose] sets on its own are the gray palette, normal and
inverted. Nothing else in the suite reads them, so an edit to [gray_normal]
Expand Down
Loading
Loading