Discover request surface — taste-aware, rotating, snoozable, tag-targeted (milestone #268) #116
@@ -98,13 +98,14 @@ data class SuggestionSnoozeRef(
|
|||||||
* better than rendering an empty line.
|
* better than rendering an empty line.
|
||||||
*/
|
*/
|
||||||
fun returnsIn(nowMs: Long = System.currentTimeMillis()): String {
|
fun returnsIn(nowMs: Long = System.currentTimeMillis()): String {
|
||||||
val untilMs = runCatching { Instant.parse(snoozedUntil).toEpochMilliseconds() }
|
val remainingMs = runCatching { Instant.parse(snoozedUntil).toEpochMilliseconds() }
|
||||||
.getOrNull() ?: return "shortly"
|
.getOrNull()?.minus(nowMs)
|
||||||
// Already lapsed by our clock, yet the server still returned it — the
|
// Two ways to have nothing to state: an unparseable timestamp, or one
|
||||||
// two disagree. Say something plausible rather than "today", which
|
// already lapsed by our clock though the server still returned the row
|
||||||
// would read as a real prediction.
|
// (the two disagree). Neither is "today", which would read as a real
|
||||||
if (untilMs <= nowMs) return "shortly"
|
// prediction.
|
||||||
val days = ((untilMs - nowMs).toDouble() / MILLIS_PER_DAY).roundToInt()
|
if (remainingMs == null || remainingMs <= 0) return "shortly"
|
||||||
|
val days = (remainingMs.toDouble() / MILLIS_PER_DAY).roundToInt()
|
||||||
return when {
|
return when {
|
||||||
days < 1 -> "today"
|
days < 1 -> "today"
|
||||||
days == 1 -> "tomorrow"
|
days == 1 -> "tomorrow"
|
||||||
|
|||||||
@@ -21,8 +21,16 @@ vi.mock('$lib/api/lidarr', () => ({
|
|||||||
createLidarrSearchQuery: vi.fn()
|
createLidarrSearchQuery: vi.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// SuggestionFeed reaches for the snooze surface too (#2375). These are
|
||||||
|
// stubbed here even though this page-level suite asserts nothing about
|
||||||
|
// snoozing: a factory-shaped module mock must export everything the
|
||||||
|
// component tree imports, or rendering the feed throws before any
|
||||||
|
// assertion runs.
|
||||||
vi.mock('$lib/api/suggestions', () => ({
|
vi.mock('$lib/api/suggestions', () => ({
|
||||||
createSuggestionsQuery: vi.fn()
|
createSuggestionsQuery: vi.fn(),
|
||||||
|
createSnoozesQuery: vi.fn(),
|
||||||
|
snoozeSuggestion: vi.fn().mockResolvedValue(undefined),
|
||||||
|
unsnoozeSuggestion: vi.fn().mockResolvedValue(undefined)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('$lib/api/requests', () => ({
|
vi.mock('$lib/api/requests', () => ({
|
||||||
@@ -42,11 +50,12 @@ vi.mock('@tanstack/svelte-query', async (importOriginal) => {
|
|||||||
|
|
||||||
import DiscoverPage from './+page.svelte';
|
import DiscoverPage from './+page.svelte';
|
||||||
import { createLidarrSearchQuery } from '$lib/api/lidarr';
|
import { createLidarrSearchQuery } from '$lib/api/lidarr';
|
||||||
import { createSuggestionsQuery } from '$lib/api/suggestions';
|
import { createSuggestionsQuery, createSnoozesQuery } from '$lib/api/suggestions';
|
||||||
import { createRequest } from '$lib/api/requests';
|
import { createRequest } from '$lib/api/requests';
|
||||||
|
|
||||||
const mockedCreateQuery = createLidarrSearchQuery as ReturnType<typeof vi.fn>;
|
const mockedCreateQuery = createLidarrSearchQuery as ReturnType<typeof vi.fn>;
|
||||||
const mockedCreateSuggestionsQuery = createSuggestionsQuery as ReturnType<typeof vi.fn>;
|
const mockedCreateSuggestionsQuery = createSuggestionsQuery as ReturnType<typeof vi.fn>;
|
||||||
|
const mockedCreateSnoozesQuery = createSnoozesQuery as ReturnType<typeof vi.fn>;
|
||||||
const mockedCreateRequest = createRequest as ReturnType<typeof vi.fn>;
|
const mockedCreateRequest = createRequest as ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
function result(over: Partial<LidarrSearchResult> = {}): LidarrSearchResult {
|
function result(over: Partial<LidarrSearchResult> = {}): LidarrSearchResult {
|
||||||
@@ -69,6 +78,9 @@ beforeEach(() => {
|
|||||||
// Default: empty suggestion feed so its empty-state copy renders without
|
// Default: empty suggestion feed so its empty-state copy renders without
|
||||||
// interfering with search-mode tests.
|
// interfering with search-mode tests.
|
||||||
mockedCreateSuggestionsQuery.mockReturnValue(mockQuery({ data: [] }));
|
mockedCreateSuggestionsQuery.mockReturnValue(mockQuery({ data: [] }));
|
||||||
|
// Nothing parked, which keeps the feed's empty-state copy on the
|
||||||
|
// "no signal yet" branch that this suite's assertions expect.
|
||||||
|
mockedCreateSnoozesQuery.mockReturnValue(mockQuery({ data: [] }));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user