Prechádzať zdrojové kódy

lot more test stuff

Adam Shaw 6 rokov pred
rodič
commit
75ab21558a
25 zmenil súbory, kde vykonal 906 pridanie a 845 odobranie
  1. 1 1
      packages-premium
  2. 96 101
      packages/__tests__/src/legacy/columnHeaderFormat.js
  3. 9 22
      packages/__tests__/src/legacy/constructor.js
  4. 137 175
      packages/__tests__/src/legacy/dateClick.js
  5. 5 4
      packages/__tests__/src/legacy/dir.js
  6. 13 9
      packages/__tests__/src/legacy/eventLimitText.js
  7. 50 40
      packages/__tests__/src/legacy/external-dnd-advanced.js
  8. 11 15
      packages/__tests__/src/legacy/fixedWeekCount.js
  9. 13 11
      packages/__tests__/src/legacy/footer-rendering.js
  10. 139 154
      packages/__tests__/src/legacy/navLinks.js
  11. 14 6
      packages/__tests__/src/legacy/removeEvents.js
  12. 9 6
      packages/__tests__/src/legacy/rerenderDelay.js
  13. 16 12
      packages/__tests__/src/legacy/scrollTime.js
  14. 188 157
      packages/__tests__/src/legacy/select-method.js
  15. 37 27
      packages/__tests__/src/legacy/themeSystem.js
  16. 58 59
      packages/__tests__/src/legacy/titleFormat.js
  17. 0 37
      packages/__tests__/src/legacy/viewRender.js
  18. 2 1
      packages/__tests__/src/lib/globals.js
  19. 11 1
      packages/__tests__/src/lib/wrappers/CalendarWrapper.ts
  20. 25 0
      packages/__tests__/src/lib/wrappers/DayGridWrapper.ts
  21. 43 1
      packages/__tests__/src/lib/wrappers/DayHeaderWrapper.ts
  22. 14 0
      packages/__tests__/src/lib/wrappers/ListViewWrapper.ts
  23. 11 2
      packages/__tests__/src/lib/wrappers/TimeGridWrapper.ts
  24. 1 1
      packages/__tests__/src/lib/wrappers/ToolbarWrapper.ts
  25. 3 3
      packages/__tests__/src/toolbar/customButtons.js

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit cf0b786c07a6f9e53750cfc662a0742452894e76
+Subproject commit fecf126f98e70a00aec127d0c2f46ed9ef58a507

+ 96 - 101
packages/__tests__/src/legacy/columnHeaderFormat.js

@@ -1,148 +1,141 @@
 import frLocale from '@fullcalendar/core/locales/fr'
 import enGbLocale from '@fullcalendar/core/locales/en-gb'
 import koLocale from '@fullcalendar/core/locales/ko'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
 
 describe('columnHeaderFormat', function() {
 
   describe('when not set', function() {
+    pushOptions({
+      defaultDate: '2014-05-11'
+    })
 
-    var viewWithFormat = [
-      { view: 'dayGridMonth', expected: /^Sun$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridWeek', expected: /^Sun 5[/ ]11$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridWeek', expected: /^Sun 5[/ ]11$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridDay', expected: /^Sunday$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridDay', expected: /^Sunday$/, selector: 'th.fc-day-header.fc-sun' }
+    const VIEWS_WITH_FORMAT = [
+      { view: 'dayGridMonth', expected: /^Sun$/ },
+      { view: 'dayGridWeek', expected: /^Sun 5[/ ]11$/ },
+      { view: 'timeGridWeek', expected: /^Sun 5[/ ]11$/ },
+      { view: 'dayGridDay', expected: /^Sunday$/ },
+      { view: 'timeGridDay', expected: /^Sunday$/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-05-11'
-      })
-    })
-
     it('should have default values', function() {
+      let calendar = initCalendar()
 
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(crtView.selector).text()).toMatch(crtView.expected)
-      };
+      for (let viewWithFormat of VIEWS_WITH_FORMAT) {
+        calendar.changeView(viewWithFormat.view)
+        let header = new (viewWithFormat.view.match(/^dayGrid/) ? DayGridViewWrapper : TimeGridViewWrapper)(calendar).header
+        expect(header.getCellText(0)).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when columnHeaderFormat is set on a per-view basis', function() {
+    pushOptions({
+      defaultDate: '2014-05-11',
+      views: {
+        month: { columnHeaderFormat: { weekday: 'long' } },
+        day: { columnHeaderFormat: { weekday: 'long', month: 'long', day: 'numeric' } },
+        dayGridWeek: { columnHeaderFormat: { weekday: 'long', month: 'numeric', day: 'numeric' } }
+      }
+    })
 
-    var viewWithFormat = [
-      { view: 'dayGridMonth', expected: /^Sunday$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridDay', expected: /^Sunday, May 11$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridWeek', expected: /^Sunday, 5[/ ]11$/, selector: 'th.fc-day-header.fc-sun' }
+    const VIEWS_WITH_FORMAT = [
+      { view: 'dayGridMonth', expected: /^Sunday$/ },
+      { view: 'timeGridDay', expected: /^Sunday, May 11$/ },
+      { view: 'dayGridWeek', expected: /^Sunday, 5[/ ]11$/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-05-11',
-        views: {
-          month: { columnHeaderFormat: { weekday: 'long' } },
-          day: { columnHeaderFormat: { weekday: 'long', month: 'long', day: 'numeric' } },
-          dayGridWeek: { columnHeaderFormat: { weekday: 'long', month: 'numeric', day: 'numeric' } }
-        }
-      })
-    })
-
     it('should have the correct values', function() {
+      let calendar = initCalendar()
 
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(crtView.selector).text()).toMatch(crtView.expected)
-      };
+      for (let viewWithFormat of VIEWS_WITH_FORMAT) {
+        calendar.changeView(viewWithFormat.view)
+        let header = new (viewWithFormat.view.match(/^dayGrid/) ? DayGridViewWrapper : TimeGridViewWrapper)(calendar).header
+        expect(header.getCellText(0)).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when locale is French', function() {
+    pushOptions({
+      defaultDate: '2014-05-11',
+      locale: frLocale
+    })
 
-    var viewWithFormat = [
-      { view: 'dayGridMonth', expected: /^dim\.$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridWeek', expected: /^dim\. 11[/ ]0?5$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridWeek', expected: /^dim\. 11[/ ]0?5$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridDay', expected: /^dimanche$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridDay', expected: /^dimanche$/, selector: 'th.fc-day-header.fc-sun' }
+    const VIEWS_WITH_FORMAT = [
+      { view: 'dayGridMonth', expected: /^dim\.$/ },
+      { view: 'dayGridWeek', expected: /^dim\. 11[/ ]0?5$/ },
+      { view: 'timeGridWeek', expected: /^dim\. 11[/ ]0?5$/ },
+      { view: 'dayGridDay', expected: /^dimanche$/ },
+      { view: 'timeGridDay', expected: /^dimanche$/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-05-11',
-        locale: frLocale
-      })
-    })
-
     it('should have the translated dates', function() {
+      let calendar = initCalendar()
 
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(crtView.selector).text()).toMatch(crtView.expected)
-      };
+      for (let viewWithFormat of VIEWS_WITH_FORMAT) {
+        calendar.changeView(viewWithFormat.view)
+        let header = new (viewWithFormat.view.match(/^dayGrid/) ? DayGridViewWrapper : TimeGridViewWrapper)(calendar).header
+        expect(header.getCellText(0)).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when locale is en-gb', function() {
+    pushOptions({
+      defaultDate: '2014-05-11',
+      locale: enGbLocale
+    })
 
-    var viewWithFormat = [
-      { view: 'dayGridMonth', expected: /^Sun$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridWeek', expected: /^Sun 11[/ ]0?5$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridWeek', expected: /^Sun 11[/ ]0?5$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridDay', expected: /^Sunday$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridDay', expected: /^Sunday$/, selector: 'th.fc-day-header.fc-sun' }
+    const VIEWS_WITH_FORMAT = [
+      { view: 'dayGridMonth', expected: /^Sun$/ },
+      { view: 'dayGridWeek', expected: /^Sun 11[/ ]0?5$/ },
+      { view: 'timeGridWeek', expected: /^Sun 11[/ ]0?5$/ },
+      { view: 'dayGridDay', expected: /^Sunday$/ },
+      { view: 'timeGridDay', expected: /^Sunday$/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-05-11',
-        locale: enGbLocale
-      })
-    })
-
     it('should have the translated dates', function() {
+      let calendar = initCalendar()
 
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(crtView.selector).text()).toMatch(crtView.expected)
-      };
+      for (let viewWithFormat of VIEWS_WITH_FORMAT) {
+        calendar.changeView(viewWithFormat.view)
+        let header = new (viewWithFormat.view.match(/^dayGrid/) ? DayGridViewWrapper : TimeGridViewWrapper)(calendar).header
+        expect(header.getCellText(0)).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when locale is Korean', function() {
+    pushOptions({
+      defaultDate: '2014-05-11',
+      locale: koLocale
+    })
 
-    var viewWithFormat = [
-      { view: 'dayGridMonth', expected: /^일$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridWeek', expected: /^5[.월] 11[.일] \(?일\)?$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridWeek', expected: /^5[.월] 11[.일] \(?일\)?$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'dayGridDay', expected: /^일요일$/, selector: 'th.fc-day-header.fc-sun' },
-      { view: 'timeGridDay', expected: /^일요일$/, selector: 'th.fc-day-header.fc-sun' }
+    const VIEWS_WITH_FORMAT = [
+      { view: 'dayGridMonth', expected: /^일$/ },
+      { view: 'dayGridWeek', expected: /^5[.월] 11[.일] \(?일\)?$/ },
+      { view: 'timeGridWeek', expected: /^5[.월] 11[.일] \(?일\)?$/ },
+      { view: 'dayGridDay', expected: /^일요일$/ },
+      { view: 'timeGridDay', expected: /^일요일$/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-05-11',
-        locale: koLocale
-      })
-    })
-
     it('should have the translated dates and columnHeaderFormat should be computed differently', function() {
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(crtView.selector).text()).toMatch(crtView.expected)
-      };
+      let calendar = initCalendar()
+
+      for (let viewWithFormat of VIEWS_WITH_FORMAT) {
+        calendar.changeView(viewWithFormat.view)
+        let header = new (viewWithFormat.view.match(/^dayGrid/) ? DayGridViewWrapper : TimeGridViewWrapper)(calendar).header
+        expect(header.getCellText(0)).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('using custom views', function() {
 
     it('multi-year default only displays day-of-week', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiYear: {
             type: 'dayGrid',
@@ -152,11 +145,12 @@ describe('columnHeaderFormat', function() {
         defaultView: 'multiYear',
         defaultDate: '2014-12-25'
       })
-      expect($('.fc-day-header:first')).toHaveText('Sun')
+      let header = new DayGridViewWrapper(calendar).header
+      expect(header.getCellText(0)).toBe('Sun')
     })
 
     it('multi-month default only displays day-of-week', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiMonth: {
             type: 'dayGrid',
@@ -166,11 +160,12 @@ describe('columnHeaderFormat', function() {
         defaultView: 'multiMonth',
         defaultDate: '2014-12-25'
       })
-      expect($('.fc-day-header:first')).toHaveText('Sun')
+      let header = new DayGridViewWrapper(calendar).header
+      expect(header.getCellText(0)).toBe('Sun')
     })
 
     it('multi-week default only displays day-of-week', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiWeek: {
             type: 'dayGrid',
@@ -180,11 +175,12 @@ describe('columnHeaderFormat', function() {
         defaultView: 'multiWeek',
         defaultDate: '2014-12-25'
       })
-      expect($('.fc-day-header:first')).toHaveText('Sun')
+      let header = new DayGridViewWrapper(calendar).header
+      expect(header.getCellText(0)).toBe('Sun')
     })
 
     it('multi-day default displays short full date', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiDay: {
             type: 'dayGrid',
@@ -194,9 +190,8 @@ describe('columnHeaderFormat', function() {
         defaultView: 'multiDay',
         defaultDate: '2014-12-25'
       })
-      expect(
-        $('.fc-day-header:first').text()
-      ).toMatch(/^Thu 12[/ ]25$/)
+      let header = new DayGridViewWrapper(calendar).header
+      expect(header.getCellText('2014-12-25')).toMatch(/^Thu 12[/ ]25$/)
     })
   })
 })

+ 9 - 22
packages/__tests__/src/legacy/constructor.js

@@ -1,3 +1,4 @@
+import CalendarWrapper from "../lib/wrappers/CalendarWrapper"
 
 describe('constructor', function() {
 
@@ -47,31 +48,17 @@ describe('constructor', function() {
   })
 
   describe('when called on a div', function() {
-    beforeEach(function() {
-      initCalendar()
-    })
-
-    it('should contain a table fc-toolbar', function() {
-      var header = $(currentCalendar.el).find('.fc-toolbar')
-      expect(header[0]).not.toBeUndefined()
-    })
-
-    it('should contain a div fc-view-container', function() {
-      var content = $(currentCalendar.el).find('.fc-view-container')
-      expect(content[0]).not.toBeUndefined()
-    })
 
-    it('should only contain 2 elements', function() {
-      var calenderNodeCount = $(currentCalendar.el).find('>').length
-      expect(calenderNodeCount).toEqual(2)
+    it('should contain a toolbar', function() {
+      let calendar = initCalendar()
+      let calendarWrapper = new CalendarWrapper(calendar)
+      expect(calendarWrapper.toolbar).toBeTruthy()
     })
 
-    describe('and then called again', function() {
-      it('should still only have a single set of calendar [header,content]', function() {
-        initCalendar()
-        var count = $(currentCalendar.el).find('>').length
-        expect(count).toEqual(2)
-      })
+    it('should contain a view-container el', function() {
+      let calendar = initCalendar()
+      let calendarWrapper = new CalendarWrapper(calendar)
+      expect(calendarWrapper.getViewContainerEl()).toBeTruthy()
     })
   })
 })

+ 137 - 175
packages/__tests__/src/legacy/dateClick.js

@@ -1,157 +1,128 @@
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
+
 describe('dateClick', function() {
   pushOptions({
     defaultDate: '2014-05-27',
     selectable: false,
     timeZone: 'UTC'
-  });
-
-  [ 'ltr', 'rtl' ].forEach(function(dir) {
-    describe('when dir is ' + dir, function() {
+  })
 
-      pushOptions({ dir });
+  describeOptions('dir', {
+    'when LTR': 'ltr',
+    'when RTL': 'rtl'
+  }, function() {
 
-      [ false, true ].forEach(function(selectable) {
-        describe('when selectable is ' + selectable, function() {
-          pushOptions({
-            selectable
-          })
+    describeOptions('selectable', {
+      'when NOT selectable': false,
+      'when selectable': true
+    }, function() {
 
-          describe('when in month view', function() {
-            pushOptions({defaultView: 'dayGridMonth'})
+      describe('when in month view', function() {
+        pushOptions({
+          defaultView: 'dayGridMonth'
+        })
 
-            it('fires correctly when clicking on a cell', function(done) {
-              var options = {}
-              options.dateClick = function(arg) {
-                expect(arg.date instanceof Date).toEqual(true)
-                expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-                expect(typeof arg.view).toEqual('object') // "
-                expect(arg.allDay).toEqual(true)
-                expect(arg.date).toEqualDate('2014-05-07')
-                expect(arg.dateStr).toEqual('2014-05-07')
-                done()
-              }
+        it('fires correctly when clicking on a cell', function(done) {
+          let calendar = initCalendar({
+            dateClick(arg) {
+              expect(arg.date instanceof Date).toEqual(true)
+              expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+              expect(typeof arg.view).toEqual('object') // "
+              expect(arg.allDay).toEqual(true)
+              expect(arg.date).toEqualDate('2014-05-07')
+              expect(arg.dateStr).toEqual('2014-05-07')
+              done()
+            }
+          })
+          let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+          dayGridWrapper.clickDate('2014-05-07')
+        })
+      })
 
-              initCalendar(options)
+      describe('when in week view', function() {
+        pushOptions({
+          defaultView: 'timeGridWeek'
+        })
 
-              var dayCell = $('.fc-day:eq(10)') // 2014-05-07 (regardless of dir)
+        it('fires correctly when clicking on an all-day slot', function(done) {
+          let calendar = initCalendar({
+            dateClick(arg) {
+              expect(arg.date instanceof Date).toEqual(true)
+              expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+              expect(typeof arg.view).toEqual('object') // "
+              expect(arg.allDay).toEqual(true)
+              expect(arg.date).toEqualDate('2014-05-28')
+              expect(arg.dateStr).toEqual('2014-05-28')
+              done()
+            }
+          })
+          let dayGridWrapper = new TimeGridViewWrapper(calendar).dayGrid
+          dayGridWrapper.clickDate('2014-05-28')
+        })
 
-              // for simulating the mousedown/mouseup/click (relevant for selectable)
-              dayCell.simulate('drag')
-            })
+        it('fires correctly when clicking on a timed slot', function(done) {
+          let calendar = initCalendar({
+            contentHeight: 500, // make sure the click slot will be in scroll view
+            scrollTime: '07:00:00',
+            dateClick(arg) {
+              expect(arg.date instanceof Date).toEqual(true)
+              expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+              expect(typeof arg.view).toEqual('object') // "
+              expect(arg.allDay).toEqual(false)
+              expect(arg.date).toEqualDate('2014-05-28T09:00:00Z')
+              expect(arg.dateStr).toEqual('2014-05-28T09:00:00Z')
+              done()
+            }
           })
+          let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+          timeGridWrapper.clickDate('2014-05-28T09:00:00')
+        })
 
-          describe('when in week view', function() {
-            pushOptions({
-              defaultView: 'timeGridWeek'
-            })
-
-            it('fires correctly when clicking on an all-day slot', function(done) {
-              var options = {}
-              options.dateClick = function(arg) {
-                expect(arg.date instanceof Date).toEqual(true)
-                expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-                expect(typeof arg.view).toEqual('object') // "
-                expect(arg.allDay).toEqual(true)
-                expect(arg.date).toEqualDate('2014-05-28')
-                expect(arg.dateStr).toEqual('2014-05-28')
-                done()
-              }
-
-              initCalendar(options)
-
-              // 2014-05-28 (regardless of dir)
-              var dayContent = $('.fc-timeGrid-view .fc-day-grid .fc-day:eq(3)')
-
-              // for simulating the mousedown/mouseup/click (relevant for selectable)
-              dayContent.simulate('drag')
-            })
-
-            it('fires correctly when clicking on a timed slot', function(done) {
-              var options = {}
-              // make sure the click slot will be in scroll view
-              options.contentHeight = 500
-              options.scrollTime = '07:00:00'
-
-              options.dateClick = function(arg) {
-                expect(arg.date instanceof Date).toEqual(true)
-                expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-                expect(typeof arg.view).toEqual('object') // "
-                expect(arg.allDay).toEqual(false)
-                expect(arg.date).toEqualDate('2014-05-28T09:00:00Z')
-                expect(arg.dateStr).toEqual('2014-05-28T09:00:00Z')
-                done()
-              }
-
-              initCalendar(options)
-
-              // the middle is 2014-05-28T09:00:00 (regardless of dir)
-              var slotRow = $('.fc-slats tr:eq(18) td:not(.fc-time)')
-
-              // for simulating the mousedown/mouseup/click (relevant for selectable)
-              slotRow.simulate('drag')
-            })
-
-            // issue 2217
-            it('fires correctly when clicking on a timed slot, with minTime set', function(done) {
-              var options = {}
-              // make sure the click slot will be in scroll view
-              options.contentHeight = 500
-              options.scrollTime = '07:00:00'
-              options.minTime = '02:00:00'
-
-              options.dateClick = function(arg) {
-                expect(arg.date instanceof Date).toEqual(true)
-                expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-                expect(typeof arg.view).toEqual('object') // "
-                expect(arg.allDay).toEqual(false)
-                expect(arg.date).toEqualDate('2014-05-28T11:00:00Z')
-                expect(arg.dateStr).toEqual('2014-05-28T11:00:00Z')
-                done()
-              }
-
-              initCalendar(options)
-
-              // the middle is 2014-05-28T11:00:00 (regardless of dir)
-              var slotRow = $('.fc-slats tr:eq(18) td:not(.fc-time)')
-
-              // for simulating the mousedown/mouseup/click (relevant for selectable)
-              slotRow.simulate('drag')
-            })
-
-            // https://github.com/fullcalendar/fullcalendar/issues/4539
-            it('fires correctly when clicking on a timed slot NEAR END', function(done) {
-              var options = {}
-              // make sure the click slot will be in scroll view
-              options.contentHeight = 500
-              options.scrollTime = '23:00:00'
-
-              options.dateClick = function(arg) {
-                expect(arg.date instanceof Date).toEqual(true)
-                expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-                expect(typeof arg.view).toEqual('object') // "
-                expect(arg.allDay).toEqual(false)
-                expect(arg.date).toEqualDate('2014-05-28T23:30:00Z')
-                expect(arg.dateStr).toEqual('2014-05-28T23:30:00Z')
-                done()
-              }
-
-              initCalendar(options)
-
-              // the LAST row is 23:30
-              var slotRow = $('.fc-slats tr:eq(47) td:not(.fc-time)')
-
-              // for simulating the mousedown/mouseup/click (relevant for selectable)
-              slotRow.simulate('drag')
-            })
+        // issue 2217
+        it('fires correctly when clicking on a timed slot, with minTime set', function(done) {
+          let calendar = initCalendar({
+            contentHeight: 500, // make sure the click slot will be in scroll view
+            scrollTime: '07:00:00',
+            minTime: '02:00:00',
+            dateClick(arg) {
+              expect(arg.date instanceof Date).toEqual(true)
+              expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+              expect(typeof arg.view).toEqual('object') // "
+              expect(arg.allDay).toEqual(false)
+              expect(arg.date).toEqualDate('2014-05-28T11:00:00Z')
+              expect(arg.dateStr).toEqual('2014-05-28T11:00:00Z')
+              done()
+            }
+          })
+          let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+          timeGridWrapper.clickDate('2014-05-28T11:00:00')
+        })
 
+        // https://github.com/fullcalendar/fullcalendar/issues/4539
+        it('fires correctly when clicking on a timed slot NEAR END', function(done) {
+          let calendar = initCalendar({
+            contentHeight: 500, // make sure the click slot will be in scroll view
+            scrollTime: '23:00:00',
+            dateClick(arg) {
+              expect(arg.date instanceof Date).toEqual(true)
+              expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+              expect(typeof arg.view).toEqual('object') // "
+              expect(arg.allDay).toEqual(false)
+              expect(arg.date).toEqualDate('2014-05-28T23:30:00Z')
+              expect(arg.dateStr).toEqual('2014-05-28T23:30:00Z')
+              done()
+            }
           })
+          let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+          timeGridWrapper.clickDate('2014-05-28T23:30:00')
         })
       })
     })
   })
 
   it('will still fire if clicked on background event', function(done) {
-    initCalendar({
+    let calendar = initCalendar({
       defaultView: 'dayGridMonth',
       events: [ {
         start: '2014-05-06',
@@ -162,72 +133,63 @@ describe('dateClick', function() {
         done()
       }
     })
-
-    // for simulating the mousedown/mouseup/click (relevant for selectable)
-    $('.fc-bgevent').simulate('drag')
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    $.simulateMouseClick(dayGridWrapper.getBgEventEls()[0])
   })
 
   describe('when touch', function() {
 
     it('fires correctly when simulated short drag on a cell', function(done) {
-      var options = {}
-      options.dateClick = function(arg) {
-        expect(arg.date instanceof Date).toEqual(true)
-        expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-        expect(typeof arg.view).toEqual('object') // "
-        expect(arg.allDay).toEqual(true)
-        expect(arg.date).toEqualDate('2014-05-07')
-        expect(arg.dateStr).toEqual('2014-05-07')
-        done()
-      }
-
-      initCalendar(options)
-
-      var dayCell = $('.fc-day:eq(10)') // 2014-05-07 (regardless of dir)
-
-      // for simulating the mousedown/mouseup/click (relevant for selectable)
-      dayCell.simulate('drag', {
-        isTouch: true
+      let calendar = initCalendar({
+        dateClick(arg) {
+          expect(arg.date instanceof Date).toEqual(true)
+          expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+          expect(typeof arg.view).toEqual('object') // "
+          expect(arg.allDay).toEqual(true)
+          expect(arg.date).toEqualDate('2014-05-07')
+          expect(arg.dateStr).toEqual('2014-05-07')
+          done()
+        }
       })
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+      $.simulateTouchClick(dayGridWrapper.getDayEl('2014-05-07'))
     })
 
     it('won\'t fire if touch moves outside of date cell', function(done) {
-      var options = {}
-      options.dateClick = function(arg) {}
-      spyOn(options, 'dateClick').and.callThrough()
+      let dateClickSpy = spyOnCalendarCallback('dateClick')
+      let calendar = initCalendar()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
 
-      initCalendar(options)
+      let startCell = dayGridWrapper.getDayEl('2014-05-07')
+      let endCell = dayGridWrapper.getDayEl('2014-05-08')
 
-      var startCell = $('.fc-day[data-date="2014-05-07"]')
-      var endCell = $('.fc-day[data-date="2014-05-08"]')
-
-      startCell.simulate('drag', {
+      $(startCell).simulate('drag', {
         // FYI, when debug:true, not a good representation because the minimal  delay is required
         // to recreate bug #3332
         isTouch: true,
         end: endCell,
         callback: function() {
-          expect(options.dateClick).not.toHaveBeenCalled()
+          expect(dateClickSpy).not.toHaveBeenCalled()
           done()
         }
       })
     })
 
     it('fires correctly when simulated click on a cell', function(done) {
-      var options = {}
-      options.dateClick = function(arg) {
-        expect(arg.date instanceof Date).toEqual(true)
-        expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
-        expect(typeof arg.view).toEqual('object') // "
-        expect(arg.allDay).toEqual(true)
-        expect(arg.date).toEqualDate('2014-05-07')
-        expect(arg.dateStr).toEqual('2014-05-07')
-        done()
-      }
-
-      initCalendar(options)
+      let calendar = initCalendar({
+        dateClick(arg) {
+          expect(arg.date instanceof Date).toEqual(true)
+          expect(typeof arg.jsEvent).toEqual('object') // TODO: more descrimination
+          expect(typeof arg.view).toEqual('object') // "
+          expect(arg.allDay).toEqual(true)
+          expect(arg.date).toEqualDate('2014-05-07')
+          expect(arg.dateStr).toEqual('2014-05-07')
+          done()
+        }
+      })
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
 
-      var dayCell = $('.fc-day:eq(10)') // 2014-05-07 (regardless of dir)
+      var dayCell = dayGridWrapper.getDayEl('2014-05-07')
       $.simulateTouchClick(dayCell)
     })
   })

+ 5 - 4
packages/__tests__/src/legacy/dir.js

@@ -1,4 +1,5 @@
 import arLocale from '@fullcalendar/core/locales/ar'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 describe('dir', function() {
 
@@ -18,13 +19,13 @@ describe('dir', function() {
     })
     var $el = $(currentCalendar.el)
 
-    expect($el).toHaveClass('fc-ltr')
-    expect($el).not.toHaveClass('fc-rtl')
+    expect($el).toHaveClass(CalendarWrapper.LTR_CLASSNAME)
+    expect($el).not.toHaveClass(CalendarWrapper.RTL_CLASSNAME)
 
     currentCalendar.setOption('dir', 'rtl')
 
-    expect($el).toHaveClass('fc-rtl')
-    expect($el).not.toHaveClass('fc-ltr')
+    expect($el).toHaveClass(CalendarWrapper.RTL_CLASSNAME)
+    expect($el).not.toHaveClass(CalendarWrapper.LTR_CLASSNAME)
   })
 
 })

+ 13 - 9
packages/__tests__/src/legacy/eventLimitText.js

@@ -1,7 +1,7 @@
 import frLocale from '@fullcalendar/core/locales/fr'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
 
 describe('eventLimitText', function() {
-
   pushOptions({
     defaultDate: '2014-08-01', // important that it is the first week, so works w/ month + week views
     defaultView: 'dayGridMonth',
@@ -15,34 +15,38 @@ describe('eventLimitText', function() {
   })
 
   it('allows a string', function() {
-    initCalendar({
+    let calendar = initCalendar({
       eventLimitText: 'extra'
     })
-    expect($('.fc-more')).toHaveText('+2 extra')
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getMoreEl()).toHaveText('+2 extra')
   })
 
   it('allows a function', function() {
-    initCalendar({
+    let calendar = initCalendar({
       eventLimitText: function(n) {
         expect(typeof n).toBe('number')
         return 'there are ' + n + ' more events!'
       }
     })
-    expect($('.fc-more')).toHaveText('there are 2 more events!')
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getMoreEl()).toHaveText('there are 2 more events!')
   })
 
   it('has a default value that is affected by the custom locale', function() {
-    initCalendar({
+    let calendar = initCalendar({
       locale: frLocale
     })
-    expect($('.fc-more')).toHaveText('+2 en plus')
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getMoreEl()).toHaveText('+2 en plus')
   })
 
   it('is not affected by a custom locale when the value is explicitly specified', function() {
-    initCalendar({
+    let calendar = initCalendar({
       locale: frLocale,
       eventLimitText: 'extra'
     })
-    expect($('.fc-more')).toHaveText('+2 extra')
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getMoreEl()).toHaveText('+2 extra')
   })
 })

+ 50 - 40
packages/__tests__/src/legacy/external-dnd-advanced.js

@@ -1,6 +1,7 @@
 import { testEventDrag } from '../lib/dnd-resize-utils'
 import { ThirdPartyDraggable } from '@fullcalendar/interaction'
 import 'components-jqueryui' // for .sortable and .draggable
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 // TODO: Use the built-in Draggable for some of these tests
 
@@ -36,14 +37,19 @@ describe('advanced external dnd', function() {
   })
 
   describe('in timeGrid slots', function() {
-    pushOptions({defaultView: 'timeGridWeek'})
+    pushOptions({
+      defaultView: 'timeGridWeek'
+    })
+
     describe('when no element event data', function() {
+
       describe('when given duration through defaultTimedEventDuration', function() {
         pushOptions({
           defaultTimedEventDuration: '2:30'
         })
         defineTests()
       })
+
       describe('when given duration through data attribute', function() {
         beforeEach(function() {
           dragEl.attr('data-event', JSON.stringify({
@@ -55,10 +61,11 @@ describe('advanced external dnd', function() {
       })
 
       function defineTests() {
+
         it('fires correctly', function(done) {
-          var options = {}
-          testExternalElDrag(options, '2014-11-13T03:00:00Z', '2014-11-13T03:00:00Z', true, done)
+          testExternalElDrag({}, '2014-11-13T03:00:00Z', '2014-11-13T03:00:00Z', true, done)
         })
+
         it('is not affected by eventOverlap:false', function(done) {
           var options = {}
           options.eventOverlap = false
@@ -68,6 +75,7 @@ describe('advanced external dnd', function() {
           } ]
           testExternalElDrag(options, '2014-11-13T03:00:00Z', '2014-11-13T03:00:00Z', true, done)
         })
+
         it('is not affected by an event object\'s overlap:false', function(done) {
           var options = {}
           options.events = [ {
@@ -77,15 +85,16 @@ describe('advanced external dnd', function() {
           } ]
           testExternalElDrag(options, '2014-11-13T03:00:00Z', '2014-11-13T03:00:00Z', true, done)
         })
+
         it('is not affected by eventConstraint', function(done) {
           var options = {}
-
           options.eventConstraint = {
             start: '03:00',
             end: '10:00'
           }
           testExternalElDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, done)
         })
+
         describe('with selectOverlap:false', function() {
           pushOptions({
             selectOverlap: false,
@@ -94,11 +103,12 @@ describe('advanced external dnd', function() {
               end: '2014-11-13T08:00:00'
             }]
           })
+
           it('is not allowed to overlap an event', function(done) {
-            var options = {}
-            testExternalElDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', false, done)
+            testExternalElDrag({}, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', false, done)
           })
         })
+
         describe('with a selectConstraint', function() {
           pushOptions({
             selectConstraint: {
@@ -106,15 +116,13 @@ describe('advanced external dnd', function() {
               endTime: '08:00'
             }
           })
-          it('can be dropped within', function(done) {
-            var options = {}
 
-            testExternalElDrag(options, '2014-11-13T05:30:00Z', '2014-11-13T05:30:00Z', true, done)
+          it('can be dropped within', function(done) {
+            testExternalElDrag({}, '2014-11-13T05:30:00Z', '2014-11-13T05:30:00Z', true, done)
           })
-          it('cannot be dropped when not fully contained', function(done) {
-            var options = {}
 
-            testExternalElDrag(options, '2014-11-13T06:00:00Z', '2014-11-13T06:00:00Z', false, done)
+          it('cannot be dropped when not fully contained', function(done) {
+            testExternalElDrag({}, '2014-11-13T06:00:00Z', '2014-11-13T06:00:00Z', false, done)
           })
         })
       }
@@ -123,12 +131,10 @@ describe('advanced external dnd', function() {
     describe('when event data is given', function() {
 
       it('fires correctly', function(done) {
-        var options = {}
-
         dragEl.attr('data-event', JSON.stringify({
           title: 'hey'
         }))
-        testExternalEventDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, done)
+        testExternalEventDrag({}, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, done)
       })
 
       describe('when given a start time', function() {
@@ -140,8 +146,7 @@ describe('advanced external dnd', function() {
           })
 
           it('voids the given time when dropped on a timed slot', function(done) {
-            var options = {}
-            testExternalEventDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, done)
+            testExternalEventDrag({}, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, done)
             // will test the resulting event object's start
           })
         })
@@ -156,9 +161,7 @@ describe('advanced external dnd', function() {
           })
 
           it('accepts the given duration when dropped on a timed slot', function(done) {
-            var options = {}
-
-            testExternalEventDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, function() {
+            testExternalEventDrag({}, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, function() {
               var event = currentCalendar.getEvents()[0]
               expect(event.start).toEqualDate('2014-11-13T02:00:00Z')
               expect(event.end).toEqualDate('2014-11-13T07:00:00Z')
@@ -177,15 +180,14 @@ describe('advanced external dnd', function() {
           })
 
           it('keeps the event when navigating away and back', function(done) {
-            var options = {}
-
-            testExternalEventDrag(options, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, function() {
+            testExternalEventDrag({}, '2014-11-13T02:00:00Z', '2014-11-13T02:00:00Z', true, function() {
               setTimeout(function() { // make sure to escape handlers
-                expect($('.fc-event').length).toBe(1)
+                let calendarWrapper = new CalendarWrapper(currentCalendar)
+                expect(calendarWrapper.getEventEls().length).toBe(1)
                 currentCalendar.next()
-                expect($('.fc-event').length).toBe(0)
+                expect(calendarWrapper.getEventEls().length).toBe(0)
                 currentCalendar.prev()
-                expect($('.fc-event').length).toBe(1)
+                expect(calendarWrapper.getEventEls().length).toBe(1)
                 done()
               }, 0)
             })
@@ -194,6 +196,7 @@ describe('advanced external dnd', function() {
       })
 
       describe('when an overlap is specified', function() {
+
         describe('via eventOverlap', function() {
           pushOptions({
             eventOverlap: false,
@@ -202,11 +205,14 @@ describe('advanced external dnd', function() {
               end: '2014-11-13T08:00:00'
             } ]
           })
+
           beforeEach(function() {
             dragEl.attr('data-event', '{}')
           })
+
           defineTests()
         })
+
         describe('via an overlap on this event', function() {
           pushOptions({
             events: [{
@@ -214,13 +220,16 @@ describe('advanced external dnd', function() {
               end: '2014-11-13T08:00:00'
             }]
           })
+
           beforeEach(function() {
             dragEl.attr('data-event', JSON.stringify({
               overlap: false
             }))
           })
+
           defineTests()
         })
+
         describe('via an overlap on the other event', function() {
           pushOptions({
             events: [{
@@ -229,24 +238,26 @@ describe('advanced external dnd', function() {
               overlap: false
             }]
           })
+
           beforeEach(function() {
             dragEl.attr('data-event', '{}')
           })
+
           defineTests()
         })
+
         function defineTests() {
           it('allows a drop when not colliding with the other event', function(done) {
-            var options = {}
-            testExternalEventDrag(options, '2014-11-13T08:00:00Z', '2014-11-13T08:00:00Z', true, done)
+            testExternalEventDrag({}, '2014-11-13T08:00:00Z', '2014-11-13T08:00:00Z', true, done)
           })
           it('prevents a drop when colliding with the other event', function(done) {
-            var options = {}
-            testExternalEventDrag(options, '2014-11-13T06:00:00Z', '2014-11-13T06:00:00Z', false, done)
+            testExternalEventDrag({}, '2014-11-13T06:00:00Z', '2014-11-13T06:00:00Z', false, done)
           })
         }
       })
 
       describe('when a constraint is specified', function() {
+
         describe('via eventConstraint', function() {
           pushOptions({
             eventConstraint: {
@@ -254,13 +265,16 @@ describe('advanced external dnd', function() {
               endTime: '08:00'
             }
           })
+
           beforeEach(function() {
             dragEl.attr('data-event', JSON.stringify({
               duration: '02:00'
             }))
           })
+
           defineTests()
         })
+
         describe('via the event object\'s constraint property', function() {
           beforeEach(function() {
             dragEl.attr('data-event', JSON.stringify({
@@ -271,16 +285,16 @@ describe('advanced external dnd', function() {
               }
             }))
           })
+
           defineTests()
         })
+
         function defineTests() {
           it('allows a drop when inside the constraint', function(done) {
-            var options = {}
-            testExternalEventDrag(options, '2014-11-13T05:00:00Z', '2014-11-13T05:00:00Z', true, done)
+            testExternalEventDrag({}, '2014-11-13T05:00:00Z', '2014-11-13T05:00:00Z', true, done)
           })
           it('disallows a drop when partially outside of the constraint', function(done) {
-            var options = {}
-            testExternalEventDrag(options, '2014-11-13T07:00:00Z', '2014-11-13T07:00:00Z', false, done)
+            testExternalEventDrag({}, '2014-11-13T07:00:00Z', '2014-11-13T07:00:00Z', false, done)
           })
         }
       })
@@ -297,12 +311,10 @@ describe('advanced external dnd', function() {
     describe('when event data is given', function() {
 
       it('fires correctly', function(done) {
-        var options = {}
-
         dragEl.attr('data-event', JSON.stringify({
           title: 'hey'
         }))
-        testExternalEventDrag(options, '2014-11-13', '2014-11-13', true, done)
+        testExternalEventDrag({}, '2014-11-13', '2014-11-13', true, done)
       })
 
       describe('when given a start time', function() {
@@ -315,9 +327,7 @@ describe('advanced external dnd', function() {
           })
 
           it('accepts the given start time for the dropped day', function(done) {
-            var options = {}
-
-            testExternalEventDrag(options, '2014-11-13', '2014-11-13T05:00:00Z', true, function() {
+            testExternalEventDrag({}, '2014-11-13', '2014-11-13T05:00:00Z', true, function() {
               // the whole-day start was already checked. we still need to check the exact time
               var event = currentCalendar.getEvents()[0]
               expect(event.start).toEqualDate('2014-11-13T05:00:00Z')

+ 11 - 15
packages/__tests__/src/legacy/fixedWeekCount.js

@@ -1,3 +1,5 @@
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
+
 describe('fixedWeekCount', function() {
 
   pushOptions({
@@ -6,47 +8,41 @@ describe('fixedWeekCount', function() {
   })
 
   describe('when true', function() {
-
     pushOptions({
       fixedWeekCount: true
     })
 
     it('renders a 5-week month with 6 rows', function() {
-      initCalendar()
-      var weeks = $('.fc-week')
-      expect(weeks.length).toBe(6)
+      let calendar = initCalendar()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+      expect(dayGridWrapper.getRowEls().length).toBe(6)
     })
-
   })
 
   describe('when false', function() {
-
     pushOptions({
       fixedWeekCount: false
     })
 
     it('renders a 5-week month with 5 rows', function() {
-      initCalendar()
-      var weeks = $('.fc-week')
-      expect(weeks.length).toBe(5)
+      let calendar = initCalendar()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+      expect(dayGridWrapper.getRowEls().length).toBe(5)
     })
-
   });
 
   [ true, false ].forEach(function(bool) {
     describe('regardless of value (' + bool + ')', function() {
-
       pushOptions({
         fixedWeekCount: bool,
         defaultDate: '2014-08-01' // has 6 weeks
       })
 
       it('should render a 6-week month consistently', function() {
-        initCalendar()
-        var weeks = $('.fc-week')
-        expect(weeks.length).toBe(6)
+        let calendar = initCalendar()
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        expect(dayGridWrapper.getRowEls().length).toBe(6)
       })
-
     })
   })
 

+ 13 - 11
packages/__tests__/src/legacy/footer-rendering.js

@@ -1,44 +1,46 @@
-describe('footer rendering', function() {
+import CalendarWrapper from "../lib/wrappers/CalendarWrapper"
 
+describe('footer rendering', function() {
   pushOptions({
     defaultDate: '2014-06-04',
     defaultView: 'timeGridWeek'
   })
 
   describe('when supplying footer options', function() {
-    it('should append a .fc-footer-toolbar to the DOM', function() {
-      initCalendar({
+    it('should append a footer element to the DOM', function() {
+      let calendar = initCalendar({
         footer: {
           left: 'next,prev',
           center: 'prevYear today nextYear timeGridDay,timeGridWeek',
           right: 'title'
         }
       })
-      var footer = $('#calendar .fc-footer-toolbar')
-      expect(footer.length).toBe(1)
+      let calendarWrapper = new CalendarWrapper(calendar)
+      expect(calendarWrapper.footer).toBeTruthy()
     })
   })
 
   describe('when setting footer to false', function() {
     it('should not have footer table', function() {
-      initCalendar({
+      let calendar = initCalendar({
         footer: false
       })
-      expect($('.fc-footer-toolbar')).not.toBeInDOM()
+      let calendarWrapper = new CalendarWrapper(calendar)
+      expect(calendarWrapper.footer).toBeFalsy()
     })
   })
 
   it('allow for dynamically changing', function() {
-    initCalendar({
+    let calendar = initCalendar({
       footer: {
         left: 'next,prev',
         center: 'prevYear today nextYear timeGridDay,timeGridWeek',
         right: 'title'
       }
     })
-    expect($('.fc-footer-toolbar')).toBeInDOM()
+    let calendarWrapper = new CalendarWrapper(calendar)
+    expect(calendarWrapper.footer).toBeTruthy()
     currentCalendar.setOption('footer', false)
-    expect($('.fc-footer-toolbar')).not.toBeInDOM()
+    expect(calendarWrapper.footer).toBeFalsy()
   })
-
 })

+ 139 - 154
packages/__tests__/src/legacy/navLinks.js

@@ -1,129 +1,161 @@
-import { formatIsoDay } from '../lib/datelib-utils'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
+import ListViewWrapper from '../lib/wrappers/ListViewWrapper'
+import { addDays } from '@fullcalendar/core'
 
 describe('navLinks', function() {
-  var options
-
-  beforeEach(function() {
-    options = {
-      now: '2016-08-20',
-      navLinks: true,
-      header: {
-        left: 'prev,next today',
-        center: 'title',
-        right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' // affects which view is jumped to by default
-      },
-      dateClick: function() { }
+  pushOptions({
+    now: '2016-08-20',
+    navLinks: true,
+    header: {
+      left: 'prev,next today',
+      center: 'title',
+      right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' // affects which view is jumped to by default
     }
-    spyOn(options, 'dateClick')
   })
 
   describeTimeZones(function(tz) {
 
     describe('in month view', function() {
-      beforeEach(function() {
-        options.defaultView = 'dayGridMonth'
+      pushOptions({
+        defaultView: 'dayGridMonth'
       })
 
       it('moves to day', function() {
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expectDayView('timeGridDay', '2016-08-09')
-        expect(options.dateClick).not.toHaveBeenCalled()
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let calendar = initCalendar()
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expectDayView(calendar, 'timeGridDay', tz.parseDate('2016-08-09'))
+        expect(dateClickSpy).not.toHaveBeenCalled()
       })
 
       // https://github.com/fullcalendar/fullcalendar/issues/4619
       it('moves to day when no toolbars', function() {
-        options.header = null
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expectDayView('dayGridDay', '2016-08-09') // is hash-key order-dependent I think :(
-        expect(options.dateClick).not.toHaveBeenCalled()
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let calendar = initCalendar({
+          header: null
+        })
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expectDayView(calendar, 'dayGridDay', tz.parseDate('2016-08-09')) // is hash-key order-dependent I think :(
+        expect(dateClickSpy).not.toHaveBeenCalled()
       })
 
       // https://github.com/fullcalendar/fullcalendar/issues/3869
       it('moves to two different days', function() {
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expectDayView('timeGridDay', '2016-08-09')
-        expect(options.dateClick).not.toHaveBeenCalled()
-        currentCalendar.changeView('dayGridMonth')
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-10'))
-        expectDayView('timeGridDay', '2016-08-10')
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let calendar = initCalendar()
+
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expectDayView(calendar, 'timeGridDay', tz.parseDate('2016-08-09'))
+        expect(dateClickSpy).not.toHaveBeenCalled()
+
+        calendar.changeView('dayGridMonth')
+        let dayGridWrapper2 = new DayGridViewWrapper(calendar).dayGrid
+        dayGridWrapper2.clickNavLink('2016-08-10')
+        expectDayView(calendar, 'timeGridDay', tz.parseDate('2016-08-10'))
       })
 
       it('moves to day specifically', function() {
-        options.navLinkDayClick = 'day'
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expectDayView('timeGridDay', '2016-08-09')
-        expect(options.dateClick).not.toHaveBeenCalled()
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let calendar = initCalendar({
+          navLinkDayClick: 'day'
+        })
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expectDayView(calendar, 'timeGridDay', tz.parseDate('2016-08-09'))
+        expect(dateClickSpy).not.toHaveBeenCalled()
       })
 
       it('moves to dayGridDay specifically', function() {
-        options.navLinkDayClick = 'dayGridDay'
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expectDayView('dayGridDay', '2016-08-09')
-        expect(options.dateClick).not.toHaveBeenCalled()
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let calendar = initCalendar({
+          navLinkDayClick: 'dayGridDay'
+        })
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expectDayView(calendar, 'dayGridDay', tz.parseDate('2016-08-09'))
+        expect(dateClickSpy).not.toHaveBeenCalled()
       })
 
       it('executes a custom handler', function() {
-        options.navLinkDayClick = function(date, ev) {
+        let dateClickSpy = spyOnCalendarCallback('dateClick')
+        let navLinkDayClickSpy = spyOnCalendarCallback('navLinkDayClick', (date, ev) => {
           expect(date).toEqualDate(tz.parseDate('2016-08-09'))
           expect(typeof ev).toBe('object')
-        }
-        spyOn(options, 'navLinkDayClick').and.callThrough()
-        initCalendar(options)
-        $.simulateMouseClick(getDayGridNumberEl('2016-08-09'))
-        expect(options.navLinkDayClick).toHaveBeenCalled()
-        expect(options.dateClick).not.toHaveBeenCalled()
+        })
+        let calendar = initCalendar()
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+        dayGridWrapper.clickNavLink('2016-08-09')
+        expect(dateClickSpy).not.toHaveBeenCalled()
+        expect(navLinkDayClickSpy).toHaveBeenCalled()
       })
 
       describe('with weekNumbers', function() {
-        beforeEach(function() {
-          options.weekNumbers = true
+        pushOptions({
+          weekNumbers: true
         })
 
         it('moves to week', function() {
-          initCalendar(options)
-          $.simulateMouseClick(getDayGridClassicWeekLinks().eq(1))
-          expectWeekView('timeGridWeek', '2016-08-07')
-          expect(options.dateClick).not.toHaveBeenCalled()
+          let dateClickSpy = spyOnCalendarCallback('dateClick')
+          let calendar = initCalendar()
+          let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+          $.simulateMouseClick(dayGridWrapper.getWeekNavLinkEls(false)[1])
+          expectWeekView(calendar, 'timeGridWeek', tz.parseDate('2016-08-07'))
+          expect(dateClickSpy).not.toHaveBeenCalled()
         })
 
         it('moves to week with within-day rendering', function() {
-          options.weekNumbersWithinDays = true
-          initCalendar(options)
-          $.simulateMouseClick(getDayGridEmbeddedWeekLinks().eq(1))
-          expectWeekView('timeGridWeek', '2016-08-07')
-          expect(options.dateClick).not.toHaveBeenCalled()
+          let dateClickSpy = spyOnCalendarCallback('dateClick')
+          let calendar = initCalendar({
+            weekNumbersWithinDays: true
+          })
+          let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+          $.simulateMouseClick(dayGridWrapper.getWeekNavLinkEls(true)[1])
+          expectWeekView(calendar, 'timeGridWeek', tz.parseDate('2016-08-07'))
+          expect(dateClickSpy).not.toHaveBeenCalled()
         })
       })
 
       it('does not have clickable day header', function() {
-        initCalendar(options)
-        expect(getDayHeaderLinks().length).toBe(0)
+        let calendar = initCalendar()
+        let headerWrapper = new DayGridViewWrapper(calendar).header
+
+        expect(headerWrapper.getNavLinkEls().length).toBe(0)
       })
     })
   })
 
   describe('in week view', function() {
-    beforeEach(function() {
-      options.defaultView = 'timeGridWeek'
+    pushOptions({
+      defaultView: 'timeGridWeek'
     })
 
     it('moves to day view', function() {
-      initCalendar(options)
-      $.simulateMouseClick(getDayHeaderLink('2016-08-15'))
-      expectDayView('timeGridDay', '2016-08-15')
-      expect(options.dateClick).not.toHaveBeenCalled()
+      let dateClickSpy = spyOnCalendarCallback('dateClick')
+      let calendar = initCalendar()
+      let headerWrapper = new TimeGridViewWrapper(calendar).header
+
+      headerWrapper.clickNavLink('2016-08-15')
+      expectDayView(calendar, 'timeGridDay', '2016-08-15')
+      expect(dateClickSpy).not.toHaveBeenCalled()
     })
   })
 
   describe('in listWeek', function() {
-    beforeEach(function() {
-      options.defaultView = 'listWeek'
-      options.events = [
+    pushOptions({
+      defaultView: 'listWeek',
+      events: [
         {
           title: 'event 1',
           start: '2016-08-20'
@@ -132,108 +164,61 @@ describe('navLinks', function() {
     })
 
     it('moves to day view', function() {
-      initCalendar(options)
-      $.simulateMouseClick(getListDayHeaderLink('2016-08-20'))
-      expectDayView('timeGridDay', '2016-08-20')
-      expect(options.dateClick).not.toHaveBeenCalled()
+      let dateClickSpy = spyOnCalendarCallback('dateClick')
+      let calendar = initCalendar()
+      let listWrapper = new ListViewWrapper(calendar)
+
+      listWrapper.clickNavLink('2016-08-20')
+      expectDayView(calendar, 'timeGridDay', '2016-08-20')
+      expect(dateClickSpy).not.toHaveBeenCalled()
     })
   })
 
   describe('in day view', function() {
-    beforeEach(function() {
-      options.defaultView = 'timeGridDay'
+    pushOptions({
+      defaultView: 'timeGridDay'
     })
 
     it('moves to week view', function() {
-      options.weekNumbers = true
-      initCalendar(options)
-      $.simulateMouseClick(getTimeGridWeekNumberLink())
-      expectWeekView('timeGridWeek', '2016-08-14')
-      expect(options.dateClick).not.toHaveBeenCalled()
+      let dateClickSpy = spyOnCalendarCallback('dateClick')
+      let calendar = initCalendar({
+        weekNumbers: true
+      })
+      let headerWrapper = new TimeGridViewWrapper(calendar).header
+
+      $.simulateMouseClick(headerWrapper.getWeekNavLinkEl())
+      expectWeekView(calendar, 'timeGridWeek', '2016-08-14')
+      expect(dateClickSpy).not.toHaveBeenCalled()
     })
 
     it('does not have a clickable day header', function() {
-      initCalendar(options)
-      expect(getDayHeaderLinks().length).toBe(0)
+      let calendar = initCalendar()
+      let headerWrapper = new TimeGridViewWrapper(calendar).header
+
+      expect(headerWrapper.getNavLinkEls().length).toBe(0)
     })
   })
 
 
-  /* utils
-  ------------------------------------------------------------------------------------------------------------------ */
+  function expectDayView(calendar, viewName, dayDate) {
+    let calendarWrapper = new CalendarWrapper(calendar)
+    let start = calendar.view.activeStart
+    let end = calendar.view.activeEnd
 
-  function expectDayView(viewName, dayDate) {
-    if (typeof dayDate === 'string') {
-      dayDate = new Date(dayDate)
-    }
-    expect(getCurrentViewName()).toBe(viewName)
-    var dates = getDayGridDates()
-    expect(dates.length).toBe(1)
-    expect(dates[0]).toEqualDate(dayDate)
+    expect(calendarWrapper.getViewName()).toBe(viewName)
+    expect(start).toEqualDate(dayDate)
+    expect(addDays(end, -1)).toEqualDate(dayDate)
   }
 
-  function expectWeekView(viewName, firstDayDate) {
-    if (typeof firstDayDate === 'string') {
-      firstDayDate = new Date(firstDayDate)
-    }
-    expect(getCurrentViewName()).toBe(viewName)
-    var dates = getDayGridDates()
-    expect(dates.length).toBe(7)
-    expect(dates[0]).toEqualDate(firstDayDate)
-  }
 
-  function getCurrentViewName() {
-    return $('.fc-view').attr('class').match(/fc-(\w+)-view/)[1]
-  }
+  function expectWeekView(calendar, viewName, firstDayDate) {
+    let calendarWrapper = new CalendarWrapper(calendar)
+    let start = calendar.view.activeStart
+    let end = calendar.view.activeEnd
 
-  // day headers (for both day grid and time grid)
-
-  function getDayHeaderLink(dayDate) {
-    if (typeof dayDate === 'string') {
-      dayDate = new Date(dayDate)
-    }
-    return $('.fc-day-header[data-date="' + formatIsoDay(dayDate) + '"] a')
-  }
-
-  function getDayHeaderLinks(dayDate) {
-    return $('.fc-day-header a')
-  }
-
-  // day grid
-
-  function getDayGridNumberEl(dayDate) {
-    if (typeof dayDate === 'string') {
-      dayDate = new Date(dayDate)
-    }
-    return $('.fc-day-top[data-date="' + formatIsoDay(dayDate) + '"] .fc-day-number')
-  }
-
-  function getDayGridClassicWeekLinks() { // along the sides of the row
-    return $('.fc-day-grid .fc-week-number a')
+    expect(calendarWrapper.getViewName()).toBe(viewName)
+    expect(start).toEqualDate(firstDayDate)
+    expect(addDays(end, -7)).toEqualDate(firstDayDate)
   }
 
-  function getDayGridEmbeddedWeekLinks() { // within the day cells
-    return $('.fc-day-top a.fc-week-number')
-  }
-
-  function getDayGridDates() {
-    return $('.fc-day-grid .fc-day').map(function(i, el) {
-      return new Date(el.getAttribute('data-date'))
-    }).get()
-  }
-
-  // list view
-
-  function getListDayHeaderLink(dayDate) {
-    if (typeof dayDate === 'string') {
-      dayDate = new Date(dayDate)
-    }
-    return $('.fc-list-heading[data-date="' + formatIsoDay(dayDate) + '"] a.fc-list-heading-main')
-  }
-
-  // timeGrid view
-
-  function getTimeGridWeekNumberLink() {
-    return $('th.fc-axis a')
-  }
 })

+ 14 - 6
packages/__tests__/src/legacy/removeEvents.js

@@ -1,3 +1,5 @@
+import CalendarWrapper from "../lib/wrappers/CalendarWrapper"
+
 describe('removeEvents', function() {
 
   pushOptions({
@@ -38,7 +40,8 @@ describe('removeEvents', function() {
           },
           function() {
             expect(currentCalendar.getEvents().length).toEqual(0)
-            expect($('.fc-event').length).toEqual(0)
+            let calendarWrapper = new CalendarWrapper(currentCalendar)
+            expect(calendarWrapper.getEventEls().length).toEqual(0)
           },
           done
         )
@@ -56,7 +59,8 @@ describe('removeEvents', function() {
           },
           function() {
             expect(currentCalendar.getEvents().length).toEqual(2)
-            expect($('.fc-event').length).toEqual(2)
+            let calendarWrapper = new CalendarWrapper(currentCalendar)
+            expect(calendarWrapper.getEventEls().length).toEqual(2)
             expect($('.event-zero').length).toEqual(1)
             expect($('.event-two').length).toEqual(1)
           },
@@ -75,7 +79,8 @@ describe('removeEvents', function() {
       },
       function() {
         expect(currentCalendar.getEvents().length).toEqual(2)
-        expect($('.fc-event').length).toEqual(2)
+        let calendarWrapper = new CalendarWrapper(currentCalendar)
+        expect(calendarWrapper.getEventEls().length).toEqual(2)
         expect($('.event-zero').length).toEqual(1)
         expect($('.event-two').length).toEqual(1)
       },
@@ -91,7 +96,8 @@ describe('removeEvents', function() {
       },
       function() {
         expect(currentCalendar.getEvents().length).toEqual(2)
-        expect($('.fc-event').length).toEqual(2)
+        let calendarWrapper = new CalendarWrapper(currentCalendar)
+        expect(calendarWrapper.getEventEls().length).toEqual(2)
         expect($('.event-zero').length).toEqual(1)
         expect($('.event-two').length).toEqual(1)
       },
@@ -107,7 +113,8 @@ describe('removeEvents', function() {
       },
       function() {
         expect(currentCalendar.getEvents().length).toEqual(2)
-        expect($('.fc-event').length).toEqual(2)
+        let calendarWrapper = new CalendarWrapper(currentCalendar)
+        expect(calendarWrapper.getEventEls().length).toEqual(2)
         expect($('.event-zero').length).toEqual(0)
         expect($('.event-non-zero').length).toEqual(2)
       },
@@ -148,7 +155,8 @@ describe('removeEvents', function() {
   // has internal info on all the events.
   function checkAllEvents() {
     expect(currentCalendar.getEvents().length).toEqual(3)
-    expect($('.fc-event').length).toEqual(3)
+    let calendarWrapper = new CalendarWrapper(currentCalendar)
+    expect(calendarWrapper.getEventEls().length).toEqual(3)
   }
 
 })

+ 9 - 6
packages/__tests__/src/legacy/rerenderDelay.js

@@ -1,3 +1,5 @@
+import CalendarWrapper from "../lib/wrappers/CalendarWrapper"
+
 describe('rerenderDelay', function() {
 
   it('batches together many event renders', function(done) {
@@ -13,7 +15,7 @@ describe('rerenderDelay', function() {
     var extraEvent2 = { title: 'event6', start: '2016-12-04T06:00:00', className: 'event6', id: '6' }
     var eventRenderCnt = 0
 
-    initCalendar({
+    let calendar = initCalendar({
       defaultDate: '2016-12-04',
       defaultView: 'timeGridDay',
       events: eventSource1,
@@ -37,19 +39,20 @@ describe('rerenderDelay', function() {
         }
       }
     })
+    let calendarWrapper = new CalendarWrapper(calendar)
 
-    expect($('.fc-event').length).toBe(2)
+    expect(calendarWrapper.getEventEls().length).toBe(2)
 
     currentCalendar.addEventSource(eventSource2)
-    expect($('.fc-event').length).toBe(2)
+    expect(calendarWrapper.getEventEls().length).toBe(2)
 
     currentCalendar.addEvent(extraEvent1)
-    expect($('.fc-event').length).toBe(2)
+    expect(calendarWrapper.getEventEls().length).toBe(2)
 
     var refined2 = currentCalendar.addEvent(extraEvent2)
-    expect($('.fc-event').length).toBe(2)
+    expect(calendarWrapper.getEventEls().length).toBe(2)
 
     refined2.remove()
-    expect($('.fc-event').length).toBe(2)
+    expect(calendarWrapper.getEventEls().length).toBe(2)
   })
 })

+ 16 - 12
packages/__tests__/src/legacy/scrollTime.js

@@ -1,3 +1,5 @@
+import TimeGridViewWrapper from "../lib/wrappers/TimeGridViewWrapper"
+
 describe('scrollTime', function() {
 
   pushOptions({
@@ -5,30 +7,32 @@ describe('scrollTime', function() {
   })
 
   it('accepts a string Duration', function() {
-    initCalendar({
+    let calendar = initCalendar({
       scrollTime: '02:00:00',
       height: 400 // short enough to make scrolling happen
     })
-    var slotCell = $('.fc-slats tr:eq(4)') // 2am slot
-    var slotTop = slotCell.position().top
-    var scrollContainer = $('.scrollgrid .fc-body:last-child .fc-scroller')
-    var scrollTop = scrollContainer.scrollTop()
-    var diff = Math.abs(slotTop - scrollTop)
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let timeGridWrapper = viewWrapper.timeGrid
+    let slotTop = viewWrapper.timeGrid.getTimeTop('02:00:00') - $(timeGridWrapper.el).offset().top
+    let scrollTop = viewWrapper.getScrollerEl().scrollTop
+    let diff = Math.abs(slotTop - scrollTop)
+
     expect(slotTop).toBeGreaterThan(0)
     expect(scrollTop).toBeGreaterThan(0)
     expect(diff).toBeLessThan(3)
   })
 
   it('accepts a Duration object', function() {
-    initCalendar({
+    let calendar = initCalendar({
       scrollTime: { hours: 2 },
       height: 400 // short enough to make scrolling happen
     })
-    var slotCell = $('.fc-slats tr:eq(4)') // 2am slot
-    var slotTop = slotCell.position().top
-    var scrollContainer = $('.scrollgrid .fc-body:last-child .fc-scroller')
-    var scrollTop = scrollContainer.scrollTop()
-    var diff = Math.abs(slotTop - scrollTop)
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let timeGridWrapper = viewWrapper.timeGrid
+    let slotTop = timeGridWrapper.getTimeTop('02:00:00') - $(timeGridWrapper.el).offset().top
+    let scrollTop = viewWrapper.getScrollerEl().scrollTop
+    let diff = Math.abs(slotTop - scrollTop)
+
     expect(slotTop).toBeGreaterThan(0)
     expect(scrollTop).toBeGreaterThan(0)
     expect(diff).toBeLessThan(3)

+ 188 - 157
packages/__tests__/src/legacy/select-method.js

@@ -1,13 +1,11 @@
-describe('select method', function() {
-
-  var options
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
 
-  beforeEach(function() {
-    options = {
-      defaultDate: '2014-05-25',
-      selectable: true
-    }
-  });
+describe('select method', function() {
+  pushOptions({
+    defaultDate: '2014-05-25',
+    selectable: true
+  })
 
   /*
   THINGS TO IMPLEMENT IN SRC (in addition to notes further down):
@@ -17,171 +15,204 @@ describe('select method', function() {
     - for dayGrid/month views, when given timed dates, should really be all-day
   */
 
-  [ 'ltr', 'rtl' ].forEach(function(dir) {
-    describe('when dir is ' + dir, function() {
-      beforeEach(function() {
-        options.dir = dir
+  describeOptions('dir', {
+    'when LTR': 'ltr',
+    'when RTL': 'rtl'
+  }, function() {
+
+    describe('when in month view', function() {
+      pushOptions({
+        defaultView: 'dayGridMonth'
       })
-      describe('when in month view', function() {
-        beforeEach(function() {
-          options.defaultView = 'dayGridMonth'
-        })
-        describe('when called with all-day date strings', function() {
-          describe('when in bounds', function() {
-            it('renders a selection', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-07', '2014-05-09')
-              expect($('.fc-highlight')).toBeVisible()
-            })
-            it('renders a selection when called with one argument', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-07')
-              expect($('.fc-highlight')).toBeVisible()
-            })
-            it('fires a selection event', function() {
-              options.select = function(arg) {
-                expect(arg.allDay).toEqual(true)
-                expect(arg.start).toEqualDate('2014-05-07')
-                expect(arg.end).toEqualDate('2014-05-09')
-              }
-              spyOn(options, 'select').and.callThrough()
-              initCalendar(options)
-              currentCalendar.select('2014-05-07', '2014-05-09')
-              expect(options.select).toHaveBeenCalled()
-            })
+
+      describe('when called with all-day date strings', function() {
+
+        describe('when in bounds', function() {
+
+          it('renders a selection', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-07', '2014-05-09')
+            let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+            expect(dayGridWrapper.getHighlightEls()).toBeVisible()
+          })
+
+          it('renders a selection when called with one argument', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-07')
+            let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+            expect(dayGridWrapper.getHighlightEls()).toBeVisible()
           })
-          describe('when out of bounds', function() {
-            it('doesn\'t render a selection', function() {
-              initCalendar(options)
-              currentCalendar.select('2015-05-07', '2015-05-09')
-              expect($('.fc-highlight')).not.toBeVisible()
+
+          it('fires a selection event', function() {
+            let selectSpy = spyOnCalendarCallback('select', (arg) => {
+              expect(arg.allDay).toEqual(true)
+              expect(arg.start).toEqualDate('2014-05-07')
+              expect(arg.end).toEqualDate('2014-05-09')
             })
-            /*
-            TODO: implement this behavior
-            it('doesn\'t fire a selection event', function() {
-              options.select = function(arg) {
-                expect(arg.start).toEqualDate('2014-05-07');
-                expect(arg.end).toEqualDate('2014-05-09');
-              };
-              spyOn(options, 'select').and.callThrough();
-              initCalendar(options);
-              currentCalendar.select('2015-05-07', '2015-05-09');
-              expect(options.select).not.toHaveBeenCalled();
-            });
-            */
+            let calendar = initCalendar()
+            calendar.select('2014-05-07', '2014-05-09')
+            expect(selectSpy).toHaveBeenCalled()
           })
         })
-        describe('when called with timed date strings', function() {
-          it('renders a selection', function() {
-            initCalendar(options)
-            currentCalendar.select('2014-05-07T06:00:00', '2014-05-09T07:00:00')
-            expect($('.fc-highlight')).toBeVisible()
+
+        describe('when out of bounds', function() {
+
+          it('doesn\'t render a selection', function() {
+            let calendar = initCalendar()
+            calendar.select('2015-05-07', '2015-05-09')
+            let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+            expect(dayGridWrapper.getHighlightEls()).not.toBeVisible()
           })
-          it('fires a selection event', function() {
+
+          /*
+          TODO: implement this behavior
+          it('doesn\'t fire a selection event', function() {
             options.select = function(arg) {
-              expect(arg.allDay).toEqual(false)
-              expect(arg.start).toEqualDate('2014-05-07T06:00:00Z')
-              expect(arg.end).toEqualDate('2014-05-09T06:00:00Z')
-            }
-            spyOn(options, 'select').and.callThrough()
-            initCalendar(options)
-            currentCalendar.select('2014-05-07T06:00:00', '2014-05-09T06:00:00')
-            expect(options.select).toHaveBeenCalled()
+              expect(arg.start).toEqualDate('2014-05-07');
+              expect(arg.end).toEqualDate('2014-05-09');
+            };
+            spyOn(options, 'select').and.callThrough();
+            let calendar = initCalendar(options);
+            calendar.select('2015-05-07', '2015-05-09');
+            expect(options.select).not.toHaveBeenCalled();
+          });
+          */
+        })
+      })
+
+      describe('when called with timed date strings', function() {
+
+        it('renders a selection', function() {
+          let calendar = initCalendar()
+          calendar.select('2014-05-07T06:00:00', '2014-05-09T07:00:00')
+          let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+          expect(dayGridWrapper.getHighlightEls()).toBeVisible()
+        })
+
+        it('fires a selection event', function() {
+          let selectSpy = spyOnCalendarCallback('select', (arg) => {
+            expect(arg.allDay).toEqual(false)
+            expect(arg.start).toEqualDate('2014-05-07T06:00:00Z')
+            expect(arg.end).toEqualDate('2014-05-09T06:00:00Z')
           })
+          let calendar = initCalendar()
+          calendar.select('2014-05-07T06:00:00', '2014-05-09T06:00:00')
+          expect(selectSpy).toHaveBeenCalled()
         })
       })
-      describe('when in week view', function() { // May 25 - 31
-        beforeEach(function() {
-          options.defaultView = 'timeGridWeek'
-          options.scrollTime = '01:00:00' // so that most events will be below the divider
-          options.height = 400 // short enought to make scrolling happen
+    })
+
+    describe('when in week view', function() { // May 25 - 31
+      pushOptions({
+        defaultView: 'timeGridWeek',
+        scrollTime: '01:00:00', // so that most events will be below the divider
+        height: 400 // short enought to make scrolling happen
+      })
+
+      describe('when called with timed date strings', function() {
+
+        describe('when in bounds', function() {
+
+          it('renders a selection when called with one argument', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-26T06:00:00')
+            let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+            expect(timeGridWrapper.getHighlightEls()).toBeVisible()
+          })
+
+          it('renders a selection over the slot area', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-26T06:00:00', '2014-05-26T08:00:00')
+            let viewWrapper = new TimeGridViewWrapper(calendar)
+            let highlightEls = viewWrapper.timeGrid.getHighlightEls()
+            expect(highlightEls).toBeVisible()
+            let slotAreaTop = $(viewWrapper.getScrollerEl()).offset().top
+            let overlayTop = $(highlightEls[0]).offset().top
+            expect(overlayTop).toBeGreaterThan(slotAreaTop)
+          })
         })
-        describe('when called with timed date strings', function() {
-          describe('when in bounds', function() {
-            it('renders a selection when called with one argument', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-26T06:00:00')
-              expect($('.fc-highlight')).toBeVisible()
-            })
-            it('renders a selection over the slot area', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-26T06:00:00', '2014-05-26T08:00:00')
-              expect($('.fc-highlight')).toBeVisible()
-              var slotAreaTop = $('.scrollgrid .fc-body:last-child .fc-scroller').offset().top
-              var overlayTop = $('.fc-highlight').offset().top
-              expect(overlayTop).toBeGreaterThan(slotAreaTop)
-            })
+
+        describe('when out of bounds', function() {
+
+          it('doesn\'t render a selection', function() {
+            let calendar = initCalendar()
+            calendar.select('2015-05-26T06:00:00', '2015-05-26T07:00:00')
+            let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+            expect(timeGridWrapper.getHighlightEls()).not.toBeVisible()
+          })
+
+          /*
+          TODO: implement this behavior
+          it('doesn\'t fire a selection event', function() {
+            options.select = function(arg) {
+              expect(arg.start).toEqualDate('2015-05-07T06:00:00Z');
+              expect(arg.end).toEqualDate('2015-05-09T07:00:00Z');
+            };
+            spyOn(options, 'select').and.callThrough();
+            let calendar = initCalendar(options);
+            calendar.select('2015-05-07T06:00:00', '2015-05-09T07:00:00');
+            expect(options.select).not.toHaveBeenCalled();
+          });
+          */
+        })
+      })
+
+      describe('when called with all-day date strings', function() { // forget about in/out bounds for this :)
+
+        describe('when allDaySlot is on', function() {
+          pushOptions({
+            allDaySlot: true
+          })
+
+          it('renders a selection over the day area', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-26', '2014-05-28')
+            let viewWrapper = new TimeGridViewWrapper(calendar)
+            let highlightEls = viewWrapper.dayGrid.getHighlightEls()
+            expect(highlightEls).toBeVisible()
+            let slotAreaTop = $(viewWrapper.getScrollerEl()).offset().top
+            let overlayTop = $(highlightEls[0]).offset().top
+            expect(overlayTop).toBeLessThan(slotAreaTop)
           })
-          describe('when out of bounds', function() {
-            it('doesn\'t render a selection', function() {
-              initCalendar(options)
-              currentCalendar.select('2015-05-26T06:00:00', '2015-05-26T07:00:00')
-              expect($('.fc-highlight')).not.toBeVisible()
+
+          it('fires a selection event', function() {
+            let selectSpy = spyOnCalendarCallback('select', (arg) => {
+              expect(arg.allDay).toEqual(true)
+              expect(arg.start).toEqualDate('2014-05-26')
+              expect(arg.end).toEqualDate('2014-05-28')
             })
-            /*
-            TODO: implement this behavior
-            it('doesn\'t fire a selection event', function() {
-              options.select = function(arg) {
-                expect(arg.start).toEqualDate('2015-05-07T06:00:00Z');
-                expect(arg.end).toEqualDate('2015-05-09T07:00:00Z');
-              };
-              spyOn(options, 'select').and.callThrough();
-              initCalendar(options);
-              currentCalendar.select('2015-05-07T06:00:00', '2015-05-09T07:00:00');
-              expect(options.select).not.toHaveBeenCalled();
-            });
-            */
+            let calendar = initCalendar()
+            calendar.select('2014-05-26', '2014-05-28')
+            expect(selectSpy).toHaveBeenCalled()
           })
         })
-        describe('when called with all-day date strings', function() { // forget about in/out bounds for this :)
-          describe('when allDaySlot is on', function() {
-            beforeEach(function() {
-              options.allDaySlot = true
-            })
-            it('renders a selection over the day area', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-26', '2014-05-28')
-              expect($('.fc-highlight')).toBeVisible()
-              var slotAreaTop = $('.scrollgrid .fc-body:last-child .fc-scroller').offset().top
-              var overlayTop = $('.fc-highlight').offset().top
-              expect(overlayTop).toBeLessThan(slotAreaTop)
-            })
-            it('fires a selection event', function() {
-              options.select = function(arg) {
-                expect(arg.allDay).toEqual(true)
-                expect(arg.start).toEqualDate('2014-05-26')
-                expect(arg.end).toEqualDate('2014-05-28')
-              }
-              spyOn(options, 'select').and.callThrough()
-              initCalendar(options)
-              currentCalendar.select('2014-05-26', '2014-05-28')
-              expect(options.select).toHaveBeenCalled()
-            })
+
+        describe('when allDaySlot is off', function() {
+          pushOptions({
+            allDaySlot: false
           })
-          describe('when allDaySlot is off', function() {
-            beforeEach(function() {
-              options.allDaySlot = false
-            })
-            it('doesn\'t render', function() {
-              initCalendar(options)
-              currentCalendar.select('2014-05-26', '2014-05-28')
-              expect($('.fc-highlight')).not.toBeVisible()
-            })
-            /*
-            TODO: implement
-            it('doesn\'t fire a selection event', function() {
-              options.select = function(arg) {
-                expect(arg.allDay).toEqual(true);
-                expect(arg.start).toEqualDate('2014-05-26');
-                expect(arg.end).toEqualDate('2014-05-28');
-              };
-              spyOn(options, 'select').and.callThrough();
-              initCalendar(options);
-              currentCalendar.select('2014-05-26', '2014-05-28');
-              expect(options.select).not.toHaveBeenCalled();
-            });
-            */
+
+          it('doesn\'t render the all-day selection over time area', function() {
+            let calendar = initCalendar()
+            calendar.select('2014-05-26', '2014-05-28')
+            let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+            expect(timeGridWrapper.getHighlightEls()).not.toBeVisible()
           })
+
+          /*
+          TODO: implement
+          it('doesn\'t fire a selection event', function() {
+            options.select = function(arg) {
+              expect(arg.allDay).toEqual(true);
+              expect(arg.start).toEqualDate('2014-05-26');
+              expect(arg.end).toEqualDate('2014-05-28');
+            };
+            spyOn(options, 'select').and.callThrough();
+            let calendar = initCalendar(options);
+            calendar.select('2014-05-26', '2014-05-28');
+            expect(options.select).not.toHaveBeenCalled();
+          });
+          */
         })
       })
     })

+ 37 - 27
packages/__tests__/src/legacy/themeSystem.js

@@ -1,57 +1,67 @@
 import BootstrapPlugin from '@fullcalendar/bootstrap'
 import TimeGridPlugin from '@fullcalendar/timegrid'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
 
 describe('themeSystem', function() {
-
   pushOptions({
     plugins: [ BootstrapPlugin, TimeGridPlugin ],
-    defaultView: 'timeGridWeek'
+    defaultView: 'timeGridWeek',
+    header: {
+      left: '',
+      center: '',
+      right: 'next'
+    }
   })
 
   it('can be changed dynamically', function() {
-    initCalendar()
+    let calendar = initCalendar()
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+    let buttonInfo = toolbarWrapper.getButtonInfo('next')
 
-    expect($('.fc')).toHaveClass('fc-unthemed')
-    expect($('.fc')).not.toHaveClass('fc-bootstrap')
-    expect($('.fc-toolbar button .fc-icon').length).toBeGreaterThan(0)
-    expect($('.fc-toolbar button .fa').length).toBe(0) // FontAwesome icon
+    expect(calendar.el).toHaveClass(CalendarWrapper.ROOT_CLASSNAME)
+    expect(calendar.el).toHaveClass(CalendarWrapper.UNTHEMED_CLASSNAME)
+    expect(calendar.el).not.toHaveClass(CalendarWrapper.BOOTSTRAP_CLASSNAME)
+    expect(buttonInfo.iconName).toBeTruthy()
     expect($('.table-bordered').length).toBe(0)
 
-    $('.fc-scroller').scrollTop(99999) // scroll all the way down
-    var scrollTop = $('.fc-scroller').scrollTop()
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let scrollEl = viewWrapper.getScrollerEl()
+
+    scrollEl.scrollTop = 99999 // scroll all the way down
+    let scrollTop = scrollEl.scrollTop
 
     // change option!
-    currentCalendar.setOption('themeSystem', 'bootstrap')
+    calendar.setOption('themeSystem', 'bootstrap')
 
-    expect($('.fc')).toHaveClass('fc-bootstrap')
-    expect($('.fc')).not.toHaveClass('fc-unthemed')
-    expect($('.fc-toolbar button .fc-icon').length).toBe(0)
-    expect($('.fc-toolbar button .fa').length).toBeGreaterThan(0) // FontAwesome icon
+    buttonInfo = toolbarWrapper.getButtonInfo('next', 'fa')
+    expect(calendar.el).toHaveClass(CalendarWrapper.ROOT_CLASSNAME)
+    expect(calendar.el).toHaveClass(CalendarWrapper.BOOTSTRAP_CLASSNAME)
+    expect(calendar.el).not.toHaveClass(CalendarWrapper.UNTHEMED_CLASSNAME)
+    expect(buttonInfo.iconName).toBeTruthy()
     expect($('.table-bordered').length).toBeGreaterThan(0)
 
     // similar scroll state after the change
-    expect(Math.abs(scrollTop - $('.fc-scroller').scrollTop())).toBeLessThan(5)
+    expect(Math.abs(scrollTop - scrollEl.scrollTop)).toBeLessThan(5)
   })
 
-
   // this tests the options setter with a single hash argument.
   // TODO: not best place for this.
   it('can be change with other options', function() {
-    initCalendar()
+    let calendar = initCalendar()
 
-    expect($('.fc')).toHaveClass('fc-unthemed')
-    expect($('.fc')).not.toHaveClass('fc-bootstrap')
-    expect($('.fc-nonbusiness').length).toBe(0)
+    expect(calendar.el).toHaveClass(CalendarWrapper.ROOT_CLASSNAME)
+    expect(calendar.el).toHaveClass(CalendarWrapper.UNTHEMED_CLASSNAME)
+    expect(calendar.el).not.toHaveClass(CalendarWrapper.BOOTSTRAP_CLASSNAME)
 
     // change option!
-    currentCalendar.batchRendering(function() {
-      currentCalendar.setOption('themeSystem', 'bootstrap')
-      currentCalendar.setOption('businessHours', true)
+    calendar.batchRendering(function() {
+      calendar.setOption('themeSystem', 'bootstrap')
+      calendar.setOption('businessHours', true)
     })
 
-    expect($('.fc')).toHaveClass('fc-bootstrap')
-    expect($('.fc')).not.toHaveClass('fc-unthemed')
-    expect($('.fc-nonbusiness').length).toBeGreaterThan(0)
+    expect(calendar.el).toHaveClass(CalendarWrapper.ROOT_CLASSNAME)
+    expect(calendar.el).toHaveClass(CalendarWrapper.BOOTSTRAP_CLASSNAME)
+    expect(calendar.el).not.toHaveClass(CalendarWrapper.UNTHEMED_CLASSNAME)
   })
-
 })

+ 58 - 59
packages/__tests__/src/legacy/titleFormat.js

@@ -1,12 +1,15 @@
 import frLocale from '@fullcalendar/core/locales/fr'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 describe('titleFormat', function() {
 
-  var SELECTOR = '.fc-toolbar h2'
-
   describe('when default', function() {
+    pushOptions({
+      defaultDate: '2014-06-12',
+      titleRangeSeparator: ' - '
+    })
 
-    var viewWithFormat = [
+    const VIEWS_WITH_FORMATS = [
       { view: 'dayGridMonth', expected: 'June 2014' },
       { view: 'dayGridWeek', expected: /Jun 8 - 14,? 2014/ },
       { view: 'timeGridWeek', expected: /Jun 8 - 14,? 2014/ },
@@ -14,58 +17,55 @@ describe('titleFormat', function() {
       { view: 'timeGridDay', expected: /June 12,? 2014/ }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-06-12',
-        titleRangeSeparator: ' - '
-      })
-    })
-
     it('should have default values', function() {
-      var cal = $(currentCalendar.el)
+      let calendar = initCalendar()
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
 
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect(cal.find(SELECTOR).text()).toMatch(crtView.expected)
-      };
+      for (let viewWithFormat of VIEWS_WITH_FORMATS) {
+        calendar.changeView(viewWithFormat.view)
+        expect(toolbarWrapper.getTitleText()).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when set on a per-view basis', function() {
+    pushOptions({
+      defaultDate: '2014-06-12',
+      titleRangeSeparator: ' - ',
+      views: {
+        month: { titleFormat: { year: 'numeric', month: 'long' } },
+        dayGridWeek: { titleFormat: { day: 'numeric', month: 'short', year: 'numeric' } },
+        week: { titleFormat: { day: 'numeric', month: 'long', year: 'numeric' } },
+        dayGridDay: { titleFormat: { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' } }
+      }
+    })
 
-    var viewWithFormat = [
+    const VIEWS_WITH_FORMATS = [
       { view: 'dayGridMonth', expected: 'June 2014' },
       { view: 'dayGridWeek', expected: 'Jun 8 - 14, 2014' },
       { view: 'timeGridWeek', expected: 'June 8 - 14, 2014' },
       { view: 'dayGridDay', expected: 'Thursday, June 12, 2014' }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-06-12',
-        titleRangeSeparator: ' - ',
-        views: {
-          month: { titleFormat: { year: 'numeric', month: 'long' } },
-          dayGridWeek: { titleFormat: { day: 'numeric', month: 'short', year: 'numeric' } },
-          week: { titleFormat: { day: 'numeric', month: 'long', year: 'numeric' } },
-          dayGridDay: { titleFormat: { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' } }
-        }
-      })
-    })
-
     it('should have the correct values', function() {
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(SELECTOR).text()).toBe(crtView.expected)
-      };
+      let calendar = initCalendar()
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+      for (let viewWithFormat of VIEWS_WITH_FORMATS) {
+        calendar.changeView(viewWithFormat.view)
+        expect(toolbarWrapper.getTitleText()).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('when default and locale is French', function() {
+    pushOptions({
+      defaultDate: '2014-06-12',
+      titleRangeSeparator: ' - ',
+      locale: frLocale
+    })
 
-    var viewWithFormat = [
+    const VIEWS_WITH_FORMATS = [
       { view: 'dayGridMonth', expected: 'juin 2014' },
       { view: 'dayGridWeek', expected: '9 - 15 juin 2014' },
       { view: 'timeGridWeek', expected: '9 - 15 juin 2014' },
@@ -73,27 +73,21 @@ describe('titleFormat', function() {
       { view: 'timeGridDay', expected: '12 juin 2014' }
     ]
 
-    beforeEach(function() {
-      initCalendar({
-        defaultDate: '2014-06-12',
-        titleRangeSeparator: ' - ',
-        locale: frLocale
-      })
-    })
-
     it('should have the translated dates', function() {
-      for (var i = 0; i < viewWithFormat.length; i++) {
-        var crtView = viewWithFormat[i]
-        currentCalendar.changeView(crtView.view)
-        expect($(currentCalendar.el).find(SELECTOR).text()).toBe(crtView.expected)
-      };
+      let calendar = initCalendar()
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+      for (let viewWithFormat of VIEWS_WITH_FORMATS) {
+        calendar.changeView(viewWithFormat.view)
+        expect(toolbarWrapper.getTitleText()).toMatch(viewWithFormat.expected)
+      }
     })
   })
 
   describe('using custom views', function() {
 
     it('multi-year default only displays year', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiYear: {
             type: 'dayGrid',
@@ -104,11 +98,12 @@ describe('titleFormat', function() {
         defaultDate: '2014-12-25',
         titleRangeSeparator: ' - '
       })
-      expect($('h2')).toHaveText('2014 - 2015')
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      expect(toolbarWrapper.getTitleText()).toBe('2014 - 2015')
     })
 
     it('multi-month default only displays month/year', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiMonth: {
             type: 'dayGrid',
@@ -119,11 +114,12 @@ describe('titleFormat', function() {
         defaultDate: '2014-12-25',
         titleRangeSeparator: ' - '
       })
-      expect($('h2')).toHaveText('December 2014 - January 2015')
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      expect(toolbarWrapper.getTitleText()).toBe('December 2014 - January 2015')
     })
 
     it('multi-week default displays short full date', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiWeek: {
             type: 'dayGrid',
@@ -134,11 +130,12 @@ describe('titleFormat', function() {
         defaultDate: '2014-12-25',
         titleRangeSeparator: ' - '
       })
-      expect($('h2').text()).toMatch(/Dec 21,? 2014 - Jan 3,? 2015/)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      expect(toolbarWrapper.getTitleText()).toMatch(/Dec 21,? 2014 - Jan 3,? 2015/)
     })
 
     it('multi-day default displays short full date', function() {
-      initCalendar({
+      let calendar = initCalendar({
         views: {
           multiDay: {
             type: 'dayGrid',
@@ -149,20 +146,22 @@ describe('titleFormat', function() {
         defaultDate: '2014-12-25',
         titleRangeSeparator: ' - '
       })
-      expect($('h2').text()).toMatch(/Dec 25 - 26,? 2014/)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      expect(toolbarWrapper.getTitleText()).toMatch(/Dec 25 - 26,? 2014/)
     })
   })
 
   describe('when not all days are shown', function() {
 
     it('doesn\'t include hidden days in the title', function() {
-      initCalendar({
+      let calendar = initCalendar({
         defaultView: 'timeGridWeek',
         defaultDate: '2017-02-13',
         weekends: false,
         titleRangeSeparator: ' - '
       })
-      expect($('h2')).toHaveText('Feb 13 - 17, 2017') // does not include Sunday
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      expect(toolbarWrapper.getTitleText()).toBe('Feb 13 - 17, 2017') // does not include Sunday
     })
   })
 })

+ 0 - 37
packages/__tests__/src/legacy/viewRender.js

@@ -1,37 +0,0 @@
-describe('datesRender', function() {
-
-  pushOptions({
-    defaultDate: '2015-02-20'
-  })
-
-  describe('when in month view', function() {
-    pushOptions({
-      defaultView: 'dayGridMonth'
-    })
-    defineTests()
-  })
-
-  describe('when in week view', function() {
-    pushOptions({
-      defaultView: 'timeGridWeek'
-    })
-    defineTests()
-  })
-
-  function defineTests() {
-
-    it('fires after the view is rendered, with correct arguments', function(done) {
-      initCalendar({
-        datesRender: function(arg) {
-          var viewObj = currentCalendar.view
-          var viewEl = $('.fc-view', currentCalendar.el)
-
-          expect(viewObj).toBe(arg.view)
-          expect(viewEl[0]).toBe(arg.el)
-          expect(viewEl.children().length >= 1).toBe(true) // has it rendered content?
-          done()
-        }
-      })
-    })
-  }
-})

+ 2 - 1
packages/__tests__/src/lib/globals.js

@@ -38,7 +38,8 @@ function pushOptions(options) {
 }
 
 // called within an `it`
-function spyOnCalendarCallback(name, func) {
+// needs to be called *before* initCalendar
+function spyOnCalendarCallback(name, func = function(){}) {
 
   /** @type {any} */
   var options = {}

+ 11 - 1
packages/__tests__/src/lib/wrappers/CalendarWrapper.ts

@@ -18,6 +18,11 @@ export default class CalendarWrapper {
   static PAST_CLASSNAME = 'fc-past'
   static FUTURE_CLASSNAME = 'fc-future'
   static DOW_CLASSNAMES = [ 'fc-sun', 'fc-mon', 'fc-tue', 'fc-wed', 'fc-thu', 'fc-fri', 'fc-sat' ]
+  static LTR_CLASSNAME = 'fc-ltr'
+  static RTL_CLASSNAME = 'fc-rtl'
+  static BOOTSTRAP_CLASSNAME = 'fc-bootstrap'
+  static UNTHEMED_CLASSNAME = 'fc-unthemed'
+  static ROOT_CLASSNAME = 'fc'
 
 
   constructor(private calendar: Calendar) {
@@ -43,7 +48,12 @@ export default class CalendarWrapper {
 
 
   getViewEl() {
-    return this.calendar.el.querySelector('.fc-view')
+    return this.calendar.el.querySelector('.fc-view') as HTMLElement
+  }
+
+
+  getViewName() {
+    return this.getViewEl().getAttribute('class').match(/fc-(\w+)-view/)[1]
   }
 
 

+ 25 - 0
packages/__tests__/src/lib/wrappers/DayGridWrapper.ts

@@ -74,6 +74,26 @@ export default class DayGridWrapper {
   }
 
 
+  getWeekNavLinkEls(isEmbedded) { // along the sides of the row
+    return isEmbedded
+      ? findElements(this.el, '.fc-day-top a.fc-week-number')
+      : findElements(this.el, '.fc-week-number a')
+  }
+
+
+  getNavLinkEl(date) {
+    if (typeof date === 'string') {
+      date = new Date(date)
+    }
+    return this.el.querySelector(`.fc-day-top[data-date="${formatIsoDay(date)}"] a:not(.fc-week-number)`)
+  }
+
+
+  clickNavLink(date) {
+    $.simulateMouseClick(this.getNavLinkEl(date))
+  }
+
+
   openMorePopover(index?) {
     if (index == null) {
       $(this.getMoreEl()).simulate('click')
@@ -156,6 +176,11 @@ export default class DayGridWrapper {
   }
 
 
+  clickDate(date) {
+    $.simulateMouseClick(this.getDayEl(date))
+  }
+
+
   selectDates(start, inclusiveEnd) {
     return new Promise((resolve) => {
       $(this.getDayEls(start)).simulate('drag', {

+ 43 - 1
packages/__tests__/src/lib/wrappers/DayHeaderWrapper.ts

@@ -1,5 +1,7 @@
 import { findElements } from '@fullcalendar/core'
-import { parseIsoAsUtc } from '../datelib-utils'
+import { parseIsoAsUtc, formatIsoDay } from '../datelib-utils'
+import { parseUtcDate } from '../date-parsing'
+import CalendarWrapper from './CalendarWrapper'
 
 
 export default class DayHeaderWrapper {
@@ -23,6 +25,23 @@ export default class DayHeaderWrapper {
   }
 
 
+  getCellEl(dateOrDow) {
+    if (typeof dateOrDow === 'number') {
+      return this.el.querySelector(`.fc-day-header.${CalendarWrapper.DOW_CLASSNAMES[dateOrDow]}`)
+    } else {
+      if (typeof dateOrDow === 'string') {
+        dateOrDow = parseUtcDate(dateOrDow)
+      }
+      return this.el.querySelector(`.fc-day-header[data-date="${formatIsoDay(dateOrDow)}"]`)
+    }
+  }
+
+
+  getCellText(dateOrDow) {
+    return $(this.getCellEl(dateOrDow)).text()
+  }
+
+
   getAxisEl() {
     return this.el.querySelector('.fc-axis')
   }
@@ -38,8 +57,31 @@ export default class DayHeaderWrapper {
   }
 
 
+  getWeekNavLinkEl() {
+    return this.el.querySelector('.fc-week-number a')
+  }
+
+
   getWeekNumberTitle() {
     return $(this.getWeekNumberEl()).text()
   }
 
+
+  getNavLinkEls() {
+    return findElements(this.el, '.fc-day-header[data-date] a')
+  }
+
+
+  getNavLinkEl(dayDate) {
+    if (typeof dayDate === 'string') {
+      dayDate = new Date(dayDate)
+    }
+    return this.el.querySelector('.fc-day-header[data-date="' + formatIsoDay(dayDate) + '"] a')
+  }
+
+
+  clickNavLink(date) {
+    $.simulateMouseClick(this.getNavLinkEl(date))
+  }
+
 }

+ 14 - 0
packages/__tests__/src/lib/wrappers/ListViewWrapper.ts

@@ -1,5 +1,6 @@
 import ViewWrapper from './ViewWrapper'
 import { Calendar, findElements } from '@fullcalendar/core'
+import { formatIsoDay } from '../datelib-utils'
 
 
 export default class ListViewWrapper extends ViewWrapper {
@@ -53,4 +54,17 @@ export default class ListViewWrapper extends ViewWrapper {
     return Boolean(this.el.querySelector('.fc-list-empty'))
   }
 
+
+  getNavLinkEl(dayDate) {
+    if (typeof dayDate === 'string') {
+      dayDate = new Date(dayDate)
+    }
+    return this.el.querySelector('.fc-list-heading[data-date="' + formatIsoDay(dayDate) + '"] a.fc-list-heading-main')
+  }
+
+
+  clickNavLink(dayDate) {
+    $.simulateMouseClick(this.getNavLinkEl(dayDate))
+  }
+
 }

+ 11 - 2
packages/__tests__/src/lib/wrappers/TimeGridWrapper.ts

@@ -1,4 +1,4 @@
-import { findElements, startOfDay, createDuration, parseMarker, addDays, addMs, getRectCenter } from '@fullcalendar/core'
+import { findElements, startOfDay, createDuration, parseMarker, addDays, addMs, getRectCenter, asRoughMs } from '@fullcalendar/core'
 import { formatIsoDay, formatIsoTime, ensureDate } from '../datelib-utils'
 import { parseUtcDate } from '../date-parsing'
 import { getBoundingRect } from '../dom-geom'
@@ -7,7 +7,7 @@ import { addPoints } from '../geom'
 
 export default class TimeGridWrapper {
 
-  constructor(private el: HTMLElement) {
+  constructor(public el: HTMLElement) {
   }
 
 
@@ -79,6 +79,11 @@ export default class TimeGridWrapper {
   }
 
 
+  getHighlightEls() { // FG events
+    return findElements(this.el, '.fc-highlight')
+  }
+
+
   // TODO: discourage use
   getDowEls(dayAbbrev) {
     return findElements(this.el, `.fc-day.fc-${dayAbbrev}`)
@@ -310,6 +315,10 @@ export default class TimeGridWrapper {
 
 
   getTimeTop(targetTimeMs) {
+    if (typeof targetTimeMs !== 'number') {
+      targetTimeMs = asRoughMs(createDuration(targetTimeMs))
+    }
+
     const topBorderWidth = 1 // TODO: kill
     let slotEl = this.getSlotElByTime(targetTimeMs)
     let $slotEl // used within loop, but we access last val

+ 1 - 1
packages/__tests__/src/lib/wrappers/ToolbarWrapper.ts

@@ -11,7 +11,7 @@ export default class ToolbarWrapper {
   }
 
 
-  getCustomButtonInfo(name, iconPrefix='fc-icon') { // prefix doesnt have dash
+  getButtonInfo(name, iconPrefix='fc-icon') { // prefix doesnt have dash
     let el = this.getButtonEl(name)
 
     if (el) {

+ 3 - 3
packages/__tests__/src/toolbar/customButtons.js

@@ -15,7 +15,7 @@ describe('customButtons', function() {
       header: { left: 'mybutton', center: '', right: '' }
     })
     let toolbarWrapper = new CalendarWrapper(calendar).toolbar
-    let buttonInfo = toolbarWrapper.getCustomButtonInfo('mybutton')
+    let buttonInfo = toolbarWrapper.getButtonInfo('mybutton')
     expect(buttonInfo.text).toBe('asdf')
   })
 
@@ -27,7 +27,7 @@ describe('customButtons', function() {
       header: { left: 'mybutton', center: '', right: '' }
     })
     let toolbarWrapper = new CalendarWrapper(calendar).toolbar
-    let buttonInfo = toolbarWrapper.getCustomButtonInfo('mybutton')
+    let buttonInfo = toolbarWrapper.getButtonInfo('mybutton')
     expect(buttonInfo.iconName).toBe('asdf')
   })
 
@@ -40,7 +40,7 @@ describe('customButtons', function() {
       header: { left: 'mybutton', center: '', right: '' }
     })
     let toolbarWrapper = new CalendarWrapper(calendar).toolbar
-    let buttonInfo = toolbarWrapper.getCustomButtonInfo('mybutton', 'fa')
+    let buttonInfo = toolbarWrapper.getButtonInfo('mybutton', 'fa')
     expect(buttonInfo.iconName).toBe('asdf')
   })