From d69942c9236a1a8d1df4770f4b7734a8cecf9909 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 04:54:15 +0000 Subject: [PATCH 1/9] Fix #410: Preserve !important tag in inlined styles Co-Authored-By: m@martinnormark.com --- .../StyleClassApplierTests.cs | 19 ++++++- .../PreMailer.Net/CssElementStyleResolver.cs | 4 +- PreMailer.Net/PreMailer.Net/StyleClass.cs | 2 +- .../PreMailer.Net/StyleClassApplier.cs | 49 ++++++++++++++----- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/PreMailer.Net/PreMailer.Net.Tests/StyleClassApplierTests.cs b/PreMailer.Net/PreMailer.Net.Tests/StyleClassApplierTests.cs index 8088bb30..1f40b342 100644 --- a/PreMailer.Net/PreMailer.Net.Tests/StyleClassApplierTests.cs +++ b/PreMailer.Net/PreMailer.Net.Tests/StyleClassApplierTests.cs @@ -58,7 +58,24 @@ public void ApplyInlineStylesWithoutImportant() var result = StyleClassApplier.ApplyAllStyles(elementDictionary); - Assert.Equal("
", result.ElementAt(0).Key.OuterHtml); + Assert.Equal("
", result.ElementAt(0).Key.OuterHtml); + } + + [Fact] + public void ApplyInlineStylesWithImportant() + { + var document = new HtmlParser().ParseDocument("
"); + + var clazz = new StyleClass(); + clazz.Attributes["color"] = CssAttribute.FromRule("color: #000"); + + var elementDictionary = new Dictionary { + {document.Body.FirstElementChild, clazz} + }; + + var result = StyleClassApplier.ApplyAllStyles(elementDictionary); + + Assert.Equal("
", result.ElementAt(0).Key.OuterHtml); } } } diff --git a/PreMailer.Net/PreMailer.Net/CssElementStyleResolver.cs b/PreMailer.Net/PreMailer.Net/CssElementStyleResolver.cs index 6d4f5c62..20e95dbf 100644 --- a/PreMailer.Net/PreMailer.Net/CssElementStyleResolver.cs +++ b/PreMailer.Net/PreMailer.Net/CssElementStyleResolver.cs @@ -15,7 +15,7 @@ public static IEnumerable GetAllStyles(IElement domElement, Styl AddSpecialPremailerAttributes(attributeCssList, styleClass); if (styleClass.Attributes.Count > 0) - attributeCssList.Add(new AttributeToCss { AttributeName = "style", CssValue = styleClass.ToString() }); + attributeCssList.Add(new AttributeToCss { AttributeName = "style", CssValue = styleClass.ToString(emitImportant: true) }); attributeCssList.AddRange(CssStyleEquivalence.FindEquivalent(domElement, styleClass)); @@ -44,4 +44,4 @@ private static void AddSpecialPremailerAttributes(List attribute } } } -} \ No newline at end of file +} diff --git a/PreMailer.Net/PreMailer.Net/StyleClass.cs b/PreMailer.Net/PreMailer.Net/StyleClass.cs index 8dd8e6d8..4b8b57f3 100644 --- a/PreMailer.Net/PreMailer.Net/StyleClass.cs +++ b/PreMailer.Net/PreMailer.Net/StyleClass.cs @@ -37,7 +37,7 @@ public StyleClass() public void Merge(StyleClass styleClass, bool canOverwrite) { foreach (var item in styleClass.Attributes) { if (!Attributes.TryGetValue(item.Style, out var existing) || - canOverwrite && (!existing.Important || item.Important)) + (canOverwrite && (!existing.Important || item.Important))) // Overwrite if existing is not important or if new is important { Attributes.Merge(item); } diff --git a/PreMailer.Net/PreMailer.Net/StyleClassApplier.cs b/PreMailer.Net/PreMailer.Net/StyleClassApplier.cs index 930f7a16..0a29b0da 100644 --- a/PreMailer.Net/PreMailer.Net/StyleClassApplier.cs +++ b/PreMailer.Net/PreMailer.Net/StyleClassApplier.cs @@ -15,24 +15,49 @@ public static Dictionary ApplyAllStyles(Dictionary Date: Wed, 21 May 2025 04:54:54 +0000 Subject: [PATCH 2/9] Fix #410: Minor code formatting in PreMailer.cs Co-Authored-By: m@martinnormark.com --- PreMailer.Net/PreMailer.Net/PreMailer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PreMailer.Net/PreMailer.Net/PreMailer.cs b/PreMailer.Net/PreMailer.Net/PreMailer.cs index 34a402c5..aa2992dc 100644 --- a/PreMailer.Net/PreMailer.Net/PreMailer.cs +++ b/PreMailer.Net/PreMailer.Net/PreMailer.cs @@ -440,7 +440,8 @@ private Dictionary> SortBySpecificity( } else // Ensure that existing inline styles always win. { - sortedStyles.Add(_cssParser.ParseStyleClass("inline", styleAttr.Value)); + var inlineStyle = _cssParser.ParseStyleClass("inline", styleAttr.Value); + sortedStyles.Add(inlineStyle); } result[style.Key] = sortedStyles; @@ -537,4 +538,4 @@ public void Dispose() _document.Dispose(); } } -} \ No newline at end of file +} From 5f1db61646c1a7f4590e60846e8b482d7c4f9598 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 12:29:33 +0000 Subject: [PATCH 3/9] Address PR feedback: Fix code style issues Co-Authored-By: m@martinnormark.com --- PreMailer.Net/PreMailer.Net/PreMailer.cs | 3 +-- PreMailer.Net/PreMailer.Net/StyleClass.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/PreMailer.Net/PreMailer.Net/PreMailer.cs b/PreMailer.Net/PreMailer.Net/PreMailer.cs index aa2992dc..c259aa84 100644 --- a/PreMailer.Net/PreMailer.Net/PreMailer.cs +++ b/PreMailer.Net/PreMailer.Net/PreMailer.cs @@ -440,8 +440,7 @@ private Dictionary> SortBySpecificity( } else // Ensure that existing inline styles always win. { - var inlineStyle = _cssParser.ParseStyleClass("inline", styleAttr.Value); - sortedStyles.Add(inlineStyle); + sortedStyles.Add(_cssParser.ParseStyleClass("inline", styleAttr.Value)); } result[style.Key] = sortedStyles; diff --git a/PreMailer.Net/PreMailer.Net/StyleClass.cs b/PreMailer.Net/PreMailer.Net/StyleClass.cs index 4b8b57f3..8dd8e6d8 100644 --- a/PreMailer.Net/PreMailer.Net/StyleClass.cs +++ b/PreMailer.Net/PreMailer.Net/StyleClass.cs @@ -37,7 +37,7 @@ public StyleClass() public void Merge(StyleClass styleClass, bool canOverwrite) { foreach (var item in styleClass.Attributes) { if (!Attributes.TryGetValue(item.Style, out var existing) || - (canOverwrite && (!existing.Important || item.Important))) // Overwrite if existing is not important or if new is important + canOverwrite && (!existing.Important || item.Important)) { Attributes.Merge(item); } From 1c6049476a33b7f1c9d1ea0527c19de38471f4e3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 12:45:00 +0000 Subject: [PATCH 4/9] Add test case for issue #410 to prove the need for additional logic Co-Authored-By: m@martinnormark.com --- .../PreMailer.Net.Tests/PreMailerTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs b/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs index 4db0e0e8..9ee76044 100644 --- a/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs +++ b/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs @@ -188,6 +188,23 @@ public void MoveCssInline_ImportantFlag_HonorsImportantFlagInline() Assert.Contains("
+.test { + color:red; + } + + +

test

+"; + + var premailedOutput = PreMailer.MoveCssInline(input); + + Assert.Contains("font-weight: bold !important", premailedOutput.Html); + } + [Fact] public void MoveCssInline_AbsoluteBackgroundUrl_ShouldNotBeCleanedAsComment() { From bfeec2b10eef3af293db6b60233f30118164f26c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 12:45:32 +0000 Subject: [PATCH 5/9] Add dedicated test case for issue #410 Co-Authored-By: m@martinnormark.com --- .../PreMailer.Net.Tests/Issue410Tests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs diff --git a/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs b/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs new file mode 100644 index 00000000..3aa148ab --- /dev/null +++ b/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs @@ -0,0 +1,25 @@ +using AngleSharp.Html.Parser; +using Xunit; + +namespace PreMailer.Net.Tests +{ + public class Issue410Tests + { + [Fact] + public void MoveCssInline_PreservesImportantInInlineStyles() + { + string input = @" + +

test

+"; + + var result = PreMailer.MoveCssInline(input); + + Assert.Contains("font-weight: bold !important", result.Html); + } + } +} From b4f4e1c97a8bbfa6795ef521c6a2b8aa41021582 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 12:52:16 +0000 Subject: [PATCH 6/9] Address PR feedback: Move test from Issue410Tests to PreMailerTests with descriptive name Co-Authored-By: m@martinnormark.com --- .../PreMailer.Net.Tests/Issue410Tests.cs | 25 ------------------- .../PreMailer.Net.Tests/PreMailerTests.cs | 14 +++++------ 2 files changed, 7 insertions(+), 32 deletions(-) delete mode 100644 PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs diff --git a/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs b/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs deleted file mode 100644 index 3aa148ab..00000000 --- a/PreMailer.Net/PreMailer.Net.Tests/Issue410Tests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using AngleSharp.Html.Parser; -using Xunit; - -namespace PreMailer.Net.Tests -{ - public class Issue410Tests - { - [Fact] - public void MoveCssInline_PreservesImportantInInlineStyles() - { - string input = @" - -

test

-"; - - var result = PreMailer.MoveCssInline(input); - - Assert.Contains("font-weight: bold !important", result.Html); - } - } -} diff --git a/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs b/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs index 9ee76044..9923ece2 100644 --- a/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs +++ b/PreMailer.Net/PreMailer.Net.Tests/PreMailerTests.cs @@ -188,10 +188,10 @@ public void MoveCssInline_ImportantFlag_HonorsImportantFlagInline() Assert.Contains("
+ [Fact] + public void MoveCssInline_ShouldPreserveImportantFlagInInlineStyles_WhenApplyingClassStyles() + { + string input = @"