Skip to content

Commit

Permalink
test: Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zou-yu committed Nov 14, 2024
1 parent 7c997c3 commit 49fc412
Show file tree
Hide file tree
Showing 5 changed files with 562 additions and 84 deletions.
143 changes: 141 additions & 2 deletions test/src/email.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,144 @@
import { describe, it } from 'vitest'
import { describe, it, expect } from 'vitest'
import { Email, type EmailOptions, type User } from '../../src/email'

describe('Email', () => {
it('can create a new email', async () => {})
describe('constructor', () => {
it('should create an email with minimal options', () => {
const options: EmailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
text: 'Test content'
}
const email = new Email(options)
expect(email.from).toEqual({ email: '[email protected]' })
expect(email.to).toEqual([{ email: '[email protected]' }])
expect(email.subject).toBe('Test Subject')
expect(email.text).toBe('Test content')
})

it('should handle complex user objects', () => {
const options: EmailOptions = {
from: { name: 'Sender Name', email: '[email protected]' },
to: [
{ name: 'Recipient1', email: '[email protected]' },
{ name: 'Recipient2', email: '[email protected]' }
],
subject: 'Test Subject',
html: '<p>Test content</p>'
}
const email = new Email(options)
expect(email.from).toEqual({ name: 'Sender Name', email: '[email protected]' })
expect(email.to).toEqual([
{ name: 'Recipient1', email: '[email protected]' },
{ name: 'Recipient2', email: '[email protected]' }
])
})

it('should throw error if neither text nor html is provided', () => {
const options: EmailOptions = {
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject'
}
expect(() => new Email(options)).toThrow()
})
})

describe('getEmailData', () => {
it('should generate correct email data with text content', () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
text: 'Hello World'
})
const data = email.getEmailData()
expect(data).toContain('From: [email protected]')
expect(data).toContain('To: [email protected]')
expect(data).toContain('Subject: Test Subject')
expect(data).toContain('Content-Type: text/plain')
expect(data).toContain('Hello World')
})

it('should generate correct email data with HTML content', () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
html: '<p>Hello World</p>'
})
const data = email.getEmailData()
expect(data).toContain('Content-Type: text/html')
expect(data).toContain('<p>Hello World</p>')
})

it('should include CC and BCC headers when provided', () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
// @ts-expect-error it works
cc: ['[email protected]', { name: 'CC2', email: '[email protected]' }],
bcc: '[email protected]',
subject: 'Test Subject',
text: 'Hello World'
})
const data = email.getEmailData()
expect(data).toContain('CC: [email protected], CC2 <[email protected]>')
expect(data).toContain('BCC: [email protected]')
})

it('should include Reply-To when provided', () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
reply: { name: 'Reply Name', email: '[email protected]' },
subject: 'Test Subject',
text: 'Hello World'
})
const data = email.getEmailData()
expect(data).toContain('Reply-To: Reply Name <[email protected]>')
})

it('should include custom headers when provided', () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
text: 'Hello World',
headers: {
'X-Custom-Header': 'Custom Value'
}
})
const data = email.getEmailData()
expect(data).toContain('X-Custom-Header: Custom Value')
})
})

describe('sent promise', () => {
it('should resolve when setSent is called', async () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
text: 'Hello World'
})

setTimeout(() => email.setSent(), 0)
await expect(email.sent).resolves.toBeUndefined()
})

it('should reject when setSentError is called', async () => {
const email = new Email({
from: '[email protected]',
to: '[email protected]',
subject: 'Test Subject',
text: 'Hello World'
})

const error = new Error('Test error')
setTimeout(() => email.setSentError(error), 0)
await expect(email.sent).rejects.toBe(error)
})
})
})
119 changes: 119 additions & 0 deletions test/src/logger.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import Logger, { LogLevel } from '../../src/logger'

describe('Logger', () => {
let consoleSpy: {
debug: any;
info: any;
warn: any;
error: any;
}

beforeEach(() => {
// Mock console methods
consoleSpy = {
debug: vi.spyOn(console, 'debug').mockImplementation(() => {}),
info: vi.spyOn(console, 'info').mockImplementation(() => {}),
warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
error: vi.spyOn(console, 'error').mockImplementation(() => {})
}
})

afterEach(() => {
// Clear all mocks
vi.clearAllMocks()
})

describe('constructor', () => {
it('should create logger with default level INFO', () => {
const logger = new Logger(undefined, '[Test]')
expect(logger).toBeInstanceOf(Logger)
})

it('should create logger with specified level', () => {
const logger = new Logger(LogLevel.DEBUG, '[Test]')
expect(logger).toBeInstanceOf(Logger)
})
})

describe('logging methods', () => {
it('should log debug messages when level is DEBUG', () => {
const logger = new Logger(LogLevel.DEBUG, '[Test]')
const message = 'debug message'
logger.debug(message)
expect(consoleSpy.debug).toHaveBeenCalledWith('[Test]debug message')
})

it('should not log debug messages when level is INFO', () => {
const logger = new Logger(LogLevel.INFO, '[Test]')
logger.debug('debug message')
expect(consoleSpy.debug).not.toHaveBeenCalled()
})

it('should log info messages when level is INFO', () => {
const logger = new Logger(LogLevel.INFO, '[Test]')
const message = 'info message'
logger.info(message)
expect(consoleSpy.info).toHaveBeenCalledWith('[Test]info message')
})

it('should not log info messages when level is WARN', () => {
const logger = new Logger(LogLevel.WARN, '[Test]')
logger.info('info message')
expect(consoleSpy.info).not.toHaveBeenCalled()
})

it('should log warn messages when level is WARN', () => {
const logger = new Logger(LogLevel.WARN, '[Test]')
const message = 'warn message'
logger.warn(message)
expect(consoleSpy.warn).toHaveBeenCalledWith('[Test]warn message')
})

it('should not log warn messages when level is ERROR', () => {
const logger = new Logger(LogLevel.ERROR, '[Test]')
logger.warn('warn message')
expect(consoleSpy.warn).not.toHaveBeenCalled()
})

it('should log error messages when level is ERROR', () => {
const logger = new Logger(LogLevel.ERROR, '[Test]')
const message = 'error message'
logger.error(message)
expect(consoleSpy.error).toHaveBeenCalledWith('[Test]error message')
})

it('should not log any messages when level is NONE', () => {
const logger = new Logger(LogLevel.NONE, '[Test]')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
expect(consoleSpy.debug).not.toHaveBeenCalled()
expect(consoleSpy.info).not.toHaveBeenCalled()
expect(consoleSpy.warn).not.toHaveBeenCalled()
expect(consoleSpy.error).not.toHaveBeenCalled()
})
})

describe('log formatting', () => {
it('should format message with additional arguments', () => {
const logger = new Logger(LogLevel.INFO, '[Test]')
logger.info('message with %s', 'argument')
expect(consoleSpy.info).toHaveBeenCalledWith('[Test]message with %s', 'argument')
})

it('should handle multiple arguments', () => {
const logger = new Logger(LogLevel.INFO, '[Test]')
logger.info('message with %s and %d', 'string', 42)
expect(consoleSpy.info).toHaveBeenCalledWith('[Test]message with %s and %d', 'string', 42)
})

it('should handle objects in arguments', () => {
const logger = new Logger(LogLevel.INFO, '[Test]')
const obj = { key: 'value' }
logger.info('message with object:', obj)
expect(consoleSpy.info).toHaveBeenCalledWith('[Test]message with object:', obj)
})
})
})
Loading

0 comments on commit 49fc412

Please sign in to comment.