Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/greeting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Greeting Utility Module
*
* Provides a simple greeting message generator with time-based context.
*/

/**
* Get a greeting message based on the current time of day.
* @param {string} name - The name of the person to greet.
* @param {Date} [date] - Optional date object for testing.
* @returns {string} A formatted greeting message.
*/
function getGreeting(name, date = new Date()) {
if (!name || typeof name !== 'string') {
throw new Error('A valid name string is required');
}

const hour = date.getHours();
let period;

if (hour >= 5 && hour < 12) {
period = 'morning';
} else if (hour >= 12 && hour < 17) {
period = 'afternoon';
} else if (hour >= 17 && hour < 21) {
period = 'evening';
} else {
period = 'night';
}

const greetings = {
morning: 'Good morning',
afternoon: 'Good afternoon',
evening: 'Good evening',
night: 'Good night',
};

return `${greetings[period]}, ${name}!`;
}

/**
* Generate a formal greeting for professional contexts.
* @param {string} name - The name of the person.
* @param {object} [options] - Optional settings.
* @param {string} [options.title] - Professional title (e.g. "Dr.", "Prof.").
* @param {string} [options.timezone] - Timezone identifier for localized greeting.
* @returns {string} A formal greeting message.
*/
function getFormalGreeting(name, options = {}) {
if (!name || typeof name !== 'string') {
throw new Error('A valid name string is required');
}

const prefix = options.title ? `${options.title} ` : '';
const greeting = getGreeting(name, options.timezone
? new Date(new Date().toLocaleString('en-US', { timeZone: options.timezone }))
: undefined
);

return greeting.replace(name, `${prefix}${name}`);
}

module.exports = { getGreeting, getFormalGreeting };
52 changes: 52 additions & 0 deletions src/greeting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { getGreeting, getFormalGreeting } = require('./greeting');

describe('getGreeting', () => {
test('should return a morning greeting', () => {
const date = new Date('2026-01-01T08:00:00');
expect(getGreeting('Alice', date)).toBe('Good morning, Alice!');
});

test('should return an afternoon greeting', () => {
const date = new Date('2026-01-01T14:00:00');
expect(getGreeting('Bob', date)).toBe('Good afternoon, Bob!');
});

test('should return an evening greeting', () => {
const date = new Date('2026-01-01T18:00:00');
expect(getGreeting('Charlie', date)).toBe('Good evening, Charlie!');
});

test('should return a night greeting', () => {
const date = new Date('2026-01-01T23:00:00');
expect(getGreeting('Diana', date)).toBe('Good night, Diana!');
});

test('should throw error for empty name', () => {
expect(() => getGreeting('')).toThrow('A valid name string is required');
});

test('should throw error for non-string name', () => {
expect(() => getGreeting(123)).toThrow('A valid name string is required');
});

test('should use current date when not provided', () => {
const result = getGreeting('Test');
expect(result).toMatch(/Good (morning|afternoon|evening|night), Test!/);
});
});

describe('getFormalGreeting', () => {
test('should include title in greeting', () => {
const date = new Date('2026-01-01T10:00:00');
expect(getFormalGreeting('Smith', { title: 'Dr.', date })).toContain('Dr. Smith');
});

test('should work without title', () => {
const date = new Date('2026-01-01T10:00:00');
expect(getFormalGreeting('Smith', { date })).toBe('Good morning, Smith!');
});

test('should throw error for empty name', () => {
expect(() => getFormalGreeting('')).toThrow('A valid name string is required');
});
});
47 changes: 47 additions & 0 deletions src/math.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Math Utility Module
*
* Provides common math helper functions.
*/

/**
* Calculate the factorial of a non-negative integer.
* @param {number} n - Non-negative integer
* @returns {number} Factorial of n
* @throws {Error} If n is negative or not an integer
*/
function factorial(n) {
if (!Number.isInteger(n) || n < 0) {
throw new Error('Input must be a non-negative integer');
}
if (n === 0 || n === 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}

/**
* Calculate the Fibonacci number at position n (0-indexed).
* @param {number} n - Position in the Fibonacci sequence
* @returns {number} The nth Fibonacci number
* @throws {Error} If n is negative or not an integer
*/
function fibonacci(n) {
if (!Number.isInteger(n) || n < 0) {
throw new Error('Input must be a non-negative integer');
}
if (n === 0) return 0;
if (n === 1) return 1;
let prev = 0;
let curr = 1;
for (let i = 2; i <= n; i++) {
const next = prev + curr;
prev = curr;
curr = next;
}
return curr;
}

module.exports = { factorial, fibonacci };