// C o p y r i g h t © 2 0 1 5 A b h i s h e k B a n t h i a
import CoreModelKit
@ testable import Clocker
import XCTest
class ClockerUnitTests : XCTestCase {
private let california = [ " customLabel " : " Test " ,
" formattedAddress " : " San Francisco " ,
" place_id " : " TestIdentifier " ,
" timezoneID " : " America/Los_Angeles " ,
" nextUpdate " : " " ,
" latitude " : " 37.7749295 " ,
" longitude " : " -122.4194155 " ]
private let mumbai = [ " customLabel " : " Ghar " ,
" formattedAddress " : " Mumbai " ,
" place_id " : " ChIJwe1EZjDG5zsRaYxkjY_tpF0 " ,
" timezoneID " : " Asia/Calcutta " ,
" nextUpdate " : " " ,
" latitude " : " 19.0759837 " ,
" longitude " : " 72.8776559 " ]
private let auckland = [ " customLabel " : " Auckland " ,
" formattedAddress " : " New Zealand " ,
" place_id " : " ChIJh5Z3Fw4gLG0RM0dqdeIY1rE " ,
" timezoneID " : " Pacific/Auckland " ,
" nextUpdate " : " " ,
" latitude " : " -40.900557 " ,
" longitude " : " 174.885971 " ]
private let florida = [ " customLabel " : " Gainesville " ,
" formattedAddress " : " Florida " ,
" place_id " : " ChIJvypWkWV2wYgR0E7HW9MTLvc " ,
" timezoneID " : " America/New_York " ,
" nextUpdate " : " " ,
" latitude " : " 27.664827 " ,
" longitude " : " -81.5157535 " ]
private let onlyTimezone : [ String : Any ] = [ " timezoneID " : " Africa/Algiers " ,
" formattedAddress " : " Africa/Algiers " ,
" place_id " : " " ,
" customLabel " : " " ,
" latitude " : " " ,
" longitude " : " " ]
private let omaha : [ String : Any ] = [ " timezoneID " : " America/Chicago " ,
" formattedAddress " : " Omaha " ,
" place_id " : " ChIJ7fwMtciNk4cRBLY3rk9NQkY " ,
" customLabel " : " " ,
" latitude " : " 41.2565369 " ,
" longitude " : " -95.9345034 " ]
private var operations : TimezoneDataOperations {
return TimezoneDataOperations ( with : TimezoneData ( with : mumbai ) , store : DataStore . shared ( ) )
}
private var californiaOperations : TimezoneDataOperations {
return TimezoneDataOperations ( with : TimezoneData ( with : california ) , store : DataStore . shared ( ) )
}
private var floridaOperations : TimezoneDataOperations {
return TimezoneDataOperations ( with : TimezoneData ( with : florida ) , store : DataStore . shared ( ) )
}
private var aucklandOperations : TimezoneDataOperations {
return TimezoneDataOperations ( with : TimezoneData ( with : auckland ) , store : DataStore . shared ( ) )
}
private var omahaOperations : TimezoneDataOperations {
return TimezoneDataOperations ( with : TimezoneData ( with : omaha ) , store : DataStore . shared ( ) )
}
func testOverridingSecondsComponent_shouldHideSeconds ( ) {
let dummyDefaults = UserDefaults . standard
dummyDefaults . set ( NSNumber ( value : 4 ) , forKey : CLSelectedTimeZoneFormatKey ) // 4 i s 1 2 h o u r w i t h s e c o n d s
let timezoneObjects = [ TimezoneData ( with : mumbai ) ,
TimezoneData ( with : auckland ) ,
TimezoneData ( with : california ) ]
timezoneObjects . forEach {
let operationsObject = TimezoneDataOperations ( with : $0 , store : DataStore . shared ( ) )
let currentTime = operationsObject . time ( with : 0 )
XCTAssert ( currentTime . count = = 8 ) // 8 i n c l u d e s 2 c o l o n s
$0 . setShouldOverrideGlobalTimeFormat ( 1 )
let newTime = operationsObject . time ( with : 0 )
XCTAssert ( newTime . count >= 7 ) // 5 i n c l u d e s c o l o n
}
}
func testAddingATimezoneToDefaults ( ) {
let timezoneData = TimezoneData ( with : california )
let currentFavourites = DataStore . shared ( ) . timezones ( )
let oldCount = currentFavourites . count
let operationsObject = TimezoneDataOperations ( with : timezoneData , store : DataStore . shared ( ) )
operationsObject . saveObject ( )
let newDefaults = DataStore . shared ( ) . timezones ( )
XCTAssert ( newDefaults . isEmpty = = false )
XCTAssert ( newDefaults . count = = oldCount + 1 )
}
func testDecoding ( ) {
let timezone1 = TimezoneData . customObject ( from : nil )
XCTAssertNotNil ( timezone1 )
let data = Data ( )
let timezone2 = TimezoneData . customObject ( from : data )
XCTAssertNil ( timezone2 )
}
func testDescription ( ) {
let timezoneData = TimezoneData ( with : california )
XCTAssertFalse ( timezoneData . description . isEmpty )
XCTAssertFalse ( timezoneData . debugDescription . isEmpty )
}
func testHashing ( ) {
let timezoneData = TimezoneData ( with : california )
XCTAssert ( timezoneData . hash != - 1 )
timezoneData . placeID = nil
timezoneData . timezoneID = nil
XCTAssert ( timezoneData . hash = = - 1 )
}
func testBadInputDictionaryForInitialization ( ) {
let badInput : [ String : Any ] = [ " customLabel " : " " ,
" latitude " : " 41.2565369 " ,
" longitude " : " -95.9345034 " ]
let badTimezoneData = TimezoneData ( with : badInput )
XCTAssertEqual ( badTimezoneData . placeID , " Error " )
XCTAssertEqual ( badTimezoneData . timezoneID , " Error " )
XCTAssertEqual ( badTimezoneData . formattedAddress , " Error " )
}
func testDeletingATimezone ( ) {
var currentFavourites = DataStore . shared ( ) . timezones ( )
// C h e c k i f t i m e z o n e w i t h t e s t i d e n t i f i e r i s p r e s e n t .
let filteredCount = currentFavourites . filter {
let timezone = TimezoneData . customObject ( from : $0 )
return timezone ? . placeID = = " TestIdentifier "
}
// C a l i f o r n i a i s a b s e n t . A d d i t !
if filteredCount . count = = 0 {
let timezoneData = TimezoneData ( with : california )
let operationsObject = TimezoneDataOperations ( with : timezoneData , store : DataStore . shared ( ) )
operationsObject . saveObject ( )
}
let oldCount = DataStore . shared ( ) . timezones ( ) . count
currentFavourites = currentFavourites . filter {
let timezone = TimezoneData . customObject ( from : $0 )
return timezone ? . placeID != " TestIdentifier "
}
DataStore . shared ( ) . setTimezones ( currentFavourites )
XCTAssertTrue ( currentFavourites . count = = oldCount - 1 , " Current Favourites Count \( currentFavourites . count ) and Old Count \( oldCount - 1 ) don't line up. " )
}
// T h e b e l o w t e s t m i g h t f a i l o u t s i d e C a l i f o r n i a o r i f D S T i s a c t i v e !
// C I i s c a l i b r a t e d t o b e o n L A t i m e z o n e !
func testTimeDifference ( ) {
let observingDaylightSavings = TimeZone . autoupdatingCurrent . isDaylightSavingTime ( for : Date ( ) )
let expectedDifference = observingDaylightSavings ? " , 9h 30m ahead " : " , 10h 30m ahead "
let expectedDifferenceForAuckland = observingDaylightSavings ? " , 16h ahead " : " , 18h ahead "
XCTAssertTrue ( operations . timeDifference ( ) = = expectedDifference , " Difference was unexpectedly: \( operations . timeDifference ( ) ) " )
XCTAssertTrue ( californiaOperations . timeDifference ( ) = = " , 3h behind " , " Difference was unexpectedly: \( californiaOperations . timeDifference ( ) ) " )
XCTAssertTrue ( floridaOperations . timeDifference ( ) = = " " , " Difference was unexpectedly: \( floridaOperations . timeDifference ( ) ) " )
XCTAssertTrue ( aucklandOperations . timeDifference ( ) = = expectedDifferenceForAuckland , " Difference was unexpectedly: \( aucklandOperations . timeDifference ( ) ) " )
XCTAssertTrue ( omahaOperations . timeDifference ( ) = = " , 1h behind " , " Difference was unexpectedly: \( omahaOperations . timeDifference ( ) ) " )
}
func testSunriseSunset ( ) {
let dataObject = TimezoneData ( with : mumbai )
let operations = TimezoneDataOperations ( with : dataObject , store : DataStore . shared ( ) )
XCTAssertNotNil ( operations . formattedSunriseTime ( with : 0 ) )
XCTAssertNotNil ( dataObject . sunriseTime )
XCTAssertNotNil ( dataObject . sunriseTime )
let timezoneObject = TimezoneData ( with : onlyTimezone )
timezoneObject . selectionType = . timezone
let timezoneOperations = TimezoneDataOperations ( with : timezoneObject , store : DataStore . shared ( ) )
XCTAssertTrue ( timezoneOperations . formattedSunriseTime ( with : 0 ) = = " " )
XCTAssertNil ( timezoneObject . sunriseTime )
XCTAssertNil ( timezoneObject . sunsetTime )
}
func testDateWithSliderValue ( ) {
let dataObject = TimezoneData ( with : mumbai )
let operations = TimezoneDataOperations ( with : dataObject , store : DataStore . shared ( ) )
XCTAssertNotNil ( operations . date ( with : 0 , displayType : . menu ) )
}
func testTimezoneFormat ( ) {
let dataObject = TimezoneData ( with : mumbai )
UserDefaults . standard . set ( NSNumber ( value : 0 ) , forKey : CLSelectedTimeZoneFormatKey ) // S e t t o 1 2 h o u r f o r m a t
dataObject . setShouldOverrideGlobalTimeFormat ( 0 ) // R e s p e c t G l o b a l P r e f e r e n c e
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " h:mm a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 1 ) // 1 2 - H o u r F o r m a t
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " h:mm a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 2 ) // 2 4 - H o u r f o r m a t
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " HH:mm " )
// S k i p 3 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 4 ) // 1 2 - H o u r w i t h s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " h:mm:ss a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 5 ) // 2 4 - H o u r f o r m a t w i t h s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " HH:mm:ss " )
// S k i p 6 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 7 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d n o s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 8 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm:ss a " )
// S k i p 9 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 10 ) // 1 2 - h o u r w i t h o u t a m / p m a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm " )
dataObject . setShouldOverrideGlobalTimeFormat ( 11 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm:ss " )
// W r o n g i n p u t
dataObject . setShouldOverrideGlobalTimeFormat ( 0 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( 88 ) = = " h:mm a " )
}
func testTimezoneFormatWithDefaultSetAs24HourFormat ( ) {
let dataObject = TimezoneData ( with : california )
UserDefaults . standard . set ( NSNumber ( value : 1 ) , forKey : CLSelectedTimeZoneFormatKey ) // S e t t o 2 4 - H o u r F o r m a t
dataObject . setShouldOverrideGlobalTimeFormat ( 0 )
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " HH:mm " ,
" Unexpected format returned: \( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) ) " )
dataObject . setShouldOverrideGlobalTimeFormat ( 1 ) // 1 2 - H o u r F o r m a t
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " h:mm a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 2 ) // 2 4 - H o u r f o r m a t
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " HH:mm " )
// S k i p 3 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 4 ) // 1 2 - H o u r w i t h s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " h:mm:ss a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 5 ) // 2 4 - H o u r f o r m a t w i t h s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " HH:mm:ss " )
// S k i p 6 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 7 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d n o s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm a " )
dataObject . setShouldOverrideGlobalTimeFormat ( 8 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm:ss a " )
// S k i p 9 s i n c e i t ' s a p l a c e h o l d e r
dataObject . setShouldOverrideGlobalTimeFormat ( 10 ) // 1 2 - h o u r w i t h o u t a m / p m a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm " )
dataObject . setShouldOverrideGlobalTimeFormat ( 11 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " hh:mm:ss " )
dataObject . setShouldOverrideGlobalTimeFormat ( 12 ) // 1 2 - h o u r w i t h p r e c e d i n g z e r o a n d s e c o n d s
XCTAssertTrue ( dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) = = " epoch " )
}
func testSecondsDisplayForOverridenTimezone ( ) {
let dataObject = TimezoneData ( with : california )
UserDefaults . standard . set ( NSNumber ( value : 1 ) , forKey : CLSelectedTimeZoneFormatKey ) // S e t t o 2 4 - H o u r F o r m a t
// T e s t d e f a u l t b e h a v i o u r
let timezoneWithSecondsKeys = [ 4 , 5 , 8 , 11 ]
for timezoneKey in timezoneWithSecondsKeys {
dataObject . setShouldOverrideGlobalTimeFormat ( timezoneKey )
XCTAssertTrue ( dataObject . shouldShowSeconds ( DataStore . shared ( ) . timezoneFormat ( ) ) )
}
let timezoneWithoutSecondsKeys = [ 1 , 2 , 7 , 10 ]
for timezoneKey in timezoneWithoutSecondsKeys {
dataObject . setShouldOverrideGlobalTimeFormat ( timezoneKey )
XCTAssertFalse ( dataObject . shouldShowSeconds ( DataStore . shared ( ) . timezoneFormat ( ) ) )
}
// T e s t w r o n g o v e r r i d e t i m e z o n e k e y
let wrongTimezoneKey = 88
dataObject . setShouldOverrideGlobalTimeFormat ( wrongTimezoneKey )
XCTAssertFalse ( dataObject . shouldShowSeconds ( DataStore . shared ( ) . timezoneFormat ( ) ) )
// T e s t w r o n g g l o b a l p r e f e r e n c e k e y
dataObject . setShouldOverrideGlobalTimeFormat ( 0 )
XCTAssertFalse ( dataObject . shouldShowSeconds ( 88 ) )
}
func testTimezoneRetrieval ( ) {
let dataObject = TimezoneData ( with : mumbai )
let autoupdatingTimezone = TimeZone . autoupdatingCurrent . identifier
XCTAssertEqual ( dataObject . timezone ( ) , " Asia/Calcutta " )
// U n l i k e l y
dataObject . timezoneID = nil
XCTAssertEqual ( dataObject . timezone ( ) , autoupdatingTimezone )
dataObject . isSystemTimezone = true
XCTAssertEqual ( dataObject . timezone ( ) , autoupdatingTimezone )
}
func testFormattedLabel ( ) {
let dataObject = TimezoneData ( with : mumbai )
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " Ghar " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
dataObject . setLabel ( " " )
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " Mumbai " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
dataObject . formattedAddress = nil
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " Asia " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
dataObject . setLabel ( " Jogeshwari " )
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " Jogeshwari " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
// U n l i k e l y s c e n a r i o
dataObject . setLabel ( " " )
dataObject . timezoneID = " GMT "
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " GMT " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
// A n o t h e r u n l i k e l y s c e n a r i o
dataObject . setLabel ( " " )
dataObject . timezoneID = nil
XCTAssertTrue ( dataObject . formattedTimezoneLabel ( ) = = " Error " , " Incorrect custom label returned by model \( dataObject . formattedTimezoneLabel ( ) ) " )
}
func testEquality ( ) {
let dataObject1 = TimezoneData ( with : mumbai )
let dataObject2 = TimezoneData ( with : auckland )
XCTAssertFalse ( dataObject1 = = dataObject2 )
XCTAssertFalse ( dataObject1 . isEqual ( dataObject2 ) )
let dataObject3 = TimezoneData ( with : mumbai )
XCTAssertTrue ( dataObject1 = = dataObject3 )
XCTAssertTrue ( dataObject1 . isEqual ( dataObject3 ) )
XCTAssertFalse ( dataObject1 . isEqual ( nil ) )
}
func testWithAllLocales ( ) {
let dataObject1 = TimezoneData ( with : mumbai )
let operations = TimezoneDataOperations ( with : dataObject1 , store : DataStore . shared ( ) )
for locale in Locale . availableIdentifiers {
let currentLocale = Locale ( identifier : locale )
let localizedDate = operations . todaysDate ( with : 0 , locale : currentLocale )
XCTAssertNotNil ( localizedDate )
}
}
func testTimeWithAllLocales ( ) {
let dataObject = TimezoneData ( with : mumbai )
let cal = NSCalendar ( calendarIdentifier : NSCalendar . Identifier . gregorian )
guard let newDate = cal ? . date ( byAdding : . minute ,
value : 0 ,
to : Date ( ) ,
options : . matchFirst )
else {
XCTFail ( " Unable to add dates! " )
return
}
for locale in Locale . availableIdentifiers {
let currentLocale = Locale ( identifier : locale )
let dateFormatter = DateFormatterManager . dateFormatterWithFormat ( with : . none ,
format : dataObject . timezoneFormat ( DataStore . shared ( ) . timezoneFormat ( ) ) ,
timezoneIdentifier : dataObject . timezone ( ) ,
locale : currentLocale )
let convertedDate = dateFormatter . string ( from : newDate )
XCTAssertNotNil ( convertedDate )
}
}
func testStringFiltering ( ) {
let stringWithComma = " Mumbai, Maharashtra "
let stringWithoutComma = " Mumbai "
let emptyString = " "
XCTAssertEqual ( stringWithComma . filteredName ( ) , " Mumbai " )
XCTAssertEqual ( stringWithoutComma . filteredName ( ) , " Mumbai " )
XCTAssertEqual ( emptyString . filteredName ( ) , " " )
}
func testToasty ( ) {
let view = NSView ( frame : CGRect . zero )
view . makeToast ( " Hello, this is a toast " )
XCTAssertEqual ( view . subviews . first ? . accessibilityIdentifier ( ) , " ToastView " )
let toastExpectation = expectation ( description : " Toast View should hide after 1 second " )
let result = XCTWaiter . wait ( for : [ toastExpectation ] , timeout : 1.5 ) // S e t 1 . 5 s e c o n d s h e r e f o r a l i t t l e l e e w a y
if result = = XCTWaiter . Result . timedOut {
XCTAssertTrue ( view . subviews . isEmpty )
}
}
func testPointingHandButton ( ) {
let sampleRect = CGRect ( x : 0 , y : 0 , width : 200 , height : 200 )
let pointingHandCursorButton = PointingHandCursorButton ( frame : CGRect . zero )
pointingHandCursorButton . draw ( sampleRect )
pointingHandCursorButton . resetCursorRects ( )
XCTAssertEqual ( pointingHandCursorButton . pointingHandCursor , NSCursor . pointingHand )
}
func testNoTimezoneView ( ) {
let sampleRect = CGRect ( x : 0 , y : 0 , width : 200 , height : 200 )
let subject = NoTimezoneView ( frame : sampleRect )
// P e r f o r m a l a y o u t t o a d d s u b v i e w s
subject . layout ( )
XCTAssertEqual ( subject . subviews . count , 2 ) // T w o t e x t f i e l d s
XCTAssertEqual ( subject . subviews . first ? . layer ? . animationKeys ( ) , [ " notimezone.emoji " ] )
}
func testDefaultsWiping ( ) {
let defaultsDict : [ String : Any ] = [ " test1 " : " testString " , " test2 " : 24 ]
let domainName = " com.test.clocker "
let defaults = UserDefaults ( suiteName : domainName )
defaults ? . setPersistentDomain ( defaultsDict , forName : domainName )
defaults ? . wipe ( for : domainName )
XCTAssertNil ( defaults ? . object ( forKey : " test1 " ) )
XCTAssertNil ( defaults ? . object ( forKey : " test2 " ) )
}
}