verify-email-snapshots
Snapshot test email templates using Verify to catch regressions. Validates rendered HTML output matches approved baseline. Works with MJML templates and any email renderer.
Install / Use
npx skills add Aaronontheweb/dotnet-skills --skill verify-email-snapshotsInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
CommunicationSupported Platforms
Our assessment of verify-email-snapshots
verify-email-snapshots scores 92/100 on our quality scale, 175th of 447 Communication skills we index (top 40%).
Its SKILL.md is 8.2 KB long, well organised into 23 sections with 12 code examples: a thorough specification that gives an agent plenty to work with.
With 1,183 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 15 days ago, so verify-email-snapshots is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
verify-email-snapshots compared with similar skills
All 4 of these similar skills score higher than verify-email-snapshots; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| verify-email-snapshots (this skill)by Aaronontheweb | 92 | 1.2k | 15d ago | SKILL.md |
| algorithmic-artby anthropics | 100 | 177.9k | 10d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 10d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 130.2k | 12d ago | SKILL.md |
| ui-ux-pro-maxby nextlevelbuilder | 100 | 130.2k | 12d ago | SKILL.md |
Frequently asked questions
- How do I install verify-email-snapshots?
- Run
npx skills add Aaronontheweb/dotnet-skills --skill verify-email-snapshots. The install tabs above show the steps for each supported agent. - Which AI agents does verify-email-snapshots work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is verify-email-snapshots safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is verify-email-snapshots still maintained?
- The repository was last updated 15 days ago, so verify-email-snapshots is actively maintained.
Skill content
View source on GitHubname: verify-email-snapshots description: Snapshot test email templates using Verify to catch regressions. Validates rendered HTML output matches approved baseline. Works with MJML templates and any email renderer. invocable: false
Snapshot Testing Email Templates with Verify
When to Use This Skill
Use this skill when:
- Testing email template rendering for regressions
- Validating MJML templates compile to expected HTML
- Reviewing email changes in code review (diffs are visual)
- Ensuring variable substitution works correctly
Related skills:
aspnetcore/mjml-email-templates- MJML template authoringaspire/mailpit-integration- Test email delivery locallytesting/snapshot-testing- General Verify patterns
Why Snapshot Test Emails?
Email templates are:
- Visual - Small changes can break rendering across clients
- Hard to unit test - Output is complex HTML, not simple values
- Prone to regression - Template changes can have unintended effects
Snapshot testing captures the rendered HTML and fails when it changes unexpectedly.
Installation
dotnet add package Verify.Xunit # or Verify.NUnit, Verify.MSTest
Basic Email Snapshot Test
[Fact]
public async Task UserSignupInvitation_RendersCorrectly()
{
// Arrange
var renderer = _services.GetRequiredService<IMjmlTemplateRenderer>();
var variables = new Dictionary<string, string>
{
{ "PreviewText", "You've been invited to join Acme Corp" },
{ "OrganizationName", "Acme Corporation" },
{ "InviteeName", "John Doe" },
{ "InviterName", "Jane Admin" },
{ "InvitationLink", "https://example.com/invite/abc123" },
{ "ExpirationDate", "December 31, 2025" }
};
// Act
var html = await renderer.RenderTemplateAsync(
"UserInvitations/UserSignupInvitation",
variables);
// Assert
await Verify(html, extension: "html");
}
This creates UserSignupInvitation_RendersCorrectly.verified.html on first run.
Reviewing Email Changes
When a template changes, the test fails with a diff. Review options:
1. Visual Diff Tool
# Configure diff tool (one-time)
dotnet tool install -g verify.tool
verify accept # Accept all pending changes
verify review # Open diff tool
2. Browser Preview
Open the .received.html file in a browser to see the actual rendering.
3. IDE Integration
Most IDEs show inline diffs for .verified.html vs .received.html files.
Test Each Template Variant
Create tests for each email template to catch regressions:
public class EmailTemplateSnapshotTests : IClassFixture<EmailTestFixture>
{
private readonly IMjmlTemplateRenderer _renderer;
public EmailTemplateSnapshotTests(EmailTestFixture fixture)
{
_renderer = fixture.Services.GetRequiredService<IMjmlTemplateRenderer>();
}
[Fact]
public async Task WelcomeEmail_NewUser() =>
await VerifyTemplate("Welcome/NewUser", new Dictionary<string, string>
{
{ "UserName", "John Doe" },
{ "LoginUrl", "https://example.com/login" }
});
[Fact]
public async Task WelcomeEmail_InvitedUser() =>
await VerifyTemplate("Welcome/InvitedUser", new Dictionary<string, string>
{
{ "UserName", "John Doe" },
{ "InviterName", "Jane Admin" },
{ "OrganizationName", "Acme Corp" }
});
[Fact]
public async Task PasswordReset() =>
await VerifyTemplate("PasswordReset/PasswordReset", new Dictionary<string, string>
{
{ "UserName", "John Doe" },
{ "ResetLink", "https://example.com/reset/abc123" },
{ "ExpirationMinutes", "30" }
});
[Fact]
public async Task PaymentReceipt() =>
await VerifyTemplate("Billing/PaymentReceipt", new Dictionary<string, string>
{
{ "UserName", "John Doe" },
{ "Amount", "$10.00" },
{ "InvoiceNumber", "INV-2025-001" },
{ "Date", "January 15, 2025" }
});
private async Task VerifyTemplate(
string templateName,
Dictionary<string, string> variables)
{
var html = await _renderer.RenderTemplateAsync(templateName, variables);
await Verify(html, extension: "html")
.UseMethodName(templateName.Replace("/", "_"));
}
}
Scrubbing Dynamic Values
Some values change between test runs. Scrub them:
[Fact]
public async Task EmailWithTimestamp_ScrubsDynamicValues()
{
var html = await _renderer.RenderTemplateAsync("Welcome", variables);
await Verify(html, extension: "html")
.ScrubLinesContaining("Generated at:")
.ScrubInlineGuids(); // Scrubs GUIDs in URLs
}
Common Scrubbers
// Scrub dates
.ScrubLinesContaining("Date:")
.AddScrubber(s => Regex.Replace(s, @"\d{4}-\d{2}-\d{2}", "SCRUBBED-DATE"))
// Scrub URLs with tokens
.AddScrubber(s => Regex.Replace(s, @"token=[a-zA-Z0-9]+", "token=SCRUBBED"))
// Scrub GUIDs
.ScrubInlineGuids()
Test Fixture for Email Tests
public class EmailTestFixture : IAsyncLifetime
{
public IServiceProvider Services { get; private set; } = null!;
public async Task InitializeAsync()
{
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["SiteUrl"] = "https://example.com"
})
.Build());
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
Services = services.BuildServiceProvider();
await Task.CompletedTask;
}
public Task DisposeAsync() => Task.CompletedTask;
}
Composer Snapshot Tests
Test the full composer output including subject and metadata:
[Fact]
public async Task SignupInvitation_ComposesCorrectEmail()
{
var composer = _services.GetRequiredService<IUserEmailComposer>();
var email = await composer.ComposeSignupInvitationAsync(
recipientEmail: new EmailAddress("john@example.com"),
recipientName: new PersonName("John Doe"),
inviterName: new PersonName("Jane Admin"),
organizationName: new OrganizationName("Acme Corp"),
invitationUrl: new AbsoluteUri("https://example.com/invite/abc123"),
expiresAt: new DateTimeOffset(2025, 12, 31, 0, 0, 0, TimeSpan.Zero));
// Verify the full email object (subject, to, body)
await Verify(new
{
email.To,
email.Subject,
HtmlBody = email.HtmlBody // Will be stored as .html extension
});
}
CI Integration
Fail on Missing Baseline
In CI, fail if no .verified.html file exists (prevents accidental acceptance):
// In test setup or ModuleInitializer
VerifierSettings.ThrowOnMissingVerifiedFile();
Git Configuration
Add to .gitattributes to improve diff handling:
*.verified.html linguist-language=HTML
*.verified.html diff=html
Best Practices
DO
// DO: Test each template variant
[Fact] Task WelcomeEmail_NewUser_RendersCorrectly()
[Fact] Task WelcomeEmail_InvitedUser_RendersCorrectly()
// DO: Use descriptive test names
[Fact] Task PaymentReceipt_WithRefund_ShowsRefundAmount()
// DO: Scrub dynamic values consistently
.ScrubLinesContaining("Generated at:")
// DO: Review diffs carefully before accepting
verify review
DON'T
// DON'T: Skip email testing
// DON'T: Auto-accept changes without review
verify accept --all // Dangerous!
// DON'T: Test only happy path
// DON'T: Ignore snapshot test failures
Workflow
- Create template - Write MJML template
- Write test - Add snapshot test with sample variables
- Run test - First run creates
.verified.html - Review - Open in browser, verify rendering
- Commit - Include
.verified.htmlin source control - Iterate - Changes fail test, review diff, accept if correct
Resources
- Verify: https://github.com/VerifyTests/Verify
- Verify.Xunit: https://github.com/VerifyTests/Verify#xunit
- Diff Tools: https://github.com/VerifyTests/DiffEngine
Related Skills
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
130.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
ui-ux-pro-max
130.2kUI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
