From 5e5dd1fb886ccc2b3446ad0c36237b0ad72d4355 Mon Sep 17 00:00:00 2001 From: alichherawalla Date: Thu, 16 Jul 2026 13:26:02 +0530 Subject: [PATCH] chore(tests): fix SonarCloud test-assertion smells in integration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S5906: replace generic assertions with dedicated matchers per Sonar's exact suggestions (toHaveLength / toBeNull / toBeGreaterThan / toBeLessThanOrEqual) across 36 integration test files. Every asserted value is unchanged — tests assert exactly the same things. S5976: parameterize the 3 near-identical -tag stripping tests in imageGenerationFlow.test.ts into a single it.each table (coverage identical). Pure test-quality nits; no production code touched. --- ...ceModeImageJourney.rendered.happy.test.tsx | 2 +- ...endEnhancedImage.rendered.redflow.test.tsx | 6 +- ...esendImageRoutes.rendered.redflow.test.tsx | 4 +- ...voiceNoteChatModeEmptyTurn.redflow.test.ts | 4 +- ...wnloadedCountBadge.rendered.happy.test.tsx | 2 +- ...geStagingPurgedRedownloads.redflow.test.ts | 2 +- ...urgedRedownloads.rendered.redflow.test.tsx | 2 +- ...osInterruptedNoFailedEntry.redflow.test.ts | 2 +- ...extRetryReissues.rendered.redflow.test.tsx | 2 +- ...queuedDownloadsSurviveKill.redflow.test.ts | 2 +- ...osLostDownloadId.rendered.redflow.test.tsx | 2 +- ...cementNoThinking.rendered.redflow.test.tsx | 2 +- ...treamingProgress.rendered.redflow.test.tsx | 2 +- .../generation/generationFlow.test.ts | 4 +- .../generation/imageGenerationFlow.test.ts | 90 ++++++++----------- ...agePreservesMode.rendered.redflow.test.tsx | 10 +-- .../generation/queuedSendFeedback.test.ts | 2 +- ...aceKeepsThinking.rendered.redflow.test.tsx | 2 +- ...emoteParallelTools.rendered.happy.test.tsx | 2 +- ...esendImageRoutes.rendered.redflow.test.tsx | 4 +- ...ImageRoutesLlama.rendered.redflow.test.tsx | 4 +- ...nFallsBackToText.rendered.redflow.test.tsx | 4 +- .../happy/imageBackends.happy.test.tsx | 2 +- .../happy/imageIntentRouting.happy.test.tsx | 4 +- .../happy/imageLightbox.happy.test.tsx | 2 +- .../happy/imageModeToggle.happy.test.tsx | 4 +- .../happy/imageOomCard.happy.test.tsx | 2 +- .../happy/smartBudgeting.happy.test.tsx | 2 +- .../happy/speakMessage.happy.test.tsx | 2 +- .../integration/happy/tools.happy.test.tsx | 2 +- ...TunablesReadFreshFromStore.redflow.test.ts | 2 +- .../models/activeModelService.test.ts | 12 +-- .../spotlightFlowIntegration.test.ts | 20 ++--- .../integration/rag/embeddingFlow.test.ts | 4 +- __tests__/integration/rag/ragFlow.test.ts | 14 +-- .../stores/chatStoreIntegration.test.ts | 6 +- 36 files changed, 107 insertions(+), 127 deletions(-) diff --git a/__tests__/integration/audio/voiceModeImageJourney.rendered.happy.test.tsx b/__tests__/integration/audio/voiceModeImageJourney.rendered.happy.test.tsx index 48f172f85..cb5af9515 100644 --- a/__tests__/integration/audio/voiceModeImageJourney.rendered.happy.test.tsx +++ b/__tests__/integration/audio/voiceModeImageJourney.rendered.happy.test.tsx @@ -34,7 +34,7 @@ describe('T084 (rendered) — voice-mode image journey (STT → route → image await h.voiceSend('draw a dog'); // The image generation ran... - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 6000 }); // ...and the generated image renders on screen (the terminal artifact the user sees). await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('generated-image')).not.toBeNull(); }, { timeout: 6000 }); }); diff --git a/__tests__/integration/audio/voiceModeResendEnhancedImage.rendered.redflow.test.tsx b/__tests__/integration/audio/voiceModeResendEnhancedImage.rendered.redflow.test.tsx index 47498c9fe..cc76f78bb 100644 --- a/__tests__/integration/audio/voiceModeResendEnhancedImage.rendered.redflow.test.tsx +++ b/__tests__/integration/audio/voiceModeResendEnhancedImage.rendered.redflow.test.tsx @@ -42,7 +42,7 @@ describe('T062 (voice + enhancement) — resend of an enhanced image request re- // then the image is drawn. h.boundary.litert.scriptTurn({ content: 'a photorealistic dog in a park' }); await h.voiceSend('draw a dog'); - await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('generated-image-content').length).toBe(1); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('generated-image-content')).toHaveLength(1); }, { timeout: 6000 }); // RESEND via the real action menu (3-dots) on the image-result message → Retry. Regenerate REPLACES the // reply, so a correct re-draw leaves one rendered image; a misroute-to-text would leave ZERO (the image @@ -53,7 +53,7 @@ describe('T062 (voice + enhancement) — resend of an enhanced image request re- // SPEC: the resend re-ran the IMAGE pipeline (a second generateImage) AND the user still sees a rendered // image (not a text answer). RED (B33) would be zero rendered images + the text answer. - expect(h.boundary.diffusion.calls.generateImage.length).toBe(2); - expect(h.view!.queryAllByTestId('generated-image-content').length).toBe(1); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(2); + expect(h.view!.queryAllByTestId('generated-image-content')).toHaveLength(1); }); }); diff --git a/__tests__/integration/audio/voiceModeResendImageRoutes.rendered.redflow.test.tsx b/__tests__/integration/audio/voiceModeResendImageRoutes.rendered.redflow.test.tsx index 8a4a8a9d1..2e978ab0a 100644 --- a/__tests__/integration/audio/voiceModeResendImageRoutes.rendered.redflow.test.tsx +++ b/__tests__/integration/audio/voiceModeResendImageRoutes.rendered.redflow.test.tsx @@ -43,7 +43,7 @@ describe('T062 (voice-mode) — resend of an image request re-draws, not text (D // Switch to Voice mode via the chat-input quick-settings, then VOICE-send "draw a dog" → IMAGE. await h.enterVoiceMode(); await h.voiceSend('draw a dog'); - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 6000 }); // RESEND via the real action menu (3-dots) on the image-result message → Retry. In audio mode the image // result renders as the same core ChatMessage, so the action menu is identical to text mode. @@ -52,7 +52,7 @@ describe('T062 (voice-mode) — resend of an image request re-draws, not text (D // SPEC: resend re-runs the IMAGE pipeline → a SECOND generateImage; NO text answer leaked. // RED (B33 in voice mode): resend goes to the text model → generateImage stays 1 + the scripted text renders. - expect(h.boundary.diffusion.calls.generateImage.length).toBe(2); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(2); expect(h.view!.queryByText(/domestic animal/)).toBeNull(); }); }); diff --git a/__tests__/integration/chat/voiceNoteChatModeEmptyTurn.redflow.test.ts b/__tests__/integration/chat/voiceNoteChatModeEmptyTurn.redflow.test.ts index 2d798c02d..b636dcdd5 100644 --- a/__tests__/integration/chat/voiceNoteChatModeEmptyTurn.redflow.test.ts +++ b/__tests__/integration/chat/voiceNoteChatModeEmptyTurn.redflow.test.ts @@ -64,7 +64,7 @@ describe('chat-mode STT is dictation-to-the-input-box on every engine (LiteRT to expect(transcriptArgs.some(t => t.trim() === 'draw a dog')).toBe(true); // And it is NOT dispatched as a voice-note attachment, nor auto-sent — the user reviews/edits then sends. // RED before the fix: the transcript went to onAudioAttachment (a dispatched note), composer stayed empty. - expect(attachmentArgs.length).toBe(0); - expect(autoSendArgs.length).toBe(0); + expect(attachmentArgs).toHaveLength(0); + expect(autoSendArgs).toHaveLength(0); }); }); diff --git a/__tests__/integration/downloads/downloadedCountBadge.rendered.happy.test.tsx b/__tests__/integration/downloads/downloadedCountBadge.rendered.happy.test.tsx index d8446c6ec..91e839887 100644 --- a/__tests__/integration/downloads/downloadedCountBadge.rendered.happy.test.tsx +++ b/__tests__/integration/downloads/downloadedCountBadge.rendered.happy.test.tsx @@ -69,7 +69,7 @@ describe('T012 (rendered) — ModelsScreen reflects N downloaded models', () => // The count of downloaded marks the user sees on ModelsScreen must equal N. await waitFor(() => { - expect(view.queryAllByTestId(/^model-card-\d+-downloaded$/).length).toBe(N); + expect(view.queryAllByTestId(/^model-card-\d+-downloaded$/)).toHaveLength(N); }, { timeout: 4000 }); }); }); diff --git a/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.redflow.test.ts b/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.redflow.test.ts index 5a6e1c71b..0c72e868b 100644 --- a/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.redflow.test.ts +++ b/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.redflow.test.ts @@ -104,7 +104,7 @@ describe.each(['ios', 'android'] as const)( }; // Precondition: no native download in flight, and the row is a dead 'failed' (the screenshot state). - expect(boundary.download!.active().length).toBe(0); + expect(boundary.download!.active()).toHaveLength(0); expect(useDownloadStore.getState().downloads[modelKey].status).toBe('failed'); // ACT: the retry/resume-finalize path for an all-bytes-present image download. diff --git a/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.rendered.redflow.test.tsx b/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.rendered.redflow.test.tsx index c56d49969..84f2de223 100644 --- a/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.rendered.redflow.test.tsx +++ b/__tests__/integration/downloads/iosImageStagingPurgedRedownloads.rendered.redflow.test.tsx @@ -66,7 +66,7 @@ describe('rendered — iOS image staging purged: Retry recovers the failed card' return btn; }); expect(view.queryByText(/SDXL|coreml_apple/)).not.toBeNull(); - expect(boundary.download!.active().length).toBe(0); + expect(boundary.download!.active()).toHaveLength(0); // GESTURE: tap Retry, the way the user did on the device. fireEvent.press(retry!); diff --git a/__tests__/integration/downloads/iosInterruptedNoFailedEntry.redflow.test.ts b/__tests__/integration/downloads/iosInterruptedNoFailedEntry.redflow.test.ts index 2cf7ee89a..ab0efe90b 100644 --- a/__tests__/integration/downloads/iosInterruptedNoFailedEntry.redflow.test.ts +++ b/__tests__/integration/downloads/iosInterruptedNoFailedEntry.redflow.test.ts @@ -18,7 +18,7 @@ describe('D4 — iOS interrupted download leaves no failed entry (red-flow)', () boundary.download!.seedActive({ downloadId: 'dl-txt', fileName: 'gemma-4b.gguf', modelId: 'gemma-4b', modelType: 'text', status: 'running', bytesDownloaded: 2 * 1024 ** 3, totalBytes: 6 * 1024 ** 3 }); await hydrateDownloadStore(); - expect(Object.keys(useDownloadStore.getState().downloads).length).toBe(1); // precondition: shown while running + expect(Object.keys(useDownloadStore.getState().downloads)).toHaveLength(1); // precondition: shown while running // iOS force-quit: URLSession drops the row entirely. boundary.download!.simulateRelaunch(); diff --git a/__tests__/integration/downloads/iosTextRetryReissues.rendered.redflow.test.tsx b/__tests__/integration/downloads/iosTextRetryReissues.rendered.redflow.test.tsx index d5ea3409d..e6df80571 100644 --- a/__tests__/integration/downloads/iosTextRetryReissues.rendered.redflow.test.tsx +++ b/__tests__/integration/downloads/iosTextRetryReissues.rendered.redflow.test.tsx @@ -57,7 +57,7 @@ describe('iOS text retry (rendered, red-flow)', () => { await waitFor(() => { expect(view.queryByText(/model\.gguf/)).not.toBeNull(); }); // Pre-condition: nothing is downloading at the native boundary yet (so a false green can't hide). - expect(boundary.download!.active().length).toBe(0); + expect(boundary.download!.active()).toHaveLength(0); fireEvent.press(view.getByTestId('failed-retry-button')); diff --git a/__tests__/integration/downloads/queuedDownloadsSurviveKill.redflow.test.ts b/__tests__/integration/downloads/queuedDownloadsSurviveKill.redflow.test.ts index 1ad8961da..44c878b80 100644 --- a/__tests__/integration/downloads/queuedDownloadsSurviveKill.redflow.test.ts +++ b/__tests__/integration/downloads/queuedDownloadsSurviveKill.redflow.test.ts @@ -204,7 +204,7 @@ describe('queued downloads survive an app kill (red-flow)', () => { // so a second kill right now would not lose them (the found=11 → found=1 loss). await flush(); const persisted = await loadQueuedDownloads(); - expect(persisted.length).toBe(3); + expect(persisted).toHaveLength(3); }); it('no regression: an in-flight (started) download still hydrates via the native path', async () => { diff --git a/__tests__/integration/downloads/textRetryReissuesOnIosLostDownloadId.rendered.redflow.test.tsx b/__tests__/integration/downloads/textRetryReissuesOnIosLostDownloadId.rendered.redflow.test.tsx index 30b76dba9..d82ad0a60 100644 --- a/__tests__/integration/downloads/textRetryReissuesOnIosLostDownloadId.rendered.redflow.test.tsx +++ b/__tests__/integration/downloads/textRetryReissuesOnIosLostDownloadId.rendered.redflow.test.tsx @@ -100,7 +100,7 @@ describe('iOS text retry re-issues a rehydrated failed download that lost its do await waitFor(() => expect(getByText('Retry')).toBeTruthy(), { timeout: 4000 }); // No native download exists yet (the row was lost on the kill). - expect(boundary.download!.active().length).toBe(0); + expect(boundary.download!.active()).toHaveLength(0); // Tap Retry. await act(async () => { fireEvent.press(getByText('Retry')); }); diff --git a/__tests__/integration/generation/enhancementNoThinking.rendered.redflow.test.tsx b/__tests__/integration/generation/enhancementNoThinking.rendered.redflow.test.tsx index 25be1c40e..1fb90b583 100644 --- a/__tests__/integration/generation/enhancementNoThinking.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/enhancementNoThinking.rendered.redflow.test.tsx @@ -45,7 +45,7 @@ describe('T071 (rendered) — prompt enhancement must not think (DEV-B30)', () = h.boundary.llama!.scriptCompletion({ text: 'a photorealistic cat in a garden' }); // the rewritten prompt await h.tapSend('draw a cat'); - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 6000 }); // The enhancement completion reached the text engine. const enhancementReq = h.boundary.llama!.calls.completion.map(c => c[0] as { enable_thinking?: boolean; messages?: Array<{ role: string; content?: string }> }).find(isEnhancementRequest); diff --git a/__tests__/integration/generation/enhancementStreamingProgress.rendered.redflow.test.tsx b/__tests__/integration/generation/enhancementStreamingProgress.rendered.redflow.test.tsx index 392b51ea9..ac36b275a 100644 --- a/__tests__/integration/generation/enhancementStreamingProgress.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/enhancementStreamingProgress.rendered.redflow.test.tsx @@ -82,7 +82,7 @@ describe('T073 (rendered) — enhancement must stream / show live progress (DEV- // Release the held stream so the turn completes cleanly (no dangling promise / open handle). h.boundary.llama!.releaseStream(); await h.rtl.waitFor( - () => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, + () => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 6000 }, ); }); diff --git a/__tests__/integration/generation/generationFlow.test.ts b/__tests__/integration/generation/generationFlow.test.ts index a71ec4b46..f36046ebe 100644 --- a/__tests__/integration/generation/generationFlow.test.ts +++ b/__tests__/integration/generation/generationFlow.test.ts @@ -283,7 +283,7 @@ describe('Generation Flow Integration', () => { // Verify message was finalized const chatState = getChatState(); expect(chatState.streamingMessage).toBe(''); - expect(chatState.streamingForConversationId).toBe(null); + expect(chatState.streamingForConversationId).toBeNull(); expect(chatState.isStreaming).toBe(false); // Verify assistant message was added @@ -368,7 +368,7 @@ describe('Generation Flow Integration', () => { // Verify streaming state was cleared const chatState = getChatState(); expect(chatState.streamingMessage).toBe(''); - expect(chatState.streamingForConversationId).toBe(null); + expect(chatState.streamingForConversationId).toBeNull(); expect(chatState.isStreaming).toBe(false); }); }); diff --git a/__tests__/integration/generation/imageGenerationFlow.test.ts b/__tests__/integration/generation/imageGenerationFlow.test.ts index 8854e6aba..1f0dd2d51 100644 --- a/__tests__/integration/generation/imageGenerationFlow.test.ts +++ b/__tests__/integration/generation/imageGenerationFlow.test.ts @@ -687,7 +687,7 @@ describe('Image Generation Flow Integration', () => { // Should have: system + context messages + user enhance prompt // system (1) + conversation messages (3) + user enhance (1) = 5 - expect(enhancementMessages.length).toBe(5); + expect(enhancementMessages).toHaveLength(5); expect(enhancementMessages[0].role).toBe('system'); expect(enhancementMessages[0].content).toContain('conversation history'); expect(enhancementMessages[1].content).toBe('Draw me a cat'); @@ -709,7 +709,7 @@ describe('Image Generation Flow Integration', () => { const enhancementMessages = callArgs[0] as Message[]; // Should have: system + user enhance prompt only (no context) - expect(enhancementMessages.length).toBe(2); + expect(enhancementMessages).toHaveLength(2); expect(enhancementMessages[0].role).toBe('system'); expect(enhancementMessages[0].content).not.toContain('conversation history'); expect(enhancementMessages[1].role).toBe('user'); @@ -736,7 +736,7 @@ describe('Image Generation Flow Integration', () => { // The context message should be truncated to 500 chars const contextMsg = enhancementMessages.find(m => m.id.startsWith('ctx-')); expect(contextMsg).toBeDefined(); - expect(contextMsg!.content.length).toBe(500); + expect(contextMsg!.content).toHaveLength(500); }); it('should limit conversation context to last 10 messages', async () => { @@ -761,7 +761,7 @@ describe('Image Generation Flow Integration', () => { const enhancementMessages = callArgs[0] as Message[]; // system (1) + last 10 context messages + user enhance (1) = 12 - expect(enhancementMessages.length).toBe(12); + expect(enhancementMessages).toHaveLength(12); // First context message should be message 6 (index 5), not message 1 const firstContextMsg = enhancementMessages[1]; expect(firstContextMsg.content).toBe('Message 6'); @@ -786,7 +786,7 @@ describe('Image Generation Flow Integration', () => { const enhancementMessages = callArgs[0] as Message[]; // system (1) + 2 context (user + assistant, system skipped) + user enhance (1) = 4 - expect(enhancementMessages.length).toBe(4); + expect(enhancementMessages).toHaveLength(4); const contextMessages = enhancementMessages.filter(m => m.id.startsWith('ctx-')); expect(contextMessages).toHaveLength(2); expect(contextMessages.every(m => m.role !== 'system')).toBe(true); @@ -835,7 +835,7 @@ describe('Image Generation Flow Integration', () => { const enhancementMessages = callArgs[0] as Message[]; // system + user enhance only (no context from empty conversation) - expect(enhancementMessages.length).toBe(2); + expect(enhancementMessages).toHaveLength(2); expect(enhancementMessages[0].role).toBe('system'); expect(enhancementMessages[0].content).not.toContain('conversation history'); }); @@ -1180,62 +1180,42 @@ describe('Image Generation Flow Integration', () => { mockLlmService.isCurrentlyGenerating.mockReturnValue(false); }; - it('should strip tags from thinking model responses', async () => { + it.each([ + { + name: 'should strip tags from thinking model responses', + // Simulate a thinking model that wraps reasoning in tags + inputPrompt: 'sunset over mountains', + enhancedResponse: + 'Let me enhance this prompt by adding artistic details...A majestic sunset over mountains, golden hour lighting, oil painting style', + // The prompt passed to image generation should NOT contain tags + expectedPrompt: 'A majestic sunset over mountains, golden hour lighting, oil painting style', + }, + { + name: 'should handle thinking model response that is only a think block', + // Simulate a model that only outputs thinking with no actual response + inputPrompt: 'a cat', + enhancedResponse: 'I need to think about how to enhance this prompt...', + // When stripping produces empty string, should fall back to original prompt + expectedPrompt: 'a cat', + }, + { + name: 'should handle response without think tags normally', + // Non-thinking model returns plain enhanced prompt + inputPrompt: 'simple prompt', + enhancedResponse: 'A beautiful enhanced prompt with details', + expectedPrompt: 'A beautiful enhanced prompt with details', + }, + ])('$name', async ({ inputPrompt, enhancedResponse, expectedPrompt }) => { setupThinkingModelEnhancement(); - // Simulate a thinking model that wraps reasoning in tags - mockLlmService.generateResponse.mockResolvedValue( - 'Let me enhance this prompt by adding artistic details...A majestic sunset over mountains, golden hour lighting, oil painting style' - ); - - await imageGenerationService.generateImage({ - prompt: 'sunset over mountains', - }); - - // The prompt passed to image generation should NOT contain tags - expect(mockLocalDreamService.generateImage).toHaveBeenCalledWith( - expect.objectContaining({ - prompt: 'A majestic sunset over mountains, golden hour lighting, oil painting style', - }), - expect.any(Function), - expect.any(Function), - ); - }); - - it('should handle thinking model response that is only a think block', async () => { - setupThinkingModelEnhancement(); - // Simulate a model that only outputs thinking with no actual response - mockLlmService.generateResponse.mockResolvedValue( - 'I need to think about how to enhance this prompt...' - ); - - await imageGenerationService.generateImage({ - prompt: 'a cat', - }); - - // When stripping produces empty string, should fall back to original prompt - expect(mockLocalDreamService.generateImage).toHaveBeenCalledWith( - expect.objectContaining({ - prompt: 'a cat', - }), - expect.any(Function), - expect.any(Function), - ); - }); - - it('should handle response without think tags normally', async () => { - setupThinkingModelEnhancement(); - // Non-thinking model returns plain enhanced prompt - mockLlmService.generateResponse.mockResolvedValue( - 'A beautiful enhanced prompt with details' - ); + mockLlmService.generateResponse.mockResolvedValue(enhancedResponse); await imageGenerationService.generateImage({ - prompt: 'simple prompt', + prompt: inputPrompt, }); expect(mockLocalDreamService.generateImage).toHaveBeenCalledWith( expect.objectContaining({ - prompt: 'A beautiful enhanced prompt with details', + prompt: expectedPrompt, }), expect.any(Function), expect.any(Function), diff --git a/__tests__/integration/generation/queuedForceImagePreservesMode.rendered.redflow.test.tsx b/__tests__/integration/generation/queuedForceImagePreservesMode.rendered.redflow.test.tsx index 5c36e517b..4043175c0 100644 --- a/__tests__/integration/generation/queuedForceImagePreservesMode.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/queuedForceImagePreservesMode.rendered.redflow.test.tsx @@ -64,7 +64,7 @@ describe('#510 (rendered) — a queued force-image send preserves its force flag await h.settle(50); // let handleSendFn enqueue // No image generated yet — turn #2 is queued, turn #1 still holds. - expect(h.boundary.diffusion.calls.generateImage.length).toBe(0); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(0); // ---- Release turn #1 → the queue drains and dispatches the queued force-image message. ---- h.boundary.llama!.releaseStream(); @@ -73,10 +73,10 @@ describe('#510 (rendered) — a queued force-image send preserves its force flag // SPEC: the queued force-image send draws → the generated-image bubble renders on screen, and the // scripted TEXT reply must NOT appear for turn #2. // RED (#510): force lost → classified text → QUEUED_TEXT_LEAK renders again as a second reply, no image. - await rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 4000 }); + await rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 4000 }); expect(view.queryByTestId('generated-image')).not.toBeNull(); // Only turn #1's single text reply may exist — turn #2 must NOT have produced a second text reply. - expect(view.queryAllByText(new RegExp(QUEUED_TEXT_LEAK)).length).toBe(1); + expect(view.queryAllByText(new RegExp(QUEUED_TEXT_LEAK))).toHaveLength(1); }); it('COALESCE (M16): two sends queue together and one forced image — the merged dispatch draws an image', async () => { @@ -101,12 +101,12 @@ describe('#510 (rendered) — a queued force-image send preserves its force flag await rtl.waitFor(() => { expect(view.queryByTestId('image-mode-force-badge')).not.toBeNull(); }); await h.tapSend('tell me about cats'); // force → queued (now 2 queued, one forced) await h.settle(50); - expect(h.boundary.diffusion.calls.generateImage.length).toBe(0); // nothing drawn while #1 holds + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(0); // nothing drawn while #1 holds // Drain: the 2 queued messages coalesce; because one was force, the merged dispatch must draw. h.boundary.llama!.releaseStream(); await h.settle(400); - await rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 4000 }); + await rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 4000 }); expect(view.queryByTestId('generated-image')).not.toBeNull(); }); }); diff --git a/__tests__/integration/generation/queuedSendFeedback.test.ts b/__tests__/integration/generation/queuedSendFeedback.test.ts index 076899226..5d1865286 100644 --- a/__tests__/integration/generation/queuedSendFeedback.test.ts +++ b/__tests__/integration/generation/queuedSendFeedback.test.ts @@ -118,7 +118,7 @@ describe('BUG #29(b) — second send while generating surfaces a queued indicato await handleSendFn(deps, { text: 'second message', startGeneration, setDebugInfo: jest.fn() }); await flushPromises(); - expect(generationService.getState().queuedMessages.length).toBe(1); + expect(generationService.getState().queuedMessages).toHaveLength(1); // The subscription the screen reads now reflects the queued message → QueueRow renders. expect(queueCount).toBe(1); expect(queuedTexts).toEqual(['second message']); diff --git a/__tests__/integration/generation/reloadRaceKeepsThinking.rendered.redflow.test.tsx b/__tests__/integration/generation/reloadRaceKeepsThinking.rendered.redflow.test.tsx index 27125ee3b..5aa8c2902 100644 --- a/__tests__/integration/generation/reloadRaceKeepsThinking.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/reloadRaceKeepsThinking.rendered.redflow.test.tsx @@ -108,7 +108,7 @@ describe('reload race — a send during the load window keeps thinking (device 2 expect(h.view!.queryByText(/seventeen has no divisors below its root/)).not.toBeNull(); // BOTH turns carry the thinking affordance (the block collapses to its preview after completion). const blocks = h.view!.queryAllByTestId('thinking-block'); - expect(blocks.length).toBe(2); + expect(blocks).toHaveLength(2); // Expand the racing turn's block: the full reasoning renders in the block content. // (walking-up press: the toggle's onPress lives on the composite above the testID host) const toggles = h.view!.queryAllByTestId('thinking-block-toggle'); diff --git a/__tests__/integration/generation/remoteParallelTools.rendered.happy.test.tsx b/__tests__/integration/generation/remoteParallelTools.rendered.happy.test.tsx index aa8b6be56..1e15df30a 100644 --- a/__tests__/integration/generation/remoteParallelTools.rendered.happy.test.tsx +++ b/__tests__/integration/generation/remoteParallelTools.rendered.happy.test.tsx @@ -50,7 +50,7 @@ describe('T048 (rendered) — remote parallel tool_calls render as bubbles + fin await h.tapSend('compute 47*83, 128*256, and 0.3*400'); // The three parallel calculator calls each render a tool-result bubble (accumulate-by-index worked). - await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('tool-result-label-calculator').length).toBe(3); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('tool-result-label-calculator')).toHaveLength(3); }, { timeout: 6000 }); // ...and the remote model's final reply renders. await h.rtl.waitFor(() => { expect(h.view!.queryByText(/Results: 3901, 32768, and 120/)).not.toBeNull(); }, { timeout: 6000 }); }); diff --git a/__tests__/integration/generation/resendImageRoutes.rendered.redflow.test.tsx b/__tests__/integration/generation/resendImageRoutes.rendered.redflow.test.tsx index 95f4bc80d..dc5ee0bfa 100644 --- a/__tests__/integration/generation/resendImageRoutes.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/resendImageRoutes.rendered.redflow.test.tsx @@ -25,7 +25,7 @@ describe('T062 (rendered) — resend of an image request re-draws, not text (DEV // Original send → IMAGE (device-confirmed correct). await h.tapSend('draw a dog'); - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); // RESEND via the real action menu (3-dots) on the image-result message → Retry. await h.regenerateLast({ content: 'A dog is a domestic animal.' }, 'dots'); // scripted text is what leaks if it misroutes @@ -33,7 +33,7 @@ describe('T062 (rendered) — resend of an image request re-draws, not text (DEV // SPEC: resend re-runs the IMAGE pipeline → a SECOND generateImage; NO text answer leaked. // RED (B33): resend goes to the text model → generateImage stays 1 + the scripted text renders. - expect(h.boundary.diffusion.calls.generateImage.length).toBe(2); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(2); expect(h.view!.queryByText(/domestic animal/)).toBeNull(); }); }); diff --git a/__tests__/integration/generation/resendImageRoutesLlama.rendered.redflow.test.tsx b/__tests__/integration/generation/resendImageRoutesLlama.rendered.redflow.test.tsx index ba7309227..f61404029 100644 --- a/__tests__/integration/generation/resendImageRoutesLlama.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/resendImageRoutesLlama.rendered.redflow.test.tsx @@ -33,13 +33,13 @@ describe('T062 (llama) — resend of an image request re-draws on the llama engi await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('image-mode-force-badge')).not.toBeNull(); }); await h.tapSend('draw a dog'); - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 6000 }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 6000 }); // RESEND via the real action menu (3-dots) → the scripted text is what leaks if it misroutes to llama. await h.regenerateLast({ text: 'A dog is a domestic animal.' }, 'dots'); await h.settle(500); - expect(h.boundary.diffusion.calls.generateImage.length).toBe(2); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(2); expect(h.view!.queryByText(/domestic animal/)).toBeNull(); }); }); diff --git a/__tests__/integration/generation/resendImageTurnFallsBackToText.rendered.redflow.test.tsx b/__tests__/integration/generation/resendImageTurnFallsBackToText.rendered.redflow.test.tsx index 5b1da5955..047dbecd0 100644 --- a/__tests__/integration/generation/resendImageTurnFallsBackToText.rendered.redflow.test.tsx +++ b/__tests__/integration/generation/resendImageTurnFallsBackToText.rendered.redflow.test.tsx @@ -39,7 +39,7 @@ describe('send/resend parity — resending an image turn with no image model fal await h.cycleImageMode(); // auto → ON (force) await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('image-mode-force-badge')).not.toBeNull(); }); await h.tapSend('a castle on a hill'); - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }, { timeout: 8000 }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, { timeout: 8000 }); await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('generated-image')).not.toBeNull(); }, { timeout: 8000 }); // The user unloads the image model (store transition — the picker is behind a fragile nested sheet; @@ -55,6 +55,6 @@ describe('send/resend parity — resending an image turn with no image model fal await h.rtl.waitFor(() => { expect(h.view!.queryByText(/A castle is a fortified stone structure\./)).not.toBeNull(); }, { timeout: 8000 }); expect(h.view!.queryByText('No image model loaded.')).toBeNull(); // The failed image path must NOT have fired a second diffusion call. - expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }, 60000); }); diff --git a/__tests__/integration/happy/imageBackends.happy.test.tsx b/__tests__/integration/happy/imageBackends.happy.test.tsx index 05510d961..2fa95653c 100644 --- a/__tests__/integration/happy/imageBackends.happy.test.tsx +++ b/__tests__/integration/happy/imageBackends.happy.test.tsx @@ -36,7 +36,7 @@ describe('happy — image generation shows the correct backend label (heavy entr await h.tapSend('a fox in snow'); // A real image was produced through the real service + native generateImage... - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); // ...and the user sees the correct backend label in the message details. await h.rtl.waitFor(() => { expect(h.view!.queryByText(new RegExp(cfg.expected.replace(/[()]/g, '\\$&')))).not.toBeNull(); }); }); diff --git a/__tests__/integration/happy/imageIntentRouting.happy.test.tsx b/__tests__/integration/happy/imageIntentRouting.happy.test.tsx index 5579dfbc8..250f4f56a 100644 --- a/__tests__/integration/happy/imageIntentRouting.happy.test.tsx +++ b/__tests__/integration/happy/imageIntentRouting.happy.test.tsx @@ -36,7 +36,7 @@ describe('happy — prompt routing picks the right model (heavy entry point)', ( await h.send('draw a cat wearing a hat', { content: 'unused-text-turn' }); // Routed to the image model: the native image generator ran exactly once. - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); }); it('control: a normal prompt routes to the text model (no image generated)', async () => { @@ -48,6 +48,6 @@ describe('happy — prompt routing picks the right model (heavy entry point)', ( // Routed to the text model: the answer renders and the image generator never ran. await h.rtl.waitFor(() => { expect(h.view!.queryByText(/The capital of France is Paris\./)).not.toBeNull(); }); - expect(h.boundary.diffusion.calls.generateImage.length).toBe(0); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(0); }); }); diff --git a/__tests__/integration/happy/imageLightbox.happy.test.tsx b/__tests__/integration/happy/imageLightbox.happy.test.tsx index 43e5bd4af..ed39afc5f 100644 --- a/__tests__/integration/happy/imageLightbox.happy.test.tsx +++ b/__tests__/integration/happy/imageLightbox.happy.test.tsx @@ -24,7 +24,7 @@ async function generateImage(h: Awaited>) { await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('image-mode-force-badge')).not.toBeNull(); }); await h.tapSend('a fox in the snow'); // The image is produced through the real service + native generateImage and rendered in the chat. - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); await h.rtl.waitFor(() => { expect(h.view!.queryByTestId('generated-image')).not.toBeNull(); }); } diff --git a/__tests__/integration/happy/imageModeToggle.happy.test.tsx b/__tests__/integration/happy/imageModeToggle.happy.test.tsx index 17853352a..8960ed1aa 100644 --- a/__tests__/integration/happy/imageModeToggle.happy.test.tsx +++ b/__tests__/integration/happy/imageModeToggle.happy.test.tsx @@ -29,7 +29,7 @@ describe('happy — image-mode toggle routes correctly (heavy entry point)', () await h.tapSend('tell me about the ocean'); // not a draw request // ON forces image regardless of the text → the native image generator runs. - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); }); it('OFF (disabled): the badge clears and a "draw …" prompt does NOT generate an image', async () => { @@ -45,6 +45,6 @@ describe('happy — image-mode toggle routes correctly (heavy entry point)', () // OFF disables image routing → the draw request is answered as text, no image generated. await h.rtl.waitFor(() => { expect(h.view!.queryByText(/A dragon is a mythical reptile\./)).not.toBeNull(); }); - expect(h.boundary.diffusion.calls.generateImage.length).toBe(0); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(0); }); }); diff --git a/__tests__/integration/happy/imageOomCard.happy.test.tsx b/__tests__/integration/happy/imageOomCard.happy.test.tsx index 7bfa709d0..3a81c2ef8 100644 --- a/__tests__/integration/happy/imageOomCard.happy.test.tsx +++ b/__tests__/integration/happy/imageOomCard.happy.test.tsx @@ -39,6 +39,6 @@ describe('happy — image-gen OOM surfaces the graceful "Not Enough Memory" card // Graceful outcome: the user sees the memory card + the override, and NO image was generated. await h.rtl.waitFor(() => { expect(h.view!.queryByText(/Not Enough Memory/)).not.toBeNull(); }); expect(h.view!.queryByText('Load Anyway')).not.toBeNull(); - expect(h.boundary.diffusion.calls.generateImage.length).toBe(0); + expect(h.boundary.diffusion.calls.generateImage).toHaveLength(0); }); }); diff --git a/__tests__/integration/happy/smartBudgeting.happy.test.tsx b/__tests__/integration/happy/smartBudgeting.happy.test.tsx index 96babcc81..809c99f19 100644 --- a/__tests__/integration/happy/smartBudgeting.happy.test.tsx +++ b/__tests__/integration/happy/smartBudgeting.happy.test.tsx @@ -27,7 +27,7 @@ describe('happy — a fittable image gen succeeds with no failure card (heavy en await h.tapSend('a red bicycle'); // The fittable load succeeded through the REAL gate: the native image generator ran... - await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(h.boundary.diffusion.calls.generateImage).toHaveLength(1); }); /* eslint-disable-next-line @typescript-eslint/no-var-requires */ const { modelResidencyManager } = require('../../../src/services/modelResidency'); expect(modelResidencyManager.isResident('image')).toBe(true); diff --git a/__tests__/integration/happy/speakMessage.happy.test.tsx b/__tests__/integration/happy/speakMessage.happy.test.tsx index b38ddc8cd..db2800869 100644 --- a/__tests__/integration/happy/speakMessage.happy.test.tsx +++ b/__tests__/integration/happy/speakMessage.happy.test.tsx @@ -44,7 +44,7 @@ describe.each(['longpress', 'dots'] as const)('happy — speak an assistant repl h.rtl.fireEvent.press(await h.rtl.waitFor(() => h.view!.getByTestId('action-speak'))); // The engine was asked to speak THIS reply's text (with its message id). - await h.rtl.waitFor(() => { expect(seam.spoken.length).toBe(1); }); + await h.rtl.waitFor(() => { expect(seam.spoken).toHaveLength(1); }); expect(seam.spoken[0].text).toContain('The capital of France is Paris.'); expect(seam.spoken[0].id).toBeTruthy(); }); diff --git a/__tests__/integration/happy/tools.happy.test.tsx b/__tests__/integration/happy/tools.happy.test.tsx index bfa4451de..bb9e6f182 100644 --- a/__tests__/integration/happy/tools.happy.test.tsx +++ b/__tests__/integration/happy/tools.happy.test.tsx @@ -70,7 +70,7 @@ describe('happy — a tool runs and its result renders (heavy entry point)', () content: 'Results: 160500 and 25.', }); // Two tool-result bubbles render (both calculator runs are visible). - await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('tool-result-label-calculator').length).toBe(2); }); + await h.rtl.waitFor(() => { expect(h.view!.queryAllByTestId('tool-result-label-calculator')).toHaveLength(2); }); // ...and the answer with both results renders. await h.rtl.waitFor(() => { expect(h.view!.queryByText(/160500 and 25/)).not.toBeNull(); }); }); diff --git a/__tests__/integration/image/imageTunablesReadFreshFromStore.redflow.test.ts b/__tests__/integration/image/imageTunablesReadFreshFromStore.redflow.test.ts index 0ab9295fb..809239d08 100644 --- a/__tests__/integration/image/imageTunablesReadFreshFromStore.redflow.test.ts +++ b/__tests__/integration/image/imageTunablesReadFreshFromStore.redflow.test.ts @@ -56,7 +56,7 @@ describe('image tunables read FRESH from the store, not a stale caller snapshot // The REAL native generateImage ran once, and the params it received are the FRESH store values. await Promise.resolve(); const calls = boundary.diffusion.calls.generateImage; - expect(calls.length).toBe(1); + expect(calls).toHaveLength(1); expect(calls[0].steps).toBe(11); // RED before: 8 (stale deps snapshot) expect(calls[0].guidanceScale).toBe(3.5); // RED before: 7.5 (stale deps snapshot) }); diff --git a/__tests__/integration/models/activeModelService.test.ts b/__tests__/integration/models/activeModelService.test.ts index a789ba241..ff44a6244 100644 --- a/__tests__/integration/models/activeModelService.test.ts +++ b/__tests__/integration/models/activeModelService.test.ts @@ -399,7 +399,7 @@ describe('ActiveModelService Integration', () => { await activeModelService.unloadTextModel(); expect(mockLlmService.unloadModel).toHaveBeenCalled(); - expect(getAppState().activeModelId).toBe(null); + expect(getAppState().activeModelId).toBeNull(); }); it('should skip unload if no model loaded', async () => { @@ -573,7 +573,7 @@ describe('ActiveModelService Integration', () => { await activeModelService.unloadImageModel(); expect(mockLocalDreamService.unloadModel).toHaveBeenCalled(); - expect(getAppState().activeImageModelId).toBe(null); + expect(getAppState().activeImageModelId).toBeNull(); }); }); @@ -774,8 +774,8 @@ describe('ActiveModelService Integration', () => { await activeModelService.syncWithNativeState(); const loadedIds = activeModelService.getLoadedModelIds(); - expect(loadedIds.textModelId).toBe(null); - expect(loadedIds.imageModelId).toBe(null); + expect(loadedIds.textModelId).toBeNull(); + expect(loadedIds.imageModelId).toBeNull(); }); }); @@ -821,9 +821,9 @@ describe('ActiveModelService Integration', () => { const info = activeModelService.getActiveModels(); - expect(info.text.model).toBe(null); + expect(info.text.model).toBeNull(); expect(info.text.isLoaded).toBe(false); - expect(info.image.model).toBe(null); + expect(info.image.model).toBeNull(); expect(info.image.isLoaded).toBe(false); }); }); diff --git a/__tests__/integration/onboarding/spotlightFlowIntegration.test.ts b/__tests__/integration/onboarding/spotlightFlowIntegration.test.ts index c015473a5..934d59cf1 100644 --- a/__tests__/integration/onboarding/spotlightFlowIntegration.test.ts +++ b/__tests__/integration/onboarding/spotlightFlowIntegration.test.ts @@ -88,13 +88,13 @@ describe('Onboarding Spotlight Flow Integration', () => { }); it('checklist step completes when model finishes downloading', () => { - expect(getAppState().downloadedModels.length).toBe(0); + expect(getAppState().downloadedModels).toHaveLength(0); // Simulate download completion useAppStore.getState().addDownloadedModel(createDownloadedModel()); const state = getAppState(); - expect(state.downloadedModels.length).toBe(1); + expect(state.downloadedModels).toHaveLength(1); // useOnboardingSteps checks: downloadedModels.length > 0 }); }); @@ -361,14 +361,14 @@ describe('Onboarding Spotlight Flow Integration', () => { // 4 is NOT enough const fourProjects = Array.from({ length: 4 }, (_, i) => createProject({ id: `proj-${i}` })); useProjectStore.setState({ projects: fourProjects }); - expect(useProjectStore.getState().projects.length).toBe(4); - expect(useProjectStore.getState().projects.length > 4).toBe(false); + expect(useProjectStore.getState().projects).toHaveLength(4); + expect(useProjectStore.getState().projects.length).toBeLessThanOrEqual(4); // 5 completes it const fiveProjects = [...fourProjects, createProject({ id: 'proj-4' })]; useProjectStore.setState({ projects: fiveProjects }); - expect(useProjectStore.getState().projects.length).toBe(5); - expect(useProjectStore.getState().projects.length > 4).toBe(true); + expect(useProjectStore.getState().projects).toHaveLength(5); + expect(useProjectStore.getState().projects.length).toBeGreaterThan(4); }); }); @@ -442,7 +442,7 @@ describe('Onboarding Spotlight Flow Integration', () => { expect(state.shownSpotlights).toEqual({}); // App data preserved - expect(state.downloadedModels.length).toBe(1); + expect(state.downloadedModels).toHaveLength(1); expect(state.activeModelId).toBe('model-1'); }); @@ -462,9 +462,9 @@ describe('Onboarding Spotlight Flow Integration', () => { // Initial state: no reactive conditions met let state = getAppState(); - expect(state.downloadedImageModels.length).toBe(0); + expect(state.downloadedImageModels).toHaveLength(0); expect(state.activeImageModelId).toBeNull(); - expect(state.generatedImages.length).toBe(0); + expect(state.generatedImages).toHaveLength(0); // Part 2 condition not yet met (no image model downloaded) expect( @@ -516,7 +516,7 @@ describe('Onboarding Spotlight Flow Integration', () => { // Part 5 condition not yet met (no image generated) state = getAppState(); - expect(state.generatedImages.length).toBe(0); + expect(state.generatedImages).toHaveLength(0); // Generate image → Part 5 triggers store.addGeneratedImage(createGeneratedImage()); diff --git a/__tests__/integration/rag/embeddingFlow.test.ts b/__tests__/integration/rag/embeddingFlow.test.ts index a016b222e..91b6f8ae5 100644 --- a/__tests__/integration/rag/embeddingFlow.test.ts +++ b/__tests__/integration/rag/embeddingFlow.test.ts @@ -194,7 +194,7 @@ describe('Embedding Flow Integration', () => { const embInserts = mockExecuteSync.mock.calls.filter( (c: any[]) => typeof c[0] === 'string' && c[0].includes('INSERT INTO rag_embeddings') ); - expect(embInserts.length).toBe(2); + expect(embInserts).toHaveLength(2); }); }); @@ -208,7 +208,7 @@ describe('Embedding Flow Integration', () => { const deleteCalls = mockExecuteSync.mock.calls.filter( (c: any[]) => typeof c[0] === 'string' && c[0].includes('DELETE') ); - expect(deleteCalls.length).toBe(3); + expect(deleteCalls).toHaveLength(3); // Order: embeddings, chunks, document expect(deleteCalls[0][0]).toContain('rag_embeddings'); expect(deleteCalls[0][1]).toEqual([42]); diff --git a/__tests__/integration/rag/ragFlow.test.ts b/__tests__/integration/rag/ragFlow.test.ts index 2ccb383e9..e53d0dba2 100644 --- a/__tests__/integration/rag/ragFlow.test.ts +++ b/__tests__/integration/rag/ragFlow.test.ts @@ -93,7 +93,7 @@ describe('RAG Flow Integration', () => { const docInserts = mockExecuteSync.mock.calls.filter( (c: any[]) => typeof c[0] === 'string' && c[0].includes('INSERT INTO rag_documents') ); - expect(docInserts.length).toBe(1); + expect(docInserts).toHaveLength(1); expect(docInserts[0][1]).toEqual(expect.arrayContaining(['proj-1', 'guide.pdf'])); // Verify chunks were inserted @@ -188,7 +188,7 @@ describe('RAG Flow Integration', () => { }); expect(result.truncated).toBe(true); - expect(result.chunks.length).toBe(0); // First chunk exceeds budget + expect(result.chunks).toHaveLength(0); // First chunk exceeds budget }); it('includes all results when within budget', async () => { @@ -213,7 +213,7 @@ describe('RAG Flow Integration', () => { }); expect(result.truncated).toBe(false); - expect(result.chunks.length).toBe(2); + expect(result.chunks).toHaveLength(2); }); }); @@ -249,7 +249,7 @@ describe('RAG Flow Integration', () => { const updateCalls = mockExecuteSync.mock.calls.filter( (c: any[]) => typeof c[0] === 'string' && c[0].includes('UPDATE') ); - expect(updateCalls.length).toBe(1); + expect(updateCalls).toHaveLength(1); expect(updateCalls[0][1]).toEqual([0, 1]); // enabled=0, docId=1 }); @@ -259,7 +259,7 @@ describe('RAG Flow Integration', () => { const deleteCalls = mockExecuteSync.mock.calls.filter( (c: any[]) => typeof c[0] === 'string' && c[0].includes('DELETE') ); - expect(deleteCalls.length).toBe(3); + expect(deleteCalls).toHaveLength(3); expect(deleteCalls[0][0]).toContain('rag_embeddings'); expect(deleteCalls[1][0]).toContain('rag_chunks'); expect(deleteCalls[2][0]).toContain('rag_documents'); @@ -272,7 +272,7 @@ describe('RAG Flow Integration', () => { (c: any[]) => typeof c[0] === 'string' && c[0].includes('DELETE') ); // 1 embeddings delete + 1 chunks delete + 1 docs delete - expect(deleteCalls.length).toBe(3); + expect(deleteCalls).toHaveLength(3); expect(deleteCalls[0][0]).toContain('rag_embeddings'); expect(deleteCalls[1][0]).toContain('rag_chunks'); expect(deleteCalls[2][0]).toContain('rag_documents'); @@ -376,7 +376,7 @@ describe('RAG Flow Integration', () => { it('chunking handles empty paragraphs gracefully', () => { const text = 'First paragraph is here.\n\n\n\n\n\nSecond paragraph is here.'; const chunks = chunkDocument(text, { chunkSize: 500 }); - expect(chunks.length).toBe(1); + expect(chunks).toHaveLength(1); expect(chunks[0].content).toContain('First'); expect(chunks[0].content).toContain('Second'); }); diff --git a/__tests__/integration/stores/chatStoreIntegration.test.ts b/__tests__/integration/stores/chatStoreIntegration.test.ts index 172fc3e64..37494ae22 100644 --- a/__tests__/integration/stores/chatStoreIntegration.test.ts +++ b/__tests__/integration/stores/chatStoreIntegration.test.ts @@ -80,7 +80,7 @@ describe('ChatStore Streaming Integration', () => { // Streaming state should be cleared expect(state.streamingMessage).toBe(''); - expect(state.streamingForConversationId).toBe(null); + expect(state.streamingForConversationId).toBeNull(); expect(state.isStreaming).toBe(false); // Message should be added to conversation @@ -162,7 +162,7 @@ describe('ChatStore Streaming Integration', () => { // Everything should be cleared expect(state.streamingMessage).toBe(''); - expect(state.streamingForConversationId).toBe(null); + expect(state.streamingForConversationId).toBeNull(); expect(state.isStreaming).toBe(false); expect(state.isThinking).toBe(false); @@ -191,7 +191,7 @@ describe('ChatStore Streaming Integration', () => { it('should return idle state when not streaming', () => { const streamingState = useChatStore.getState().getStreamingState(); - expect(streamingState.conversationId).toBe(null); + expect(streamingState.conversationId).toBeNull(); expect(streamingState.content).toBe(''); expect(streamingState.isStreaming).toBe(false); expect(streamingState.isThinking).toBe(false);