11import { describe , it , expect , vi , beforeEach } from "vitest" ;
22import { WebFetchExecutor , stripHtmlTags } from "./web_fetch" ;
33
4- // Mock offscreen client
5- vi . mock ( "@App/app/service/offscreen/client" , ( ) => ( {
6- extractHtmlContent : vi . fn ( ) ,
7- } ) ) ;
8-
9- import { extractHtmlContent } from "@App/app/service/offscreen/client" ;
10- const mockExtract = vi . mocked ( extractHtmlContent ) ;
4+ // 通过 mockSender.sendMessage 控制 offscreen extractHtmlContent 的返回值
5+ let mockExtractReturnValue : string | null = null ;
6+ let mockExtractShouldThrow = false ;
117
128describe ( "stripHtmlTags" , ( ) => {
139 it ( "should remove HTML tags" , ( ) => {
1410 expect ( stripHtmlTags ( "<p>Hello <b>World</b></p>" ) ) . toBe ( "Hello World" ) ;
1511 } ) ;
1612
1713 it ( "should remove script and style tags with content" , ( ) => {
18- const html = ' <div>text<script>alert(1)</script> <style>.x{}</style>more</div>' ;
14+ const html = " <div>text<script>alert(1)</script> <style>.x{}</style>more</div>" ;
1915 expect ( stripHtmlTags ( html ) ) . toBe ( "text more" ) ;
2016 } ) ;
2117
@@ -25,11 +21,26 @@ describe("stripHtmlTags", () => {
2521} ) ;
2622
2723describe ( "WebFetchExecutor" , ( ) => {
28- const mockSender = { } as any ;
24+ const mockSender = {
25+ sendMessage : vi . fn ( ) . mockImplementation ( ( ) => {
26+ if ( mockExtractShouldThrow ) {
27+ return Promise . reject ( new Error ( "Offscreen unavailable" ) ) ;
28+ }
29+ return Promise . resolve ( { data : mockExtractReturnValue } ) ;
30+ } ) ,
31+ } as any ;
2932
3033 beforeEach ( ( ) => {
3134 vi . clearAllMocks ( ) ;
3235 vi . stubGlobal ( "fetch" , vi . fn ( ) ) ;
36+ mockExtractReturnValue = null ;
37+ mockExtractShouldThrow = false ;
38+ mockSender . sendMessage . mockImplementation ( ( ) => {
39+ if ( mockExtractShouldThrow ) {
40+ return Promise . reject ( new Error ( "Offscreen unavailable" ) ) ;
41+ }
42+ return Promise . resolve ( { data : mockExtractReturnValue } ) ;
43+ } ) ;
3344 } ) ;
3445
3546 it ( "should throw for missing url" , async ( ) => {
@@ -70,7 +81,7 @@ describe("WebFetchExecutor", () => {
7081 text : ( ) => Promise . resolve ( "<html><body><p>Hello World long content here for testing</p></body></html>" ) ,
7182 } ) ;
7283 vi . stubGlobal ( "fetch" , mockFetch ) ;
73- mockExtract . mockResolvedValue ( "Hello World long content here for testing extracted properly by offscreen" ) ;
84+ mockExtractReturnValue = "Hello World long content here for testing extracted properly by offscreen" ;
7485
7586 const executor = new WebFetchExecutor ( mockSender ) ;
7687 const result = JSON . parse ( ( await executor . execute ( { url : "https://example.com" } ) ) as string ) ;
@@ -86,7 +97,7 @@ describe("WebFetchExecutor", () => {
8697 text : ( ) => Promise . resolve ( "<p>Simple text</p>" ) ,
8798 } ) ;
8899 vi . stubGlobal ( "fetch" , mockFetch ) ;
89- mockExtract . mockResolvedValue ( null ) ;
100+ mockExtractReturnValue = null ;
90101
91102 const executor = new WebFetchExecutor ( mockSender ) ;
92103 const result = JSON . parse ( ( await executor . execute ( { url : "https://example.com" } ) ) as string ) ;
@@ -129,7 +140,7 @@ describe("WebFetchExecutor", () => {
129140 text : ( ) => Promise . resolve ( "<p>Fallback content</p>" ) ,
130141 } ) ;
131142 vi . stubGlobal ( "fetch" , mockFetch ) ;
132- mockExtract . mockRejectedValue ( new Error ( "Offscreen unavailable" ) ) ;
143+ mockExtractShouldThrow = true ;
133144
134145 const executor = new WebFetchExecutor ( mockSender ) ;
135146 const result = JSON . parse ( ( await executor . execute ( { url : "https://example.com" } ) ) as string ) ;
@@ -145,7 +156,7 @@ describe("WebFetchExecutor", () => {
145156 text : ( ) => Promise . resolve ( "<p>Hi</p>" ) ,
146157 } ) ;
147158 vi . stubGlobal ( "fetch" , mockFetch ) ;
148- mockExtract . mockResolvedValue ( "Hi" ) ; // shorter than 50 chars
159+ mockExtractReturnValue = "Hi" ; // shorter than 50 chars
149160
150161 const executor = new WebFetchExecutor ( mockSender ) ;
151162 const result = JSON . parse ( ( await executor . execute ( { url : "https://example.com" } ) ) as string ) ;
@@ -173,16 +184,19 @@ describe("WebFetchExecutor", () => {
173184 const mockFetch = vi . fn ( ) . mockResolvedValue ( {
174185 ok : true ,
175186 headers : new Headers ( { } ) ,
176- text : ( ) => Promise . resolve ( "<html><body>Long enough content for extraction to work properly and pass the threshold</body></html>" ) ,
187+ text : ( ) =>
188+ Promise . resolve (
189+ "<html><body>Long enough content for extraction to work properly and pass the threshold</body></html>"
190+ ) ,
177191 } ) ;
178192 vi . stubGlobal ( "fetch" , mockFetch ) ;
179- mockExtract . mockResolvedValue ( "Long enough content for extraction to work properly and pass the threshold" ) ;
193+ mockExtractReturnValue = "Long enough content for extraction to work properly and pass the threshold" ;
180194
181195 const executor = new WebFetchExecutor ( mockSender ) ;
182196 const result = JSON . parse ( ( await executor . execute ( { url : "https://example.com" } ) ) as string ) ;
183197
184198 expect ( result . content_type ) . toBe ( "html" ) ;
185- expect ( mockExtract ) . toHaveBeenCalled ( ) ;
199+ expect ( mockSender . sendMessage ) . toHaveBeenCalled ( ) ;
186200 } ) ;
187201
188202 it ( "should handle text/plain content-type as plain text" , async ( ) => {
@@ -198,7 +212,7 @@ describe("WebFetchExecutor", () => {
198212
199213 expect ( result . content_type ) . toBe ( "text" ) ;
200214 expect ( result . content ) . toBe ( "Just plain text" ) ;
201- expect ( mockExtract ) . not . toHaveBeenCalled ( ) ;
215+ expect ( mockSender . sendMessage ) . not . toHaveBeenCalled ( ) ;
202216 } ) ;
203217
204218 it ( "should use default max_length of 10000" , async ( ) => {
0 commit comments