488 lines
21 KiB
C#
488 lines
21 KiB
C#
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using DocumentFormat.OpenXml;
|
|
using DocumentFormat.OpenXml.Packaging;
|
|
using DocumentFormat.OpenXml.Wordprocessing;
|
|
using Omika.App.Controls;
|
|
using Omika.Core.Contracts.Documents;
|
|
using Omika.Core.Services;
|
|
|
|
namespace Omika.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// Tests de COMPATIBILITÉ du corps HTML des courriels WinDev ↔ Omika :
|
|
/// <list type="bullet">
|
|
/// <item><b>WinDev → Omika</b> (lecture) : le CSS/<head>/<style> ne sont
|
|
/// jamais rendus comme texte ; les tables (tbody), le bloc photo imbriqué et les
|
|
/// tableaux de contacts sont lus dans l'ordre.</item>
|
|
/// <item><b>Omika → WinDev</b> (écriture) : le HTML persisté est conservateur
|
|
/// (<table>/<tr>/<td>/<p>/<img>), la photo est empilée
|
|
/// (image puis date), les contacts sont en vraies cellules, sans flex/grid.</item>
|
|
/// <item><b>Roundtrip</b> : relecture du HTML Omika sans perte structurelle.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public class EmailHtmlCompatibiliteTests
|
|
{
|
|
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
|
|
|
|
private static readonly byte[] OctetsJpeg = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46];
|
|
|
|
// ------------------------------------------------------------------
|
|
// Helpers — lecture (parser)
|
|
// ------------------------------------------------------------------
|
|
|
|
private static IReadOnlyList<HtmlBlock> Parser(string html) => EmailHtmlParser.Parser(html);
|
|
|
|
private static IEnumerable<HtmlInline> InlinesTous(IReadOnlyList<HtmlBlock> blocs)
|
|
{
|
|
foreach (HtmlBlock bloc in blocs)
|
|
{
|
|
switch (bloc)
|
|
{
|
|
case HtmlParagraph p:
|
|
foreach (HtmlInline i in p.Inlines) yield return i;
|
|
break;
|
|
case HtmlTable t:
|
|
foreach (HtmlRow row in t.Rows)
|
|
foreach (HtmlCell cell in row.Cells)
|
|
foreach (HtmlInline i in cell.Inlines)
|
|
yield return i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static List<string> Textes(IReadOnlyList<HtmlBlock> blocs)
|
|
=> InlinesTous(blocs).OfType<HtmlRun>().Select(r => r.Text).ToList();
|
|
|
|
private static string ToutLeTexte(string html) => string.Join("\n", Textes(Parser(html)));
|
|
|
|
// ------------------------------------------------------------------
|
|
// Helpers — écriture (DOCX → HTML)
|
|
// ------------------------------------------------------------------
|
|
|
|
/// <summary>DOCX : table 1 colonne / 2 lignes — ligne 2 = 3 paragraphes (image, date, note).</summary>
|
|
private static byte[] CreerDocxPhotoBloc(string titre = "Photos")
|
|
{
|
|
using var stream = new MemoryStream();
|
|
using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
|
|
{
|
|
MainDocumentPart main = doc.AddMainDocumentPart();
|
|
main.Document = new Document();
|
|
Body body = new Body();
|
|
var table = new Table();
|
|
table.Append(new TableRow(new TableCell(new Paragraph(new Run(new Text(titre))))));
|
|
var cellule = new TableCell();
|
|
cellule.Append(new Paragraph(new Run(new Text("$TABPHOTO.IMAGE$"))));
|
|
cellule.Append(new Paragraph(new Run(new Text("$TABPHOTO.DATEIMAGE$"))));
|
|
cellule.Append(new Paragraph(new Run(new Text("$TABPHOTO.NOTEIMAGE$"))));
|
|
table.Append(new TableRow(cellule));
|
|
body.Append(table);
|
|
main.Document.Append(body);
|
|
main.Document.Save();
|
|
}
|
|
return stream.ToArray();
|
|
}
|
|
|
|
/// <summary>DOCX : table contacts 5 colonnes (en-tête + 1 ligne de données).</summary>
|
|
private static byte[] CreerDocxContacts()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
|
|
{
|
|
MainDocumentPart main = doc.AddMainDocumentPart();
|
|
main.Document = new Document();
|
|
Body body = new Body();
|
|
var table = new Table();
|
|
table.Append(new TableRow(
|
|
Cell("$TABCTSITE.NOM$"), Cell("$TABCTSITE.TELFIXE$"), Cell("$TABCTSITE.TELFAXE$"),
|
|
Cell("$TABCTSITE.ROLE$"), Cell("$TABCTSITE.ETAGE$")));
|
|
table.Append(new TableRow(
|
|
Cell("Dupont Jean"), Cell("04 00 00 00 00"), Cell("06 00 00 00 00"),
|
|
Cell("Occupant"), Cell("4")));
|
|
body.Append(table);
|
|
main.Document.Append(body);
|
|
main.Document.Save();
|
|
}
|
|
return stream.ToArray();
|
|
|
|
static TableCell Cell(string texte) => new(new Paragraph(new Run(new Text(texte))));
|
|
}
|
|
|
|
/// <summary>DOCX : bloc photo (image/date/note empilés) + tableau contacts (5 colonnes).</summary>
|
|
private static byte[] CreerDocxPhotoEtContacts()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
using (WordprocessingDocument doc = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
|
|
{
|
|
MainDocumentPart main = doc.AddMainDocumentPart();
|
|
main.Document = new Document();
|
|
Body body = new Body();
|
|
|
|
// Bloc photo (1 colonne / 2 lignes ; ligne 2 = 3 paragraphes empilés).
|
|
var photo = new Table();
|
|
photo.Append(new TableRow(new TableCell(new Paragraph(new Run(new Text("Photos"))))));
|
|
var cellulePhoto = new TableCell();
|
|
cellulePhoto.Append(new Paragraph(new Run(new Text("$TABPHOTO.IMAGE$"))));
|
|
cellulePhoto.Append(new Paragraph(new Run(new Text("$TABPHOTO.DATEIMAGE$"))));
|
|
cellulePhoto.Append(new Paragraph(new Run(new Text("$TABPHOTO.NOTEIMAGE$"))));
|
|
photo.Append(new TableRow(cellulePhoto));
|
|
body.Append(photo);
|
|
|
|
// Contacts (5 colonnes : en-têtes littéraux + 1 ligne de données déjà résolue).
|
|
var contacts = new Table();
|
|
contacts.Append(new TableRow(
|
|
new TableCell(new Paragraph(new Run(new Text("Nom - Prénom")))),
|
|
new TableCell(new Paragraph(new Run(new Text("Tel. 1")))),
|
|
new TableCell(new Paragraph(new Run(new Text("Tel. 2")))),
|
|
new TableCell(new Paragraph(new Run(new Text("Rôle")))),
|
|
new TableCell(new Paragraph(new Run(new Text("Étage"))))));
|
|
contacts.Append(new TableRow(
|
|
new TableCell(new Paragraph(new Run(new Text("Dupont Jean")))),
|
|
new TableCell(new Paragraph(new Run(new Text("04 00 00 00 00")))),
|
|
new TableCell(new Paragraph(new Run(new Text("06 00 00 00 00")))),
|
|
new TableCell(new Paragraph(new Run(new Text("Occupant")))),
|
|
new TableCell(new Paragraph(new Run(new Text("4"))))));
|
|
body.Append(contacts);
|
|
|
|
main.Document.Append(body);
|
|
main.Document.Save();
|
|
}
|
|
return stream.ToArray();
|
|
}
|
|
|
|
private static RapportChampDto TableauPhotos(string json)
|
|
=> new()
|
|
{
|
|
Id = "1",
|
|
Rubrique = "$TABPHOTO$",
|
|
Type = "Tableau",
|
|
ListeDonnesBD = json,
|
|
ListeMembre =
|
|
[
|
|
new RapportChampSousElementDto { Rubrique = "$TABPHOTO.IMAGE$", Type = "ChpImage", TypeMembre = "ChpImage", MembreMob = "cheminComplet" },
|
|
new RapportChampSousElementDto { Rubrique = "$TABPHOTO.DATEIMAGE$", Type = "ChpSimpleDate", TypeMembre = "ChpSimpleDate", MembreMob = "saDate" },
|
|
new RapportChampSousElementDto { Rubrique = "$TABPHOTO.NOTEIMAGE$", Type = "ChpSimpleChaine", TypeMembre = "ChpSimpleChaine", MembreMob = "saNotes" }
|
|
]
|
|
};
|
|
|
|
private static string RendreCorps(IReadOnlyList<RapportChampDto> champs, byte[] docx, out IReadOnlyList<EmailInlineImage> images)
|
|
{
|
|
var liste = new List<EmailInlineImage>();
|
|
byte[] rendu = EmailDocxRenderer.Rendre(docx, champs, liste);
|
|
images = liste;
|
|
return EmailImageInliner.Inliner(DocxHtmlConverter.Convertir(rendu), liste);
|
|
}
|
|
|
|
private static string JsonUnePhoto() => JsonSerializer.Serialize(new[]
|
|
{
|
|
// Les propriétés JSON suivent la convention camelCase (après normalisation
|
|
// MembreMob : saDate → date, saNotes → notes).
|
|
new { cheminComplet = Convert.ToBase64String(OctetsJpeg), date = "30/01/2026", notes = "Note photo" }
|
|
}, Json);
|
|
|
|
// ==================================================================
|
|
// §13 — WINDEV → OMIKA (lecture)
|
|
// ==================================================================
|
|
|
|
// TEST 1 : <head><style> jamais rendu comme texte
|
|
[Fact]
|
|
public void WinDev_CssJamaisRenduCommeTexte()
|
|
{
|
|
string texte = ToutLeTexte(EmailHtmlFixture.WinDevHtmlComplet());
|
|
|
|
Assert.DoesNotContain("font-family", texte);
|
|
Assert.DoesNotContain("Calibri", texte);
|
|
Assert.DoesNotContain("margin-bottom", texte);
|
|
Assert.DoesNotContain(".Normal", texte);
|
|
Assert.DoesNotContain(".TableauNormal", texte);
|
|
Assert.DoesNotContain(".xtab", texte);
|
|
Assert.DoesNotContain("{", texte);
|
|
Assert.DoesNotContain("}", texte);
|
|
}
|
|
|
|
// TEST 2 : classes .Normal/.TableauNormal/.xtab → contenu métier visible, CSS non visible
|
|
[Fact]
|
|
public void WinDev_ContenuMetierVisible_CssSourceInvisible()
|
|
{
|
|
IReadOnlyList<HtmlBlock> blocs = Parser(EmailHtmlFixture.WinDevHtmlComplet());
|
|
List<string> textes = Textes(blocs);
|
|
string tout = string.Join("\n", textes);
|
|
|
|
Assert.Contains("Rapport d'intervention", tout);
|
|
Assert.Contains("Nom du client", tout);
|
|
Assert.Contains("Photos", tout);
|
|
Assert.Contains("Dupont Jean", tout);
|
|
Assert.Contains("Occupant", tout);
|
|
|
|
Assert.DoesNotContain("Grilledutableau", tout);
|
|
Assert.DoesNotContain("padding-left", tout);
|
|
Assert.DoesNotContain("display:block", tout);
|
|
}
|
|
|
|
// TEST 3 : table photo WinDev → image + date, date située après/sous l'image
|
|
[Fact]
|
|
public void WinDev_TablePhoto_ImagePuisDate()
|
|
{
|
|
List<HtmlInline> inlines = InlinesTous(Parser(EmailHtmlFixture.WinDevHtmlComplet())).ToList();
|
|
|
|
int idxImage = inlines.FindIndex(i => i is HtmlImage);
|
|
int idxDate = inlines.FindIndex(i => i is HtmlRun r && r.Text.Contains("30/01/2026"));
|
|
int idxNote = inlines.FindIndex(i => i is HtmlRun r && r.Text.Contains("Note photo"));
|
|
|
|
Assert.True(idxImage >= 0, "image absente");
|
|
Assert.True(idxDate >= 0, "date absente");
|
|
Assert.True(idxNote >= 0, "note absente");
|
|
Assert.True(idxImage < idxDate, "la date doit suivre l'image dans la structure");
|
|
Assert.True(idxDate < idxNote, "la note doit suivre la date dans la structure");
|
|
}
|
|
|
|
// TEST 4 : table contacts WinDev → toutes les cellules dans le bon ordre
|
|
[Fact]
|
|
public void WinDev_TableContacts_CellulesDansOrdre()
|
|
{
|
|
List<string> textes = Textes(Parser(EmailHtmlFixture.WinDevHtmlComplet()));
|
|
|
|
string[] attendus =
|
|
[
|
|
"Nom - Prénom", "Tel. 1", "Tel. 2", "Rôle", "Étage",
|
|
"Dupont Jean", "04 00 00 00 00", "06 00 00 00 00", "Occupant", "4"
|
|
];
|
|
|
|
foreach (string attendu in attendus)
|
|
{
|
|
Assert.Contains(attendu, textes);
|
|
}
|
|
|
|
// L'ordre des en-têtes et des cellules est préservé.
|
|
Assert.True(textes.IndexOf("Nom - Prénom") < textes.IndexOf("Tel. 1"));
|
|
Assert.True(textes.IndexOf("Tel. 1") < textes.IndexOf("Tel. 2"));
|
|
Assert.True(textes.IndexOf("Dupont Jean") < textes.IndexOf("Occupant"));
|
|
}
|
|
|
|
// TEST 5 : aucune donnée de <style>/<script> dans le texte final
|
|
[Fact]
|
|
public void WinDev_AucuneDonneeStyleScript_DansTexteFinal()
|
|
{
|
|
List<string> textes = Textes(Parser(EmailHtmlFixture.WinDevHtmlComplet()));
|
|
|
|
Assert.DoesNotContain(textes, t => t.Contains("td *"));
|
|
Assert.DoesNotContain(textes, t => t.Contains("text/css"));
|
|
Assert.DoesNotContain(textes, t => t.Contains("PCSOFT"));
|
|
Assert.DoesNotContain(textes, t => t.Contains("<style"));
|
|
Assert.DoesNotContain(textes, t => t.Contains("<script"));
|
|
}
|
|
|
|
// ==================================================================
|
|
// §14 — OMIKA → WINDEV (écriture)
|
|
// ==================================================================
|
|
|
|
// TEST 6 : photo → image sur sa ligne, date sur la ligne suivante (paragraphes séparés)
|
|
[Fact]
|
|
public void Omika_Photo_ImagePuisDate_SurParagraphesSepares()
|
|
{
|
|
string html = RendreCorps([TableauPhotos(JsonUnePhoto())], CreerDocxPhotoBloc(), out IReadOnlyList<EmailInlineImage> images);
|
|
|
|
Assert.Single(images);
|
|
|
|
// L'image, la date et la note sont chacune dans leur PROPRE <p> : jamais
|
|
// « <img/>30/01/2026 » dans le même bloc.
|
|
Assert.Contains("<p><img", html);
|
|
Assert.Contains("<p>30/01/2026</p>", html);
|
|
Assert.Contains("<p>Note photo</p>", html);
|
|
}
|
|
|
|
// TEST 7 : contacts → vraies lignes/cellules HTML
|
|
[Fact]
|
|
public void Omika_Contacts_VraiesCellules()
|
|
{
|
|
string html = RendreCorps(
|
|
[TableauPhotos(JsonUnePhoto())],
|
|
CreerDocxContacts(),
|
|
out _);
|
|
|
|
Assert.Contains("<table", html);
|
|
Assert.Contains("<tr>", html);
|
|
Assert.Contains("<td ", html);
|
|
Assert.Contains("Dupont Jean", html);
|
|
Assert.Contains("04 00 00 00 00", html);
|
|
Assert.Contains("Occupant", html);
|
|
}
|
|
|
|
// TEST 8 : le HTML de stockage ne dépend pas de display:flex / display:grid
|
|
[Fact]
|
|
public void Omika_HtmlStockage_SansFlexGrid()
|
|
{
|
|
string html = RendreCorps([TableauPhotos(JsonUnePhoto())], CreerDocxPhotoBloc(), out _);
|
|
|
|
Assert.DoesNotContain("display:flex", html);
|
|
Assert.DoesNotContain("display: flex", html);
|
|
Assert.DoesNotContain("display:grid", html);
|
|
Assert.DoesNotContain("display: grid", html);
|
|
Assert.DoesNotContain("color-mix", html);
|
|
}
|
|
|
|
// TEST 9 : toutes les données restent présentes (photo + contacts)
|
|
[Fact]
|
|
public void Omika_ToutesDonneesPresentes()
|
|
{
|
|
string html = RendreCorps(
|
|
[TableauPhotos(JsonUnePhoto())],
|
|
CreerDocxPhotoEtContacts(),
|
|
out _);
|
|
|
|
foreach (string attendu in new[]
|
|
{
|
|
"Photos", "30/01/2026", "Note photo",
|
|
"Dupont Jean", "04 00 00 00 00", "06 00 00 00 00", "Occupant", "4"
|
|
})
|
|
{
|
|
Assert.Contains(attendu, html);
|
|
}
|
|
|
|
Assert.DoesNotContain("$TABPHOTO", html);
|
|
Assert.DoesNotContain("$TABCTSITE", html);
|
|
}
|
|
|
|
// TEST 10 : les images restent exploitables par le pipeline SMTP (data URI → CID)
|
|
[Fact]
|
|
public void Omika_Images_ExploitablesParPipelineSmtp()
|
|
{
|
|
string html = RendreCorps([TableauPhotos(JsonUnePhoto())], CreerDocxPhotoBloc(), out IReadOnlyList<EmailInlineImage> images);
|
|
|
|
Assert.Single(images);
|
|
Assert.Contains("<img src=\"data:image/jpeg;base64,", html);
|
|
|
|
(string htmlSmtp, IReadOnlyList<EmailSmtpInline.Ressource> ressources) = EmailSmtpInline.Convertir(html);
|
|
Assert.Contains("cid:", htmlSmtp);
|
|
Assert.Single(ressources);
|
|
Assert.DoesNotContain("base64", htmlSmtp);
|
|
}
|
|
|
|
// ==================================================================
|
|
// §15 — ROUNDTRIP STRUCTUREL (Omika → parser → relecture)
|
|
// ==================================================================
|
|
|
|
[Fact]
|
|
public void Roundtrip_Omika_PasDePerteStructurelle()
|
|
{
|
|
string html = RendreCorps([TableauPhotos(JsonUnePhoto())], CreerDocxPhotoBloc(), out _);
|
|
|
|
// Relecture par le parser Omika (même chemin que l'aperçu).
|
|
IReadOnlyList<HtmlBlock> blocs = Parser(html);
|
|
List<string> textes = Textes(blocs);
|
|
List<HtmlInline> inlines = InlinesTous(blocs).ToList();
|
|
|
|
Assert.Contains("Photos", textes); // TEXTE conservé
|
|
Assert.Contains("30/01/2026", textes); // DATE conservée
|
|
Assert.Contains("Note photo", textes); // TEXTE conservé
|
|
Assert.Contains(blocs, b => b is HtmlTable); // TABLE conservée
|
|
Assert.Contains(inlines, i => i is HtmlImage); // IMAGE conservée
|
|
|
|
// Aucun CSS/token ne fuite.
|
|
string tout = string.Join("\n", textes);
|
|
Assert.DoesNotContain("$TABPHOTO", tout);
|
|
Assert.DoesNotContain("font-family", tout);
|
|
}
|
|
|
|
// ==================================================================
|
|
// §7 — FINITION TABLEAUX (bordures compatibles + espacement)
|
|
// ==================================================================
|
|
|
|
// TEST TABLE A : HTML Omika généré → bordures compatibles + structure cellule.
|
|
[Fact]
|
|
public void TableA_Omika_TableBordureCompatible()
|
|
{
|
|
string html = RendreCorps([TableauPhotos(JsonUnePhoto())], CreerDocxPhotoEtContacts(), out _);
|
|
|
|
// Attributs HTML historiques (indépendants du <style> global).
|
|
Assert.Contains("cellspacing=\"0\"", html);
|
|
Assert.Contains("cellpadding=\"0\"", html);
|
|
// Bordure inline fine sur chaque cellule (style simple, pas de CSS global).
|
|
Assert.Contains("border:1pt solid #000000", html);
|
|
// Structure réelle table/tr/td conservée.
|
|
Assert.Contains("<table", html);
|
|
Assert.Contains("<tr>", html);
|
|
Assert.Contains("<td ", html);
|
|
Assert.DoesNotContain("display:flex", html);
|
|
Assert.DoesNotContain("display:grid", html);
|
|
}
|
|
|
|
// TEST TABLE B : fixture WinDev → aucune hauteur fixe de table conservée.
|
|
[Fact]
|
|
public void TableB_WinDev_AucuneHauteurFixeExcessive()
|
|
{
|
|
IReadOnlyList<HtmlBlock> blocs = Parser(EmailHtmlFixture.WinDevHtmlComplet());
|
|
List<string> textes = Textes(blocs);
|
|
|
|
// Le modèle structuré n'expose AUCUNE hauteur (table/tr/td) : le
|
|
// « height:153.1pt » de WinDev n'est pas traduit en Height fixe Avalonia.
|
|
Assert.DoesNotContain(textes, t => t.Contains("153.1pt"));
|
|
Assert.DoesNotContain(textes, t => t.Contains("height"));
|
|
|
|
// Dimensionnement au contenu : photo / date / note toujours présents.
|
|
Assert.Contains("Photos", textes);
|
|
Assert.Contains("30/01/2026", textes);
|
|
Assert.Contains("Note photo", textes);
|
|
}
|
|
|
|
// TEST TABLE C : image WinDev → dimensions width/height préservées.
|
|
[Fact]
|
|
public void TableC_WinDev_ImageTaillePreservee()
|
|
{
|
|
List<HtmlInline> inlines = InlinesTous(Parser(EmailHtmlFixture.WinDevHtmlComplet())).ToList();
|
|
|
|
HtmlImage image = Assert.IsType<HtmlImage>(inlines.Single(i => i is HtmlImage));
|
|
Assert.Equal(305, image.Width);
|
|
Assert.Equal(206, image.Height);
|
|
}
|
|
|
|
// TEST TABLE D : paragraphes de cellule — ordre conservé, sans espacement excessif.
|
|
[Fact]
|
|
public void TableD_WinDev_ParagraphesCellule_SansEspacementExcessif()
|
|
{
|
|
List<HtmlInline> inlines = InlinesTous(Parser(EmailHtmlFixture.WinDevHtmlComplet())).ToList();
|
|
|
|
int idxImage = inlines.FindIndex(i => i is HtmlImage);
|
|
int idxDate = inlines.FindIndex(i => i is HtmlRun r && r.Text.Contains("30/01/2026"));
|
|
int idxNote = inlines.FindIndex(i => i is HtmlRun r && r.Text.Contains("Note photo"));
|
|
|
|
Assert.True(idxImage >= 0, "image absente");
|
|
Assert.True(idxDate >= 0, "date absente");
|
|
Assert.True(idxNote >= 0, "note absente");
|
|
Assert.True(idxImage < idxDate && idxDate < idxNote, "ordre image → date → note non respecté");
|
|
|
|
// Entre image et date, puis entre date et note : UN SEUL saut de ligne
|
|
// (aucun saut doublé → aucune ligne vide cumulée).
|
|
IReadOnlyList<HtmlInline> entreImageDate = inlines.Skip(idxImage + 1).Take(idxDate - idxImage - 1).ToList();
|
|
Assert.Single(entreImageDate);
|
|
Assert.IsType<HtmlLineBreak>(entreImageDate[0]);
|
|
|
|
IReadOnlyList<HtmlInline> entreDateNote = inlines.Skip(idxDate + 1).Take(idxNote - idxDate - 1).ToList();
|
|
Assert.Single(entreDateNote);
|
|
Assert.IsType<HtmlLineBreak>(entreDateNote[0]);
|
|
}
|
|
|
|
// TEST TABLE E : aucune régression (CSS invisible, contenu/photo/date présents).
|
|
[Fact]
|
|
public void TableE_AucuneRegression()
|
|
{
|
|
string texte = ToutLeTexte(EmailHtmlFixture.WinDevHtmlComplet());
|
|
IReadOnlyList<HtmlBlock> blocs = Parser(EmailHtmlFixture.WinDevHtmlComplet());
|
|
|
|
// WINDEV CSS TEXT = NOT VISIBLE
|
|
Assert.DoesNotContain("font-family", texte);
|
|
Assert.DoesNotContain(".Normal", texte);
|
|
Assert.DoesNotContain("Calibri", texte);
|
|
|
|
// TABLE CONTENT = PRESENT
|
|
Assert.Contains("Nom - Prénom", texte);
|
|
Assert.Contains("Dupont Jean", texte);
|
|
Assert.Contains(blocs, b => b is HtmlTable);
|
|
|
|
// PHOTO = PRESENT, DATE = PRESENT
|
|
Assert.Contains(InlinesTous(blocs), i => i is HtmlImage);
|
|
Assert.Contains("30/01/2026", texte);
|
|
}
|
|
}
|