Commit 315a024ccc465597a9e6aa57a3fec6e2339c4338

Authored by YunaiV
1 parent e6dcbde8

feat(mail): 增强邮件模板参数解析和内容格式化功能

yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java
@@ -131,52 +131,47 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -131,52 +131,47 @@ public class MailTemplateServiceImpl implements MailTemplateService {
131 131
132 @Override 132 @Override
133 public String formatMailTemplateContent(String content, Map<String, Object> params) { 133 public String formatMailTemplateContent(String content, Map<String, Object> params) {
134 - // 先替换模板变量 134 + // 1. 先替换模板变量
135 String formattedContent = StrUtil.format(content, params); 135 String formattedContent = StrUtil.format(content, params);
136 136
137 - // 反转义HTML特殊字符 137 + // 关联 Pull Request:https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1461 讨论
  138 + // 2.1 反转义HTML特殊字符
138 formattedContent = unescapeHtml(formattedContent); 139 formattedContent = unescapeHtml(formattedContent);
139 -  
140 - // 处理代码块(确保<pre><code>标签格式正确) 140 + // 2.2 处理代码块(确保<pre><code>标签格式正确)
141 formattedContent = formatHtmlCodeBlocks(formattedContent); 141 formattedContent = formatHtmlCodeBlocks(formattedContent);
142 -  
143 - // 将最外层的pre标签替换为div标签 142 + // 2.3 将最外层的 pre 标签替换为 div 标签
144 formattedContent = replaceOuterPreWithDiv(formattedContent); 143 formattedContent = replaceOuterPreWithDiv(formattedContent);
145 -  
146 return formattedContent; 144 return formattedContent;
147 } 145 }
148 146
149 private String replaceOuterPreWithDiv(String content) { 147 private String replaceOuterPreWithDiv(String content) {
150 - if (content == null) {  
151 - return null; 148 + if (StrUtil.isEmpty(content)) {
  149 + return content;
152 } 150 }
153 -  
154 - // 使用正则表达式匹配所有的<pre>标签,包括嵌套的<code>标签 151 + // 使用正则表达式匹配所有的 <pre> 标签,包括嵌套的 <code> 标签
155 String regex = "(?s)<pre[^>]*>(.*?)</pre>"; 152 String regex = "(?s)<pre[^>]*>(.*?)</pre>";
156 Pattern pattern = Pattern.compile(regex); 153 Pattern pattern = Pattern.compile(regex);
157 Matcher matcher = pattern.matcher(content); 154 Matcher matcher = pattern.matcher(content);
158 -  
159 - StringBuffer sb = new StringBuffer(); 155 + StringBuilder sb = new StringBuilder();
160 while (matcher.find()) { 156 while (matcher.find()) {
161 - // 提取<pre>标签内的内容 157 + // 提取 <pre> 标签内的内容
162 String innerContent = matcher.group(1); 158 String innerContent = matcher.group(1);
163 - // 返回div标签包裹的内容 159 + // 返回 div 标签包裹的内容
164 matcher.appendReplacement(sb, "<div>" + innerContent + "</div>"); 160 matcher.appendReplacement(sb, "<div>" + innerContent + "</div>");
165 } 161 }
166 matcher.appendTail(sb); 162 matcher.appendTail(sb);
167 -  
168 return sb.toString(); 163 return sb.toString();
169 } 164 }
170 165
171 /** 166 /**
172 - * 反转义HTML特殊字符 167 + * 反转义 HTML 特殊字符
173 * 168 *
174 * @param input 输入字符串 169 * @param input 输入字符串
175 * @return 反转义后的字符串 170 * @return 反转义后的字符串
176 */ 171 */
177 private String unescapeHtml(String input) { 172 private String unescapeHtml(String input) {
178 - if (input == null) {  
179 - return null; 173 + if (StrUtil.isEmpty(input)) {
  174 + return input;
180 } 175 }
181 return input 176 return input
182 .replace("&amp;", "&") 177 .replace("&amp;", "&")
@@ -188,27 +183,24 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -188,27 +183,24 @@ public class MailTemplateServiceImpl implements MailTemplateService {
188 } 183 }
189 184
190 /** 185 /**
191 - * 格式化HTML中的代码块 186 + * 格式化 HTML 中的代码块
192 * 187 *
193 * @param content 邮件内容 188 * @param content 邮件内容
194 * @return 格式化后的邮件内容 189 * @return 格式化后的邮件内容
195 */ 190 */
196 private String formatHtmlCodeBlocks(String content) { 191 private String formatHtmlCodeBlocks(String content) {
197 - // 匹配<pre><code>标签的代码块 192 + // 匹配 <pre><code> 标签的代码块
198 Pattern codeBlockPattern = Pattern.compile("<pre\\s*.*?><code\\s*.*?>(.*?)</code></pre>", Pattern.DOTALL); 193 Pattern codeBlockPattern = Pattern.compile("<pre\\s*.*?><code\\s*.*?>(.*?)</code></pre>", Pattern.DOTALL);
199 Matcher matcher = codeBlockPattern.matcher(content); 194 Matcher matcher = codeBlockPattern.matcher(content);
200 -  
201 - StringBuffer sb = new StringBuffer(); 195 + StringBuilder sb = new StringBuilder();
202 while (matcher.find()) { 196 while (matcher.find()) {
203 // 获取代码块内容 197 // 获取代码块内容
204 String codeBlock = matcher.group(1); 198 String codeBlock = matcher.group(1);
205 -  
206 // 为代码块添加样式 199 // 为代码块添加样式
207 String replacement = "<pre style=\"background-color: #f5f5f5; padding: 10px; border-radius: 5px; overflow-x: auto;\"><code>" + codeBlock + "</code></pre>"; 200 String replacement = "<pre style=\"background-color: #f5f5f5; padding: 10px; border-radius: 5px; overflow-x: auto;\"><code>" + codeBlock + "</code></pre>";
208 matcher.appendReplacement(sb, replacement); 201 matcher.appendReplacement(sb, replacement);
209 } 202 }
210 matcher.appendTail(sb); 203 matcher.appendTail(sb);
211 -  
212 return sb.toString(); 204 return sb.toString();
213 } 205 }
214 206
@@ -216,6 +208,7 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -216,6 +208,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
216 public long getMailTemplateCountByAccountId(Long accountId) { 208 public long getMailTemplateCountByAccountId(Long accountId) {
217 return mailTemplateMapper.selectCountByAccountId(accountId); 209 return mailTemplateMapper.selectCountByAccountId(accountId);
218 } 210 }
  211 +
219 /** 212 /**
220 * 解析标题和内容中的参数 213 * 解析标题和内容中的参数
221 */ 214 */
@@ -223,7 +216,6 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -223,7 +216,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
223 public List<String> parseTemplateTitleAndContentParams(String title, String content) { 216 public List<String> parseTemplateTitleAndContentParams(String title, String content) {
224 List<String> titleParams = ReUtil.findAllGroup1(PATTERN_PARAMS, title); 217 List<String> titleParams = ReUtil.findAllGroup1(PATTERN_PARAMS, title);
225 List<String> contentParams = ReUtil.findAllGroup1(PATTERN_PARAMS, content); 218 List<String> contentParams = ReUtil.findAllGroup1(PATTERN_PARAMS, content);
226 -  
227 // 合并参数并去重 219 // 合并参数并去重
228 List<String> allParams = new ArrayList<>(titleParams); 220 List<String> allParams = new ArrayList<>(titleParams);
229 for (String param : contentParams) { 221 for (String param : contentParams) {
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java
@@ -221,7 +221,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -221,7 +221,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
221 221
222 // 调用,并断言结果 222 // 调用,并断言结果
223 String result = mailTemplateService.formatMailTemplateContent(content, params); 223 String result = mailTemplateService.formatMailTemplateContent(content, params);
224 - // 断言pre标签被替换为div标签 224 + // 断言 pre 标签被替换为 div 标签
225 assertTrue(result.contains("<div><code>public class Test {")); 225 assertTrue(result.contains("<div><code>public class Test {"));
226 assertTrue(result.contains("System.out.println(\"Hello 测试\"")); 226 assertTrue(result.contains("System.out.println(\"Hello 测试\""));
227 assertTrue(result.contains("</code></div>")); 227 assertTrue(result.contains("</code></div>"));
@@ -233,10 +233,10 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -233,10 +233,10 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
233 Map<String, Object> params = new HashMap<>(); 233 Map<String, Object> params = new HashMap<>();
234 params.put("content", "测试内容"); 234 params.put("content", "测试内容");
235 235
236 - // 测试pre标签替换为div标签 236 + // 测试 pre 标签替换为 div 标签
237 String content = "<pre><code>{content}</code></pre>"; 237 String content = "<pre><code>{content}</code></pre>";
238 String result = mailTemplateService.formatMailTemplateContent(content, params); 238 String result = mailTemplateService.formatMailTemplateContent(content, params);
239 - // 断言结果中包含div标签,而不包含pre标签 239 + // 断言结果中包含 div 标签,而不包含 pre 标签
240 assertTrue(result.contains("<div><code>测试内容</code></div>")); 240 assertTrue(result.contains("<div><code>测试内容</code></div>"));
241 } 241 }
242 242
@@ -247,7 +247,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -247,7 +247,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
247 params.put("username", "testuser"); 247 params.put("username", "testuser");
248 params.put("company", "测试公司"); 248 params.put("company", "测试公司");
249 249
250 - // 测试完整的HTML邮件模板 250 + // 测试完整的 HTML 邮件模板
251 String content = "<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <meta charset=\"UTF-8\">\n <title>Title</title>\n </head>\n <body>\n <div>\n <includetail>\n <div>\n <div class=\"open_email\" style=\"margin: 8px; \">\n <div>\n <br>\n <span class=\"genEmailContent\">\n <div id=\"cTMail-Wrap\" style=\"word-break: break-all;box-sizing:border-box;text-align:left;min-width:320px; max-width:660px; border:1px solid #f6f6f6; background-color:#f7f8fa; margin:auto; padding:20px 0 30px; font-family:'helvetica neue',PingFangSC-Light,arial,'hiragino sans gb','microsoft yahei ui','microsoft yahei',simsun,sans-serif\">\n <div class=\"main-content\">\n <table style=\"width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse\">\n <tbody>\n <tr style=\"font-weight:300\">\n <td style=\"width:3%;max-width:30px;\"></td>\n <td style=\"max-width:600px;\">\n <div id=\"cTMail-logo\" style=\"width:92px; height:36px;\">\n <a href=\"\">\n <img border=\"0\" src=\"左上角图片logo\" style=\"width:120px; height:36px;display:block\">\n </a>\n </div>\n <div style=\"color: #C2C5C9;width: 260px;float: right;font-size: 12px;\">此邮件由系统发出,请勿直接回复或转发他人</div>\n <p style=\"height:2px;background-color: #00a4ff;border: 0;font-size:0;padding:0;width:100%;margin-top:20px;\"></p>\n <div id=\"cTMail-inner\" style=\"background-color:#fff; padding:23px 0 20px;box-shadow: 0px 1px 1px 0px rgba(122, 55, 55, 0.2);text-align:left;\">\n <table style=\"width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse;\">\n <tbody>\n <tr style=\"font-weight:300\">\n <td style=\"width:3.2%;max-width:30px;\"></td>\n <td style=\"max-width:480px;\">\n <h1 id=\"cTMail-title\" style=\"font-size: 20px; line-height: 36px; margin: 0px 0px 22px;\">\n 尊敬的 {username},\n </h1>\n <dl style=\"font-size: 14px; color: #595E65; line-height: 18px;\">\n <dd style=\"margin: 0px 0px 6px; padding: 0px; font-size: 14px; line-height: 22px;\">\n <p id=\"cTMail-sender\" style=\"font-size: 14px; line-height: 26px; word-wrap: break-word; word-break: break-all; margin-top: 32px;\">\n 内容<br>\n 内容<br>\n 内容123<br><br>\n\n 如果您在使用过程中遇到任何问题或者有任何建议,都可以随时联系我们的客户团队,我们将竭诚为您服务。\n\n\n\n\n <br>\n <br>\n {company}<br>\n 地址:xxxxx<br>\n 邮箱:lambc77@163.com\n </p>\n </dd>\n </dl>\n <hr style=\"border: 0.1px solid #e5e5e5;\"/>\n <dl style=\"font-size: 14px; color: #595E65; line-height: 18px;\">\n <div style=\"color: #93979B;\">\n <strong>声明:本邮件含有保密信息,仅限于收件人所用。禁止任何人未经发件人许可,以任何形式(包括但不限于部分的泄露、复制或散发)不当的使用本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件,谢谢!\n </strong><br>\n </div>\n </dl>\n </td>\n <td style=\"width:3.2%;max-width:30px;\"></td>\n </tr>\n </tbody>\n </table>\n </div>\n </td>\n <td style=\"width:3%;max-width:30px;\"></td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </span>\n </div>\n </div>\n </div>\n </includetail>\n </div>\n </body>\n <script>\n\n </script>\n </html>"; 251 String content = "<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <meta charset=\"UTF-8\">\n <title>Title</title>\n </head>\n <body>\n <div>\n <includetail>\n <div>\n <div class=\"open_email\" style=\"margin: 8px; \">\n <div>\n <br>\n <span class=\"genEmailContent\">\n <div id=\"cTMail-Wrap\" style=\"word-break: break-all;box-sizing:border-box;text-align:left;min-width:320px; max-width:660px; border:1px solid #f6f6f6; background-color:#f7f8fa; margin:auto; padding:20px 0 30px; font-family:'helvetica neue',PingFangSC-Light,arial,'hiragino sans gb','microsoft yahei ui','microsoft yahei',simsun,sans-serif\">\n <div class=\"main-content\">\n <table style=\"width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse\">\n <tbody>\n <tr style=\"font-weight:300\">\n <td style=\"width:3%;max-width:30px;\"></td>\n <td style=\"max-width:600px;\">\n <div id=\"cTMail-logo\" style=\"width:92px; height:36px;\">\n <a href=\"\">\n <img border=\"0\" src=\"左上角图片logo\" style=\"width:120px; height:36px;display:block\">\n </a>\n </div>\n <div style=\"color: #C2C5C9;width: 260px;float: right;font-size: 12px;\">此邮件由系统发出,请勿直接回复或转发他人</div>\n <p style=\"height:2px;background-color: #00a4ff;border: 0;font-size:0;padding:0;width:100%;margin-top:20px;\"></p>\n <div id=\"cTMail-inner\" style=\"background-color:#fff; padding:23px 0 20px;box-shadow: 0px 1px 1px 0px rgba(122, 55, 55, 0.2);text-align:left;\">\n <table style=\"width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse;\">\n <tbody>\n <tr style=\"font-weight:300\">\n <td style=\"width:3.2%;max-width:30px;\"></td>\n <td style=\"max-width:480px;\">\n <h1 id=\"cTMail-title\" style=\"font-size: 20px; line-height: 36px; margin: 0px 0px 22px;\">\n 尊敬的 {username},\n </h1>\n <dl style=\"font-size: 14px; color: #595E65; line-height: 18px;\">\n <dd style=\"margin: 0px 0px 6px; padding: 0px; font-size: 14px; line-height: 22px;\">\n <p id=\"cTMail-sender\" style=\"font-size: 14px; line-height: 26px; word-wrap: break-word; word-break: break-all; margin-top: 32px;\">\n 内容<br>\n 内容<br>\n 内容123<br><br>\n\n 如果您在使用过程中遇到任何问题或者有任何建议,都可以随时联系我们的客户团队,我们将竭诚为您服务。\n\n\n\n\n <br>\n <br>\n {company}<br>\n 地址:xxxxx<br>\n 邮箱:lambc77@163.com\n </p>\n </dd>\n </dl>\n <hr style=\"border: 0.1px solid #e5e5e5;\"/>\n <dl style=\"font-size: 14px; color: #595E65; line-height: 18px;\">\n <div style=\"color: #93979B;\">\n <strong>声明:本邮件含有保密信息,仅限于收件人所用。禁止任何人未经发件人许可,以任何形式(包括但不限于部分的泄露、复制或散发)不当的使用本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件,谢谢!\n </strong><br>\n </div>\n </dl>\n </td>\n <td style=\"width:3.2%;max-width:30px;\"></td>\n </tr>\n </tbody>\n </table>\n </div>\n </td>\n <td style=\"width:3%;max-width:30px;\"></td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </span>\n </div>\n </div>\n </div>\n </includetail>\n </div>\n </body>\n <script>\n\n </script>\n </html>";
252 252
253 // 调用,并断言成功处理 253 // 调用,并断言成功处理
@@ -255,7 +255,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -255,7 +255,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
255 // 断言结果中包含替换后的变量 255 // 断言结果中包含替换后的变量
256 assertTrue(result.contains("尊敬的 testuser")); 256 assertTrue(result.contains("尊敬的 testuser"));
257 assertTrue(result.contains("测试公司")); 257 assertTrue(result.contains("测试公司"));
258 - // 断言结果是有效的HTML 258 + // 断言结果是有效的 HTML
259 assertTrue(result.startsWith("<!DOCTYPE html>")); 259 assertTrue(result.startsWith("<!DOCTYPE html>"));
260 } 260 }
261 261
@@ -287,7 +287,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -287,7 +287,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
287 params.put("param1", "value1"); 287 params.put("param1", "value1");
288 params.put("param2", "value2"); 288 params.put("param2", "value2");
289 289
290 - // 测试多个pre标签的情况 290 + // 测试多个 pre 标签的情况
291 String content = "<pre><code>First code block: {param1}</code></pre>\n" + 291 String content = "<pre><code>First code block: {param1}</code></pre>\n" +
292 "<p>Some text between code blocks</p>\n" + 292 "<p>Some text between code blocks</p>\n" +
293 "<pre><code>Second code block: {param2}</code></pre>"; 293 "<pre><code>Second code block: {param2}</code></pre>";
@@ -302,7 +302,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -302,7 +302,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
302 // 准备参数 302 // 准备参数
303 Map<String, Object> params = new HashMap<>(); 303 Map<String, Object> params = new HashMap<>();
304 304
305 - // 简化测试,只测试基本的HTML特殊字符 305 + // 简化测试,只测试基本的 HTML 特殊字符
306 String content = "&lt;div&gt;测试 &amp; 特殊字符&lt;/div&gt;"; 306 String content = "&lt;div&gt;测试 &amp; 特殊字符&lt;/div&gt;";
307 String result = mailTemplateService.formatMailTemplateContent(content, params); 307 String result = mailTemplateService.formatMailTemplateContent(content, params);
308 // 断言特殊字符被正确反转义 308 // 断言特殊字符被正确反转义
@@ -327,7 +327,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -327,7 +327,7 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
327 327
328 @Test 328 @Test
329 public void testDifferenceWithHtmlContent() { 329 public void testDifferenceWithHtmlContent() {
330 - // 准备包含HTML格式的模板内容 330 + // 准备包含 HTML 格式的模板内容
331 String content = "<div style='font-family: Arial, sans-serif; color: #333;'>" + 331 String content = "<div style='font-family: Arial, sans-serif; color: #333;'>" +
332 "<h1>Welcome, {username}!</h1>" + 332 "<h1>Welcome, {username}!</h1>" +
333 "<p>Your account has been created successfully.</p>" + 333 "<p>Your account has been created successfully.</p>" +
@@ -347,28 +347,28 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest { @@ -347,28 +347,28 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
347 params.put("role", "admin"); 347 params.put("role", "admin");
348 params.put("activationLink", "https://example.com/activate?code=12345"); 348 params.put("activationLink", "https://example.com/activate?code=12345");
349 349
350 - // 1. 使用parseTemplateContentParams:只提取参数名称,忽略了HTML格式 350 + // 1. 使用 parseTemplateContentParams:只提取参数名称,忽略了 HTML 格式
351 List<String> parsedParams = mailTemplateService.parseTemplateContentParams(content); 351 List<String> parsedParams = mailTemplateService.parseTemplateContentParams(content);
352 System.out.println("parseTemplateContentParams结果:" + parsedParams); 352 System.out.println("parseTemplateContentParams结果:" + parsedParams);
353 353
354 - // 断言:只提取了纯参数名称,没有HTML格式 354 + // 断言:只提取了纯参数名称,没有 HTML 格式
355 assertEquals(6, parsedParams.size()); 355 assertEquals(6, parsedParams.size());
356 // 检查所有参数类型 356 // 检查所有参数类型
357 - assertTrue(parsedParams.stream().filter("username"::equals).count() == 3);  
358 - assertTrue(parsedParams.stream().filter("email"::equals).count() == 1);  
359 - assertTrue(parsedParams.stream().filter("role"::equals).count() == 1);  
360 - assertTrue(parsedParams.stream().filter("activationLink"::equals).count() == 1);  
361 - // 断言:没有包含任何HTML标签 357 + assertEquals(3, parsedParams.stream().filter("username"::equals).count());
  358 + assertEquals(1, parsedParams.stream().filter("email"::equals).count());
  359 + assertEquals(1, parsedParams.stream().filter("role"::equals).count());
  360 + assertEquals(1, parsedParams.stream().filter("activationLink"::equals).count());
  361 + // 断言:没有包含任何 HTML 标签
362 for (String param : parsedParams) { 362 for (String param : parsedParams) {
363 assertFalse(param.contains("<")); 363 assertFalse(param.contains("<"));
364 assertFalse(param.contains(">")); 364 assertFalse(param.contains(">"));
365 } 365 }
366 366
367 - // 2. 使用formatMailTemplateContent:处理HTML格式,生成最终内容 367 + // 2. 使用 formatMailTemplateContent:处理 HTML 格式,生成最终内容
368 String formattedContent = mailTemplateService.formatMailTemplateContent(content, params); 368 String formattedContent = mailTemplateService.formatMailTemplateContent(content, params);
369 System.out.println("formatMailTemplateContent结果:" + formattedContent); 369 System.out.println("formatMailTemplateContent结果:" + formattedContent);
370 370
371 - // 断言:HTML格式被保留并处理 371 + // 断言:HTML 格式被保留并处理
372 assertTrue(formattedContent.contains("<div style='font-family: Arial, sans-serif; color: #333;'>")); 372 assertTrue(formattedContent.contains("<div style='font-family: Arial, sans-serif; color: #333;'>"));
373 assertTrue(formattedContent.contains("<h1>Welcome, testuser!</h1>")); 373 assertTrue(formattedContent.contains("<h1>Welcome, testuser!</h1>"));
374 assertTrue(formattedContent.contains("<a href='https://example.com/activate?code=12345'>here</a>")); 374 assertTrue(formattedContent.contains("<a href='https://example.com/activate?code=12345'>here</a>"));