@@ -186,6 +186,75 @@ describe('Crawler', () => {
186186 } ) ;
187187 } ) ;
188188
189+ describe ( 'abort()' , ( ) => {
190+ it ( 'abort() 後に deal() の signal オプションに渡された AbortSignal が aborted になる' , async ( ) => {
191+ const { deal } = await import ( '@d-zero/dealer' ) ;
192+ const { default : Crawler } = await import ( './crawler.js' ) ;
193+
194+ let receivedSignal : AbortSignal | undefined ;
195+
196+ vi . mocked ( deal ) . mockImplementation ( ( _items , _factory , options ) => {
197+ receivedSignal = options ?. signal ;
198+ return Promise . resolve ( ) ;
199+ } ) ;
200+
201+ const crawler = new Crawler ( defaultOptions ) ;
202+ crawler . start ( parseUrl ( 'https://example.com/' ) ! ) ;
203+
204+ await vi . waitFor ( ( ) => {
205+ expect ( receivedSignal ) . toBeDefined ( ) ;
206+ } ) ;
207+
208+ expect ( receivedSignal ! . aborted ) . toBe ( false ) ;
209+ crawler . abort ( ) ;
210+ expect ( receivedSignal ! . aborted ) . toBe ( true ) ;
211+ } ) ;
212+
213+ it ( 'deal 正常完了時に crawlEnd イベントが emit される' , async ( ) => {
214+ const { deal } = await import ( '@d-zero/dealer' ) ;
215+ const { default : Crawler } = await import ( './crawler.js' ) ;
216+
217+ vi . mocked ( deal ) . mockImplementation ( ( _items , _factory , options ) => {
218+ // Simulate: abort is called, deal checks signal and resolves normally
219+ expect ( options ?. signal ) . toBeInstanceOf ( AbortSignal ) ;
220+ return Promise . resolve ( ) ;
221+ } ) ;
222+
223+ const crawler = new Crawler ( defaultOptions ) ;
224+ let crawlEndEmitted = false ;
225+ crawler . on ( 'crawlEnd' , ( ) => {
226+ crawlEndEmitted = true ;
227+ } ) ;
228+
229+ crawler . start ( parseUrl ( 'https://example.com/' ) ! ) ;
230+
231+ await vi . waitFor ( ( ) => {
232+ expect ( crawlEndEmitted ) . toBe ( true ) ;
233+ } ) ;
234+ } ) ;
235+
236+ it ( '二重 abort でもエラーにならない' , async ( ) => {
237+ const { deal } = await import ( '@d-zero/dealer' ) ;
238+ const { default : Crawler } = await import ( './crawler.js' ) ;
239+
240+ vi . mocked ( deal ) . mockResolvedValue ( ) ;
241+
242+ const crawler = new Crawler ( defaultOptions ) ;
243+ crawler . start ( parseUrl ( 'https://example.com/' ) ! ) ;
244+
245+ crawler . abort ( ) ;
246+ expect ( ( ) => crawler . abort ( ) ) . not . toThrow ( ) ;
247+ expect ( crawler . signal . aborted ) . toBe ( true ) ;
248+ } ) ;
249+
250+ it ( 'signal getter が AbortSignal を返す' , async ( ) => {
251+ const { default : Crawler } = await import ( './crawler.js' ) ;
252+ const crawler = new Crawler ( defaultOptions ) ;
253+ expect ( crawler . signal ) . toBeInstanceOf ( AbortSignal ) ;
254+ expect ( crawler . signal . aborted ) . toBe ( false ) ;
255+ } ) ;
256+ } ) ;
257+
189258 describe ( 'worker-level error handling' , ( ) => {
190259 it ( 'ワーカー内の例外が error イベントとして emit され処理が継続する' , async ( ) => {
191260 const { deal } = await import ( '@d-zero/dealer' ) ;
0 commit comments