@@ -42,6 +42,7 @@ define(function (require, exports, module) {
4242 CodeInspection ,
4343 CommandManager ,
4444 Commands = require ( "command/Commands" ) ,
45+ DocumentManager ,
4546 EditorManager ,
4647 prefs ;
4748
@@ -111,6 +112,7 @@ define(function (require, exports, module) {
111112 brackets = testWindow . brackets ;
112113 Strings = testWindow . require ( "strings" ) ;
113114 CommandManager = brackets . test . CommandManager ;
115+ DocumentManager = brackets . test . DocumentManager ;
114116 EditorManager = brackets . test . EditorManager ;
115117 prefs = brackets . test . PreferencesManager . getExtensionPrefs ( "linting" ) ;
116118 CodeInspection = brackets . test . CodeInspection ;
@@ -137,6 +139,7 @@ define(function (require, exports, module) {
137139 $ = null ;
138140 brackets = null ;
139141 CommandManager = null ;
142+ DocumentManager = null ;
140143 EditorManager = null ;
141144 SpecRunnerUtils . closeTestWindow ( ) ;
142145 } ) ;
@@ -413,8 +416,29 @@ define(function (require, exports, module) {
413416 describe ( "Code Inspection UI" , function ( ) {
414417 beforeEach ( function ( ) {
415418 CodeInspection . _unregisterAll ( ) ;
419+ CodeInspection . toggleEnabled ( true ) ;
416420 } ) ;
417421
422+ // Utility to create an async provider where the testcase can control when each async result resolves
423+ function makeAsyncLinter ( ) {
424+ return {
425+ name : "Test Async Linter" ,
426+ scanFileAsync : function ( text , fullPath ) {
427+ if ( ! this . futures [ fullPath ] ) {
428+ this . futures [ fullPath ] = [ ] ;
429+ this . filesCalledOn . push ( fullPath ) ;
430+ }
431+
432+ var result = new $ . Deferred ( ) ;
433+ this . futures [ fullPath ] . push ( result ) ;
434+ return result . promise ( ) ;
435+ } ,
436+ futures : { } , // map from full path to array of Deferreds (in call order)
437+ filesCalledOn : [ ] // in order of first call for each path
438+ } ;
439+ }
440+
441+
418442 it ( "should run test linter when a JavaScript document opens and indicate errors in the panel" , function ( ) {
419443 var codeInspector = createCodeInspector ( "javascript linter" , failLintResult ( ) ) ;
420444 CodeInspection . register ( "javascript" , codeInspector ) ;
@@ -427,45 +451,124 @@ define(function (require, exports, module) {
427451 expect ( $statusBar . is ( ":visible" ) ) . toBe ( true ) ;
428452 } ) ;
429453 } ) ;
430-
431- it ( "should show only warnings for the current file" , function ( ) {
454+
455+ it ( "should ignore async results from previous file" , function ( ) {
432456 CodeInspection . toggleEnabled ( false ) ;
433457
434- var firstTime = true ,
435- deferred1 = new $ . Deferred ( ) ,
436- deferred2 = new $ . Deferred ( ) ;
437-
438- var asyncProvider = {
439- name : "Test Async Linter" ,
440- scanFileAsync : function ( ) {
441- if ( firstTime ) {
442- firstTime = false ;
443- return deferred1 . promise ( ) ;
444- } else {
445- return deferred2 . promise ( ) ;
446- }
447- }
448- } ;
449-
458+ var asyncProvider = makeAsyncLinter ( ) ;
450459 CodeInspection . register ( "javascript" , asyncProvider ) ;
451460
452- waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "no-errors.js" , "errors.js" ] , "open test files" ) ) ;
461+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "no-errors.js" , "errors.js" ] ) , "open test files" ) ;
462+
463+ var errorsJS = SpecRunnerUtils . makeAbsolute ( "errors.js" ) ,
464+ noErrorsJS = SpecRunnerUtils . makeAbsolute ( "no-errors.js" ) ;
453465
454466 runs ( function ( ) {
467+ // Start linting the first file
455468 CodeInspection . toggleEnabled ( true ) ;
456- CommandManager . execute ( Commands . FILE_CLOSE ) ;
469+ expect ( asyncProvider . filesCalledOn ) . toEqual ( [ errorsJS ] ) ;
470+
471+ // Close that file, switching to the 2nd one
472+ waitsForDone ( CommandManager . execute ( Commands . FILE_CLOSE ) ) ;
457473 } ) ;
458474
459- // Close the file which was started to lint
460475 runs ( function ( ) {
461- // let the linter finish
462- deferred1 . resolve ( failLintResult ( ) ) ;
476+ // Verify that we started linting the 2nd file
477+ expect ( DocumentManager . getCurrentDocument ( ) . file . fullPath ) . toBe ( noErrorsJS ) ;
478+ expect ( asyncProvider . filesCalledOn ) . toEqual ( [ errorsJS , noErrorsJS ] ) ;
479+
480+ // Finish old (stale) linting session - verify results not shown
481+ asyncProvider . futures [ errorsJS ] [ 0 ] . resolve ( failLintResult ( ) ) ;
463482 expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( false ) ;
464- deferred2 . resolve ( successfulLintResult ( ) ) ;
483+
484+ // Finish new (current) linting session
485+ asyncProvider . futures [ noErrorsJS ] [ 0 ] . resolve ( successfulLintResult ( ) ) ;
465486 expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( false ) ;
466487 } ) ;
467488 } ) ;
489+
490+ it ( "should ignore async results from previous run in same file - finishing in order" , function ( ) {
491+ CodeInspection . toggleEnabled ( false ) ;
492+
493+ var asyncProvider = makeAsyncLinter ( ) ;
494+ CodeInspection . register ( "javascript" , asyncProvider ) ;
495+
496+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "no-errors.js" ] ) , "open test files" ) ;
497+
498+ var noErrorsJS = SpecRunnerUtils . makeAbsolute ( "no-errors.js" ) ;
499+
500+ runs ( function ( ) {
501+ // Start linting the file
502+ CodeInspection . toggleEnabled ( true ) ;
503+ expect ( asyncProvider . filesCalledOn ) . toEqual ( [ noErrorsJS ] ) ;
504+
505+ // "Modify" the file
506+ $ ( DocumentManager ) . triggerHandler ( "documentSaved" , DocumentManager . getCurrentDocument ( ) ) ;
507+ expect ( asyncProvider . futures [ noErrorsJS ] . length ) . toBe ( 2 ) ;
508+
509+ // Finish old (stale) linting session - verify results not shown
510+ asyncProvider . futures [ noErrorsJS ] [ 0 ] . resolve ( failLintResult ( ) ) ;
511+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( false ) ;
512+
513+ // Finish new (current) linting session - verify results are shown
514+ asyncProvider . futures [ noErrorsJS ] [ 1 ] . resolve ( failLintResult ( ) ) ;
515+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( true ) ;
516+ } ) ;
517+ } ) ;
518+
519+ it ( "should ignore async results from previous run in same file - finishing reverse order" , function ( ) {
520+ CodeInspection . toggleEnabled ( false ) ;
521+
522+ var asyncProvider = makeAsyncLinter ( ) ;
523+ CodeInspection . register ( "javascript" , asyncProvider ) ;
524+
525+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "no-errors.js" ] ) , "open test files" ) ;
526+
527+ var noErrorsJS = SpecRunnerUtils . makeAbsolute ( "no-errors.js" ) ;
528+
529+ runs ( function ( ) {
530+ // Start linting the file
531+ CodeInspection . toggleEnabled ( true ) ;
532+ expect ( asyncProvider . filesCalledOn ) . toEqual ( [ noErrorsJS ] ) ;
533+
534+ // "Modify" the file
535+ $ ( DocumentManager ) . triggerHandler ( "documentSaved" , DocumentManager . getCurrentDocument ( ) ) ;
536+ expect ( asyncProvider . futures [ noErrorsJS ] . length ) . toBe ( 2 ) ;
537+
538+ // Finish new (current) linting session - verify results are shown
539+ asyncProvider . futures [ noErrorsJS ] [ 1 ] . resolve ( failLintResult ( ) ) ;
540+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( true ) ;
541+
542+ // Finish old (stale) linting session - verify results don't replace current results
543+ asyncProvider . futures [ noErrorsJS ] [ 0 ] . resolve ( successfulLintResult ( ) ) ;
544+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( true ) ;
545+ } ) ;
546+ } ) ;
547+
548+ it ( "should ignore async results after linting disabled" , function ( ) {
549+ CodeInspection . toggleEnabled ( false ) ;
550+
551+ var asyncProvider = makeAsyncLinter ( ) ;
552+ CodeInspection . register ( "javascript" , asyncProvider ) ;
468553
554+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "no-errors.js" ] ) , "open test files" ) ;
555+
556+ var noErrorsJS = SpecRunnerUtils . makeAbsolute ( "no-errors.js" ) ;
557+
558+ runs ( function ( ) {
559+ // Start linting the file
560+ CodeInspection . toggleEnabled ( true ) ;
561+ expect ( asyncProvider . filesCalledOn ) . toEqual ( [ noErrorsJS ] ) ;
562+
563+ // Disable linting
564+ CodeInspection . toggleEnabled ( false ) ;
565+
566+ // Finish old (stale) linting session - verify results not shown
567+ asyncProvider . futures [ noErrorsJS ] [ 0 ] . resolve ( failLintResult ( ) ) ;
568+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( false ) ;
569+ } ) ;
570+ } ) ;
571+
469572 it ( "should show problems panel after too many errors" , function ( ) {
470573 var lintResult = {
471574 errors : [
@@ -517,8 +620,8 @@ define(function (require, exports, module) {
517620 } ) ;
518621 } ) ;
519622
520- it ( "should not show the problems panel when there is no linting error" , function ( ) {
521- var codeInspector = createCodeInspector ( "javascript linter" , successfulLintResult ( ) ) ;
623+ it ( "should not show the problems panel when there is no linting error - empty errors array " , function ( ) {
624+ var codeInspector = createCodeInspector ( "javascript linter" , { errors : [ ] } ) ;
522625 CodeInspection . register ( "javascript" , codeInspector ) ;
523626
524627 waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" , 5000 ) ;
@@ -530,19 +633,20 @@ define(function (require, exports, module) {
530633 } ) ;
531634 } ) ;
532635
533- it ( "status icon should toggle Errors panel when errors present " , function ( ) {
534- var codeInspector = createCodeInspector ( "javascript linter" , failLintResult ( ) ) ;
636+ it ( "should not show the problems panel when there is no linting error - null result " , function ( ) {
637+ var codeInspector = createCodeInspector ( "javascript linter" , null ) ;
535638 CodeInspection . register ( "javascript" , codeInspector ) ;
536639
537- waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" ) ;
640+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" , 5000 ) ;
538641
539642 runs ( function ( ) {
540- toggleJSLintResults ( false ) ;
541- toggleJSLintResults ( true ) ;
643+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( false ) ;
644+ var $statusBar = $ ( "#status-inspection" ) ;
645+ expect ( $statusBar . is ( ":visible" ) ) . toBe ( true ) ;
542646 } ) ;
543647 } ) ;
544648
545- it ( "should run two linters and display two expanded collapsible sections in the errors panel" , function ( ) {
649+ it ( "should display two expanded, collapsible sections in the errors panel when two linters have errors " , function ( ) {
546650 var codeInspector1 = createCodeInspector ( "javascript linter 1" , failLintResult ( ) ) ;
547651 var codeInspector2 = createCodeInspector ( "javascript linter 2" , failLintResult ( ) ) ;
548652 CodeInspection . register ( "javascript" , codeInspector1 ) ;
@@ -561,17 +665,56 @@ define(function (require, exports, module) {
561665 } ) ;
562666 } ) ;
563667
564- it ( "should run the linter and display no collapsible header section in the errors panel" , function ( ) {
565- var codeInspector1 = createCodeInspector ( "javascript linter 1" , failLintResult ( ) ) ;
668+ it ( "should display no header section when only one linter has errors" , function ( ) {
669+ var codeInspector1 = createCodeInspector ( "javascript linter 1" , failLintResult ( ) ) ,
670+ codeInspector2 = createCodeInspector ( "javascript linter 2" , { errors : [ ] } ) , // 1st way of reporting 0 errors
671+ codeInspector3 = createCodeInspector ( "javascript linter 3" , null ) ; // 2nd way of reporting 0 errors
566672 CodeInspection . register ( "javascript" , codeInspector1 ) ;
673+ CodeInspection . register ( "javascript" , codeInspector2 ) ;
674+ CodeInspection . register ( "javascript" , codeInspector3 ) ;
567675
568676 waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" , 5000 ) ;
569677
570678 runs ( function ( ) {
679+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( true ) ;
571680 expect ( $ ( ".inspector-section" ) . is ( ":visible" ) ) . toBeFalsy ( ) ;
572681 } ) ;
573682 } ) ;
574683
684+ it ( "should only display header sections for linters with errors" , function ( ) {
685+ var codeInspector1 = createCodeInspector ( "javascript linter 1" , failLintResult ( ) ) ,
686+ codeInspector2 = createCodeInspector ( "javascript linter 2" , { errors : [ ] } ) , // 1st way of reporting 0 errors
687+ codeInspector3 = createCodeInspector ( "javascript linter 3" , null ) , // 2nd way of reporting 0 errors
688+ codeInspector4 = createCodeInspector ( "javascript linter 4" , failLintResult ( ) ) ;
689+ CodeInspection . register ( "javascript" , codeInspector1 ) ;
690+ CodeInspection . register ( "javascript" , codeInspector2 ) ;
691+ CodeInspection . register ( "javascript" , codeInspector3 ) ;
692+ CodeInspection . register ( "javascript" , codeInspector4 ) ;
693+
694+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" , 5000 ) ;
695+
696+ runs ( function ( ) {
697+ expect ( $ ( "#problems-panel" ) . is ( ":visible" ) ) . toBe ( true ) ;
698+
699+ var $inspectorSections = $ ( ".inspector-section td" ) ;
700+ expect ( $inspectorSections . length ) . toEqual ( 2 ) ;
701+ expect ( $inspectorSections [ 0 ] . innerHTML . indexOf ( "javascript linter 1 (1)" ) ) . not . toBe ( - 1 ) ;
702+ expect ( $inspectorSections [ 1 ] . innerHTML . indexOf ( "javascript linter 4 (1)" ) ) . not . toBe ( - 1 ) ;
703+ } ) ;
704+ } ) ;
705+
706+ it ( "status icon should toggle Errors panel when errors present" , function ( ) {
707+ var codeInspector = createCodeInspector ( "javascript linter" , failLintResult ( ) ) ;
708+ CodeInspection . register ( "javascript" , codeInspector ) ;
709+
710+ waitsForDone ( SpecRunnerUtils . openProjectFiles ( [ "errors.js" ] ) , "open test file" ) ;
711+
712+ runs ( function ( ) {
713+ toggleJSLintResults ( false ) ;
714+ toggleJSLintResults ( true ) ;
715+ } ) ;
716+ } ) ;
717+
575718 it ( "status icon should not toggle Errors panel when no errors present" , function ( ) {
576719 var codeInspector = createCodeInspector ( "javascript linter" , successfulLintResult ( ) ) ;
577720 CodeInspection . register ( "javascript" , codeInspector ) ;
@@ -810,7 +953,7 @@ define(function (require, exports, module) {
810953 } ) ;
811954 } ) ;
812955
813- it ( "should report an async linter which throws an exception " , function ( ) {
956+ it ( "should report an async linter which rejects " , function ( ) {
814957 var errorMessage = "I'm full of bugs on purpose" ,
815958 providerName = "Buggy Async Linter" ,
816959 buggyAsyncProvider = {
0 commit comments