Adam Shaw 6 лет назад
Родитель
Сommit
5caf618b5e

+ 102 - 107
packages/__tests__/src/legacy/buttonText.js

@@ -1,7 +1,7 @@
 import frLocale from '@fullcalendar/core/locales/fr'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 describe('button text', function() {
-
   pushOptions({
     header: {
       left: 'prevYear,prev,today,next,nextYear',
@@ -15,24 +15,24 @@ describe('button text', function() {
     describe('with default buttonIcons', function() {
 
       it('should contain default text values', function() {
-        initCalendar()
+        let calendar = initCalendar()
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
 
         // will have button icons, to text will be empty
-        expect($('.fc-next-button')).toHaveText('')
-        expect($('.fc-nextYear-button')).toHaveText('')
-        expect($('.fc-prev-button')).toHaveText('')
-        expect($('.fc-prevYear-button')).toHaveText('')
-
-        expect($('.fc-today-button')).toHaveText('today')
-        expect($('.fc-dayGridMonth-button')).toHaveText('month')
-        expect($('.fc-dayGridWeek-button')).toHaveText('week')
-        expect($('.fc-timeGridWeek-button')).toHaveText('week')
-        expect($('.fc-dayGridDay-button')).toHaveText('day')
-        expect($('.fc-dayGridDay-button')).toHaveText('day')
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('today')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('month')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('week')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('week')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('day')
       })
 
       it('should contain specified text values', function() {
-        initCalendar({
+        let calendar = initCalendar({
           buttonText: {
             prev: '<-',
             next: '->',
@@ -44,47 +44,46 @@ describe('button text', function() {
             day: 'dei'
           }
         })
-
-        expect($('.fc-next-button')).toHaveText('->')
-        expect($('.fc-nextYear-button')).toHaveText('-->')
-        expect($('.fc-prev-button')).toHaveText('<-')
-        expect($('.fc-prevYear-button')).toHaveText('<--')
-
-        expect($('.fc-today-button')).toHaveText('tidei')
-        expect($('.fc-dayGridMonth-button')).toHaveText('mun')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-timeGridWeek-button')).toHaveText('wiki')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-dayGridWeek-button')).toHaveText('wiki')
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('->')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('-->')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('<-')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('<--')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('tidei')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('mun')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('wiki')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('dei')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('wiki')
       })
 
     })
 
     describe('with buttonIcons turned off', function() {
-
       pushOptions({
         buttonIcons: false
       })
 
       it('should contain default text values', function() {
-        initCalendar()
+        let calendar = initCalendar()
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
 
         // will have actual text now
-        expect($('.fc-next-button')).toHaveText('next')
-        expect($('.fc-nextYear-button')).toHaveText('next year')
-        expect($('.fc-prev-button')).toHaveText('prev')
-        expect($('.fc-prevYear-button')).toHaveText('prev year')
-
-        expect($('.fc-today-button')).toHaveText('today')
-        expect($('.fc-dayGridMonth-button')).toHaveText('month')
-        expect($('.fc-dayGridWeek-button')).toHaveText('week')
-        expect($('.fc-timeGridWeek-button')).toHaveText('week')
-        expect($('.fc-dayGridDay-button')).toHaveText('day')
-        expect($('.fc-dayGridDay-button')).toHaveText('day')
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('next')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('next year')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('prev')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('prev year')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('today')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('month')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('week')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('day')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('week')
       })
 
       it('should contain specified text values', function() {
-        initCalendar({
+        let calendar = initCalendar({
           buttonText: {
             prev: '<-',
             next: '->',
@@ -96,18 +95,18 @@ describe('button text', function() {
             day: 'dei'
           }
         })
-
-        expect($('.fc-next-button')).toHaveText('->')
-        expect($('.fc-nextYear-button')).toHaveText('-->')
-        expect($('.fc-prev-button')).toHaveText('<-')
-        expect($('.fc-prevYear-button')).toHaveText('<--')
-
-        expect($('.fc-today-button')).toHaveText('tidei')
-        expect($('.fc-dayGridMonth-button')).toHaveText('mun')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-timeGridWeek-button')).toHaveText('wiki')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-dayGridWeek-button')).toHaveText('wiki')
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('->')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('-->')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('<-')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('<--')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('tidei')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('mun')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('wiki')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('dei')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('wiki')
       })
 
     })
@@ -115,7 +114,6 @@ describe('button text', function() {
   })
 
   describe('when locale is not default', function() {
-
     pushOptions({
       locale: frLocale
     })
@@ -123,24 +121,24 @@ describe('button text', function() {
     describe('with default buttonIcons', function() {
 
       it('should contain default text values', function() {
-        initCalendar()
+        let calendar = initCalendar()
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
 
         // will contain icons, so will contain no text
-        expect($('.fc-next-button')).toHaveText('')
-        expect($('.fc-nextYear-button')).toHaveText('')
-        expect($('.fc-prev-button')).toHaveText('')
-        expect($('.fc-prevYear-button')).toHaveText('')
-
-        expect($('.fc-today-button')).toHaveText('Aujourd\'hui')
-        expect($('.fc-dayGridMonth-button')).toHaveText('Mois')
-        expect($('.fc-dayGridWeek-button')).toHaveText('Semaine')
-        expect($('.fc-timeGridWeek-button')).toHaveText('Semaine')
-        expect($('.fc-dayGridDay-button')).toHaveText('Jour')
-        expect($('.fc-dayGridDay-button')).toHaveText('Jour')
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('Aujourd\'hui')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('Mois')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('Semaine')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('Jour')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('Semaine')
       })
 
       it('should contain specified text values', function() {
-        initCalendar({
+        let calendar = initCalendar({
           buttonText: {
             prev: '<-',
             next: '->',
@@ -152,48 +150,48 @@ describe('button text', function() {
             day: 'dei'
           }
         })
-
-        expect($('.fc-next-button')).toHaveText('->')
-        expect($('.fc-nextYear-button')).toHaveText('-->')
-        expect($('.fc-prev-button')).toHaveText('<-')
-        expect($('.fc-prevYear-button')).toHaveText('<--')
-
-        expect($('.fc-today-button')).toHaveText('tidei')
-        expect($('.fc-dayGridMonth-button')).toHaveText('mun')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-timeGridWeek-button')).toHaveText('wiki')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-dayGridWeek-button')).toHaveText('wiki')
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('->')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('-->')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('<-')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('<--')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('tidei')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('mun')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('wiki')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('dei')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('wiki')
       })
 
     })
 
     describe('with buttonIcons turned off', function() {
-
       pushOptions({
         buttonIcons: false
       })
 
       it('should contain default text values', function() {
-        initCalendar()
+        let calendar = initCalendar()
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
 
         // will have the locale's actual text now
-        expect($('.fc-next-button')).toHaveText('Suivant')
-        expect($('.fc-prev-button')).toHaveText('Précédent')
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('Suivant')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('Précédent')
         /// / locales files don't have data for prev/next *year*
-        // expect($('.fc-nextYear-button')).toHaveText('Suivant');
-        // expect($('.fc-prevYear-button')).toHaveText('Précédent');
-
-        expect($('.fc-today-button')).toHaveText('Aujourd\'hui')
-        expect($('.fc-dayGridMonth-button')).toHaveText('Mois')
-        expect($('.fc-dayGridWeek-button')).toHaveText('Semaine')
-        expect($('.fc-timeGridWeek-button')).toHaveText('Semaine')
-        expect($('.fc-dayGridDay-button')).toHaveText('Jour')
-        expect($('.fc-dayGridDay-button')).toHaveText('Jour')
+        // expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('Suivant');
+        // expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('Précédent');
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('Aujourd\'hui')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('Mois')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('Semaine')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('Semaine')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('Jour')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('Jour')
       })
 
       it('should contain specified text values', function() {
-        initCalendar({
+        let calendar = initCalendar({
           buttonText: {
             prev: '<-',
             next: '->',
@@ -205,22 +203,19 @@ describe('button text', function() {
             day: 'dei'
           }
         })
-
-        expect($('.fc-next-button')).toHaveText('->')
-        expect($('.fc-nextYear-button')).toHaveText('-->')
-        expect($('.fc-prev-button')).toHaveText('<-')
-        expect($('.fc-prevYear-button')).toHaveText('<--')
-
-        expect($('.fc-today-button')).toHaveText('tidei')
-        expect($('.fc-dayGridMonth-button')).toHaveText('mun')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-timeGridWeek-button')).toHaveText('wiki')
-        expect($('.fc-dayGridDay-button')).toHaveText('dei')
-        expect($('.fc-dayGridWeek-button')).toHaveText('wiki')
+        let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+        expect(toolbarWrapper.getButtonInfo('next').text).toBe('->')
+        expect(toolbarWrapper.getButtonInfo('nextYear').text).toBe('-->')
+        expect(toolbarWrapper.getButtonInfo('prev').text).toBe('<-')
+        expect(toolbarWrapper.getButtonInfo('prevYear').text).toBe('<--')
+
+        expect(toolbarWrapper.getButtonInfo('today').text).toBe('tidei')
+        expect(toolbarWrapper.getButtonInfo('dayGridMonth').text).toBe('mun')
+        expect(toolbarWrapper.getButtonInfo('dayGridWeek').text).toBe('wiki')
+        expect(toolbarWrapper.getButtonInfo('dayGridDay').text).toBe('dei')
+        expect(toolbarWrapper.getButtonInfo('timeGridWeek').text).toBe('wiki')
       })
-
     })
-
   })
-
 })

+ 137 - 62
packages/__tests__/src/legacy/custom-view-duration.js

@@ -1,5 +1,8 @@
 import frLocale from '@fullcalendar/core/locales/fr'
 import { View, createPlugin } from '@fullcalendar/core'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
 
 describe('custom view', function() {
 
@@ -13,11 +16,14 @@ describe('custom view', function() {
     }
     options.defaultView = 'dayGridFourDay'
     options.defaultDate = '2014-12-25'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(1)
-    expect($('.fc-day-grid .fc-row .fc-day').length).toBe(4)
-    expect($('.fc-day-grid .fc-row .fc-day:first'))
-      .toBeMatchedBy('[data-date="2014-12-25"]') // starts on defaultDate
+
+    let calendar = initCalendar(options)
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    let dayEls = dayGridWrapper.getAllDayEls()
+
+    expect(dayGridWrapper.getRowEls().length).toBe(1)
+    expect(dayEls.length).toBe(4)
+    expect(dayEls[0].getAttribute('data-date')).toBe('2014-12-25') // starts on defaultDate
   })
 
   it('renders a 2 week dayGrid view', function() {
@@ -31,11 +37,15 @@ describe('custom view', function() {
     options.defaultView = 'dayGridTwoWeek'
     options.defaultDate = '2014-12-25'
     options.firstDay = 2 // Tues
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(2)
-    expect($('.fc-day-grid .fc-day').length).toBe(14)
-    expect($('.fc-day-grid .fc-day:first')).toBeMatchedBy('.fc-tue') // respects start-of-week
-    expect($('.fc-day-grid .fc-day:first')).toBeMatchedBy('[data-date="2014-12-23"]') // week start. tues
+
+    let calendar = initCalendar(options)
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    let dayEls = dayGridWrapper.getAllDayEls()
+
+    expect(dayGridWrapper.getRowEls().length).toBe(2)
+    expect(dayEls.length).toBe(14)
+    expect(dayEls[0]).toHaveClass(CalendarWrapper.DOW_CLASSNAMES[2]) // respects start-of-week
+    expect(dayEls[0].getAttribute('data-date')).toBe('2014-12-23') // week start. tues
   })
 
   it('will use the provided options', function() {
@@ -48,8 +58,10 @@ describe('custom view', function() {
       titleFormat: function() { return 'special' }
     }
     options.defaultView = 'dayGridFourDay'
-    initCalendar(options)
-    expect($('h2')).toHaveText('special')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+    expect(toolbarWrapper.getTitleText()).toBe('special')
   })
 
   it('will inherit options from the parent view type', function() {
@@ -64,8 +76,10 @@ describe('custom view', function() {
       duration: { days: 4 }
     }
     options.defaultView = 'dayGridFourDay'
-    initCalendar(options)
-    expect($('h2')).toHaveText('dayGridtitle')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+    expect(toolbarWrapper.getTitleText()).toBe('dayGridtitle')
   })
 
   it('will override an option from the parent view type', function() {
@@ -81,8 +95,10 @@ describe('custom view', function() {
       titleFormat: function() { return 'dayGridfourweekttitle' }
     }
     options.defaultView = 'dayGridFourDay'
-    initCalendar(options)
-    expect($('h2')).toHaveText('dayGridfourweekttitle')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+    expect(toolbarWrapper.getTitleText()).toBe('dayGridfourweekttitle')
   })
 
   it('will inherit options from generic "week" type', function() {
@@ -97,8 +113,11 @@ describe('custom view', function() {
       duration: { weeks: 1 }
     }
     options.defaultView = 'dayGridOneWeek'
-    initCalendar(options)
-    expect($('h2')).toHaveText('weektitle')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+    expect(toolbarWrapper.getTitleText()).toBe('weektitle')
   })
 
   it('generic type options for "dayGrid" will override generic "week" options', function() {
@@ -116,8 +135,11 @@ describe('custom view', function() {
       duration: { weeks: 1 }
     }
     options.defaultView = 'dayGridOneWeek'
-    initCalendar(options)
-    expect($('h2')).toHaveText('dayGridtitle')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+    expect(toolbarWrapper.getTitleText()).toBe('dayGridtitle')
   })
 
   it('will not inherit "week" options if more than a single week', function() {
@@ -133,8 +155,11 @@ describe('custom view', function() {
       duration: { weeks: 2 }
     }
     options.defaultView = 'dayGridTwoWeek'
-    initCalendar(options)
-    expect($('h2')).toHaveText('defaultitle')
+
+    let calendar = initCalendar(options)
+    let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+
+    expect(toolbarWrapper.getTitleText()).toBe('defaultitle')
   })
 
   it('renders a 4 day timeGrid view', function() {
@@ -147,11 +172,15 @@ describe('custom view', function() {
     }
     options.defaultView = 'timeGridFourDay'
     options.defaultDate = '2014-12-25'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(1)
-    expect($('.fc-day-grid .fc-row .fc-day').length).toBe(4)
-    expect($('.fc-time-grid .fc-day').length).toBe(4)
-    expect($('.fc-time-grid .fc-day:first')).toBeMatchedBy('[data-date="2014-12-25"]') // starts on defaultDate
+
+    let calendar = initCalendar(options)
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let timeGridDayEls = viewWrapper.timeGrid.getAllDayEls()
+
+    expect(viewWrapper.dayGrid.getRowEls().length).toBe(1)
+    expect(viewWrapper.dayGrid.getAllDayEls().length).toBe(4)
+    expect(timeGridDayEls.length).toBe(4)
+    expect(timeGridDayEls[0].getAttribute('data-date')).toBe('2014-12-25') // starts on defaultDate
   })
 
   it('renders a two week timeGrid view', function() {
@@ -164,11 +193,15 @@ describe('custom view', function() {
     }
     options.defaultView = 'timeGridTwoWeek'
     options.defaultDate = '2014-12-25'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(1)
-    expect($('.fc-day-grid .fc-row .fc-day').length).toBe(14) // one long row
-    expect($('.fc-time-grid .fc-day').length).toBe(14)
-    expect($('.fc-time-grid .fc-day:first')).toBeMatchedBy('[data-date="2014-12-21"]') // week start
+
+    let calendar = initCalendar(options)
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let timeGridDayEls = viewWrapper.timeGrid.getAllDayEls()
+
+    expect(viewWrapper.dayGrid.getRowEls().length).toBe(1)
+    expect(viewWrapper.dayGrid.getAllDayEls().length).toBe(14)
+    expect(timeGridDayEls.length).toBe(14)
+    expect(timeGridDayEls[0].getAttribute('data-date')).toBe('2014-12-21') // week start
   })
 
   it('renders a two month timeGrid view', function() {
@@ -181,12 +214,16 @@ describe('custom view', function() {
     }
     options.defaultView = 'timeGridTwoWeek'
     options.defaultDate = '2014-11-27'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(1)
-    expect($('.fc-day-grid .fc-row .fc-day').length).toBe(61) // one long row
-    expect($('.fc-time-grid .fc-day').length).toBe(61)
-    expect($('.fc-time-grid .fc-day:first')).toBeMatchedBy('[data-date="2014-11-01"]')
-    expect($('.fc-time-grid .fc-day:last')).toBeMatchedBy('[data-date="2014-12-31"]')
+
+    let calendar = initCalendar(options)
+    let viewWrapper = new TimeGridViewWrapper(calendar)
+    let timeGridDayEls = viewWrapper.timeGrid.getAllDayEls()
+
+    expect(viewWrapper.dayGrid.getRowEls().length).toBe(1)
+    expect(viewWrapper.dayGrid.getAllDayEls().length).toBe(61)
+    expect(timeGridDayEls.length).toBe(61)
+    expect(timeGridDayEls[0].getAttribute('data-date')).toBe('2014-11-01')
+    expect(timeGridDayEls[timeGridDayEls.length - 1].getAttribute('data-date')).toBe('2014-12-31') // last
   })
 
   it('renders a two month dayGrid view', function() {
@@ -199,11 +236,15 @@ describe('custom view', function() {
     }
     options.defaultView = 'dayGridTwoWeek'
     options.defaultDate = '2014-11-27'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-row').length).toBe(10)
-    expect($('.fc-day-grid .fc-row:first .fc-day').length).toBe(7)
-    expect($('.fc-day-grid .fc-day:first')).toBeMatchedBy('[data-date="2014-10-26"]')
-    expect($('.fc-day-grid .fc-day:last')).toBeMatchedBy('[data-date="2015-01-03"]')
+
+    let calendar = initCalendar(options)
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    let dayEls = dayGridWrapper.getAllDayEls()
+
+    expect(dayGridWrapper.getRowEls().length).toBe(10)
+    expect(dayGridWrapper.getDayElsInRow(0).length).toBe(7)
+    expect(dayEls[0].getAttribute('data-date')).toBe('2014-10-26')
+    expect(dayEls[dayEls.length - 1].getAttribute('data-date')).toBe('2015-01-03')
   })
 
   it('renders a one year dayGrid view', function() {
@@ -216,9 +257,13 @@ describe('custom view', function() {
     }
     options.defaultView = 'dayGridYear'
     options.defaultDate = '2014-11-27'
-    initCalendar(options)
-    expect($('.fc-day-grid .fc-day:first')).toBeMatchedBy('[data-date="2013-12-29"]')
-    expect($('.fc-day-grid .fc-day:last')).toBeMatchedBy('[data-date="2015-01-03"]')
+
+    let calendar = initCalendar(options)
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    let dayEls = dayGridWrapper.getAllDayEls()
+
+    expect(dayEls[0]).toBeMatchedBy('[data-date="2013-12-29"]')
+    expect(dayEls[dayEls.length - 1]).toBeMatchedBy('[data-date="2015-01-03"]')
   })
 
   describe('buttonText', function() {
@@ -239,8 +284,12 @@ describe('custom view', function() {
         center: 'custom,dayGridMonth'
       }
       options.defaultView = 'custom'
-      initCalendar(options)
-      expect($('.fc-custom-button')).toHaveText('over-ridden')
+
+      let calendar = initCalendar(options)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('over-ridden')
     })
 
     it('accepts buttonText single-unit-match override', function() {
@@ -259,8 +308,12 @@ describe('custom view', function() {
         center: 'custom,dayGridMonth'
       }
       options.defaultView = 'custom'
-      initCalendar(options)
-      expect($('.fc-custom-button')).toHaveText('1day-over-ridden')
+
+      let calendar = initCalendar(options)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('1day-over-ridden')
     })
 
     it('does not accept buttonText unit-match override when unit is more than one', function() {
@@ -279,12 +332,16 @@ describe('custom view', function() {
         center: 'custom,dayGridMonth'
       }
       options.defaultView = 'custom'
-      initCalendar(options)
-      expect($('.fc-custom-button')).toHaveText('awesome')
+
+      let calendar = initCalendar(options)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('awesome')
     })
 
     it('accepts locale\'s single-unit-match override', function() {
-      initCalendar({
+      let calendar = initCalendar({
         locale: frLocale,
         header: {
           center: 'custom,dayGridMonth'
@@ -297,11 +354,15 @@ describe('custom view', function() {
           }
         }
       })
-      expect($('.fc-custom-button')).toHaveText('Jour')
+
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('Jour')
     })
 
     it('accepts explicit View-Specific buttonText, overriding locale\'s single-unit-match override', function() {
-      initCalendar({
+      let calendar = initCalendar({
         locale: frLocale,
         header: {
           center: 'custom,dayGridMonth'
@@ -315,7 +376,11 @@ describe('custom view', function() {
           }
         }
       })
-      expect($('.fc-custom-button')).toHaveText('awesome')
+
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('awesome')
     })
 
     it('respects custom view\'s value', function() {
@@ -331,8 +396,12 @@ describe('custom view', function() {
         center: 'custom,dayGridMonth'
       }
       options.defaultView = 'custom'
-      initCalendar(options)
-      expect($('.fc-custom-button')).toHaveText('awesome')
+
+      let calendar = initCalendar(options)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('custom')
+
+      expect(buttonInfo.text).toBe('awesome')
     })
 
     it('respects custom view\'s value, even when a "smart" property name', function() {
@@ -348,8 +417,12 @@ describe('custom view', function() {
         center: 'dayGridFourDay,dayGridMonth'
       }
       options.defaultView = 'dayGridFourDay'
-      initCalendar(options)
-      expect($('.fc-dayGridFourDay-button')).toHaveText('awesome')
+
+      let calendar = initCalendar(options)
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('dayGridFourDay')
+
+      expect(buttonInfo.text).toBe('awesome')
     })
 
     it('falls back to view name when view lacks metadata', function() {
@@ -359,7 +432,7 @@ describe('custom view', function() {
         render() {}
       }
 
-      initCalendar({
+      let calendar = initCalendar({
         plugins: [
           createPlugin({
             views: {
@@ -372,8 +445,10 @@ describe('custom view', function() {
         },
         defaultView: 'crazy'
       })
+      let toolbarWrapper = new CalendarWrapper(calendar).toolbar
+      let buttonInfo = toolbarWrapper.getButtonInfo('crazy')
 
-      expect($('.fc-crazy-button')).toHaveText('crazy')
+      expect(buttonInfo.text).toBe('crazy')
     })
   })
 

+ 26 - 0
packages/__tests__/src/legacy/datesRender.js

@@ -0,0 +1,26 @@
+import CalendarWrapper from "../lib/wrappers/CalendarWrapper"
+
+describe('datesRender', function() {
+  pushOptions({
+    defaultDate: '2015-02-20'
+  })
+
+  describeOptions({
+    'when in month view': 'dayGridMonth',
+    'when in week view': 'timeGridWeek'
+  }, function() {
+
+    it('fires after the view is rendered, with correct arguments', function(done) {
+      initCalendar({
+        datesRender(arg) {
+          var viewObj = this.view
+          var viewEl = new CalendarWrapper(this).getViewEl()
+
+          expect(viewObj).toBe(arg.view)
+          expect(viewEl).toBe(arg.el)
+          done()
+        }
+      })
+    })
+  })
+})

+ 332 - 362
packages/__tests__/src/legacy/event-dnd.js

@@ -1,68 +1,63 @@
 import { createDuration } from '@fullcalendar/core'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import { waitEventDrag2 } from '../lib/wrappers/interaction-util'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
+import { queryEventElInfo } from '../lib/wrappers/TimeGridWrapper'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 describe('eventDrop', function() {
-  var options
-
-  beforeEach(function() {
-    options = {
-      timeZone: 'UTC',
-      defaultDate: '2014-06-11',
-      editable: true,
-      dragScroll: false,
-      longPressDelay: 100
-    }
-  })
-
-  afterEach(function() {
-    currentCalendar.destroy()
+  pushOptions({
+    timeZone: 'UTC',
+    defaultDate: '2014-06-11',
+    editable: true,
+    dragScroll: false,
+    longPressDelay: 100
   })
 
   describe('when in month view', function() {
-    beforeEach(function() {
-      options.defaultView = 'dayGridMonth'
-    });
+    pushOptions({
+      defaultView: 'dayGridMonth'
+    })
 
     // TODO: test that event's dragged via touch that don't wait long enough for longPressDelay
     // SHOULD NOT drag
 
-    [ false, true ].forEach(function(isTouch) {
+    ;[ false, true ].forEach(function(isTouch) {
       describe('with ' + (isTouch ? 'touch' : 'mouse'), function() {
+
         describe('when dragging an all-day event to another day', function() {
           it('should be given correct arguments, with whole-day delta', function(done) {
+            let calendar = initCalendarWithSpies({
+              events: [ {
+                title: 'all-day event',
+                start: '2014-06-11',
+                allDay: true
+              } ]
+            })
 
-            options.events = [ {
-              title: 'all-day event',
-              start: '2014-06-11',
-              allDay: true
-            } ]
-
-            init(
-              function() {
-                setTimeout(function() {
-                  $('.fc-event').simulate('drag', {
-                    dx: $('.fc-day').width() * 2,
-                    dy: $('.fc-day').height(),
-                    isTouch: isTouch,
-                    delay: isTouch ? 200 : 0
-                  })
-                }, 0)
-              },
-              function(arg) {
-                var delta = createDuration({ day: 9 })
-                expect(arg.delta).toEqual(delta)
-
-                expect(arg.event.start).toEqualDate('2014-06-20')
-                expect(arg.event.end).toBeNull()
-
-                arg.revert()
-                var event = currentCalendar.getEvents()[0]
-
-                expect(event.start).toEqualDate('2014-06-11')
-                expect(event.end).toBeNull()
-
-                done()
-              }
+            let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+            let dragging = dayGridWrapper.dragEventToDate(
+              dayGridWrapper.getFirstEventEl(),
+              '2014-06-11',
+              '2014-06-20',
+              isTouch
             )
+
+            waitEventDrag2(calendar, dragging).then((arg) => {
+              var delta = createDuration({ day: 9 })
+              expect(arg.delta).toEqual(delta)
+
+              expect(arg.event.start).toEqualDate('2014-06-20')
+              expect(arg.event.end).toBeNull()
+
+              arg.revert()
+              var event = currentCalendar.getEvents()[0]
+
+              expect(event.start).toEqualDate('2014-06-11')
+              expect(event.end).toBeNull()
+
+              done()
+            })
           })
         })
       })
@@ -70,155 +65,152 @@ describe('eventDrop', function() {
 
     describe('when gragging a timed event to another day', function() {
       it('should be given correct arguments, with whole-day delta', function(done) {
-        options.events = [ {
-          title: 'timed event',
-          start: '2014-06-11T06:00:00',
-          allDay: false
-        } ]
-
-        init(
-          function() {
-            $('.fc-event').simulate('drag', {
-              dx: $('.fc-day').width() * -2,
-              dy: $('.fc-day').height()
-            })
-          },
-          function(arg) {
-            var delta = createDuration({ day: 5 })
-            expect(arg.delta).toEqual(delta)
+        let calendar = initCalendarWithSpies({
+          events: [ {
+            title: 'timed event',
+            start: '2014-06-11T06:00:00',
+            allDay: false
+          } ]
+        })
 
-            expect(arg.event.start).toEqualDate('2014-06-16T06:00:00Z')
-            expect(arg.event.end).toBeNull()
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let dragging = dayGridWrapper.dragEventToDate(
+          dayGridWrapper.getFirstEventEl(),
+          '2014-06-11',
+          '2014-06-16'
+        )
 
-            arg.revert()
-            var event = currentCalendar.getEvents()[0]
+        waitEventDrag2(calendar, dragging).then((arg) => {
+          var delta = createDuration({ day: 5 })
+          expect(arg.delta).toEqual(delta)
 
-            expect(event.start).toEqualDate('2014-06-11T06:00:00Z')
-            expect(event.end).toBeNull()
+          expect(arg.event.start).toEqualDate('2014-06-16T06:00:00Z')
+          expect(arg.event.end).toBeNull()
 
-            done()
-          }
-        )
+          arg.revert()
+          var event = currentCalendar.getEvents()[0]
+
+          expect(event.start).toEqualDate('2014-06-11T06:00:00Z')
+          expect(event.end).toBeNull()
+
+          done()
+        })
       })
     })
 
     // https://github.com/fullcalendar/fullcalendar/issues/4458
     describe('when dragging an event back in time when duration not editable', function() {
       it('should work', function(done) {
-        options.defaultDate = '2019-01-16'
-        options.eventDurationEditable = false
-        options.events = [ {
-          title: 'event',
-          start: '2019-01-16T10:30:00+00:00',
-          end: '2019-01-16T12:30:00+00:00'
-        } ]
-
-        init(
-          function() {
-            $('.fc-event').simulate('drag', {
-              dx: $('.fc-day').width() * -2 // back two day
-            })
-          },
-          function(arg) {
-            expect(arg.delta).toEqual(createDuration({ day: -2 }))
-            expect(arg.event.start).toEqualDate('2019-01-14T10:30:00+00:00')
-            expect(arg.event.end).toEqualDate('2019-01-14T12:30:00+00:00')
-            done()
-          }
+        let calendar = initCalendarWithSpies({
+          defaultDate: '2019-01-16',
+          eventDurationEditable: false,
+          events: [ {
+            title: 'event',
+            start: '2019-01-16T10:30:00+00:00',
+            end: '2019-01-16T12:30:00+00:00'
+          } ]
+        })
+
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let dragging = dayGridWrapper.dragEventToDate(
+          dayGridWrapper.getFirstEventEl(),
+          '2019-01-16',
+          '2019-01-14'
         )
+
+        waitEventDrag2(calendar, dragging).then((arg) => {
+          expect(arg.delta).toEqual(createDuration({ day: -2 }))
+          expect(arg.event.start).toEqualDate('2019-01-14T10:30:00+00:00')
+          expect(arg.event.end).toEqualDate('2019-01-14T12:30:00+00:00')
+          done()
+        })
       })
     })
 
     // TODO: tests for eventMouseEnter/eventMouseLeave firing correctly when no dragging
     it('should not fire any eventMouseEnter/eventMouseLeave events while dragging', function(done) { // issue 1297
-      options.events = [
-        {
-          title: 'all-day event',
-          start: '2014-06-11',
-          allDay: true,
-          className: 'event1'
-        },
-        {
-          title: 'event2',
-          start: '2014-06-10',
-          allDay: true,
-          className: 'event2'
-        }
-      ]
-      options.eventMouseEnter = function() { }
-      options.eventMouseLeave = function() { }
-      spyOn(options, 'eventMouseEnter')
-      spyOn(options, 'eventMouseLeave')
-
-      init(
-        function() {
-          $('.event1').simulate('drag', {
-            dx: $('.fc-day').width() * 2,
-            dy: $('.fc-day').height(),
-            moves: 10,
-            duration: 1000
-          })
-          setTimeout(function() { // wait until half way through drag
-            $('.event2')
-              .simulate('mouseover')
-              .simulate('mouseenter')
-              .simulate('mouseout')
-              .simulate('mouseleave')
-          }, 500)
-        },
-        function(arg) {
-          expect(options.eventMouseEnter).not.toHaveBeenCalled()
-          expect(options.eventMouseLeave).not.toHaveBeenCalled()
+      let eventMouseEnterSpy = spyOnCalendarCallback('eventMouseEnter')
+      let eventMouseLeaveSpy = spyOnCalendarCallback('eventMouseLeave')
+      let calendar = initCalendar({
+        events: [
+          {
+            title: 'all-day event',
+            start: '2014-06-11',
+            allDay: true,
+            className: 'event1'
+          },
+          {
+            title: 'event2',
+            start: '2014-06-10',
+            allDay: true,
+            className: 'event2'
+          }
+        ]
+      })
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      $('.event1').simulate('drag', {
+        end: dayGridWrapper.getDayEl('2014-06-20'),
+        moves: 10,
+        duration: 1000,
+        onRelease() {
           done()
         }
-      )
+      })
+
+      setTimeout(function() { // wait until half way through drag
+        $('.event2')
+          .simulate('mouseover')
+          .simulate('mouseenter')
+          .simulate('mouseout')
+          .simulate('mouseleave')
+
+        setTimeout(function() {
+          expect(eventMouseEnterSpy).not.toHaveBeenCalled()
+          expect(eventMouseLeaveSpy).not.toHaveBeenCalled()
+        }, 0)
+      }, 500)
     })
   })
 
   describe('when in timeGrid view', function() {
-    beforeEach(function() {
-      options.defaultView = 'timeGridWeek'
-    });
+    pushOptions({
+      defaultView: 'timeGridWeek'
+    })
 
-    [ false, true ].forEach(function(isTouch) {
+    ;[ false, true ].forEach(function(isTouch) {
       describe('with ' + (isTouch ? 'touch' : 'mouse'), function() {
+
         describe('when dragging a timed event to another time on a different day', function() {
           it('should be given correct arguments and delta with days/time', function(done) {
-            options.events = [ {
-              title: 'timed event',
-              start: '2014-06-11T06:00:00',
-              allDay: false
-            } ]
-
-            init(
-              function() {
-                // setTimeout waits for full render, so there's no scroll,
-                // because scroll kills touch drag
-                setTimeout(function() {
-                  $('.fc-event .fc-time').simulate('drag', {
-                    dx: $('th.fc-wed').width(), // 1 day
-                    dy: $('.fc-slats tr:eq(1)').outerHeight() * 2.9, // 1.5 hours
-                    isTouch: isTouch,
-                    delay: isTouch ? 200 : 0 // delay for FF
-                  })
-                }, 100) // delay for FF
-              },
-              function(arg) {
-                var delta = createDuration({ day: 1, hour: 1, minute: 30 })
-                expect(arg.delta).toEqual(delta)
-
-                expect(arg.event.start).toEqualDate('2014-06-12T07:30:00Z')
-                expect(arg.event.end).toBeNull()
-
-                arg.revert()
-                var event = currentCalendar.getEvents()[0]
-
-                expect(event.start).toEqualDate('2014-06-11T06:00:00Z')
-                expect(event.end).toBeNull()
-
-                done()
-              }
+            let calendar = initCalendarWithSpies({
+              events: [ {
+                title: 'timed event',
+                start: '2014-06-11T06:00:00',
+                allDay: false
+              } ]
+            })
+            let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+            let dragging = timeGridWrapper.dragEventToDate(
+              timeGridWrapper.getFirstEventEl(),
+              '2014-06-12T07:30:00'
             )
+
+            waitEventDrag2(calendar, dragging).then((arg) => {
+              var delta = createDuration({ day: 1, hour: 1, minute: 30 })
+              expect(arg.delta).toEqual(delta)
+
+              expect(arg.event.start).toEqualDate('2014-06-12T07:30:00Z')
+              expect(arg.event.end).toBeNull()
+
+              arg.revert()
+              var event = currentCalendar.getEvents()[0]
+
+              expect(event.start).toEqualDate('2014-06-11T06:00:00Z')
+              expect(event.end).toBeNull()
+
+              done()
+            })
           })
         })
       })
@@ -226,267 +218,245 @@ describe('eventDrop', function() {
 
     describe('when dragging an all-day event to another all-day', function() {
       it('should be given correct arguments, with whole-day delta', function(done) {
-        options.events = [ {
-          title: 'all-day event',
-          start: '2014-06-11',
-          allDay: true
-        } ]
-
-        init(
-          function() {
-            $('.fc-event').simulate('drag', {
-              dx: $('th.fc-wed').width() * 2 // 2 days
-            })
-          },
-          function(arg) {
-            var delta = createDuration({ day: 2 })
-            expect(arg.delta).toEqual(delta)
+        let calendar = initCalendarWithSpies({
+          events: [ {
+            title: 'all-day event',
+            start: '2014-06-11',
+            allDay: true
+          } ]
+        })
+        let dayGridWrapper = new TimeGridViewWrapper(calendar).dayGrid
+        let dragging = dayGridWrapper.dragEventToDate(
+          dayGridWrapper.getFirstEventEl(),
+          '2014-06-11',
+          '2014-06-13'
+        )
 
-            expect(arg.event.start).toEqualDate('2014-06-13')
-            expect(arg.event.end).toBeNull()
+        waitEventDrag2(calendar, dragging).then((arg) => {
+          var delta = createDuration({ day: 2 })
+          expect(arg.delta).toEqual(delta)
 
-            arg.revert()
-            var event = currentCalendar.getEvents()[0]
+          expect(arg.event.start).toEqualDate('2014-06-13')
+          expect(arg.event.end).toBeNull()
 
-            expect(event.start).toEqualDate('2014-06-11')
-            expect(event.end).toBeNull()
+          arg.revert()
+          var event = currentCalendar.getEvents()[0]
 
-            done()
-          }
-        )
+          expect(event.start).toEqualDate('2014-06-11')
+          expect(event.end).toBeNull()
+
+          done()
+        })
       })
     })
 
     describe('when dragging an all-day event to a time slot on a different day', function() {
       it('should be given correct arguments and delta with days/time', function(done) {
-        options.scrollTime = '01:00:00'
-        options.height = 400 // short enough to make scrolling happen
-        options.events = [ {
-          title: 'all-day event',
-          start: '2014-06-11',
-          allDay: true
-        } ]
-
-        init(
-          function() {
-            var allDayGrid = $('.fc-timeGrid-view .fc-day-grid')
-            var hr = $('.fc-timeGrid-view .fc-divider')
-            $('.fc-event').simulate('drag', {
-              dx: $('th.fc-wed').width() * -1,
-              dy: allDayGrid.outerHeight() + hr.outerHeight()
-            })
-          },
-          function(arg) {
-            let delta = createDuration({ day: -1, hour: 1 })
-            expect(arg.delta).toEqual(delta)
+        let calendar = initCalendarWithSpies({
+          scrollTime: '01:00:00',
+          height: 400, // short enough to make scrolling happen
+          events: [ {
+            title: 'all-day event',
+            start: '2014-06-11',
+            allDay: true
+          } ]
+        })
+        let viewWrapper = new TimeGridViewWrapper(calendar)
+        let dragging = viewWrapper.timeGrid.dragEventToDate(
+          viewWrapper.dayGrid.getFirstEventEl(),
+          '2014-06-10T01:00:00',
+        )
 
-            expect(arg.event.start).toEqualDate('2014-06-10T01:00:00Z')
-            expect(arg.event.end).toBeNull()
-            expect(arg.event.allDay).toBe(false)
+        waitEventDrag2(calendar, dragging).then((arg) => {
+          let delta = createDuration({ day: -1, hour: 1 })
+          expect(arg.delta).toEqual(delta)
 
-            arg.revert()
-            var event = currentCalendar.getEvents()[0]
+          expect(arg.event.start).toEqualDate('2014-06-10T01:00:00Z')
+          expect(arg.event.end).toBeNull()
+          expect(arg.event.allDay).toBe(false)
 
-            expect(event.start).toEqualDate('2014-06-11')
-            expect(event.end).toBeNull()
-            expect(event.allDay).toBe(true)
+          arg.revert()
+          var event = currentCalendar.getEvents()[0]
 
-            done()
-          }
-        )
+          expect(event.start).toEqualDate('2014-06-11')
+          expect(event.end).toBeNull()
+          expect(event.allDay).toBe(true)
+
+          done()
+        })
       })
     })
 
     describe('when dragging a timed event to an all-day slot on a different day', function() {
       it('should be given correct arguments, with whole-day delta', function(done) {
-        var eventElm
-
-        options.scrollTime = '01:00:00'
-        options.height = 400 // short enough to make scrolling happen
-        options.events = [ {
-          title: 'timed event',
-          start: '2014-06-11T01:00:00',
-          allDay: false
-        } ]
-
-        init(
-          function() {
-            eventElm = $('.fc-event .fc-time').simulate('drag', { // grabs the top of the event
-              dx: $('th.fc-wed').width() * -1,
-              dy: -$('.fc-timeGrid-view .fc-day-grid').outerHeight(),
-              onBeforeRelease: function() {
-                // the all day slot works off of mouse-moving coordinates
-                var offset = eventElm.offset()
-                $('.fc-timeGrid-allday .fc-day-content')
-                  .simulate('mouseover', {
-                    clientX: offset.left + 10,
-                    clientY: offset.top + 10
-                  })
-                  .simulate('mousemove', {
-                    clientX: offset.left + 10,
-                    clientY: offset.top + 10
-                  })
-              }
-            })
-          },
-          function(arg) {
-            var delta = createDuration({ day: -1 })
-            expect(arg.delta).toEqual(delta)
+        let calendar = initCalendarWithSpies({
+          scrollTime: '01:00:00',
+          height: 400, // short enough to make scrolling happen
+          events: [ {
+            title: 'timed event',
+            start: '2014-06-11T01:00:00',
+            allDay: false
+          } ]
+        })
+        let viewWrapper = new TimeGridViewWrapper(calendar)
+        let dragging = viewWrapper.dayGrid.dragEventToDate(
+          viewWrapper.timeGrid.getFirstEventEl(),
+          null,
+          '2014-06-10'
+        )
 
-            expect(arg.event.start).toEqualDate('2014-06-10')
-            expect(arg.event.end).toBeNull()
-            expect(arg.event.allDay).toBe(true)
+        waitEventDrag2(calendar, dragging).then((arg) => {
+          var delta = createDuration({ day: -1 })
+          expect(arg.delta).toEqual(delta)
 
-            arg.revert()
-            var event = currentCalendar.getEvents()[0]
+          expect(arg.event.start).toEqualDate('2014-06-10')
+          expect(arg.event.end).toBeNull()
+          expect(arg.event.allDay).toBe(true)
 
-            expect(event.start).toEqualDate('2014-06-11T01:00:00Z')
-            expect(event.end).toBeNull()
-            expect(event.allDay).toBe(false)
+          arg.revert()
+          var event = currentCalendar.getEvents()[0]
 
-            done()
-          }
-        )
+          expect(event.start).toEqualDate('2014-06-11T01:00:00Z')
+          expect(event.end).toBeNull()
+          expect(event.allDay).toBe(false)
+
+          done()
+        })
       })
     })
 
     describe('when dragging a timed event with no end time', function() {
       it('should continue to only show the updated start time', function(done) {
-        var dragged = false
-
-        options.scrollTime = '01:00:00'
-        options.height = 400 // short enough to make scrolling happen
-        options.events = [ {
-          title: 'timed event',
-          start: '2014-06-11T01:00:00',
-          allDay: false
-        } ]
-
-        init(
-          function() {
-            $('.fc-event .fc-time').simulate('drag', {
-              dy: $('.fc-slats tr:eq(1)').height() * 2.9, // 1.5 hours
-              onBeforeRelease: function() {
-                dragged = true
-                expect($('.fc-event.fc-mirror .fc-time')).toHaveText('2:30')
-              }
-            })
-          },
-          function() {
-            expect(dragged).toBe(true)
-            done()
+        let dragged = false
+        let calendar = initCalendarWithSpies({
+          scrollTime: '01:00:00',
+          height: 400, // short enough to make scrolling happen
+          events: [ {
+            title: 'timed event',
+            start: '2014-06-11T01:00:00',
+            allDay: false
+          } ]
+        })
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+
+        let dragging = timeGridWrapper.dragEventToDate(
+          timeGridWrapper.getFirstEventEl(),
+          '2014-06-11T02:30:00',
+          () => { // onBeforeRelease
+            dragged = true
+            let mirrorEls = timeGridWrapper.getMirrorEls()
+            expect(mirrorEls.length).toBe(1)
+            expect(queryEventElInfo(mirrorEls[0]).timeText).toBe('2:30')
           }
         )
+
+        waitEventDrag2(calendar, dragging).then(() => {
+          expect(dragged).toBe(true)
+          done()
+        })
       })
     })
 
     describe('when dragging a timed event with an end time', function() {
       it('should continue to show the updated start and end time', function(done) {
-        var dragged = false
-
-        options.scrollTime = '01:00:00'
-        options.height = 400 // short enough to make scrolling happen
-        options.events = [ {
-          title: 'timed event',
-          start: '2014-06-11T01:00:00',
-          end: '2014-06-11T02:00:00',
-          allDay: false
-        } ]
-
-        init(
-          function() {
-            $('.fc-event .fc-time').simulate('drag', {
-              dy: $('.fc-slats tr:eq(1)').height() * 2.9, // 1.5 hours
-              onBeforeRelease: function() {
-                dragged = true
-                expect($('.fc-event.fc-mirror .fc-time')).toHaveText('2:30 - 3:30')
-              }
-            })
-          },
-          function() {
-            expect(dragged).toBe(true)
-            done()
+        let dragged = false
+        let calendar = initCalendarWithSpies({
+          scrollTime: '01:00:00',
+          height: 400, // short enough to make scrolling happen
+          events: [ {
+            title: 'timed event',
+            start: '2014-06-11T01:00:00',
+            end: '2014-06-11T02:00:00',
+            allDay: false
+          } ]
+        })
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+
+        let dragging = timeGridWrapper.dragEventToDate(
+          timeGridWrapper.getFirstEventEl(),
+          '2014-06-11T02:30:00',
+          () => { // onBeforeRelease
+            dragged = true
+            let mirrorEls = timeGridWrapper.getMirrorEls()
+            expect(mirrorEls.length).toBe(1)
+            expect(queryEventElInfo(mirrorEls[0]).timeText).toBe('2:30 - 3:30')
           }
         )
+
+        waitEventDrag2(calendar, dragging).then(() => {
+          expect(dragged).toBe(true)
+          done()
+        })
       })
     })
 
     // https://github.com/fullcalendar/fullcalendar/issues/4503
     describe('when dragging to one of the last slots', function() {
       it('should work', function(done) {
-        options.scrollTime = '23:00:00'
-        options.height = 400 // short enough to make scrolling happen
-        options.events = [ {
-          title: 'timed event',
-          start: '2014-06-11T18:00:00', // should be in view without scrolling
-          allDay: false
-        } ]
-
-        init(
-          function() {
-            $('.fc-event .fc-time').simulate('drag', {
-              end: $('.fc-slats tr:eq(47)')
-            })
-          },
-          function() {
-            var event = currentCalendar.getEvents()[0]
-
-            expect(event.start).toEqualDate('2014-06-11T23:30:00Z')
-            expect(event.end).toBeNull()
-            expect(event.allDay).toBe(false)
+        let calendar = initCalendarWithSpies({
+          scrollTime: '23:00:00',
+          height: 400, // short enough to make scrolling happen
+          events: [ {
+            title: 'timed event',
+            start: '2014-06-11T18:00:00', // should be in view without scrolling
+            allDay: false
+          } ]
+        })
 
-            done()
-          }
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let dragging = timeGridWrapper.dragEventToDate(
+          timeGridWrapper.getFirstEventEl(),
+          '2014-06-11T23:30:00'
         )
+
+        waitEventDrag2(calendar, dragging).then(() => {
+          let event = currentCalendar.getEvents()[0]
+
+          expect(event.start).toEqualDate('2014-06-11T23:30:00Z')
+          expect(event.end).toBeNull()
+          expect(event.allDay).toBe(false)
+
+          done()
+        })
       })
     })
   })
 
   // Initialize a calendar, run a drag, and do type-checking of all arguments for all handlers.
   // TODO: more descrimination instead of just checking for 'object'
-  function init(dragFunc, dropHandler) {
-    var eventsRendered = false
-
-    options._eventsPositioned = function() {
-      if (!eventsRendered) { // because event rerendering will happen upon drop
-        dragFunc()
-        eventsRendered = true
-      }
-    }
-    options.eventDragStart = function(arg) {
+  function initCalendarWithSpies(options) {
+
+    options.eventDragStart = (arg) => {
       expect(arg.el instanceof Element).toBe(true)
-      expect(arg.el).toHaveClass('fc-event')
+      expect(arg.el).toHaveClass(CalendarWrapper.EVENT_CLASSNAME)
       expect(typeof arg.event).toBe('object')
       expect(typeof arg.jsEvent).toBe('object')
       expect(typeof arg.view).toBe('object')
     }
-    options.eventDragStop = function(arg) {
-      expect(options.eventDragStart).toHaveBeenCalled()
 
+    options.eventDragStop = (arg) => {
+      expect(options.eventDragStart).toHaveBeenCalled()
       expect(arg.el instanceof Element).toBe(true)
-      expect(arg.el).toHaveClass('fc-event')
+      expect(arg.el).toHaveClass(CalendarWrapper.EVENT_CLASSNAME)
       expect(typeof arg.event).toBe('object')
       expect(typeof arg.jsEvent).toBe('object')
       expect(typeof arg.view).toBe('object')
     }
-    options.eventDrop = function(arg) {
-      expect(options.eventDragStop).toHaveBeenCalled()
 
+    options.eventDrop = (arg) => {
+      expect(options.eventDragStop).toHaveBeenCalled()
       expect(arg.el instanceof Element).toBe(true)
-      expect(arg.el).toHaveClass('fc-event')
+      expect(arg.el).toHaveClass(CalendarWrapper.EVENT_CLASSNAME)
       expect(typeof arg.delta).toBe('object')
       expect(typeof arg.revert).toBe('function')
       expect(typeof arg.jsEvent).toBe('object')
       expect(typeof arg.view).toBe('object')
-
-      dropHandler.apply(this, arguments)
     }
 
     spyOn(options, 'eventDragStart').and.callThrough()
     spyOn(options, 'eventDragStop').and.callThrough()
 
-    setTimeout(function() { // hack. timeGrid view scroll state would get messed up between tests
-      initCalendar(options)
-    }, 0)
+    return initCalendar(options)
   }
+
 })

+ 15 - 10
packages/__tests__/src/legacy/eventLimit-popoverDestroy.js

@@ -1,5 +1,6 @@
-describe('eventLimit popover', function() {
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
 
+describe('eventLimit popover', function() {
   pushOptions({
     defaultView: 'dayGridMonth',
     defaultDate: '2014-08-01',
@@ -18,7 +19,8 @@ describe('eventLimit popover', function() {
     var eventsRendered = {}
     var renderCount = 0
     var activated = false
-    initCalendar({
+
+    let calendar = initCalendar({
       eventRender: function(eventObject, element, view) {
         if (activated) {
           eventsRendered[eventObject.title] = eventObject
@@ -30,17 +32,18 @@ describe('eventLimit popover', function() {
         --renderCount
       }
     })
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
 
     // Activate flags and pop event limit popover
     activated = true
-    $('.fc-more').simulate('click')
+    dayGridWrapper.openMorePopover()
     setTimeout(function() {
 
-      expect($('.fc-more-popover')).toBeVisible()
+      expect(dayGridWrapper.getMorePopoverEl()).toBeVisible()
 
-      $('.fc-more-popover .fc-close').simulate('click')
+      dayGridWrapper.closeMorePopover()
       setTimeout(function() {
-        expect($('.fc-more-popover')).not.toBeVisible()
+        expect(dayGridWrapper.getMorePopoverEl()).not.toBeVisible()
         expect(Object.keys(eventsRendered).length).toEqual(0)
         expect(renderCount).toEqual(0)
         done()
@@ -52,7 +55,8 @@ describe('eventLimit popover', function() {
     var eventsRendered = {}
     var renderCount = 0
     var activated = false
-    initCalendar({
+
+    let calendar = initCalendar({
       eventRender: function(eventObject, element, view) {
         if (activated) {
           eventsRendered[eventObject.title] = eventObject
@@ -64,18 +68,19 @@ describe('eventLimit popover', function() {
         --renderCount
       }
     })
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
 
     // Activate flags and pop event limit popover
     activated = true
-    $('.fc-more').simulate('click')
+    dayGridWrapper.openMorePopover()
     setTimeout(function() {
 
-      expect($('.fc-more-popover')).toBeVisible()
+      expect(dayGridWrapper.getMorePopoverEl()).toBeVisible()
 
       $('body').simulate('mousedown').simulate('click')
       setTimeout(function() {
 
-        expect($('.fc-more-popover')).not.toBeVisible()
+        expect(dayGridWrapper.getMorePopoverEl()).not.toBeVisible()
         expect(Object.keys(eventsRendered).length).toEqual(0)
         expect(renderCount).toEqual(0)
         done()

+ 37 - 23
packages/__tests__/src/legacy/eventLimitClick.js

@@ -1,5 +1,7 @@
-describe('eventLimitClick', function() { // simulate a click
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
+import DayGridWrapper from '../lib/wrappers/DayGridWrapper'
 
+describe('eventLimitClick', function() { // simulate a click
   pushOptions({
     defaultDate: '2014-08-01', // important that it is the first week, so works w/ month + week views
     defaultView: 'dayGridMonth',
@@ -12,10 +14,6 @@ describe('eventLimitClick', function() { // simulate a click
     ]
   })
 
-  function openWithClick() {
-    $('.fc-more').simulate('click')
-  }
-
   describe('when set to "popover"', function() {
 
     pushOptions({
@@ -23,10 +21,12 @@ describe('eventLimitClick', function() { // simulate a click
     })
 
     it('renders a popover upon click', function(done) {
-      initCalendar()
-      openWithClick()
+      let calendar = initCalendar()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      dayGridWrapper.openMorePopover()
       setTimeout(function() {
-        expect($('.fc-more-popover')).toBeVisible()
+        expect(dayGridWrapper.getMorePopoverEl()).toBeVisible()
         done()
       })
     })
@@ -41,14 +41,16 @@ describe('eventLimitClick', function() { // simulate a click
     })
 
     it('should go to dayGridWeek if it is one of the available views', function(done) {
-      initCalendar({
+      let calendar = initCalendar({
         header: {
           left: 'prev,next today',
           center: 'title',
           right: 'dayGridMonth,dayGridWeek,dayGridDay'
         }
       })
-      openWithClick()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      dayGridWrapper.openMorePopover()
       setTimeout(function() {
         var view = currentCalendar.view
         expect(view.type).toBe('dayGridWeek')
@@ -57,14 +59,16 @@ describe('eventLimitClick', function() { // simulate a click
     })
 
     it('should go to week if it is one of the available views', function(done) {
-      initCalendar({
+      let calendar = initCalendar({
         header: {
           left: 'prev,next today',
           center: 'title',
           right: 'dayGridMonth,timeGridWeek,timeGridDay'
         }
       })
-      openWithClick()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      dayGridWrapper.openMorePopover()
       setTimeout(function() {
         var view = currentCalendar.view
         expect(view.type).toBe('timeGridWeek')
@@ -80,14 +84,16 @@ describe('eventLimitClick', function() { // simulate a click
     })
 
     it('should go to dayGridDay if it is one of the available views', function(done) {
-      initCalendar({
+      let calendar = initCalendar({
         header: {
           left: 'prev,next today',
           center: 'title',
           right: 'dayGridMonth,dayGridWeek,dayGridDay'
         }
       })
-      openWithClick()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      dayGridWrapper.openMorePopover()
       setTimeout(function() {
         var view = currentCalendar.view
         expect(view.type).toBe('dayGridDay')
@@ -96,14 +102,16 @@ describe('eventLimitClick', function() { // simulate a click
     })
 
     it('should go to day if it is one of the available views', function(done) {
-      initCalendar({
+      let calendar = initCalendar({
         header: {
           left: 'prev,next today',
           center: 'title',
           right: 'dayGridMonth,timeGridWeek,timeGridDay'
         }
       })
-      openWithClick()
+      let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+      dayGridWrapper.openMorePopover()
       setTimeout(function() {
         var view = currentCalendar.view
         expect(view.type).toBe('timeGridDay')
@@ -113,7 +121,7 @@ describe('eventLimitClick', function() { // simulate a click
   })
 
   it('works with an explicit view name', function(done) {
-    initCalendar({
+    let calendar = initCalendar({
       eventLimitClick: 'timeGridWeek',
       header: {
         left: 'prev,next today',
@@ -121,7 +129,9 @@ describe('eventLimitClick', function() { // simulate a click
         right: 'dayGridMonth,dayGridWeek,dayGridDay'
       }
     })
-    openWithClick()
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+    dayGridWrapper.openMorePopover()
     setTimeout(function() {
       var view = currentCalendar.view
       expect(view.type).toBe('timeGridWeek')
@@ -130,28 +140,32 @@ describe('eventLimitClick', function() { // simulate a click
   })
 
   it('works with custom function and all the arguments are correct', function(done) {
-    initCalendar({
+    let calendar = initCalendar({
       eventLimitClick: function(arg) {
         expect(typeof arg).toBe('object')
         expect(arg.date).toEqualDate('2014-07-29')
         expect(arg.dayEl.getAttribute('data-date')).toBe('2014-07-29')
         expect(arg.hiddenSegs.length).toBe(2)
         expect(arg.segs.length).toBe(4)
-        expect(arg.moreEl).toHaveClass('fc-more')
+        expect(arg.moreEl).toHaveClass(DayGridWrapper.MORE_LINK_CLASSNAME)
         expect(typeof arg.jsEvent).toBe('object')
       }
     })
-    openWithClick()
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+    dayGridWrapper.openMorePopover()
     setTimeout(done)
   })
 
   it('works with custom function, and can return a view name', function(done) {
-    initCalendar({
+    let calendar = initCalendar({
       eventLimitClick: function(cellInfo, jsEvent) {
         return 'timeGridDay'
       }
     })
-    openWithClick()
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+
+    dayGridWrapper.openMorePopover()
     setTimeout(function() {
       var view = currentCalendar.view
       expect(view.type).toBe('timeGridDay')

+ 117 - 92
packages/__tests__/src/legacy/eventRender.js

@@ -1,5 +1,8 @@
-describe('eventRender', function() {
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import TimeGridViewWrapper from '../lib/wrappers/TimeGridViewWrapper'
 
+describe('eventRender', function() {
   pushOptions({
     defaultDate: '2014-11-12',
     scrollTime: '00:00:00',
@@ -9,41 +12,36 @@ describe('eventRender', function() {
     } ]
   })
 
-  $.each({
-    dayGridMonth: '.fc-day-grid',
-    timeGridWeek: '.fc-time-grid'
-  }, function(viewName, gridSelector) {
-    describe('when in ' + viewName + ' view', function() {
+  describeOptions('defaultView', {
+    'when in day-grid': 'dayGridMonth',
+    'when in time-grid': 'timeGridWeek'
+  }, function() {
 
-      pushOptions({
-        defaultView: viewName
-      })
-
-      describe('with foreground event', function() {
-        it('receives correct args AND can modify the element', function() {
-          var options = {
-            eventRender: function(arg) {
-              expect(typeof arg.event).toBe('object')
-              expect(arg.event.rendering).toBe('')
-              expect(arg.event.start).toBeDefined()
-              expect(arg.el instanceof HTMLElement).toBe(true)
-              expect(typeof arg.view).toBe('object')
-              expect(arg.isMirror).toBe(false)
-              $(arg.el).css('font-size', '20px')
-            }
+    describe('with foreground event', function() {
+      it('receives correct args AND can modify the element', function() {
+        var options = {
+          eventRender: function(arg) {
+            expect(typeof arg.event).toBe('object')
+            expect(arg.event.rendering).toBe('')
+            expect(arg.event.start).toBeDefined()
+            expect(arg.el instanceof HTMLElement).toBe(true)
+            expect(typeof arg.view).toBe('object')
+            expect(arg.isMirror).toBe(false)
+            $(arg.el).css('font-size', '20px')
           }
-          spyOn(options, 'eventRender').and.callThrough()
-          initCalendar(options)
+        }
+        spyOn(options, 'eventRender').and.callThrough()
+        let calendar = initCalendar(options)
+        let calendarWrapper = new CalendarWrapper(calendar)
+        let eventEl = calendarWrapper.getFirstEventEl()
 
-          expect($(gridSelector).find('.fc-event').css('font-size')).toBe('20px')
-          expect(options.eventRender).toHaveBeenCalled()
-        })
+        expect($(eventEl).css('font-size')).toBe('20px')
+        expect(options.eventRender).toHaveBeenCalled()
       })
     })
   })
 
   describe('when in month view', function() {
-
     pushOptions({
       defaultView: 'dayGridMonth',
       events: [ {
@@ -55,34 +53,37 @@ describe('eventRender', function() {
     describe('with a foreground event', function() {
 
       it('can return a new element', function() {
-        var options = {
-          eventRender: function(arg) {
-            return $('<div class="fc-event sup" style="background-color:green">sup g</div>')[0]
+        let options = {
+          eventRender() {
+            return $(`<div class="sup ${CalendarWrapper.EVENT_CLASSNAME}" style="background-color:green">sup g</div>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let eventEl = dayGridWrapper.getFirstEventEl()
 
-        expect($('.fc-day-grid .sup').length).toBe(1)
+        expect(eventEl).toHaveClass('sup')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return false and cancel rendering', function() {
-        var options = {
+        let options = {
           eventRender() {
             return false
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let eventEls = dayGridWrapper.getEventEls()
 
-        expect($('.fc-day-grid .fc-event').length).toBe(0)
+        expect(eventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
     })
 
     describe('with an all-day background event', function() {
-
       pushOptions({
         events: [ {
           title: 'my event',
@@ -92,65 +93,74 @@ describe('eventRender', function() {
       })
 
       it('receives correct args AND can modify the element', function() {
-        var options = {
-          eventRender: function(arg) {
+        let options = {
+          eventRender(arg) {
             expect(typeof arg.event).toBe('object')
             expect(arg.event.rendering).toBe('background')
             expect(arg.event.start).toBeDefined()
             expect(arg.el instanceof HTMLElement).toBe(true)
             expect(typeof arg.view).toBe('object')
             $(arg.el).css('font-size', '20px')
-          },
+          }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let bgEventEls = dayGridWrapper.getBgEventEls()
 
-        expect($('.fc-day-grid .fc-bgevent').css('font-size')).toBe('20px')
+        expect(bgEventEls.length).toBe(1)
+        expect($(bgEventEls).css('font-size')).toBe('20px')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return a new element', function() {
-        var options = {
-          eventRender: function(arg) {
-            return $('<td class="sup" style="background-color:green">sup g</td>')[0]
+        let options = {
+          eventRender() {
+            return $(`<td class="sup ${CalendarWrapper.BG_EVENT_CLASSNAME}" style="background-color:green">sup g</td>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let bgEventEls = dayGridWrapper.getBgEventEls()
 
-        expect($('.fc-day-grid .sup').length).toBe(1)
+        expect(bgEventEls.length).toBe(1)
+        expect(bgEventEls[0]).toHaveClass('sup')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('won\'t rendering when returning a new element of the wrong type', function() {
-        var options = {
-          eventRender: function(arg) {
-            return $('<div class="sup" style="background-color:green">sup g</div>')[0]
+        let options = {
+          eventRender() {
+            return $(`<div class="sup ${CalendarWrapper.BG_EVENT_CLASSNAME}" style="background-color:green">sup g</div>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let bgEventEls = dayGridWrapper.getBgEventEls()
 
-        expect($('.fc-day-grid .sup').length).toBe(0)
+        expect(bgEventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return false and cancel rendering', function() {
-        var options = {
+        let options = {
           eventRender: function(arg) {
             return false
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let bgEventEls = dayGridWrapper.getBgEventEls()
 
-        expect($('.fc-day-grid .fc-bgevent').length).toBe(0)
+        expect(bgEventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
     })
 
     describe('with a timed background event', function() { // not exactly related to eventRender!
-
       pushOptions({
         events: [ {
           title: 'my event',
@@ -160,26 +170,26 @@ describe('eventRender', function() {
       })
 
       it('won\'t render or call eventRender', function() {
-        var options = {
-          eventRender: function(arg) {},
+        let options = {
+          eventRender() {}
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let bgEventEls = dayGridWrapper.getBgEventEls()
 
-        expect($('.fc-day-grid .fc-bgevent').length).toBe(0)
+        expect(bgEventEls.length).toBe(0)
         expect(options.eventRender).not.toHaveBeenCalled()
       })
     })
   })
 
   describe('when in week view', function() {
-
     pushOptions({
       defaultView: 'timeGridWeek'
     })
 
     describe('with a foreground event', function() {
-
       pushOptions({
         events: [ {
           title: 'my event',
@@ -188,34 +198,38 @@ describe('eventRender', function() {
       })
 
       it('can return a new element', function() {
-        var options = {
-          eventRender: function(arg) {
-            return $('<div class="fc-event sup" style="background-color:green">sup g</div>')[0]
+        let options = {
+          eventRender() {
+            return $(`<div class="${CalendarWrapper.EVENT_CLASSNAME} sup" style="background-color:green">sup g</div>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let eventEls = timeGridWrapper.getEventEls()
 
-        expect($('.fc-time-grid .sup').length).toBe(1)
+        expect(eventEls.length).toBe(1)
+        expect(eventEls[0]).toHaveClass('sup')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return false and cancel rendering', function() {
-        var options = {
-          eventRender: function(arg) {
+        let options = {
+          eventRender() {
             return false
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let eventEls = timeGridWrapper.getEventEls()
 
-        expect($('.fc-time-grid .fc-event').length).toBe(0)
+        expect(eventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
     })
 
     describe('with a timed background event', function() {
-
       pushOptions({
         events: [ {
           title: 'my event',
@@ -225,7 +239,7 @@ describe('eventRender', function() {
       })
 
       it('receives correct args AND can modify the element', function() {
-        var options = {
+        let options = {
           eventRender: function(arg) {
             expect(typeof arg.event).toBe('object')
             expect(arg.event.rendering).toBe('background')
@@ -236,48 +250,58 @@ describe('eventRender', function() {
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let bgEventEls = timeGridWrapper.getBgEventEls()
 
-        expect($('.fc-time-grid .fc-bgevent').css('font-size')).toBe('20px')
+        expect(bgEventEls.length).toBe(1)
+        expect($(bgEventEls[0]).css('font-size')).toBe('20px')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return a new element', function() {
-        var options = {
+        let options = {
           eventRender: function() {
-            return $('<div class="fc-bgevent sup" style="background-color:green">sup g</div>')[0]
+            return $(`<div class="sup ${CalendarWrapper.BG_EVENT_CLASSNAME}" style="background-color:green">sup g</div>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let bgEventEls = timeGridWrapper.getBgEventEls()
 
-        expect($('.fc-time-grid .sup').length).toBe(1)
+        expect(bgEventEls.length).toBe(1)
+        expect(bgEventEls[0]).toHaveClass('sup')
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('won\'t rendering when returning a new element of the wrong type', function() {
-        var options = {
-          eventRender: function() {
-            return $('<p class="fc-bgevent sup" style="background-color:green">sup g</p>')[0]
+        let options = {
+          eventRender() {
+            return $(`<p class="sup ${CalendarWrapper.BG_EVENT_CLASSNAME}" style="background-color:green">sup g</p>`)[0]
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let bgEventEls = timeGridWrapper.getBgEventEls()
 
-        expect($('.fc-time-grid .sup').length).toBe(0)
+        expect(bgEventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
 
       it('can return false and cancel rendering', function() {
-        var options = {
-          eventRender: function() {
+        let options = {
+          eventRender() {
             return false
           }
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let timeGridWrapper = new TimeGridViewWrapper(calendar).timeGrid
+        let bgEventEls = timeGridWrapper.getBgEventEls()
 
-        expect($('.fc-time-grid .fc-bgevent').length).toBe(0)
+        expect(bgEventEls.length).toBe(0)
         expect(options.eventRender).toHaveBeenCalled()
       })
     })
@@ -292,14 +316,15 @@ describe('eventRender', function() {
       })
 
       it('will render in all-day AND timed slots', function() {
-        var options = {
+        let options = {
           eventRender: function() {}
         }
         spyOn(options, 'eventRender').and.callThrough()
-        initCalendar(options)
+        let calendar = initCalendar(options)
+        let viewWrapper = new TimeGridViewWrapper(calendar)
 
-        expect($('.fc-day-grid .fc-bgevent').length).toBe(1)
-        expect($('.fc-time-grid .fc-bgevent').length).toBe(1)
+        expect(viewWrapper.dayGrid.getBgEventEls().length).toBe(1)
+        expect(viewWrapper.timeGrid.getBgEventEls().length).toBe(1)
         expect(options.eventRender).toHaveBeenCalled()
       })
     })

+ 26 - 26
packages/__tests__/src/legacy/eventTimeFormat.js

@@ -1,7 +1,7 @@
 import enGbLocale from '@fullcalendar/core/locales/en-gb'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 describe('eventTimeFormat', function() {
-
   pushOptions({
     defaultDate: '2014-06-04',
     events: [ {
@@ -11,64 +11,57 @@ describe('eventTimeFormat', function() {
     } ]
   })
 
-  function getRenderedEventTime() {
-    return $('.fc-event:first .fc-time').text()
-  }
-
   describe('when in month view', function() {
-
     pushOptions({
       defaultView: 'dayGridMonth'
     })
 
     it('renders correctly when default', function() {
-      initCalendar()
-      expect(getRenderedEventTime()).toBe('3p')
+      let calendar = initCalendar()
+      expectEventTimeText(calendar, '3p')
     })
 
     it('renders correctly when default and the locale is customized', function() {
-      initCalendar({
+      let calendar = initCalendar({
         locale: enGbLocale
       })
-      expect(getRenderedEventTime()).toBe('15')
+      expectEventTimeText(calendar, '15')
     })
 
     it('renders correctly when customized', function() {
-      initCalendar({
+      let calendar = initCalendar({
         eventTimeFormat: { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }
       })
-      expect(getRenderedEventTime()).toBe('15:00:00')
+      expectEventTimeText(calendar, '15:00:00')
     })
   })
 
   describe('when in week view', function() {
-
     pushOptions({
       defaultView: 'timeGridWeek'
     })
 
     it('renders correctly when default', function() {
-      initCalendar()
-      expect(getRenderedEventTime()).toBe('3:00 - 5:00')
+      let calendar = initCalendar()
+      expectEventTimeText(calendar, '3:00 - 5:00')
     })
 
     it('renders correctly when default and the locale is customized', function() {
-      initCalendar({
+      let calendar = initCalendar({
         locale: enGbLocale
       })
-      expect(getRenderedEventTime()).toBe('15:00 - 17:00')
+      expectEventTimeText(calendar, '15:00 - 17:00')
     })
 
     it('renders correctly when customized', function() {
-      initCalendar({
+      let calendar = initCalendar({
         eventTimeFormat: { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }
       })
-      expect(getRenderedEventTime()).toBe('15:00:00 - 17:00:00')
+      expectEventTimeText(calendar, '15:00:00 - 17:00:00')
     })
   })
 
   describe('when in multi-day custom dayGrid view', function() {
-
     pushOptions({
       views: {
         dayGridTwoDay: {
@@ -80,21 +73,28 @@ describe('eventTimeFormat', function() {
     })
 
     it('defaults to no end time', function() {
-      initCalendar()
-      expect(getRenderedEventTime()).toBe('3p')
+      let calendar = initCalendar()
+      expectEventTimeText(calendar, '3p')
     })
   })
 
   describe('when in dayGridDay view', function() {
-
     pushOptions({
       defaultView: 'dayGridDay'
     })
 
-
     it('defaults to showing the end time', function() {
-      initCalendar()
-      expect(getRenderedEventTime()).toBe('3p - 5p')
+      let calendar = initCalendar()
+      expectEventTimeText(calendar, '3p - 5p')
     })
   })
+
+
+  function expectEventTimeText(calendar, expected) {
+    let calendarWrapper = new CalendarWrapper(calendar)
+    let firstEventEl = calendarWrapper.getFirstEventEl()
+    let eventInfo = calendarWrapper.getEventElInfo(firstEventEl)
+    expect(eventInfo.timeText).toBe(expected)
+  }
+
 })

+ 257 - 241
packages/__tests__/src/legacy/events-gcal.js

@@ -1,5 +1,7 @@
 import GoogleCalendarPlugin from '@fullcalendar/google-calendar'
 import DayGridPlugin from '@fullcalendar/daygrid'
+import DayGridViewWrapper from '../lib/wrappers/DayGridViewWrapper'
+import CalendarWrapper from '../lib/wrappers/CalendarWrapper'
 
 // HACK: in our CI setup, requests to the google-calendar api were failing for some reason
 // (requests to other services were working however)
@@ -12,25 +14,23 @@ if (SKIP_GCAL) {
 SKIP_GCAL ||
 describe('Google Calendar plugin', function() {
 
-  var API_KEY = 'AIzaSyDcnW6WejpTOCffshGDDb4neIrXVUA1EAE'
-  var HOLIDAY_CALENDAR_ID = 'en.usa#[email protected]'
+  const API_KEY = 'AIzaSyDcnW6WejpTOCffshGDDb4neIrXVUA1EAE'
+  const HOLIDAY_CALENDAR_ID = 'en.usa#[email protected]'
 
   // Google sometimes stops returning old events. Will need to update this sometimes.
-  var DEFAULT_MONTH = '2020-05'
-  var NUM_EVENTS = 3 // number of holidays
+  const DEFAULT_MONTH = '2020-05'
+  const NUM_EVENTS = 3 // number of holidays
 
-  var options
-  var currentWarnArgs
-  var oldConsoleWarn
+  let currentWarnArgs
+  let oldConsoleWarn
 
-  beforeEach(function() {
-
-    options = {
-      plugins: [ GoogleCalendarPlugin, DayGridPlugin ],
-      defaultView: 'dayGridMonth',
-      defaultDate: DEFAULT_MONTH + '-01'
-    }
+  pushOptions({
+    plugins: [ GoogleCalendarPlugin, DayGridPlugin ],
+    defaultView: 'dayGridMonth',
+    defaultDate: DEFAULT_MONTH + '-01'
+  })
 
+  beforeEach(function() {
     // Intercept calls to console.warn
     currentWarnArgs = null
     oldConsoleWarn = console.warn
@@ -44,111 +44,123 @@ describe('Google Calendar plugin', function() {
   })
 
   it('request/receives correctly when local timezone', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = { googleCalendarId: HOLIDAY_CALENDAR_ID }
-    options.timeZone = 'local'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      var i
-
-      expect(events.length).toBe(NUM_EVENTS)
-      for (i = 0; i < events.length; i++) {
-        expect(events[i].url).not.toMatch('ctz=')
-      }
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: { googleCalendarId: HOLIDAY_CALENDAR_ID },
+      timeZone: 'local',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        let i
+
+        expect(events.length).toBe(NUM_EVENTS)
+        for (i = 0; i < events.length; i++) {
+          expect(events[i].url).not.toMatch('ctz=')
+        }
 
-      done()
-    }
-    initCalendar(options)
+        done()
+      }
+    })
   })
 
   it('request/receives correctly when UTC timezone', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = { googleCalendarId: HOLIDAY_CALENDAR_ID }
-    options.timeZone = 'UTC'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      var i
-
-      expect(events.length).toBe(NUM_EVENTS)
-      for (i = 0; i < events.length; i++) {
-        expect(events[i].url).toMatch('ctz=UTC')
-      }
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: { googleCalendarId: HOLIDAY_CALENDAR_ID },
+      timeZone: 'UTC',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        let i
+
+        expect(events.length).toBe(NUM_EVENTS)
+        for (i = 0; i < events.length; i++) {
+          expect(events[i].url).toMatch('ctz=UTC')
+        }
 
-      done()
-    }
-    initCalendar(options)
+        done()
+      }
+    })
   })
 
   it('request/receives correctly when named timezone, defaults to not editable', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = { googleCalendarId: HOLIDAY_CALENDAR_ID }
-    options.timeZone = 'America/New_York'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      var eventEls = $('.fc-event')
-      var i
-
-      expect(events.length).toBe(NUM_EVENTS)
-      for (i = 0; i < events.length; i++) {
-        expect(events[i].url).toMatch('ctz=America/New_York')
-      }
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: { googleCalendarId: HOLIDAY_CALENDAR_ID },
+      timeZone: 'America/New_York',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let eventEls = dayGridWrapper.getEventEls()
+        let i
+
+        expect(events.length).toBe(NUM_EVENTS)
+        for (i = 0; i < events.length; i++) {
+          expect(events[i].url).toMatch('ctz=America/New_York')
+        }
+
+        expect(eventEls.length).toBe(NUM_EVENTS)
+        expect($('.' + CalendarWrapper.EVENT_RESIZER_CLASSNAME, eventEls[0]).length).toBe(0) // not editable
 
-      expect(eventEls.length).toBe(NUM_EVENTS)
-      expect(eventEls.find('.fc-resizer').length).toBe(0) // not editable
-
-      done()
-    }
-    initCalendar(options)
+        done()
+      }
+    })
   })
 
   it('allows editable to explicitly be set to true', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = {
-      googleCalendarId: HOLIDAY_CALENDAR_ID,
-      editable: true
-    }
-    options._eventsPositioned = function() {
-      var eventEls = $('.fc-event')
-      expect(eventEls.length).toBe(NUM_EVENTS)
-      expect(eventEls.find('.fc-resizer').length).toBeGreaterThan(0) // editable!
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: {
+        googleCalendarId: HOLIDAY_CALENDAR_ID,
+        editable: true
+      },
+      _eventsPositioned() {
+        let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+        let eventEls = dayGridWrapper.getEventEls()
+
+        expect(eventEls.length).toBe(NUM_EVENTS)
+
+        for (let eventEl of eventEls) {
+          expect($('.' + CalendarWrapper.EVENT_RESIZER_CLASSNAME, eventEl).length).toBeGreaterThan(0) // editable!
+        }
+
+        done()
+      }
+    })
   })
 
   it('fetches events correctly when API key is in the event source', function(done) {
-    options.events = {
-      googleCalendarId: HOLIDAY_CALENDAR_ID,
-      googleCalendarApiKey: API_KEY
-    }
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      events: {
+        googleCalendarId: HOLIDAY_CALENDAR_ID,
+        googleCalendarApiKey: API_KEY
+      },
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+        done()
+      }
+    })
   })
 
   describe('when not given an API key', function() {
     it('calls error handlers, raises warning, and receives no events', function(done) {
-      options.eventSourceFailure = function(err) {
-        expect(typeof err).toBe('object')
-      }
-      options.events = {
-        failure: function(err) {
+      let options = {
+        eventSourceFailure: function(err) {
           expect(typeof err).toBe('object')
         },
-        googleCalendarId: HOLIDAY_CALENDAR_ID
-      }
-      options._eventsPositioned = function() {
-        var events = currentCalendar.getEvents()
-
-        expect(events.length).toBe(0)
-        expect(currentWarnArgs.length).toBeGreaterThan(0)
-        expect(options.eventSourceFailure).toHaveBeenCalled()
-        expect(options.events.failure).toHaveBeenCalled()
-
-        done()
+        events: {
+          failure: function(err) {
+            expect(typeof err).toBe('object')
+          },
+          googleCalendarId: HOLIDAY_CALENDAR_ID
+        },
+        _eventsPositioned() {
+          let events = this.getEvents()
+          expect(events.length).toBe(0)
+          expect(currentWarnArgs.length).toBeGreaterThan(0)
+          expect(options.eventSourceFailure).toHaveBeenCalled()
+          expect(options.events.failure).toHaveBeenCalled()
+          done()
+        }
       }
       spyOn(options, 'eventSourceFailure').and.callThrough()
       spyOn(options.events, 'failure').and.callThrough()
@@ -158,25 +170,25 @@ describe('Google Calendar plugin', function() {
 
   describe('when given a bad API key', function() {
     it('calls error handlers, raises warning, and receives no event', function(done) {
-      options.googleCalendarApiKey = 'asdfasdfasdf'
-      options.eventSourceFailure = function(err) {
-        expect(typeof err).toBe('object')
-      }
-      options.events = {
-        failure: function(err) {
+      let options = {
+        googleCalendarApiKey: 'asdfasdfasdf',
+        eventSourceFailure(err) {
           expect(typeof err).toBe('object')
         },
-        googleCalendarId: HOLIDAY_CALENDAR_ID
-      }
-      options._eventsPositioned = function() {
-        var events = currentCalendar.getEvents()
-
-        expect(events.length).toBe(0)
-        expect(currentWarnArgs.length).toBeGreaterThan(0)
-        expect(options.eventSourceFailure).toHaveBeenCalled()
-        expect(options.events.failure).toHaveBeenCalled()
-
-        done()
+        events: {
+          failure: function(err) {
+            expect(typeof err).toBe('object')
+          },
+          googleCalendarId: HOLIDAY_CALENDAR_ID
+        },
+        _eventsPositioned() {
+          let events = this.getEvents()
+          expect(events.length).toBe(0)
+          expect(currentWarnArgs.length).toBeGreaterThan(0)
+          expect(options.eventSourceFailure).toHaveBeenCalled()
+          expect(options.events.failure).toHaveBeenCalled()
+          done()
+        }
       }
       spyOn(options, 'eventSourceFailure').and.callThrough()
       spyOn(options.events, 'failure').and.callThrough()
@@ -185,165 +197,169 @@ describe('Google Calendar plugin', function() {
   })
 
   it('works when `events` is the actual calendar ID', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = HOLIDAY_CALENDAR_ID
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: HOLIDAY_CALENDAR_ID,
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+        done()
+      }
+    })
   })
 
   it('detects a google-calendar when `events` is the actual calendar ID, with complicated characters (1)', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = '[email protected]'
-    options._eventsPositioned = function() {
-      expect(currentWarnArgs.length).toBe(2)
-      expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
-      done()
-    }
-    initCalendar(options)
+    initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: '[email protected]',
+      _eventsPositioned() {
+        expect(currentWarnArgs.length).toBe(2)
+        expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
+        done()
+      }
+    })
   })
 
   it('detects a google-calendar when `events` is the actual calendar ID, with complicated characters (2)', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = '[email protected]'
-    options._eventsPositioned = function() {
-      expect(currentWarnArgs.length).toBe(2)
-      expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
-      done()
-    }
-    initCalendar(options)
+    initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: '[email protected]',
+      _eventsPositioned() {
+        expect(currentWarnArgs.length).toBe(2)
+        expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
+        done()
+      }
+    })
   })
 
   it('detects a google-calendar when `events` is the actual calendar ID, person gmail', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = '[email protected]'
-    options._eventsPositioned = function() {
-      expect(currentWarnArgs.length).toBe(2)
-      expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
-      done()
-    }
-    initCalendar(options)
+    initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: '[email protected]',
+      _eventsPositioned() {
+        expect(currentWarnArgs.length).toBe(2)
+        expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
+        done()
+      }
+    })
   })
 
   it('detects a google-calendar when `events` is the actual calendar ID, person googlemail', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = '[email protected]'
-    options._eventsPositioned = function() {
-      expect(currentWarnArgs.length).toBe(2)
-      expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
-      done()
-    }
-    initCalendar(options)
+    initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: '[email protected]',
+      _eventsPositioned() {
+        expect(currentWarnArgs.length).toBe(2)
+        expect(typeof currentWarnArgs[1]).toBe('object') // sent the request to google, but not-found warning
+        done()
+      }
+    })
   })
 
   it('works with requesting an HTTP V1 API feed URL', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = 'http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: 'http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+        done()
+      }
+    })
   })
 
   it('works with requesting an HTTPS V1 API feed URL', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events = 'https://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: 'https://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+        done()
+      }
+    })
   })
 
   it('works with requesting an V3 API feed URL', function(done) {
-    options.googleCalendarApiKey = API_KEY
-    options.events =
-      'https://www.googleapis.com/calendar/v3/calendars/usa__en%40holiday.calendar.google.com/events'
-    options._eventsPositioned = function() {
-      var events = currentCalendar.getEvents()
-      expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-      done()
-    }
-    initCalendar(options)
+    let calendar = initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: 'https://www.googleapis.com/calendar/v3/calendars/usa__en%40holiday.calendar.google.com/events',
+      _eventsPositioned() {
+        let events = calendar.getEvents()
+        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+        done()
+      }
+    })
   })
 
   it('calls loading with true then false', function(done) {
-    var cmds = []
-
-    options.googleCalendarApiKey = API_KEY
-    options.events =
-      'https://www.googleapis.com/calendar/v3/calendars/usa__en%40holiday.calendar.google.com/events'
-
-    options.loading = function(bool) {
-      cmds.push(bool)
-
-      if (cmds.length === 1) {
-        expect(cmds).toEqual([ true ])
-      } else if (cmds.length === 2) {
-        expect(cmds).toEqual([ true, false ])
-        done()
+    let cmds = []
+
+    initCalendar({
+      googleCalendarApiKey: API_KEY,
+      events: 'https://www.googleapis.com/calendar/v3/calendars/usa__en%40holiday.calendar.google.com/events',
+      loading(bool) {
+        cmds.push(bool)
+
+        if (cmds.length === 1) {
+          expect(cmds).toEqual([ true ])
+        } else if (cmds.length === 2) {
+          expect(cmds).toEqual([ true, false ])
+          done()
+        }
       }
-    }
-    initCalendar(options)
+    })
   })
 
   describe('EventSource::remove', function() {
 
     it('works when specifying only the Google Calendar ID', function(done) {
-      var called = false
-
-      options.googleCalendarApiKey = API_KEY
-      options.eventSources = [ { googleCalendarId: HOLIDAY_CALENDAR_ID } ]
-      options._eventsPositioned = function() {
-        var events
-
-        if (called) { return } // only the first time
-        called = true
-
-        events = currentCalendar.getEvents()
-        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-
-        setTimeout(function() {
-          currentCalendar.getEventSources()[0].remove()
-          events = currentCalendar.getEvents()
-          expect(events.length).toBe(0)
-          done()
-        }, 0)
-      }
-
-      initCalendar(options)
+      let called = false
+      let calendar = initCalendar({
+        googleCalendarApiKey: API_KEY,
+        eventSources: [ { googleCalendarId: HOLIDAY_CALENDAR_ID } ],
+        _eventsPositioned() {
+          let events
+
+          if (called) { return } // only the first time
+          called = true
+
+          events = calendar.getEvents()
+          expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+
+          setTimeout(function() {
+            calendar.getEventSources()[0].remove()
+            events = calendar.getEvents()
+            expect(events.length).toBe(0)
+            done()
+          }, 0)
+        }
+      })
     })
 
     it('works when specifying a raw Google Calendar source object', function(done) {
-      var googleSource = { googleCalendarId: HOLIDAY_CALENDAR_ID }
-      var called = false
-
-      options.googleCalendarApiKey = API_KEY
-      options.eventSources = [ googleSource ]
-      options._eventsPositioned = function() {
-        var events
-
-        if (called) { return } // only the first time
-        called = true
-
-        events = currentCalendar.getEvents()
-        expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
-
-        setTimeout(function() {
-          currentCalendar.getEventSources()[0].remove()
-          events = currentCalendar.getEvents()
-          expect(events.length).toBe(0)
-          done()
-        }, 0)
-      }
-
-      initCalendar(options)
+      let googleSource = { googleCalendarId: HOLIDAY_CALENDAR_ID }
+      let called = false
+      let calendar = initCalendar({
+        googleCalendarApiKey: API_KEY,
+        eventSources: [ googleSource ],
+        _eventsPositioned() {
+          let events
+
+          if (called) { return } // only the first time
+          called = true
+
+          events = calendar.getEvents()
+          expect(events.length).toBe(NUM_EVENTS) // 5 holidays in November 2016 (and end of Oct)
+
+          setTimeout(function() {
+            calendar.getEventSources()[0].remove()
+            events = calendar.getEvents()
+            expect(events.length).toBe(0)
+            done()
+          }, 0)
+        }
+      })
     })
   })
 

+ 9 - 10
packages/__tests__/src/legacy/weekends.js

@@ -1,24 +1,23 @@
+import DayGridViewWrapper from "../lib/wrappers/DayGridViewWrapper"
 
 describe('when weekends option is set', function() {
 
   it('should show sat and sun if true', function() {
-    initCalendar({
+    let calendar = initCalendar({
       weekends: true
     })
-    var sun = $('.fc-day-header.fc-sun')[0]
-    var sat = $('.fc-day-header.fc-sun')[0]
-    expect(sun).toBeDefined()
-    expect(sat).toBeDefined()
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getDayEls(0).length).toBeGreaterThan(0) // 0=sunday
+    expect(dayGridWrapper.getDayEls(6).length).toBeGreaterThan(0) // 6=saturday
   })
 
   it('should not show sat and sun if false', function() {
-    initCalendar({
+    let calendar = initCalendar({
       weekends: false
     })
-    var sun = $('.fc-day-header.fc-sun')[0]
-    var sat = $('.fc-day-header.fc-sun')[0]
-    expect(sun).not.toBeDefined()
-    expect(sat).not.toBeDefined()
+    let dayGridWrapper = new DayGridViewWrapper(calendar).dayGrid
+    expect(dayGridWrapper.getDayEls(0).length).toBe(0) // 0=sunday
+    expect(dayGridWrapper.getDayEls(6).length).toBe(0) // 6=saturday
   })
 
 })

+ 34 - 15
packages/__tests__/src/lib/wrappers/DayGridWrapper.ts

@@ -7,6 +7,7 @@ export default class DayGridWrapper {
 
   static EVENT_IS_START_CLASSNAME = 'fc-start'
   static EVENT_IS_END_CLASSNAME = 'fc-end'
+  static MORE_LINK_CLASSNAME = 'fc-more'
 
 
   constructor(private el: HTMLElement) {
@@ -26,11 +27,15 @@ export default class DayGridWrapper {
   }
 
 
-  getDayEls(date) { // TODO: return single el
-    if (typeof date === 'string') {
-      date = new Date(date)
+  getDayEls(date) { // TODO: return single el??? accept 'tues'
+    if (typeof date === 'number') {
+      return findElements(this.el, `.fc-day.${CalendarWrapper.DOW_CLASSNAMES[date]}`)
+    } else {
+      if (typeof date === 'string') {
+        date = new Date(date)
+      }
+      return findElements(this.el, '.fc-day[data-date="' + formatIsoDay(date) + '"]')
     }
-    return findElements(this.el, '.fc-day[data-date="' + formatIsoDay(date) + '"]')
   }
 
 
@@ -208,20 +213,34 @@ export default class DayGridWrapper {
   }
 
 
-  dragEventToDate(eventEl: HTMLElement, startDate, endDate) {
+  dragEventToDate(eventEl: HTMLElement, startDate, endDate, isTouch?) {
     return new Promise((resolve) => {
-      let rect0 = this.getDayEl(startDate).getBoundingClientRect()
-      let rect1 = this.getDayEl(endDate).getBoundingClientRect()
+      if (!startDate) {
+        let rect1 = this.getDayEl(endDate).getBoundingClientRect()
+        let point1 = getRectCenter(rect1)
+
+        $(eventEl).simulate('drag', {
+          isTouch: isTouch || false,
+          delay: isTouch ? 200 : 0, // bad to hardcode ms
+          end: point1,
+          onRelease: () => resolve()
+        })
+      } else {
+        let rect0 = this.getDayEl(startDate).getBoundingClientRect()
+        let rect1 = this.getDayEl(endDate).getBoundingClientRect()
 
-      let eventRect = eventEl.getBoundingClientRect()
-      let point0 = getRectCenter(intersectRects(eventRect, rect0))
-      let point1 = getRectCenter(rect1)
+        let eventRect = eventEl.getBoundingClientRect()
+        let point0 = getRectCenter(intersectRects(eventRect, rect0))
+        let point1 = getRectCenter(rect1)
 
-      $(eventEl).simulate('drag', {
-        point: point0,
-        end: point1,
-        onRelease: () => resolve()
-      })
+        $(eventEl).simulate('drag', {
+          isTouch: isTouch || false,
+          delay: isTouch ? 200 : 0, // bad to hardcode ms
+          point: point0,
+          end: point1,
+          onRelease: () => resolve()
+        })
+      }
     })
   }
 

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

@@ -125,11 +125,12 @@ export default class TimeGridWrapper {
   }
 
 
-  dragEventToDate(eventEl: HTMLElement, dropDate) {
+  dragEventToDate(eventEl: HTMLElement, dropDate, onBeforeRelease?) {
     return new Promise((resolve) => {
       $(eventEl).simulate('drag', {
-        localPoint: { left: '50%', top: 1 }, // 1 for zoom
+        localPoint: { left: '50%', top: 5 }, // ahhh 5. overcome divider sometimes
         end: this.getPoint(dropDate),
+        onBeforeRelease,
         onRelease: () => resolve()
       })
     })
@@ -612,3 +613,10 @@ function checkEventRenderingMatch(expectedRects, eventEls) {
 
   return true
 }
+
+
+export function queryEventElInfo(eventEl: HTMLElement) {
+  return {
+    timeText: $(eventEl.querySelector('.fc-time')).text()
+  }
+}

+ 21 - 0
packages/__tests__/src/lib/wrappers/interaction-util.ts

@@ -22,6 +22,27 @@ export function waitEventDrag(calendar: Calendar, dragging: Promise<any>) {
 }
 
 
+export function waitEventDrag2(calendar: Calendar, dragging: Promise<any>) {
+  return new Promise<any>((resolve) => {
+    let theArg = false
+
+    calendar.on('eventDrop', function(arg) {
+      theArg = arg
+    })
+
+    calendar.on('_noEventDrop', function() {
+      resolve(false)
+    })
+
+    dragging.then(() => {
+      setTimeout(function() { // wait for eventDrop to fire
+        resolve(theArg)
+      })
+    })
+  })
+}
+
+
 export function waitEventResize(calendar: Calendar, dragging: Promise<any>) {
   return new Promise<any>((resolve) => {
     let modifiedEvent = false