From 7960b48f5fba61df2dab3b64565d04d6ffb6974c Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Wed, 14 Oct 2026 18:03:34 +0900 Subject: [PATCH 1/2] fix: repair main tests, docs lint and Tailwind test isolation Refs #770, #734, #735, #753, #739 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../app/(detail)/docs/build-errors/page.mdx | 7 +++--- libs/extractor/src/tailwind_children.rs | 6 +++++ libs/extractor/src/tailwind_color.rs | 5 ++++ libs/extractor/src/tailwind_effects.rs | 23 +++++++++++++++++++ libs/extractor/src/tailwind_misc.rs | 9 ++++++++ libs/extractor/src/tailwind_motion.rs | 8 +++++++ libs/sheet/src/lib.rs | 10 ++++---- 7 files changed, 59 insertions(+), 9 deletions(-) diff --git a/apps/landing/src/app/(detail)/docs/build-errors/page.mdx b/apps/landing/src/app/(detail)/docs/build-errors/page.mdx index 753b69bbc..9dabc9992 100644 --- a/apps/landing/src/app/(detail)/docs/build-errors/page.mdx +++ b/apps/landing/src/app/(detail)/docs/build-errors/page.mdx @@ -1902,15 +1902,14 @@ export function f() { const c = css; c = () => 1; return c({ color: "red" }); } **Why:** A followed alias cannot be reassigned after the rewrite removes its declaration. -**Fix:** Keep the const alias unchanged and call it. +**Fix:** Call the imported css directly instead of creating a local runtime alias. {/* probe: mutated-const-alias after */} ```tsx import { css } from '@devup-ui/react' export function f() { - const c = css - return c({ color: 'red' }) + return css({ color: 'red' }) } ``` @@ -3926,7 +3925,7 @@ The `devup/no-runtime-read` rule reports compile-time bindings read outside supp `` is read at runtime, where it does not exist: the build compiles it only where it is called or rendered. ``` -```tsx +```tsx-error import { css } from '@devup-ui/react' consume(css) ``` diff --git a/libs/extractor/src/tailwind_children.rs b/libs/extractor/src/tailwind_children.rs index 78d266b05..6ae20a798 100644 --- a/libs/extractor/src/tailwind_children.rs +++ b/libs/extractor/src/tailwind_children.rs @@ -124,6 +124,7 @@ pub fn children_utility(name: &str) -> Option> { #[allow(clippy::expect_used, clippy::unwrap_used)] mod tests { use rstest::rstest; + use serial_test::serial; use super::*; use crate::tailwind::{declarations_of, parse_class}; @@ -177,6 +178,7 @@ mod tests { ])] #[case("space-x-reverse", &[("--tw-space-x-reverse", "1")])] #[case("space-y-reverse", &[("--tw-space-y-reverse", "1")])] + #[serial] fn space_adds_margins_between_the_children( #[case] class: &str, #[case] expected: &[(&str, &str)], @@ -229,6 +231,7 @@ mod tests { } #[test] + #[serial] fn divide_reverse_style_and_color() { expect("divide-x-reverse", &[("--tw-divide-x-reverse", "1")]); expect("divide-y-reverse", &[("--tw-divide-y-reverse", "1")]); @@ -268,11 +271,13 @@ mod tests { #[case("space-x-huge")] #[case("-space-x-reverse")] #[case("space-z-4")] + #[serial] fn unknown_children_utilities_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } #[test] + #[serial] fn children_utilities_apply_under_variants_to_the_children() { let class = parse_class("space-x-4").unwrap(); assert_eq!(class.conditions.len(), 1); @@ -310,6 +315,7 @@ mod tests { } #[test] + #[serial] fn children_rules_register_their_variables() { assert_eq!( parse_class("space-x-4").unwrap().rules(), diff --git a/libs/extractor/src/tailwind_color.rs b/libs/extractor/src/tailwind_color.rs index fa48bf6b0..cca7c4ddd 100644 --- a/libs/extractor/src/tailwind_color.rs +++ b/libs/extractor/src/tailwind_color.rs @@ -437,6 +437,7 @@ mod tests { #[case("caret-fuchsia-100", "oklch(95.2% 0.037 318.852)")] #[case("fill-zinc-900", "oklch(21% 0.006 285.885)")] #[case("stroke-stone-200", "oklch(92.3% 0.003 48.717)")] + #[serial] fn palette_is_the_default_theme_of_v4(#[case] class: &str, #[case] expected: &str) { assert_eq!(color(class).as_deref(), Some(expected)); } @@ -474,6 +475,7 @@ mod tests { )] #[case("bg-black/10", "color-mix(in oklab, #000 10%, transparent)")] #[case("text-white/75", "color-mix(in oklab, #fff 75%, transparent)")] + #[serial] fn opacity_modifier_mixes_with_transparent(#[case] class: &str, #[case] expected: &str) { assert_eq!(color(class).as_deref(), Some(expected)); } @@ -489,6 +491,7 @@ mod tests { #[case("bg-red-500/50/50")] #[case("bg-red-501")] #[case("bg-unknown")] + #[serial] fn invalid_color_stays_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } @@ -541,6 +544,7 @@ mod tests { ("--tw-blur", "blur(8px) !important"), ("filter", "var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,) !important"), ])] + #[serial] fn important_marks_every_declaration(#[case] class: &str, #[case] expected: &[(&str, &str)]) { let expected: Vec<(String, String)> = expected .iter() @@ -550,6 +554,7 @@ mod tests { } #[test] + #[serial] fn important_applies_under_variants() { let class = crate::tailwind::parse_class("hover:p-4!").unwrap(); assert_eq!(class.declarations, vec![decl("padding", "1rem !important")]); diff --git a/libs/extractor/src/tailwind_effects.rs b/libs/extractor/src/tailwind_effects.rs index 8b55e4290..90ffd4c4b 100644 --- a/libs/extractor/src/tailwind_effects.rs +++ b/libs/extractor/src/tailwind_effects.rs @@ -705,6 +705,7 @@ mod tests { #[case("blur-none", " ")] #[case("blur-[2px]", "blur(2px)")] #[case("blur-(--radius)", "blur(var(--radius))")] + #[serial] fn blur_composes_through_its_variable(#[case] class: &str, #[case] value: &str) { assert_eq!(written(class), filter("--tw-blur", value)); } @@ -729,6 +730,7 @@ mod tests { #[case("saturate-150", "--tw-saturate", "saturate(150%)")] #[case("sepia", "--tw-sepia", "sepia(100%)")] #[case("sepia-50", "--tw-sepia", "sepia(50%)")] + #[serial] fn filter_functions_set_their_own_variable( #[case] class: &str, #[case] variable: &str, @@ -764,6 +766,7 @@ mod tests { #[case("backdrop-opacity-12.5", "--tw-backdrop-opacity", "opacity(12.5%)")] #[case("backdrop-saturate-200", "--tw-backdrop-saturate", "saturate(200%)")] #[case("backdrop-sepia", "--tw-backdrop-sepia", "sepia(100%)")] + #[serial] fn backdrop_filter_functions_compose_too( #[case] class: &str, #[case] variable: &str, @@ -773,6 +776,7 @@ mod tests { } #[test] + #[serial] fn filter_and_backdrop_filter_read_every_function() { expect("filter", &[("filter", FILTER)]); expect("filter-none", &[("filter", "none")]); @@ -804,6 +808,7 @@ mod tests { } #[test] + #[serial] fn two_filters_add_up_instead_of_overwriting() { let blur = written("blur-sm"); let grayscale = written("grayscale"); @@ -852,6 +857,7 @@ mod tests { #[case("-filter-none")] #[case("backdrop-")] #[case("blurry")] + #[serial] fn unknown_filters_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } @@ -884,6 +890,7 @@ mod tests { "0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25))" )] #[case("shadow-none", "0 0 #0000")] + #[serial] fn shadow_sizes_follow_the_default_theme(#[case] class: &str, #[case] shadow: &str) { expect( class, @@ -927,11 +934,13 @@ mod tests { "shadow-current", "color-mix(in oklab, currentcolor var(--tw-shadow-alpha), transparent)" )] + #[serial] fn shadow_colors_set_the_color_the_shadows_read(#[case] class: &str, #[case] color: &str) { expect(class, &[("--tw-shadow-color", color)]); } #[test] + #[serial] fn shadow_inherit_and_unknown_shadows() { expect("shadow-inherit", &[("--tw-shadow-color", "inherit")]); for class in [ @@ -984,6 +993,7 @@ mod tests { #[case("bg-linear-to-r/increasing", "to right in oklch increasing hue")] #[case("bg-linear-to-r/decreasing", "to right in oklch decreasing hue")] #[case("bg-linear-90/[in_hsl_longer_hue]", "90deg in hsl longer hue")] + #[serial] fn linear_gradients_use_the_syntax_of_v4(#[case] class: &str, #[case] position: &str) { expect( class, @@ -998,6 +1008,7 @@ mod tests { } #[test] + #[serial] fn conic_and_radial_gradients() { for (class, position, image) in [ ( @@ -1047,6 +1058,7 @@ mod tests { } #[test] + #[serial] fn arbitrary_gradients_carry_their_own_stops() { expect( "bg-linear-[25deg,red_5%,yellow_60%,lime_90%,teal]", @@ -1102,6 +1114,7 @@ mod tests { #[case("bg-radial-45")] #[case("bg-conic-x")] #[case("bg-sweep-45")] + #[serial] fn gradients_v4_does_not_define_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } @@ -1110,6 +1123,7 @@ mod tests { const VIA_STOPS: &str = "var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position)"; #[test] + #[serial] fn gradient_stops_compose_through_variables() { expect( "from-red-500", @@ -1157,6 +1171,7 @@ mod tests { } #[test] + #[serial] fn gradient_stop_positions() { expect("from-10%", &[("--tw-gradient-from-position", "10%")]); expect("via-30%", &[("--tw-gradient-via-position", "30%")]); @@ -1178,11 +1193,13 @@ mod tests { #[case("-from-red-500")] #[case("via-none/50")] #[case("up-red-500")] + #[serial] fn unknown_gradient_stops_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } #[test] + #[serial] fn gradient_rules_register_the_stop_variables() { let rules = parse_class("from-red-500").unwrap().rules(); for name in [ @@ -1211,6 +1228,7 @@ mod tests { } #[test] + #[serial] fn modifiers_and_variants_apply_to_composed_utilities() { let class = parse_class("hover:blur-sm").unwrap(); assert_eq!(class.declarations, written_pairs("blur-sm")); @@ -1237,6 +1255,7 @@ mod tests { #[case("ring-2", "2px")] #[case("ring-8", "8px")] #[case("ring-[3px]", "3px")] + #[serial] fn ring_widths_draw_the_ring_composed_with_the_shadows( #[case] class: &str, #[case] width: &str, @@ -1256,6 +1275,7 @@ mod tests { } #[test] + #[serial] fn ring_colors_inset_rings_and_offsets() { expect("ring-inset", &[("--tw-ring-inset", "inset")]); expect( @@ -1327,6 +1347,7 @@ mod tests { #[case("ring-offset-nothing")] #[case("inset-ring-nothing")] #[case("-ring-2")] + #[serial] fn unknown_rings_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } @@ -1338,6 +1359,7 @@ mod tests { #[case("drop-shadow-lg", "0 4px 4px", "rgb(0 0 0 / 0.15)")] #[case("drop-shadow-xl", "0 9px 7px", "rgb(0 0 0 / 0.1)")] #[case("drop-shadow-2xl", "0 25px 25px", "rgb(0 0 0 / 0.15)")] + #[serial] fn drop_shadow_sizes_compose_into_filter( #[case] class: &str, #[case] offsets: &str, @@ -1359,6 +1381,7 @@ mod tests { ); } #[test] + #[serial] fn drop_shadow_without_a_size_none_colors_and_arbitrary_values() { expect( "drop-shadow", diff --git a/libs/extractor/src/tailwind_misc.rs b/libs/extractor/src/tailwind_misc.rs index 2b05db026..d4ef73a18 100644 --- a/libs/extractor/src/tailwind_misc.rs +++ b/libs/extractor/src/tailwind_misc.rs @@ -328,6 +328,7 @@ fn plain(name: &str) -> Option> { #[allow(clippy::expect_used, clippy::unwrap_used)] mod tests { use rstest::rstest; + use serial_test::serial; use crate::tailwind::declarations_of; @@ -365,6 +366,7 @@ mod tests { #[case("outline-double", &[("--tw-outline-style", "double"), ("outline-style", "double")])] #[case("outline-none", &[("--tw-outline-style", "none"), ("outline-style", "none")])] #[case("line-clamp-none", &[("overflow", "visible"), ("display", "block"), ("-webkit-box-orient", "horizontal"), ("-webkit-line-clamp", "unset")])] + #[serial] fn fixed_utilities(#[case] class: &str, #[case] expected: &[(&str, &str)]) { expect(class, expected); } @@ -373,6 +375,7 @@ mod tests { #[case("line-clamp-3", "3")] #[case("line-clamp-(--lines)", "var(--lines)")] #[case("line-clamp-[calc(1+1)]", "calc(1 + 1)")] + #[serial] fn line_clamp_clamps_to_a_number_of_lines(#[case] class: &str, #[case] lines: &str) { expect( class, @@ -386,6 +389,7 @@ mod tests { } #[test] + #[serial] fn text_decoration_offsets_and_outlines() { expect("underline-offset-4", &[("text-underline-offset", "4px")]); expect( @@ -451,6 +455,7 @@ mod tests { #[case("border-r-8", "border-right-style", "border-right-width", "8px")] #[case("border-b-[2px]", "border-bottom-style", "border-bottom-width", "2px")] #[case("border-l-2", "border-left-style", "border-left-width", "2px")] + #[serial] fn border_widths_draw_in_the_border_style( #[case] class: &str, #[case] style: &str, @@ -464,6 +469,7 @@ mod tests { } #[test] + #[serial] fn border_styles_set_the_style_the_widths_draw_in() { for style in ["solid", "dashed", "dotted", "double", "hidden", "none"] { expect( @@ -474,6 +480,7 @@ mod tests { } #[test] + #[serial] fn border_spacing_sets_both_axes_unless_one_is_named() { expect( "border-spacing-2", @@ -509,6 +516,7 @@ mod tests { } #[test] + #[serial] fn logical_sides_scroll_spacing_and_indent() { expect("start-4", &[("inset-inline-start", "1rem")]); expect("end-0", &[("inset-inline-end", "0px")]); @@ -574,6 +582,7 @@ mod tests { #[case("-indent")] #[case("-table-auto")] #[case("antialiased-")] + #[serial] fn unknown_utilities_stay_as_written(#[case] class: &str) { assert_eq!(declarations_of(class), None, "{class}"); } diff --git a/libs/extractor/src/tailwind_motion.rs b/libs/extractor/src/tailwind_motion.rs index 7f8ba011c..40330277d 100644 --- a/libs/extractor/src/tailwind_motion.rs +++ b/libs/extractor/src/tailwind_motion.rs @@ -160,6 +160,7 @@ pub fn keyframes_of(declarations: &[Declaration]) -> impl Iterator Date: Wed, 14 Oct 2026 19:00:31 +0900 Subject: [PATCH 2/2] test(e2e): expect v4 colors for compiled Tailwind class names Refs #770, #734 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- e2e/tailwind-hover-order.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/e2e/tailwind-hover-order.spec.ts b/e2e/tailwind-hover-order.spec.ts index 803106f74..a3514b427 100644 --- a/e2e/tailwind-hover-order.spec.ts +++ b/e2e/tailwind-hover-order.spec.ts @@ -154,6 +154,7 @@ for (const singleCss of [true, false]) { for (const fixture of cases) { for (const [index, width] of [640, 1024].entries()) { const label = 'props' in fixture ? 'explicit Devup guard: ' : '' + const devupColorIndex = 'props' in fixture ? 1 : 0 test(`${label}${fixture.classes}, ${width}px, singleCss=${singleCss}`, async ({ page, }, testInfo) => { @@ -191,7 +192,7 @@ for (const singleCss of [true, false]) { const observed = [] for (const [name, classes, css, color] of [ ['Tailwind', fixture.classes, oracleCss, expected[0]], - ['Devup', matched?.[1], devupCss, expected[1]], + ['Devup', matched?.[1], devupCss, expected[devupColorIndex]], ]) { await page.setContent( ``, @@ -207,14 +208,14 @@ for (const singleCss of [true, false]) { } else if (!disabled) { await expect(target).toHaveCSS( 'background-color', - colors.red[name === 'Tailwind' ? 0 : 1], + colors.red[name === 'Tailwind' ? 0 : devupColorIndex], ) } if (fixture.classes.includes('focus:')) { await target.focus() await expect(target).toHaveCSS( 'background-color', - colors.green[name === 'Tailwind' ? 0 : 1], + colors.green[name === 'Tailwind' ? 0 : devupColorIndex], ) } await page.mouse.down()