mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-18 00:55:50 +00:00
Enhance VodConnectionCard to use human-readable duration format and update tests accordingly. Refactor formatDuration function to support 'human' precision and add related unit tests for various duration scenarios.
This commit is contained in:
parent
570adc2fe0
commit
14ab2a0317
4 changed files with 138 additions and 16 deletions
|
|
@ -372,7 +372,10 @@ const VodConnectionCard = ({ vodContent, stopVODClient }) => {
|
|||
{metadata.duration_secs && (
|
||||
<Tooltip label="Content Duration">
|
||||
<Badge size="sm" variant="light" color="blue">
|
||||
{formatDuration(metadata.duration_secs, { zeroValue: 'Unknown', precision: 'hm' })}
|
||||
{formatDuration(metadata.duration_secs, {
|
||||
zeroValue: 'Unknown',
|
||||
precision: 'human',
|
||||
})}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,10 @@ vi.mock('lucide-react', () => ({
|
|||
}));
|
||||
|
||||
// ── Imports after mocks ───────────────────────────────────────────────────────
|
||||
import { useDateTimeFormat } from '../../../utils/dateTimeUtils.js';
|
||||
import {
|
||||
formatDuration,
|
||||
useDateTimeFormat,
|
||||
} from '../../../utils/dateTimeUtils.js';
|
||||
import {
|
||||
calculateProgress,
|
||||
getEpisodeDisplayTitle,
|
||||
|
|
@ -397,6 +400,75 @@ describe('VodConnectionCard', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// ── Content duration badge ─────────────────────────────────────────────────
|
||||
|
||||
describe('content duration badge', () => {
|
||||
it('requests human-readable formatting for the content duration badge', () => {
|
||||
render(
|
||||
<VodConnectionCard
|
||||
vodContent={makeEpisodeContent()}
|
||||
stopVODClient={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(formatDuration).toHaveBeenCalledWith(2700, {
|
||||
zeroValue: 'Unknown',
|
||||
precision: 'human',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the human-readable duration returned by formatDuration', () => {
|
||||
vi.mocked(formatDuration).mockImplementation((seconds, options = {}) => {
|
||||
if (options?.precision === 'human') {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
||||
}
|
||||
return `${seconds}s`;
|
||||
});
|
||||
|
||||
render(
|
||||
<VodConnectionCard
|
||||
vodContent={makeEpisodeContent()}
|
||||
stopVODClient={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('45m')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows hours and minutes for long-form content', () => {
|
||||
vi.mocked(formatDuration).mockImplementation((seconds, options = {}) => {
|
||||
if (options?.precision === 'human') {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
||||
}
|
||||
return `${seconds}s`;
|
||||
});
|
||||
|
||||
render(
|
||||
<VodConnectionCard
|
||||
vodContent={makeMovieContent()}
|
||||
stopVODClient={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('2h 0m')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render the duration badge when duration_secs is missing', () => {
|
||||
render(
|
||||
<VodConnectionCard
|
||||
vodContent={makeUnknownContent()}
|
||||
stopVODClient={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(formatDuration).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Episode rendering ──────────────────────────────────────────────────────
|
||||
|
||||
describe('episode content', () => {
|
||||
|
|
|
|||
|
|
@ -73,11 +73,21 @@ describe('dateTimeUtils', () => {
|
|||
|
||||
it('should handle strict parsing', () => {
|
||||
// With strict=true, a date that doesn't match the format should be invalid
|
||||
const invalid = dateTimeUtils.initializeTime('15-01-2024', 'YYYY-MM-DD', null, true);
|
||||
const invalid = dateTimeUtils.initializeTime(
|
||||
'15-01-2024',
|
||||
'YYYY-MM-DD',
|
||||
null,
|
||||
true
|
||||
);
|
||||
expect(invalid.isValid()).toBe(false);
|
||||
|
||||
// With strict=true and a matching format, it should be valid
|
||||
const valid = dateTimeUtils.initializeTime('2024-01-15', 'YYYY-MM-DD', null, true);
|
||||
const valid = dateTimeUtils.initializeTime(
|
||||
'2024-01-15',
|
||||
'YYYY-MM-DD',
|
||||
null,
|
||||
true
|
||||
);
|
||||
expect(valid.isValid()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -873,6 +883,35 @@ describe('dateTimeUtils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('precision: human', () => {
|
||||
it('should format sub-hour content as minutes with suffix', () => {
|
||||
expect(dateTimeUtils.formatDuration(2700, { precision: 'human' })).toBe(
|
||||
'45m'
|
||||
);
|
||||
});
|
||||
|
||||
it('should floor partial minutes for short content', () => {
|
||||
expect(dateTimeUtils.formatDuration(90, { precision: 'human' })).toBe(
|
||||
'1m'
|
||||
);
|
||||
});
|
||||
|
||||
it('should format hour-plus content as hours and minutes', () => {
|
||||
expect(dateTimeUtils.formatDuration(5400, { precision: 'human' })).toBe(
|
||||
'1h 30m'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return custom zeroValue when seconds is 0', () => {
|
||||
expect(
|
||||
dateTimeUtils.formatDuration(0, {
|
||||
precision: 'human',
|
||||
zeroValue: 'Unknown',
|
||||
})
|
||||
).toBe('Unknown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('precision: m', () => {
|
||||
it('should return total minutes as integer', () => {
|
||||
expect(dateTimeUtils.formatDuration(90, { precision: 'm' })).toBe('1');
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ export const convertToMs = (dateTime) => dayjs(dateTime).valueOf();
|
|||
|
||||
export const convertToSec = (dateTime) => dayjs(dateTime).unix();
|
||||
|
||||
export const initializeTime = (dateTime, format = null, locale = null, strict = false) => {
|
||||
export const initializeTime = (
|
||||
dateTime,
|
||||
format = null,
|
||||
locale = null,
|
||||
strict = false
|
||||
) => {
|
||||
if (format && locale) {
|
||||
return dayjs(dateTime, format, locale, strict);
|
||||
} else if (format) {
|
||||
|
|
@ -26,7 +31,7 @@ export const initializeTime = (dateTime, format = null, locale = null, strict =
|
|||
} else {
|
||||
return dayjs(dateTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const startOfDay = (dateTime) => dayjs(dateTime).startOf('day');
|
||||
|
||||
|
|
@ -90,7 +95,8 @@ export const setMinute = (dateTime, value) => dayjs(dateTime).minute(value);
|
|||
|
||||
export const setSecond = (dateTime, value) => dayjs(dateTime).second(value);
|
||||
|
||||
export const setMillisecond = (dateTime, value) => dayjs(dateTime).millisecond(value);
|
||||
export const setMillisecond = (dateTime, value) =>
|
||||
dayjs(dateTime).millisecond(value);
|
||||
|
||||
export const getMonth = (dateTime) => dayjs(dateTime).month();
|
||||
|
||||
|
|
@ -376,12 +382,16 @@ export const MONTH_ABBR = [
|
|||
/**
|
||||
* @param {number} seconds
|
||||
* @param {object} [options]
|
||||
* @param {'hms'|'hm'|'m'} [options.precision='hms'] - Segments to include
|
||||
* @param {'hms'|'hm'|'m'|'human'} [options.precision='hms'] - Segments to include
|
||||
* @param {boolean} [options.alwaysShowHours=false] - Always include hours segment
|
||||
* @param {string|null} [options.zeroValue=null] - Return this when seconds is 0/falsy
|
||||
*/
|
||||
export const formatDuration = (seconds, options = {}) => {
|
||||
const { precision = 'hms', alwaysShowHours = false, zeroValue = null } = options;
|
||||
const {
|
||||
precision = 'hms',
|
||||
alwaysShowHours = false,
|
||||
zeroValue = null,
|
||||
} = options;
|
||||
|
||||
if (!seconds || seconds === 0) return zeroValue ?? '0:00';
|
||||
|
||||
|
|
@ -395,16 +405,14 @@ export const formatDuration = (seconds, options = {}) => {
|
|||
const hh = h.toString().padStart(2, '0');
|
||||
|
||||
switch (precision) {
|
||||
case 'human':
|
||||
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
||||
case 'm':
|
||||
return `${Math.floor(abs / 60)}`;
|
||||
case 'hm':
|
||||
return (alwaysShowHours || h > 0)
|
||||
? `${hh}:${mm}`
|
||||
: `${m}`;
|
||||
return alwaysShowHours || h > 0 ? `${hh}:${mm}` : `${m}`;
|
||||
case 'hms':
|
||||
default:
|
||||
return (alwaysShowHours || h > 0)
|
||||
? `${hh}:${mm}:${ss}`
|
||||
: `${m}:${ss}`;
|
||||
return alwaysShowHours || h > 0 ? `${hh}:${mm}:${ss}` : `${m}:${ss}`;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue