@@ -24,8 +24,8 @@ use futures::StreamExt as _;
24
24
use regex:: Regex ;
25
25
26
26
use crate :: {
27
- parser, runner, step, writer, ArbitraryWriter , FailureWriter , Parser ,
28
- Runner , ScenarioType , Step , World , Writer , WriterExt as _,
27
+ event , parser, runner, step, writer, ArbitraryWriter , FailureWriter ,
28
+ Parser , Runner , ScenarioType , Step , World , Writer , WriterExt as _,
29
29
} ;
30
30
31
31
/// Top-level [Cucumber] executor.
@@ -142,14 +142,14 @@ where
142
142
///
143
143
/// Output with a regular [`Cucumber::run()`]:
144
144
/// <script
145
- /// id="asciicast-Ar8XAtrZWKMNfe7mffBXbQAFb "
146
- /// src="https://asciinema.org/a/Ar8XAtrZWKMNfe7mffBXbQAFb .js"
147
- /// async data-autoplay="true" data-rows="16 ">
145
+ /// id="asciicast-hMyH3IYbHRFXT1yf84tXDNl2r "
146
+ /// src="https://asciinema.org/a/hMyH3IYbHRFXT1yf84tXDNl2r .js"
147
+ /// async data-autoplay="true" data-rows="17 ">
148
148
/// </script>
149
149
///
150
150
/// To fail all the [`Skipped`] steps setup [`Cucumber`] like this:
151
- /// ```rust
152
- /// # use std::{ convert::Infallible, panic::AssertUnwindSafe} ;
151
+ /// ```rust,should_panic
152
+ /// # use std::convert::Infallible;
153
153
/// #
154
154
/// # use async_trait::async_trait;
155
155
/// # use cucumber::WorldInit;
@@ -174,7 +174,7 @@ where
174
174
/// .await;
175
175
/// # };
176
176
/// #
177
- /// # futures::executor::block_on(AssertUnwindSafe( fut).catch_unwind() );
177
+ /// # futures::executor::block_on(fut);
178
178
/// ```
179
179
/// <script
180
180
/// id="asciicast-UsaG9kMnn40nW8y4vcmXOE2tT"
@@ -222,15 +222,15 @@ where
222
222
///
223
223
/// Output with a regular [`Cucumber::run()`]:
224
224
/// <script
225
- /// id="asciicast-Ar8XAtrZWKMNfe7mffBXbQAFb "
226
- /// src="https://asciinema.org/a/Ar8XAtrZWKMNfe7mffBXbQAFb .js"
227
- /// async data-autoplay="true" data-rows="16 ">
225
+ /// id="asciicast-hMyH3IYbHRFXT1yf84tXDNl2r "
226
+ /// src="https://asciinema.org/a/hMyH3IYbHRFXT1yf84tXDNl2r .js"
227
+ /// async data-autoplay="true" data-rows="17 ">
228
228
/// </script>
229
229
///
230
230
/// Adjust [`Cucumber`] to fail on all [`Skipped`] steps, but the ones
231
231
/// marked with `@dog` tag:
232
- /// ```rust
233
- /// # use std::{ convert::Infallible, panic::AssertUnwindSafe} ;
232
+ /// ```rust,should_panic
233
+ /// # use std::convert::Infallible;
234
234
/// #
235
235
/// # use async_trait::async_trait;
236
236
/// # use futures::FutureExt as _;
@@ -250,12 +250,12 @@ where
250
250
/// #
251
251
/// # let fut = async {
252
252
/// MyWorld::cucumber()
253
- /// .fail_on_skipped_with(|_, _, sc| sc .tags.iter().any(|t| t == "dog"))
253
+ /// .fail_on_skipped_with(|_, _, s| !s .tags.iter().any(|t| t == "dog"))
254
254
/// .run_and_exit("tests/features/readme")
255
255
/// .await;
256
256
/// # };
257
257
/// #
258
- /// # futures::executor::block_on(AssertUnwindSafe( fut).catch_unwind() );
258
+ /// # futures::executor::block_on(fut);
259
259
/// ```
260
260
/// ```gherkin
261
261
/// Feature: Animal feature
@@ -315,6 +315,285 @@ where
315
315
_parser_input : PhantomData ,
316
316
}
317
317
}
318
+
319
+ /// Re-outputs [`Skipped`] steps for easier navigation.
320
+ ///
321
+ /// # Example
322
+ ///
323
+ /// Output with a regular [`Cucumber::run()`]:
324
+ /// <script
325
+ /// id="asciicast-hMyH3IYbHRFXT1yf84tXDNl2r"
326
+ /// src="https://asciinema.org/a/hMyH3IYbHRFXT1yf84tXDNl2r.js"
327
+ /// async data-autoplay="true" data-rows="17">
328
+ /// </script>
329
+ ///
330
+ /// Adjust [`Cucumber`] to re-output all [`Skipped`] steps at the end:
331
+ /// ```rust
332
+ /// # use std::convert::Infallible;
333
+ /// #
334
+ /// # use async_trait::async_trait;
335
+ /// # use futures::FutureExt as _;
336
+ /// # use cucumber::WorldInit;
337
+ /// #
338
+ /// # #[derive(Debug, WorldInit)]
339
+ /// # struct MyWorld;
340
+ /// #
341
+ /// # #[async_trait(?Send)]
342
+ /// # impl cucumber::World for MyWorld {
343
+ /// # type Error = Infallible;
344
+ /// #
345
+ /// # async fn new() -> Result<Self, Self::Error> {
346
+ /// # Ok(Self)
347
+ /// # }
348
+ /// # }
349
+ /// #
350
+ /// # let fut = async {
351
+ /// MyWorld::cucumber()
352
+ /// .repeat_skipped()
353
+ /// .run_and_exit("tests/features/readme")
354
+ /// .await;
355
+ /// # };
356
+ /// #
357
+ /// # futures::executor::block_on(fut);
358
+ /// ```
359
+ /// <script
360
+ /// id="asciicast-BD1mPjYGELD6oWNKW8lTlyvDR"
361
+ /// src="https://asciinema.org/a/BD1mPjYGELD6oWNKW8lTlyvDR.js"
362
+ /// async data-autoplay="true" data-rows="19">
363
+ /// </script>
364
+ ///
365
+ /// [`Scenario`]: gherkin::Scenario
366
+ /// [`Skipped`]: crate::event::Step::Skipped
367
+ #[ must_use]
368
+ pub fn repeat_skipped ( self ) -> Cucumber < W , P , I , R , writer:: Repeat < W , Wr > > {
369
+ Cucumber {
370
+ parser : self . parser ,
371
+ runner : self . runner ,
372
+ writer : self . writer . repeat_skipped ( ) ,
373
+ _world : PhantomData ,
374
+ _parser_input : PhantomData ,
375
+ }
376
+ }
377
+
378
+ /// Re-outputs [`Failed`] steps for easier navigation.
379
+ ///
380
+ /// # Example
381
+ ///
382
+ /// Output with a regular [`Cucumber::fail_on_skipped()`]:
383
+ /// ```rust,should_panic
384
+ /// # use std::convert::Infallible;
385
+ /// #
386
+ /// # use async_trait::async_trait;
387
+ /// # use futures::FutureExt as _;
388
+ /// # use cucumber::WorldInit;
389
+ /// #
390
+ /// # #[derive(Debug, WorldInit)]
391
+ /// # struct MyWorld;
392
+ /// #
393
+ /// # #[async_trait(?Send)]
394
+ /// # impl cucumber::World for MyWorld {
395
+ /// # type Error = Infallible;
396
+ /// #
397
+ /// # async fn new() -> Result<Self, Self::Error> {
398
+ /// # Ok(Self)
399
+ /// # }
400
+ /// # }
401
+ /// #
402
+ /// # let fut = async {
403
+ /// MyWorld::cucumber()
404
+ /// .fail_on_skipped()
405
+ /// .run_and_exit("tests/features/readme")
406
+ /// .await;
407
+ /// # };
408
+ /// #
409
+ /// # futures::executor::block_on(fut);
410
+ /// ```
411
+ /// <script
412
+ /// id="asciicast-mDDqxWHzUaK19P0L2R2g4XRp2"
413
+ /// src="https://asciinema.org/a/mDDqxWHzUaK19P0L2R2g4XRp2.js"
414
+ /// async data-autoplay="true" data-rows="21">
415
+ /// </script>
416
+ ///
417
+ /// Adjust [`Cucumber`] to re-output all [`Failed`] steps at the end:
418
+ /// ```rust,should_panic
419
+ /// # use std::convert::Infallible;
420
+ /// #
421
+ /// # use async_trait::async_trait;
422
+ /// # use futures::FutureExt as _;
423
+ /// # use cucumber::WorldInit;
424
+ /// #
425
+ /// # #[derive(Debug, WorldInit)]
426
+ /// # struct MyWorld;
427
+ /// #
428
+ /// # #[async_trait(?Send)]
429
+ /// # impl cucumber::World for MyWorld {
430
+ /// # type Error = Infallible;
431
+ /// #
432
+ /// # async fn new() -> Result<Self, Self::Error> {
433
+ /// # Ok(Self)
434
+ /// # }
435
+ /// # }
436
+ /// #
437
+ /// # let fut = async {
438
+ /// MyWorld::cucumber()
439
+ /// .repeat_failed()
440
+ /// .fail_on_skipped()
441
+ /// .run_and_exit("tests/features/readme")
442
+ /// .await;
443
+ /// # };
444
+ /// #
445
+ /// # futures::executor::block_on(fut);
446
+ /// ```
447
+ /// <script
448
+ /// id="asciicast-qKp8Hevrb6732mMUT7VduvxJc"
449
+ /// src="https://asciinema.org/a/qKp8Hevrb6732mMUT7VduvxJc.js"
450
+ /// async data-autoplay="true" data-rows="24">
451
+ /// </script>
452
+ ///
453
+ /// > ⚠️ __WARNING__: [`Cucumber::repeat_failed()`] should be called before
454
+ /// [`Cucumber::fail_on_skipped()`], as events pass from
455
+ /// outer [`Writer`]s to inner ones. So we need to
456
+ /// transform [`Skipped`] to [`Failed`] first, and only
457
+ /// then [`Repeat`] them.
458
+ ///
459
+ /// [`Failed`]: crate::event::Step::Failed
460
+ /// [`Repeat`]: writer::Repeat
461
+ /// [`Scenario`]: gherkin::Scenario
462
+ /// [`Skipped`]: crate::event::Step::Skipped
463
+ #[ must_use]
464
+ pub fn repeat_failed ( self ) -> Cucumber < W , P , I , R , writer:: Repeat < W , Wr > > {
465
+ Cucumber {
466
+ parser : self . parser ,
467
+ runner : self . runner ,
468
+ writer : self . writer . repeat_failed ( ) ,
469
+ _world : PhantomData ,
470
+ _parser_input : PhantomData ,
471
+ }
472
+ }
473
+
474
+ /// Re-output steps by the given `filter` predicate.
475
+ ///
476
+ /// # Example
477
+ ///
478
+ /// Output with a regular [`Cucumber::fail_on_skipped()`]:
479
+ /// ```rust,should_panic
480
+ /// # use std::convert::Infallible;
481
+ /// #
482
+ /// # use async_trait::async_trait;
483
+ /// # use futures::FutureExt as _;
484
+ /// # use cucumber::WorldInit;
485
+ /// #
486
+ /// # #[derive(Debug, WorldInit)]
487
+ /// # struct MyWorld;
488
+ /// #
489
+ /// # #[async_trait(?Send)]
490
+ /// # impl cucumber::World for MyWorld {
491
+ /// # type Error = Infallible;
492
+ /// #
493
+ /// # async fn new() -> Result<Self, Self::Error> {
494
+ /// # Ok(Self)
495
+ /// # }
496
+ /// # }
497
+ /// #
498
+ /// # let fut = async {
499
+ /// MyWorld::cucumber()
500
+ /// .fail_on_skipped()
501
+ /// .run_and_exit("tests/features/readme")
502
+ /// .await;
503
+ /// # };
504
+ /// #
505
+ /// # futures::executor::block_on(fut);
506
+ /// ```
507
+ /// <script
508
+ /// id="asciicast-mDDqxWHzUaK19P0L2R2g4XRp2"
509
+ /// src="https://asciinema.org/a/mDDqxWHzUaK19P0L2R2g4XRp2.js"
510
+ /// async data-autoplay="true" data-rows="21">
511
+ /// </script>
512
+ ///
513
+ /// Adjust [`Cucumber`] to re-output all [`Failed`] steps ta the end by
514
+ /// providing a custom `filter` predicate:
515
+ /// ```rust,should_panic
516
+ /// # use std::convert::Infallible;
517
+ /// #
518
+ /// # use async_trait::async_trait;
519
+ /// # use futures::FutureExt as _;
520
+ /// # use cucumber::WorldInit;
521
+ /// #
522
+ /// # #[derive(Debug, WorldInit)]
523
+ /// # struct MyWorld;
524
+ /// #
525
+ /// # #[async_trait(?Send)]
526
+ /// # impl cucumber::World for MyWorld {
527
+ /// # type Error = Infallible;
528
+ /// #
529
+ /// # async fn new() -> Result<Self, Self::Error> {
530
+ /// # Ok(Self)
531
+ /// # }
532
+ /// # }
533
+ /// #
534
+ /// # let fut = async {
535
+ /// MyWorld::cucumber()
536
+ /// .repeat_if(|ev| {
537
+ /// use cucumber::event::{Cucumber, Feature, Rule, Scenario, Step};
538
+ ///
539
+ /// matches!(
540
+ /// ev,
541
+ /// Ok(Cucumber::Feature(
542
+ /// _,
543
+ /// Feature::Rule(
544
+ /// _,
545
+ /// Rule::Scenario(
546
+ /// _,
547
+ /// Scenario::Step(_, Step::Failed(..))
548
+ /// | Scenario::Background(_, Step::Failed(..))
549
+ /// )
550
+ /// ) | Feature::Scenario(
551
+ /// _,
552
+ /// Scenario::Step(_, Step::Failed(..))
553
+ /// | Scenario::Background(_, Step::Failed(..))
554
+ /// )
555
+ /// )) | Err(_)
556
+ /// )
557
+ /// })
558
+ /// .fail_on_skipped()
559
+ /// .run_and_exit("tests/features/readme")
560
+ /// .await;
561
+ /// # };
562
+ /// #
563
+ /// # futures::executor::block_on(fut);
564
+ /// ```
565
+ /// <script
566
+ /// id="asciicast-qKp8Hevrb6732mMUT7VduvxJc"
567
+ /// src="https://asciinema.org/a/qKp8Hevrb6732mMUT7VduvxJc.js"
568
+ /// async data-autoplay="true" data-rows="24">
569
+ /// </script>
570
+ ///
571
+ /// > ⚠️ __WARNING__: [`Cucumber::repeat_if()`] should be called before
572
+ /// [`Cucumber::fail_on_skipped()`], as events pass from
573
+ /// outer [`Writer`]s to inner ones. So we need to
574
+ /// transform [`Skipped`] to [`Failed`] first, and only
575
+ /// then [`Repeat`] them.
576
+ ///
577
+ /// [`Failed`]: crate::event::Step::Failed
578
+ /// [`Repeat`]: writer::Repeat
579
+ /// [`Scenario`]: gherkin::Scenario
580
+ /// [`Skipped`]: crate::event::Step::Skipped
581
+ #[ must_use]
582
+ pub fn repeat_if < F > (
583
+ self ,
584
+ filter : F ,
585
+ ) -> Cucumber < W , P , I , R , writer:: Repeat < W , Wr , F > >
586
+ where
587
+ F : Fn ( & parser:: Result < event:: Cucumber < W > > ) -> bool ,
588
+ {
589
+ Cucumber {
590
+ parser : self . parser ,
591
+ runner : self . runner ,
592
+ writer : self . writer . repeat_if ( filter) ,
593
+ _world : PhantomData ,
594
+ _parser_input : PhantomData ,
595
+ }
596
+ }
318
597
}
319
598
320
599
impl < W , P , I , R , Wr > Cucumber < W , P , I , R , Wr >
0 commit comments