@@ -530,6 +530,202 @@ function runTest(what, options, callback) {
530
530
return api . call ( this , paths . test , callback , query , options ) ;
531
531
}
532
532
533
+ function runTestAndWait ( what , options , callback ) {
534
+ delete options . pollResults ;
535
+ delete options . timeout ;
536
+ options = Object . assign ( options , { pollResults : 10 } ) ;
537
+
538
+ var query = { } ;
539
+
540
+ callback = callback || options ;
541
+ options = options === callback ? { } : helper . deepClone ( options ) ;
542
+
543
+ // testing url or script?
544
+ query [ reSpace . test ( what ) ? "script" : "url" ] = what ;
545
+ // set dummy url when scripting, needed when webdriver script
546
+ if ( query . script ) {
547
+ query . url = "https://www.webpagetest.org" ;
548
+ }
549
+ helper . setQuery ( mapping . commands . test , options , query ) ;
550
+
551
+ // connectivity
552
+ if ( reConnectivity . test ( options . connectivity ) && query . location ) {
553
+ query . location += "." + options . connectivity ;
554
+ }
555
+
556
+ // json output format
557
+ query . f = "json" ;
558
+
559
+ // API key
560
+ if ( ! query . k && this . config . key ) {
561
+ query . k = this . config . key ;
562
+ }
563
+
564
+ // synchronous tests with results
565
+ var testId ,
566
+ polling ,
567
+ server ,
568
+ listen ,
569
+ timerout ,
570
+ resultsOptions = { } ;
571
+
572
+ function resultsCallback ( err , data ) {
573
+ clearTimeout ( timerout ) ;
574
+ if ( options . exitOnResults ) {
575
+ process . exit ( err ) ;
576
+ } else {
577
+ callback ( err , data ) ;
578
+ }
579
+ }
580
+
581
+ function poll ( err , data ) {
582
+ // poll again when test started but not complete
583
+ // and not when specs are done testing
584
+ if (
585
+ ! err &&
586
+ ( ! data || ( data && data . data && data . statusCode !== 200 ) ) &&
587
+ ! ( typeof err === "number" && data === undefined )
588
+ ) {
589
+ console . log (
590
+ data && data . data && data . data . statusText
591
+ ? data . data . statusText
592
+ : "Testing in progress"
593
+ ) ;
594
+ polling = setTimeout (
595
+ getTestResults . bind ( this , testId , resultsOptions , poll . bind ( this ) ) ,
596
+ options . pollResults
597
+ ) ;
598
+ } else {
599
+ if ( ! data ) {
600
+ data = { testId : testId } ;
601
+ }
602
+ resultsCallback ( err , data ) ;
603
+ }
604
+ }
605
+
606
+ function testCallback ( cb , err , data ) {
607
+ if ( err || ! ( data && data . data && data . data . testId ) ) {
608
+ return callback ( err || data ) ;
609
+ }
610
+
611
+ testId = data . data . testId ;
612
+
613
+ if ( options . timeout ) {
614
+ timerout = setTimeout ( timeout , options . timeout ) ;
615
+ }
616
+
617
+ if ( cb ) {
618
+ cb . call ( this ) ;
619
+ }
620
+ }
621
+
622
+ function timeout ( ) {
623
+ if ( server ) {
624
+ server . close ( ) ;
625
+ }
626
+ clearTimeout ( polling ) ;
627
+ callback ( {
628
+ error : {
629
+ code : "TIMEOUT" ,
630
+ testId : testId ,
631
+ message : "timeout" ,
632
+ } ,
633
+ } ) ;
634
+ }
635
+
636
+ function listener ( ) {
637
+ query . pingback = url . format ( {
638
+ protocol : "http" ,
639
+ hostname : options . waitResults . hostname ,
640
+ port : options . waitResults . port ,
641
+ pathname : "/testdone" ,
642
+ } ) ;
643
+
644
+ api . call ( this , paths . test , testCallback . bind ( this , null ) , query , options ) ;
645
+ }
646
+
647
+ function wait ( ) {
648
+ server . listen ( options . waitResults . port , listen ) ;
649
+ return options . waitResults ;
650
+ }
651
+
652
+ // poll|wait results timeout
653
+ if ( options . timeout ) {
654
+ options . timeout = ( parseInt ( options . timeout , 10 ) || 0 ) * 1000 ;
655
+ }
656
+
657
+ // poll|wait results options
658
+ Object . keys ( mapping . options . results ) . forEach ( function resultsOpts ( key ) {
659
+ var name = mapping . options . results [ key ] . name ,
660
+ value = options [ name ] || options [ key ] ;
661
+
662
+ if ( value !== undefined ) {
663
+ resultsOptions [ name ] = value ;
664
+ }
665
+ } ) ;
666
+
667
+ // poll results
668
+ if ( options . pollResults && ! options . dryRun ) {
669
+ options . pollResults = parseInt ( options . pollResults * 1000 , 10 ) || 5000 ;
670
+
671
+ return api . call (
672
+ this ,
673
+ paths . test ,
674
+ testCallback . bind ( this , poll ) ,
675
+ query ,
676
+ options
677
+ ) ;
678
+ }
679
+
680
+ // wait results
681
+ if ( options . waitResults && ! options . dryRun ) {
682
+ options . waitResults = helper . localhost (
683
+ options . waitResults ,
684
+ WebPageTest . defaultWaitResultsPort
685
+ ) ;
686
+
687
+ listen = listener . bind ( this ) ;
688
+
689
+ server = http . createServer (
690
+ function ( req , res ) {
691
+ var uri = url . parse ( req . url , true ) ;
692
+
693
+ res . statusCode = 204 ;
694
+ res . end ( ) ;
695
+
696
+ if ( uri . pathname === "/testdone" && uri . query . id === testId ) {
697
+ server . close (
698
+ getTestResults . bind (
699
+ this ,
700
+ uri . query . id ,
701
+ resultsOptions ,
702
+ resultsCallback
703
+ )
704
+ ) ;
705
+ }
706
+ } . bind ( this )
707
+ ) ;
708
+
709
+ server . on (
710
+ "error" ,
711
+ function ( err ) {
712
+ if ( [ "EACCES" , "EADDRINUSE" ] . indexOf ( err . code ) > - 1 ) {
713
+ // remove old unused listener and bump port for next attempt
714
+ server . removeListener ( "listening" , listen ) ;
715
+ options . waitResults . port ++ ;
716
+ wait . call ( this ) ;
717
+ } else {
718
+ callback ( err ) ;
719
+ }
720
+ } . bind ( this )
721
+ ) ;
722
+
723
+ return wait . call ( this ) ;
724
+ }
725
+
726
+ return api . call ( this , paths . test , callback , query , options ) ;
727
+ }
728
+
533
729
function restartTest ( id , options , callback ) {
534
730
var query = { resubmit : id } ;
535
731
@@ -932,6 +1128,7 @@ WebPageTest.prototype = {
932
1128
getLocations : getLocations ,
933
1129
getTesters : getTesters ,
934
1130
runTest : runTest ,
1131
+ runTestAndWait : runTestAndWait ,
935
1132
restartTest : restartTest ,
936
1133
cancelTest : cancelTest ,
937
1134
getPageSpeedData : getPageSpeedData ,
0 commit comments