import { test, mock } from 'node:test'; import assert from 'node:assert/strict'; import { createFeedCoordinator } from '../../js/app/feedCoordinator.js'; import { FEED_TYPES } from '../../js/constants.js'; test('createFeedCoordinator - loadForYouVideos', async (t) => { const mockDeps = { devLogger: { log: mock.fn(), warn: mock.fn() }, userLogger: { warn: mock.fn() }, nostrClient: {}, getSidebarLoadingMarkup: mock.fn(() => '
Loading...
'), watchHistoryService: {}, subscriptions: {}, createActiveNostrSource: mock.fn(), createBlacklistFilterStage: mock.fn(), createDisinterestFilterStage: mock.fn(), createDedupeByRootStage: mock.fn(), createExploreDiversitySorter: mock.fn(), createExploreScorerStage: mock.fn(), createKidsAudienceFilterStage: mock.fn(), createKidsScorerStage: mock.fn(), createKidsScoreSorter: mock.fn(), createModerationStage: mock.fn(), createResolvePostedAtStage: mock.fn(), createTagPreferenceFilterStage: mock.fn(), createWatchHistorySuppressionStage: mock.fn(), createChronologicalSorter: mock.fn(), createSubscriptionAuthorsSource: mock.fn(), registerWatchHistoryFeed: mock.fn(), }; const coordinator = createFeedCoordinator(mockDeps); // Mock application context const app = { ...coordinator, nostrService: { getFilteredActiveVideos: mock.fn(() => []), // Simulate no cached videos loadVideos: mock.fn(async ({ onVideos }) => { // Simulate fetch completing with videos onVideos([], { reason: 'test-reason' }); return []; }), getVideoSubscription: mock.fn(() => 'mock-subscription'), getVideosMap: mock.fn(() => new Map()), }, videoListView: { showLoading: mock.fn(), state: {}, }, mountVideoListView: mock.fn(() => ({ innerHTML: '' })), checkRelayHealthWarning: mock.fn(), setFeedTelemetryContext: mock.fn(), isAuthorBlocked: mock.fn(() => false), refreshFeed: mock.fn(async () => {}), blacklistedEventIds: new Set(), }; // Bind methods to app context const loadForYouVideos = app.loadForYouVideos.bind(app); await t.test('loadForYouVideos executes successfully', async () => { await loadForYouVideos(false); // Verify devLogger was called assert.strictEqual(mockDeps.devLogger.log.mock.callCount() > 0, true, 'devLogger.log should be called'); // Verify telemetry context set assert.strictEqual(app.setFeedTelemetryContext.mock.calls[0].arguments[0], FEED_TYPES.FOR_YOU, 'Should set telemetry context to for-you'); // Verify mountVideoListView called with includeTags: false assert.deepStrictEqual(app.mountVideoListView.mock.calls[0].arguments[0], { includeTags: false }, 'Should mount list view with includeTags: false'); // Verify checkRelayHealthWarning called assert.strictEqual(app.checkRelayHealthWarning.mock.callCount(), 1, 'Should check relay health warning'); // Verify nostrService.loadVideos called assert.strictEqual(app.nostrService.loadVideos.mock.callCount(), 1, 'Should call nostrService.loadVideos'); // Verify refreshFeed called via callback assert.strictEqual(app.refreshFeed.mock.callCount(), 1, 'Should call refreshFeed via callback'); assert.strictEqual(app.refreshFeed.mock.calls[0].arguments[0], FEED_TYPES.FOR_YOU, 'Should call refreshFeed with FOR_YOU'); // Verify subscription updated assert.strictEqual(app.videoSubscription, 'mock-subscription', 'Should update videoSubscription'); }); }); test("loadForYouVideos renders without waiting for recommendation worker activation", async () => { let resolveActivation; const activation = new Promise((resolve) => { resolveActivation = resolve; }); let loadVideosStarted = false; const mockDeps = { devLogger: { log: mock.fn(), warn: mock.fn() }, userLogger: { warn: mock.fn() }, nostrClient: {}, getSidebarLoadingMarkup: mock.fn(() => '
Loading...
'), watchHistoryService: {}, subscriptions: {}, createActiveNostrSource: mock.fn(), createBlacklistFilterStage: mock.fn(), createDisinterestFilterStage: mock.fn(), createDedupeByRootStage: mock.fn(), createExploreDiversitySorter: mock.fn(), createExploreScorerStage: mock.fn(), createKidsAudienceFilterStage: mock.fn(), createKidsScorerStage: mock.fn(), createKidsScoreSorter: mock.fn(), createModerationStage: mock.fn(), createResolvePostedAtStage: mock.fn(), createTagPreferenceFilterStage: mock.fn(), createWatchHistorySuppressionStage: mock.fn(), createChronologicalSorter: mock.fn(), createSubscriptionAuthorsSource: mock.fn(), registerWatchHistoryFeed: mock.fn(), }; const coordinator = createFeedCoordinator(mockDeps); const app = { ...coordinator, nostrService: { getFilteredActiveVideos: mock.fn(() => [{ id: "cached" }]), loadVideos: mock.fn(async ({ onVideos }) => { loadVideosStarted = true; onVideos([{ id: "cached", title: "Cached", created_at: 1 }], { reason: "cache" }); return [{ id: "cached", title: "Cached", created_at: 1 }]; }), getVideoSubscription: mock.fn(() => null), getVideosMap: mock.fn(() => new Map()), }, videoListView: { state: {}, render: mock.fn() }, mountVideoListView: mock.fn(() => ({ innerHTML: "" })), checkRelayHealthWarning: mock.fn(), setFeedTelemetryContext: mock.fn(), isAuthorBlocked: mock.fn(() => false), refreshFeed: mock.fn(async () => {}), ensureExploreDataService: mock.fn(() => activation), blacklistedEventIds: new Set(), }; await app.loadForYouVideos(false); assert.equal(loadVideosStarted, true); resolveActivation(); });