@@ -11,6 +11,36 @@ function($, sinon, AjaxHelpers, URI, AssetsView, AssetCollection, ViewHelpers) {
1111 uploadModalTpl = readFixtures ( 'asset-upload-modal.underscore' ) ;
1212
1313 beforeEach ( function ( ) {
14+ // Fake XHR must be installed BEFORE new AssetsView() for two reasons:
15+ //
16+ // 1. AssetsView.initialize() calls createPagingView() which immediately calls
17+ // pagingView.setPage(1), firing an AJAX request. If real XHR is active at that
18+ // point, the request goes to Karma's server which returns 404. Jasmine 2.x
19+ // schedules beforeEach and it() with setTimeout(0) between them, giving the
20+ // browser event loop a window to process pending async work. The Karma 404
21+ // response fires during that window, triggering AssetsView's error callback
22+ // -> getTableBody() -> DOM creation -> .upload-button becomes visible. Tests
23+ // that check this button is hidden (in loading state) then fail.
24+ //
25+ // 2. This test suite tests the loading -> loaded state transition. Several tests
26+ // call setup() inside it(), which calls setPage(1) and responds to it, driving
27+ // the view from "loading" to "loaded". That precondition requires the view to
28+ // still be in "loading" state at the start of it(). Responding to the init
29+ // request in beforeEach (to avoid the dangling request) would put the view
30+ // into "loaded" state too early and break those assertions.
31+ //
32+ // With sinon installed first, the init setPage(1) is captured as requests[0] —
33+ // a frozen pending fake request that never gets a response unless explicitly told
34+ // to. No response means no callbacks, no DOM mutation, view stays in "loading"
35+ // state. We advance currentIndex to 1 so AjaxHelpers treats that request as
36+ // already consumed; test-driven setPage() calls start at index 1. The pending
37+ // requests[0] is abandoned when xhrFactory.restore() runs in afterEach.
38+ xhrFactory = sinon . useFakeXMLHttpRequest ( ) ;
39+ requests = [ ] ;
40+ requests . currentIndex = 0 ;
41+ requests . restore = function ( ) { xhrFactory . restore ( ) ; } ;
42+ xhrFactory . onCreate = function ( req ) { requests . push ( req ) ; } ;
43+
1444 setFixtures ( $ ( '<script>' , { id : 'asset-library-tpl' , type : 'text/template' } ) . text ( assetLibraryTpl ) ) ;
1545 appendSetFixtures ( $ ( '<script>' , { id : 'asset-tpl' , type : 'text/template' } ) . text ( assetTpl ) ) ;
1646 appendSetFixtures ( uploadModalTpl ) ;
@@ -30,15 +60,7 @@ function($, sinon, AjaxHelpers, URI, AssetsView, AssetCollection, ViewHelpers) {
3060 collection : collection ,
3161 el : $ ( '#asset_table_body' )
3262 } ) ;
33-
34- // Install fake XHR after creating assetsView so that the initial setPage(1)
35- // fired by createPagingView() during initialize() is not captured. Tests that
36- // need a specific page state drive it themselves via setPage() + respond.
37- xhrFactory = sinon . useFakeXMLHttpRequest ( ) ;
38- requests = [ ] ;
39- requests . currentIndex = 0 ;
40- requests . restore = function ( ) { xhrFactory . restore ( ) ; } ;
41- xhrFactory . onCreate = function ( req ) { requests . push ( req ) ; } ;
63+ requests . currentIndex = 1 ;
4264 } ) ;
4365
4466 afterEach ( function ( ) {
0 commit comments