489 lines
15 KiB
TypeScript
489 lines
15 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
|
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
|
|
import { Email } from '../../../ts/mail/core/classes.email.js';
|
|
|
|
let testServer: ITestServer;
|
|
|
|
tap.test('setup test SMTP server', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2567,
|
|
tlsEnabled: false,
|
|
authRequired: false
|
|
});
|
|
expect(testServer).toBeTruthy();
|
|
expect(testServer.port).toEqual(2567);
|
|
});
|
|
|
|
tap.test('CEP-07: Basic HTML email', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Create HTML email
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'HTML Email Test',
|
|
html: `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; }
|
|
.header { color: #333; background: #f0f0f0; padding: 20px; }
|
|
.content { padding: 20px; }
|
|
.footer { color: #666; font-size: 12px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>Welcome!</h1>
|
|
</div>
|
|
<div class="content">
|
|
<p>This is an <strong>HTML email</strong> with <em>formatting</em>.</p>
|
|
<ul>
|
|
<li>Feature 1</li>
|
|
<li>Feature 2</li>
|
|
<li>Feature 3</li>
|
|
</ul>
|
|
</div>
|
|
<div class="footer">
|
|
<p>© 2024 Example Corp</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`,
|
|
text: 'Welcome! This is an HTML email with formatting. Features: 1, 2, 3. © 2024 Example Corp'
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('Basic HTML email sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-07: HTML email with inline images', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 10000
|
|
});
|
|
|
|
// Create a simple 1x1 red pixel PNG
|
|
const redPixelBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
|
|
|
|
// Create HTML email with inline image
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Email with Inline Images',
|
|
html: `
|
|
<html>
|
|
<body>
|
|
<h1>Email with Inline Images</h1>
|
|
<p>Here's an inline image:</p>
|
|
<img src="cid:image001" alt="Red pixel" width="100" height="100">
|
|
<p>And here's another one:</p>
|
|
<img src="cid:logo" alt="Company logo">
|
|
</body>
|
|
</html>
|
|
`,
|
|
attachments: [
|
|
{
|
|
filename: 'red-pixel.png',
|
|
content: Buffer.from(redPixelBase64, 'base64'),
|
|
contentType: 'image/png',
|
|
cid: 'image001' // Content-ID for inline reference
|
|
},
|
|
{
|
|
filename: 'logo.png',
|
|
content: Buffer.from(redPixelBase64, 'base64'), // Reuse for demo
|
|
contentType: 'image/png',
|
|
cid: 'logo'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('HTML email with inline images sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-07: Complex HTML with multiple inline resources', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 10000
|
|
});
|
|
|
|
// Create email with multiple inline resources
|
|
const email = new Email({
|
|
from: 'newsletter@example.com',
|
|
to: 'subscriber@example.com',
|
|
subject: 'Newsletter with Rich Content',
|
|
html: `
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 0; padding: 0; }
|
|
.header { background: url('cid:header-bg') center/cover; height: 200px; }
|
|
.logo { width: 150px; }
|
|
.product { display: inline-block; margin: 10px; }
|
|
.product img { width: 100px; height: 100px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<img src="cid:logo" alt="Company Logo" class="logo">
|
|
</div>
|
|
<h1>Monthly Newsletter</h1>
|
|
<div class="products">
|
|
<div class="product">
|
|
<img src="cid:product1" alt="Product 1">
|
|
<p>Product 1</p>
|
|
</div>
|
|
<div class="product">
|
|
<img src="cid:product2" alt="Product 2">
|
|
<p>Product 2</p>
|
|
</div>
|
|
<div class="product">
|
|
<img src="cid:product3" alt="Product 3">
|
|
<p>Product 3</p>
|
|
</div>
|
|
</div>
|
|
<img src="cid:footer-divider" alt="" style="width: 100%; height: 2px;">
|
|
<p>© 2024 Example Corp</p>
|
|
</body>
|
|
</html>
|
|
`,
|
|
text: 'Monthly Newsletter - View in HTML for best experience',
|
|
attachments: [
|
|
{
|
|
filename: 'header-bg.jpg',
|
|
content: Buffer.from('fake-image-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'header-bg'
|
|
},
|
|
{
|
|
filename: 'logo.png',
|
|
content: Buffer.from('fake-logo-data'),
|
|
contentType: 'image/png',
|
|
cid: 'logo'
|
|
},
|
|
{
|
|
filename: 'product1.jpg',
|
|
content: Buffer.from('fake-product1-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'product1'
|
|
},
|
|
{
|
|
filename: 'product2.jpg',
|
|
content: Buffer.from('fake-product2-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'product2'
|
|
},
|
|
{
|
|
filename: 'product3.jpg',
|
|
content: Buffer.from('fake-product3-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'product3'
|
|
},
|
|
{
|
|
filename: 'divider.gif',
|
|
content: Buffer.from('fake-divider-data'),
|
|
contentType: 'image/gif',
|
|
cid: 'footer-divider'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('Complex HTML with multiple inline resources sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-07: HTML with external and inline images mixed', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Mix of inline and external images
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Mixed Image Sources',
|
|
html: `
|
|
<html>
|
|
<body>
|
|
<h1>Mixed Image Sources</h1>
|
|
<h2>Inline Image:</h2>
|
|
<img src="cid:inline-logo" alt="Inline Logo" width="100">
|
|
<h2>External Images:</h2>
|
|
<img src="https://via.placeholder.com/150" alt="External Image 1">
|
|
<img src="http://example.com/image.jpg" alt="External Image 2">
|
|
<h2>Data URI Image:</h2>
|
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" alt="Data URI">
|
|
</body>
|
|
</html>
|
|
`,
|
|
attachments: [
|
|
{
|
|
filename: 'logo.png',
|
|
content: Buffer.from('logo-data'),
|
|
contentType: 'image/png',
|
|
cid: 'inline-logo'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('Successfully sent email with mixed image sources');
|
|
});
|
|
|
|
tap.test('CEP-07: HTML email responsive design', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Responsive HTML email
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Responsive HTML Email',
|
|
html: `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
@media screen and (max-width: 600px) {
|
|
.container { width: 100% !important; }
|
|
.column { width: 100% !important; display: block !important; }
|
|
.mobile-hide { display: none !important; }
|
|
}
|
|
.container { width: 600px; margin: 0 auto; }
|
|
.column { width: 48%; display: inline-block; vertical-align: top; }
|
|
img { max-width: 100%; height: auto; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Responsive Design Test</h1>
|
|
<div class="column">
|
|
<img src="cid:left-image" alt="Left Column">
|
|
<p>Left column content</p>
|
|
</div>
|
|
<div class="column">
|
|
<img src="cid:right-image" alt="Right Column">
|
|
<p>Right column content</p>
|
|
</div>
|
|
<p class="mobile-hide">This text is hidden on mobile devices</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`,
|
|
text: 'Responsive Design Test - View in HTML',
|
|
attachments: [
|
|
{
|
|
filename: 'left.jpg',
|
|
content: Buffer.from('left-image-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'left-image'
|
|
},
|
|
{
|
|
filename: 'right.jpg',
|
|
content: Buffer.from('right-image-data'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'right-image'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('Successfully sent responsive HTML email');
|
|
});
|
|
|
|
tap.test('CEP-07: HTML sanitization and security', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Email with potentially dangerous HTML
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'HTML Security Test',
|
|
html: `
|
|
<html>
|
|
<body>
|
|
<h1>Security Test</h1>
|
|
<!-- Scripts should be handled safely -->
|
|
<script>alert('This should not execute');</script>
|
|
<img src="x" onerror="alert('XSS')">
|
|
<a href="javascript:alert('Click')">Dangerous Link</a>
|
|
<iframe src="https://evil.com"></iframe>
|
|
<form action="https://evil.com/steal">
|
|
<input type="text" name="data">
|
|
</form>
|
|
<!-- Safe content -->
|
|
<p>This is safe text content.</p>
|
|
<img src="cid:safe-image" alt="Safe Image">
|
|
</body>
|
|
</html>
|
|
`,
|
|
text: 'Security Test - Plain text version',
|
|
attachments: [
|
|
{
|
|
filename: 'safe.png',
|
|
content: Buffer.from('safe-image-data'),
|
|
contentType: 'image/png',
|
|
cid: 'safe-image'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('HTML security test sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-07: Large HTML email with many inline images', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 30000
|
|
});
|
|
|
|
// Create email with many inline images
|
|
const imageCount = 10; // Reduced for testing
|
|
const attachments: any[] = [];
|
|
let htmlContent = '<html><body><h1>Performance Test</h1>';
|
|
|
|
for (let i = 0; i < imageCount; i++) {
|
|
const cid = `image${i}`;
|
|
htmlContent += `<img src="cid:${cid}" alt="Image ${i}" width="50" height="50">`;
|
|
|
|
attachments.push({
|
|
filename: `image${i}.png`,
|
|
content: Buffer.from(`fake-image-data-${i}`),
|
|
contentType: 'image/png',
|
|
cid: cid
|
|
});
|
|
}
|
|
|
|
htmlContent += '</body></html>';
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: `Email with ${imageCount} inline images`,
|
|
html: htmlContent,
|
|
attachments: attachments
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log(`Performance test with ${imageCount} inline images sent successfully`);
|
|
});
|
|
|
|
tap.test('CEP-07: Alternative content for non-HTML clients', async () => {
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Email with rich HTML and good plain text alternative
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Newsletter - March 2024',
|
|
html: `
|
|
<html>
|
|
<body style="font-family: Arial, sans-serif;">
|
|
<div style="background: #f0f0f0; padding: 20px;">
|
|
<img src="cid:header" alt="Company Newsletter" style="width: 100%; max-width: 600px;">
|
|
</div>
|
|
<div style="padding: 20px;">
|
|
<h1 style="color: #333;">March Newsletter</h1>
|
|
<h2 style="color: #666;">Featured Articles</h2>
|
|
<ul>
|
|
<li><a href="https://example.com/article1">10 Tips for Spring Cleaning</a></li>
|
|
<li><a href="https://example.com/article2">New Product Launch</a></li>
|
|
<li><a href="https://example.com/article3">Customer Success Story</a></li>
|
|
</ul>
|
|
<div style="background: #e0e0e0; padding: 15px; margin: 20px 0;">
|
|
<h3>Special Offer!</h3>
|
|
<p>Get 20% off with code: <strong>SPRING20</strong></p>
|
|
<img src="cid:offer" alt="Special Offer" style="width: 100%; max-width: 400px;">
|
|
</div>
|
|
</div>
|
|
<div style="background: #333; color: #fff; padding: 20px; text-align: center;">
|
|
<p>© 2024 Example Corp | <a href="https://example.com/unsubscribe" style="color: #fff;">Unsubscribe</a></p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`,
|
|
text: `COMPANY NEWSLETTER
|
|
March 2024
|
|
|
|
FEATURED ARTICLES
|
|
* 10 Tips for Spring Cleaning
|
|
https://example.com/article1
|
|
* New Product Launch
|
|
https://example.com/article2
|
|
* Customer Success Story
|
|
https://example.com/article3
|
|
|
|
SPECIAL OFFER!
|
|
Get 20% off with code: SPRING20
|
|
|
|
---
|
|
© 2024 Example Corp
|
|
Unsubscribe: https://example.com/unsubscribe`,
|
|
attachments: [
|
|
{
|
|
filename: 'header.jpg',
|
|
content: Buffer.from('header-image'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'header'
|
|
},
|
|
{
|
|
filename: 'offer.jpg',
|
|
content: Buffer.from('offer-image'),
|
|
contentType: 'image/jpeg',
|
|
cid: 'offer'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
expect(result.success).toBeTruthy();
|
|
console.log('Newsletter with alternative content sent successfully');
|
|
});
|
|
|
|
tap.test('cleanup test SMTP server', async () => {
|
|
if (testServer) {
|
|
await stopTestServer(testServer);
|
|
}
|
|
});
|
|
|
|
export default tap.start(); |