Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

205 lines
6.9 KiB
JavaScript

/**
* Tests for services/formatting.js
*
* Tests the formatting utility functions used for displaying
* dates, durations, modes, and numbers throughout the UI.
*/
// Stub ResearchStates (used by formatStatus)
window.RESEARCH_STATUS = {
IN_PROGRESS: 'in_progress',
COMPLETED: 'completed',
FAILED: 'failed',
SUSPENDED: 'suspended',
CANCELLED: 'cancelled',
QUEUED: 'queued',
PENDING: 'pending',
ERROR: 'error',
};
window.RESEARCH_TERMINAL_STATES = new Set([
'completed', 'suspended', 'failed', 'error', 'cancelled',
]);
// Load constants first (provides ResearchStates), then formatting
import '@js/config/constants.js';
import '@js/services/formatting.js';
const fmt = window.formatting;
describe('formatting', () => {
describe('formatMode', () => {
it('maps quick to Quick Summary', () => {
expect(fmt.formatMode('quick')).toBe('Quick Summary');
});
it('maps detailed to Detailed Report', () => {
expect(fmt.formatMode('detailed')).toBe('Detailed Report');
});
it('capitalizes unknown modes', () => {
expect(fmt.formatMode('custom')).toBe('Custom');
});
});
describe('formatDate', () => {
it('returns Unknown for falsy input', () => {
expect(fmt.formatDate(null)).toBe('Unknown');
expect(fmt.formatDate('')).toBe('Unknown');
expect(fmt.formatDate(undefined)).toBe('Unknown');
});
it('formats a valid ISO date', () => {
const result = fmt.formatDate('2025-01-15T10:30:00Z');
// Should contain month, day, and year
expect(result).toMatch(/Jan/);
expect(result).toMatch(/15/);
expect(result).toMatch(/2025/);
});
it('appends duration in minutes and seconds', () => {
const result = fmt.formatDate('2025-01-15T10:30:00Z', 125);
expect(result).toContain('2m 5s');
});
it('appends duration in seconds only when < 60s', () => {
const result = fmt.formatDate('2025-01-15T10:30:00Z', 45);
expect(result).toContain('45s');
expect(result).not.toContain('m');
});
it('does not append duration when null', () => {
const result = fmt.formatDate('2025-01-15T10:30:00Z', null);
expect(result).not.toContain('s)');
});
});
describe('formatDuration', () => {
it('returns Unknown for falsy input', () => {
expect(fmt.formatDuration(null)).toBe('Unknown');
expect(fmt.formatDuration(undefined)).toBe('Unknown');
expect(fmt.formatDuration(0)).toBe('Unknown');
});
it('returns Unknown for NaN', () => {
expect(fmt.formatDuration(NaN)).toBe('Unknown');
});
it('formats seconds only when < 60', () => {
expect(fmt.formatDuration(45)).toBe('45s');
});
it('formats minutes and seconds', () => {
expect(fmt.formatDuration(125)).toBe('2m 5s');
});
it('handles exact minutes', () => {
expect(fmt.formatDuration(120)).toBe('2m 0s');
});
it('floors fractional seconds', () => {
expect(fmt.formatDuration(45.7)).toBe('45s');
});
});
describe('capitalizeFirstLetter', () => {
it('capitalizes lowercase string', () => {
expect(fmt.capitalizeFirstLetter('hello')).toBe('Hello');
});
it('returns empty string for falsy input', () => {
expect(fmt.capitalizeFirstLetter('')).toBe('');
expect(fmt.capitalizeFirstLetter(null)).toBe('');
expect(fmt.capitalizeFirstLetter(undefined)).toBe('');
});
it('preserves already capitalized strings', () => {
expect(fmt.capitalizeFirstLetter('Hello')).toBe('Hello');
});
it('handles single character', () => {
expect(fmt.capitalizeFirstLetter('a')).toBe('A');
});
});
describe('formatNumber', () => {
it('returns "0" for null/undefined', () => {
expect(fmt.formatNumber(null)).toBe('0');
expect(fmt.formatNumber(undefined)).toBe('0');
});
it('returns number as-is when < 1000', () => {
expect(fmt.formatNumber(999)).toBe('999');
});
it('adds comma separators for thousands', () => {
expect(fmt.formatNumber(1000)).toBe('1,000');
expect(fmt.formatNumber(1234567)).toBe('1,234,567');
});
it('handles zero', () => {
expect(fmt.formatNumber(0)).toBe('0');
});
});
describe('formatStatus', () => {
it('delegates to ResearchStates.formatStatus', () => {
expect(fmt.formatStatus('completed')).toBe('Completed');
expect(fmt.formatStatus('in_progress')).toBe('In Progress');
});
});
describe('formatCurrency', () => {
it('uses 6 decimal places for tiny amounts (< $0.01)', () => {
expect(fmt.formatCurrency(0.000123)).toBe('$0.000123');
expect(fmt.formatCurrency(0.005)).toBe('$0.005000');
});
it('uses 4 decimal places for amounts < $1 (boundary: 0.01)', () => {
expect(fmt.formatCurrency(0.01)).toBe('$0.0100');
expect(fmt.formatCurrency(0.5)).toBe('$0.5000');
expect(fmt.formatCurrency(0.9999)).toBe('$0.9999');
});
it('uses 2 decimal places for amounts >= $1', () => {
expect(fmt.formatCurrency(1)).toBe('$1.00');
expect(fmt.formatCurrency(12.345)).toBe('$12.35');
expect(fmt.formatCurrency(1000)).toBe('$1000.00');
});
it('zero uses the tiny-amount branch', () => {
expect(fmt.formatCurrency(0)).toBe('$0.000000');
});
});
describe('generateChartColors', () => {
it('returns empty arrays for count=0', () => {
const r = fmt.generateChartColors(0);
expect(r.background).toEqual([]);
expect(r.border).toEqual([]);
});
it('returns `count` colors', () => {
const r = fmt.generateChartColors(3);
expect(r.background).toHaveLength(3);
expect(r.border).toHaveLength(3);
});
it('cycles through the palette when count exceeds base length', () => {
const r = fmt.generateChartColors(15);
expect(r.background).toHaveLength(15);
// First and 11th entries should be identical (palette of 10)
expect(r.background[0]).toBe(r.background[10]);
});
it('border colors are opaque versions of background colors', () => {
const r = fmt.generateChartColors(2);
for (let i = 0; i < 2; i++) {
// background should use alpha 0.8, border alpha 1
expect(r.background[i]).toContain('0.8');
expect(r.border[i]).not.toContain('0.8');
expect(r.border[i]).toContain(', 1)');
}
});
});
});