@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
361 lines (292 loc) • 11.7 kB
text/typescript
jest.mock("../../../logger", () => {
const mockLogError = jest.fn();
return {
createLogger: jest.fn(() => ({
log_error: mockLogError,
})),
__mockLogError: mockLogError, // Export for test access
};
});
import { calculateReadingTime } from "../utils";
// @ts-ignore - Access the mock
import { __mockLogError } from "../../../logger";
describe("calculateReadingTime", () => {
// Default parameters for reference
const DEFAULT_WPM = 140;
const DEFAULT_MIN_PAUSE = 500;
const DEFAULT_DELAY = 700;
beforeEach(() => {
(__mockLogError as jest.Mock).mockClear();
});
describe("Type Safety", () => {
it("should accept and process string input", () => {
const result = calculateReadingTime("Hello world");
expect(result).toBeGreaterThan(0);
expect(__mockLogError).not.toHaveBeenCalled();
});
it("should accept and process number input", () => {
const result = calculateReadingTime(12345);
expect(result).toBeGreaterThan(0);
expect(__mockLogError).not.toHaveBeenCalled();
});
it("should return 0 and log error for null", () => {
expect(calculateReadingTime(null as any)).toBe(0);
expect(__mockLogError).toHaveBeenCalledWith(
"Invalid text input for reading time calculation got: null"
);
});
it("should return 0 and log error for undefined", () => {
expect(calculateReadingTime(undefined as any)).toBe(0);
expect(__mockLogError).toHaveBeenCalledWith(
"Invalid text input for reading time calculation got: undefined"
);
});
it("should return 0 and log error for boolean", () => {
calculateReadingTime(true as any);
expect(__mockLogError).toHaveBeenCalledWith(
"Invalid text input for reading time calculation got: true"
);
(__mockLogError as jest.Mock).mockClear();
calculateReadingTime(false as any);
expect(__mockLogError).toHaveBeenCalledWith(
"Invalid text input for reading time calculation got: false"
);
});
it("should return 0 and log error for object", () => {
const obj = { text: "hello" };
calculateReadingTime(obj as any);
expect(__mockLogError).toHaveBeenCalledWith(
`Invalid text input for reading time calculation got: ${obj}`
);
});
it("should return 0 and log error for array", () => {
const arr = [1, 2, 3];
calculateReadingTime(arr as any);
expect(__mockLogError).toHaveBeenCalledWith(
`Invalid text input for reading time calculation got: ${arr}`
);
});
it("should return 0 and log error for function", () => {
const fn = () => "text";
calculateReadingTime(fn as any);
expect(__mockLogError).toHaveBeenCalled();
expect((__mockLogError as jest.Mock).mock.calls[0][0]).toContain(
"Invalid text input for reading time calculation got:"
);
});
it("should return 0 and log error for symbol", () => {
const sym = Symbol("test");
calculateReadingTime(sym as any);
expect(__mockLogError).toHaveBeenCalled();
expect((__mockLogError as jest.Mock).mock.calls[0][0]).toBe(
`Invalid text input for reading time calculation got: ${String(sym)}`
);
});
});
describe("Empty and Whitespace Handling", () => {
it("should return 0 for empty string", () => {
expect(calculateReadingTime("")).toBe(0);
});
it("should return 0 for whitespace-only string", () => {
expect(calculateReadingTime(" ")).toBe(0);
expect(calculateReadingTime("\n")).toBe(0);
expect(calculateReadingTime("\t")).toBe(0);
expect(calculateReadingTime(" \n\t ")).toBe(0);
});
it("should handle leading and trailing whitespace", () => {
const withWhitespace = calculateReadingTime(" hello ");
const withoutWhitespace = calculateReadingTime("hello");
expect(withWhitespace).toBe(withoutWhitespace);
});
});
describe("Number Input Handling", () => {
it("should convert number 0 to string and process", () => {
const result = calculateReadingTime(0);
// "0" is one word
expect(result).toBeGreaterThan(0);
});
it("should convert positive numbers to string", () => {
const result = calculateReadingTime(123);
// "123" is one word
expect(result).toBeGreaterThan(0);
});
it("should convert negative numbers to string", () => {
const result = calculateReadingTime(-456);
// "-456" is processed as words
expect(result).toBeGreaterThan(0);
});
it("should convert decimal numbers to string", () => {
const result = calculateReadingTime(3.14);
// "3.14" is processed as words
expect(result).toBeGreaterThan(0);
});
it("should handle NaN", () => {
const result = calculateReadingTime(NaN);
// NaN is typeof "number", so it converts to "NaN" string
expect(result).toBeGreaterThan(0);
});
it("should handle Infinity", () => {
const result = calculateReadingTime(Infinity);
// Infinity is typeof "number", converts to "Infinity" string
expect(result).toBeGreaterThan(0);
});
});
describe("Word Counting", () => {
it("should count single word", () => {
const result = calculateReadingTime("Hello");
const expectedTime = Math.max(
DEFAULT_MIN_PAUSE,
(1 / DEFAULT_WPM) * 60 * 1000
);
expect(result).toBe(expectedTime + DEFAULT_DELAY);
});
it("should count multiple words separated by spaces", () => {
const result = calculateReadingTime("Hello world test");
// 3 words
const expectedTime = Math.max(
DEFAULT_MIN_PAUSE,
(3 / DEFAULT_WPM) * 60 * 1000
);
expect(result).toBe(expectedTime + DEFAULT_DELAY);
});
it("should handle words with punctuation", () => {
const result = calculateReadingTime("Hello, world! How are you?");
// Should split on punctuation and count words
expect(result).toBeGreaterThan(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
it("should handle alphanumeric boundaries", () => {
const result = calculateReadingTime("test123abc");
// Should split on alphanumeric boundaries
expect(result).toBeGreaterThan(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
it("should handle long text", () => {
const longText = "word ".repeat(200); // 200 words
const result = calculateReadingTime(longText);
const expectedTime = (200 / DEFAULT_WPM) * 60 * 1000 + DEFAULT_DELAY;
expect(result).toBeCloseTo(expectedTime, -1); // Within 10ms
});
});
describe("Minimum Pause", () => {
it("should return minimum pause + delay for very short text", () => {
const result = calculateReadingTime("Hi");
// 1 word, calculation would be less than minimum pause
expect(result).toBe(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
it("should respect custom minimum pause", () => {
const customMinPause = 1000;
const result = calculateReadingTime("Hi", DEFAULT_WPM, customMinPause);
expect(result).toBeGreaterThanOrEqual(customMinPause);
});
it("should exceed minimum pause for longer text", () => {
const longText = "word ".repeat(50); // 50 words
const result = calculateReadingTime(longText);
const calculatedTime = (50 / DEFAULT_WPM) * 60 * 1000;
expect(result).toBe(calculatedTime + DEFAULT_DELAY);
expect(result).toBeGreaterThan(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
});
describe("Custom Parameters", () => {
it("should respect custom words per minute", () => {
const text = "word ".repeat(140); // 140 words
const fastReading = calculateReadingTime(text, 280); // 2x speed
const normalReading = calculateReadingTime(text, 140); // normal speed
// Faster reading should take less time
expect(fastReading).toBeLessThan(normalReading);
});
it("should respect custom announcement delay", () => {
const text = "Hello world";
const shortDelay = calculateReadingTime(
text,
DEFAULT_WPM,
DEFAULT_MIN_PAUSE,
100
);
const longDelay = calculateReadingTime(
text,
DEFAULT_WPM,
DEFAULT_MIN_PAUSE,
1000
);
expect(longDelay - shortDelay).toBe(900);
});
it("should work with all custom parameters", () => {
const result = calculateReadingTime("test", 200, 1000, 500);
expect(result).toBeGreaterThanOrEqual(1500); // minimum pause + delay
});
});
describe("Real-world Use Cases", () => {
it("should handle accessibility announcement text", () => {
const announcement = "New message from John Doe";
const result = calculateReadingTime(announcement);
expect(result).toBeGreaterThan(0);
expect(result).toBeLessThan(10000); // Less than 10 seconds
});
it("should handle button labels", () => {
const label = "Submit";
const result = calculateReadingTime(label);
expect(result).toBe(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
it("should handle form error messages", () => {
const error = "Please enter a valid email address";
const result = calculateReadingTime(error);
expect(result).toBeGreaterThan(DEFAULT_MIN_PAUSE);
});
it("should handle article titles", () => {
const title = "Breaking News: Major Update Released";
const result = calculateReadingTime(title);
expect(result).toBeGreaterThan(DEFAULT_MIN_PAUSE + DEFAULT_DELAY);
});
it("should handle notification text", () => {
const notification = "You have 3 new messages";
const result = calculateReadingTime(notification);
expect(result).toBeGreaterThan(0);
});
});
describe("Edge Cases", () => {
it("should handle text with special characters", () => {
const result = calculateReadingTime("@#$%^&*()");
expect(result).toBeGreaterThanOrEqual(0);
});
it("should handle text with emojis", () => {
const result = calculateReadingTime("Hello 👋 World 🌍");
expect(result).toBeGreaterThan(0);
});
it("should handle text with newlines", () => {
const result = calculateReadingTime("Line 1\nLine 2\nLine 3");
expect(result).toBeGreaterThan(0);
});
it("should handle mixed alphanumeric text", () => {
const result = calculateReadingTime(
"Version 1.2.3 released on 2024-01-01"
);
expect(result).toBeGreaterThan(0);
});
it("should handle very large numbers", () => {
const result = calculateReadingTime(Number.MAX_SAFE_INTEGER);
expect(result).toBeGreaterThan(0);
});
it("should return consistent results for same input", () => {
const text = "Consistent test";
const result1 = calculateReadingTime(text);
const result2 = calculateReadingTime(text);
const result3 = calculateReadingTime(text);
expect(result1).toBe(result2);
expect(result2).toBe(result3);
});
});
describe("Performance Characteristics", () => {
it("should handle empty input efficiently", () => {
const start = Date.now();
calculateReadingTime("");
const duration = Date.now() - start;
expect(duration).toBeLessThan(10); // Should be nearly instant
});
it("should handle large text efficiently", () => {
const largeText = "word ".repeat(10000);
const start = Date.now();
calculateReadingTime(largeText);
const duration = Date.now() - start;
expect(duration).toBeLessThan(100); // Should complete in less than 100ms
});
});
});