// // RKFetchedResultsTableControllerTest.m // RestKit // // Created by Jeff Arena on 8/12/11. // Copyright (c) 2009-2012 RestKit. All rights reserved. // #import "RKTestEnvironment.h" #import "RKFetchedResultsTableController.h" #import "RKManagedObjectStore.h" #import "RKEntityMapping.h" #import "RKHuman.h" #import "RKEvent.h" #import "RKAbstractTableController_Internals.h" #import "RKManagedObjectCaching.h" #import "RKTableControllerTestDelegate.h" // Expose the object loader delegate for testing purposes... @interface RKFetchedResultsTableController () - (BOOL)isHeaderSection:(NSUInteger)section; - (BOOL)isHeaderRow:(NSUInteger)row; - (BOOL)isFooterSection:(NSUInteger)section; - (BOOL)isFooterRow:(NSUInteger)row; - (BOOL)isEmptySection:(NSUInteger)section; - (BOOL)isEmptyRow:(NSUInteger)row; - (BOOL)isHeaderIndexPath:(NSIndexPath *)indexPath; - (BOOL)isFooterIndexPath:(NSIndexPath *)indexPath; - (BOOL)isEmptyItemIndexPath:(NSIndexPath *)indexPath; - (NSIndexPath *)fetchedResultsIndexPathForIndexPath:(NSIndexPath *)indexPath; @end @interface RKFetchedResultsTableControllerSpecViewController : UITableViewController @end @implementation RKFetchedResultsTableControllerSpecViewController @end @interface RKFetchedResultsTableControllerTest : RKTestCase @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext; @end @implementation RKFetchedResultsTableControllerTest - (void)setUp { [RKTestFactory setUp]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:nil]; } - (void)tearDown { [RKTestFactory tearDown]; } - (void)bootstrapStoreAndCache { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"RKHuman"inManagedObjectStore:managedObjectStore]; [humanMapping mapKeyPath:@"id" toAttribute:@"railsID"]; [humanMapping mapAttributes:@"name", nil]; humanMapping.primaryKeyAttribute = @"railsID"; assertThatInt([managedObjectStore.primaryManagedObjectContext countForEntityForName:@"RKHuman" predicate:nil error:nil], is(equalToInt(0))); RKHuman *blake = [managedObjectStore.primaryManagedObjectContext insertNewObjectForEntityForName:@"RKHuman"]; blake.railsID = [NSNumber numberWithInt:1234]; blake.name = @"blake"; RKHuman *other = [managedObjectStore.primaryManagedObjectContext insertNewObjectForEntityForName:@"RKHuman"]; other.railsID = [NSNumber numberWithInt:5678]; other.name = @"other"; NSError *error = nil; [managedObjectStore.primaryManagedObjectContext save:&error]; assertThat(error, is(nilValue())); assertThatInt([managedObjectStore.primaryManagedObjectContext countForEntityForName:@"RKHuman" predicate:nil error:nil], is(equalToInt(2))); RKObjectManager *objectManager = [RKTestFactory objectManager]; [objectManager.mappingProvider setMapping:humanMapping forKeyPath:@"human"]; objectManager.managedObjectStore = managedObjectStore; [objectManager.mappingProvider setObjectMapping:humanMapping forResourcePathPattern:@"/JSON/humans/all\\.json" withFetchRequestBlock:^NSFetchRequest *(NSString *resourcePath) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RKHuman"]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; return fetchRequest; }]; [objectManager.mappingProvider setObjectMapping:humanMapping forResourcePathPattern:@"/JSON/humans/empty\\.json" withFetchRequestBlock:^NSFetchRequest *(NSString *resourcePath) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RKHuman"]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; return fetchRequest; }]; } - (NSManagedObjectContext *)managedObjectContext { return [[RKObjectManager sharedManager].managedObjectStore mainQueueManagedObjectContext]; } - (void)bootstrapNakedObjectStoreAndCache { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *eventMapping = [RKEntityMapping mappingForEntityForName:@"RKEvent"inManagedObjectStore:managedObjectStore]; [eventMapping mapKeyPath:@"event_id" toAttribute:@"eventID"]; [eventMapping mapKeyPath:@"type" toAttribute:@"eventType"]; [eventMapping mapAttributes:@"location", @"summary", nil]; eventMapping.primaryKeyAttribute = @"eventID"; assertThatInt([managedObjectStore.primaryManagedObjectContext countForEntityForName:@"RKEvent" predicate:nil error:nil], is(equalToInt(0))); RKEvent *nakedEvent = [managedObjectStore.primaryManagedObjectContext insertNewObjectForEntityForName:@"RKEvent"]; nakedEvent.eventID = @"RK4424"; nakedEvent.eventType = @"Concert"; nakedEvent.location = @"Performance Hall"; nakedEvent.summary = @"Shindig"; NSError *error = nil; [managedObjectStore.primaryManagedObjectContext save:&error]; assertThat(error, is(nilValue())); assertThatInt([managedObjectStore.primaryManagedObjectContext countForEntityForName:@"RKEvent" predicate:nil error:nil], is(equalToInt(1))); RKObjectManager *objectManager = [RKTestFactory objectManager]; [objectManager.mappingProvider addObjectMapping:eventMapping]; objectManager.managedObjectStore = managedObjectStore; id mockMappingProvider = [OCMockObject partialMockForObject:objectManager.mappingProvider]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RKEvent"]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"eventType" ascending:YES]]; [[[mockMappingProvider stub] andReturn:fetchRequest] fetchRequestForResourcePath:@"/JSON/NakedEvents.json"]; } - (void)bootstrapEmptyStoreAndCache { RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore]; RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"RKHuman"inManagedObjectStore:managedObjectStore]; [humanMapping mapKeyPath:@"id" toAttribute:@"railsID"]; [humanMapping mapAttributes:@"name", nil]; humanMapping.primaryKeyAttribute = @"railsID"; assertThatInt([managedObjectStore.primaryManagedObjectContext countForEntityForName:@"RKHuman" predicate:nil error:nil], is(equalToInt(0))); RKObjectManager *objectManager = [RKTestFactory objectManager]; [objectManager.mappingProvider setMapping:humanMapping forKeyPath:@"human"]; objectManager.managedObjectStore = managedObjectStore; id mockMappingProvider = [OCMockObject partialMockForObject:objectManager.mappingProvider]; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RKHuman"]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; [[[mockMappingProvider stub] andReturn:fetchRequest] fetchRequestForResourcePath:@"/JSON/humans/all.json"]; [[[mockMappingProvider stub] andReturn:fetchRequest] fetchRequestForResourcePath:@"/empty/array"]; } - (void)stubObjectManagerToOnline { RKObjectManager *objectManager = [RKObjectManager sharedManager]; id mockManager = [OCMockObject partialMockForObject:objectManager]; [mockManager setExpectationOrderMatters:YES]; RKObjectManagerNetworkStatus networkStatus = RKObjectManagerNetworkStatusOnline; [[[mockManager stub] andReturnValue:OCMOCK_VALUE(networkStatus)] networkStatus]; BOOL online = YES; // Initial online state for table [[[mockManager stub] andReturnValue:OCMOCK_VALUE(online)] isOnline]; } - (void)testLoadWithATableViewControllerAndResourcePath { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThat(tableController.viewController, is(equalTo(viewController))); assertThat(tableController.tableView, is(equalTo(viewController.tableView))); assertThat(tableController.resourcePath, is(equalTo(@"/JSON/humans/all.json"))); } - (void)testLoadWithATableViewControllerAndResourcePathFromNakedObjects { [self bootstrapNakedObjectStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.resourcePath = @"/JSON/NakedEvents.json"; tableController.managedObjectContext = self.managedObjectContext; [tableController setObjectMappingForClass:[RKEvent class]]; [tableController loadTable]; assertThat(tableController.viewController, is(equalTo(viewController))); assertThat(tableController.tableView, is(equalTo(viewController.tableView))); assertThat(tableController.resourcePath, is(equalTo(@"/JSON/NakedEvents.json"))); RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping mappingForClass:[UITableViewCell class]]; [cellMapping mapKeyPath:@"summary" toAttribute:@"textLabel.text"]; RKTableViewCellMappings *mappings = [RKTableViewCellMappings new]; [mappings setCellMapping:cellMapping forClass:[RKEvent class]]; tableController.cellMappings = mappings; UITableViewCell *cell = [tableController tableView:tableController.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; assertThat(cell.textLabel.text, is(equalTo(@"Shindig"))); } - (void)testLoadWithATableViewControllerAndResourcePathAndPredicateAndSortDescriptors { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; NSPredicate *predicate = [NSPredicate predicateWithValue:TRUE]; NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.predicate = predicate; tableController.sortDescriptors = sortDescriptors; [tableController loadTable]; assertThat(tableController.viewController, is(equalTo(viewController))); assertThat(tableController.resourcePath, is(equalTo(@"/JSON/humans/all.json"))); assertThat(tableController.fetchRequest, is(notNilValue())); assertThat([tableController.fetchRequest predicate], is(equalTo(predicate))); assertThat([tableController.fetchRequest sortDescriptors], is(equalTo(sortDescriptors))); } - (void)testLoadWithATableViewControllerAndResourcePathAndSectionNameAndCacheName { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; tableController.cacheName = @"allHumansCache"; [tableController loadTable]; assertThat(tableController.viewController, is(equalTo(viewController))); assertThat(tableController.resourcePath, is(equalTo(@"/JSON/humans/all.json"))); assertThat(tableController.fetchRequest, is(notNilValue())); assertThat(tableController.fetchedResultsController.sectionNameKeyPath, is(equalTo(@"name"))); assertThat(tableController.fetchedResultsController.cacheName, is(equalTo(@"allHumansCache"))); } - (void)testLoadWithAllParams { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; NSPredicate *predicate = [NSPredicate predicateWithValue:TRUE]; NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.predicate = predicate; tableController.sortDescriptors = sortDescriptors; tableController.sectionNameKeyPath = @"name"; tableController.cacheName = @"allHumansCache"; [tableController loadTable]; assertThat(tableController.viewController, is(equalTo(viewController))); assertThat(tableController.resourcePath, is(equalTo(@"/JSON/humans/all.json"))); assertThat(tableController.fetchRequest, is(notNilValue())); assertThat([tableController.fetchRequest predicate], is(equalTo(predicate))); assertThat([tableController.fetchRequest sortDescriptors], is(equalTo(sortDescriptors))); assertThat(tableController.fetchedResultsController.sectionNameKeyPath, is(equalTo(@"name"))); assertThat(tableController.fetchedResultsController.cacheName, is(equalTo(@"allHumansCache"))); } - (void)testAlwaysHaveAtLeastOneSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatInt(tableController.sectionCount, is(equalToInt(1))); } #pragma mark - Section Management - (void)testProperlyCountSections { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController loadTable]; assertThatInt(tableController.sectionCount, is(equalToInt(2))); } - (void)testProperlyCountRows { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(2))); } - (void)testProperlyCountRowsWithHeaderItems { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(3))); } - (void)testProperlyCountRowsWithEmptyItemWhenEmpty { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(1))); } - (void)testProperlyCountRowsWithEmptyItemWhenFull { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(2))); } - (void)testProperlyCountRowsWithHeaderAndEmptyItemsWhenEmptyDontShowHeaders { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = NO; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(1))); } - (void)testProperlyCountRowsWithHeaderAndEmptyItemsWhenEmptyShowHeaders { [self bootstrapEmptyStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = YES; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(2))); } - (void)testProperlyCountRowsWithHeaderAndEmptyItemsWhenFull { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(3))); } #pragma mark - UITableViewDataSource specs - (void)testRaiseAnExceptionIfSentAMessageWithATableViewItIsNotBoundTo { UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerWithTableView:tableView forViewController:viewController]; NSException *exception = nil; @try { [tableController numberOfSectionsInTableView:[UITableView new]]; } @catch (NSException *e) { exception = e; } @finally { assertThat(exception, is(notNilValue())); } } - (void)testReturnTheNumberOfSectionsInTableView { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController loadTable]; assertThatInt([tableController numberOfSectionsInTableView:tableView], is(equalToInt(2))); } - (void)testReturnTheNumberOfRowsInSectionInTableView { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatInt([tableController tableView:tableView numberOfRowsInSection:0], is(equalToInt(2))); } - (void)testReturnTheHeaderTitleForSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController loadTable]; assertThat([tableController tableView:tableView titleForHeaderInSection:1], is(equalTo(@"other"))); } - (void)testReturnTheTableViewCellForRowAtIndexPath { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping mappingForClass:[UITableViewCell class]]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; RKTableViewCellMappings *mappings = [RKTableViewCellMappings new]; [mappings setCellMapping:cellMapping forClass:[RKHuman class]]; tableController.cellMappings = mappings; UITableViewCell *cell = [tableController tableView:tableController.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; assertThat(cell.textLabel.text, is(equalTo(@"blake"))); } #pragma mark - Table Cell Mapping - (void)testReturnTheObjectForARowAtIndexPath { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; assertThatBool(blake == [tableController objectForRowAtIndexPath:indexPath], is(equalToBool(YES))); [tableController release]; } #pragma mark - Editing - (id)fetchObjectWithEntityForName:(NSString *)entityName withAttribute:(NSString *)attribute equalTo:(id)value { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K = %@", attribute, value]; fetchRequest.fetchLimit = 1; NSError *error; NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; if ([results count] == 1) { return [results objectAtIndex:0]; } return nil; } - (void)testFireADeleteRequestWhenTheCanEditRowsPropertyIsSet { [self bootstrapStoreAndCache]; [self stubObjectManagerToOnline]; [[RKObjectManager sharedManager].router.routeSet addRoute:[RKRoute routeWithClass:[RKHuman class] resourcePathPattern:@"/humans/:railsID" method:RKRequestMethodDELETE]]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.canEditRows = YES; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:1 inSection:0]; NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForRow:0 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(2))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(other))); assertThat([tableController objectForRowAtIndexPath:deleteIndexPath], is(equalTo(blake))); BOOL delegateCanEdit = [tableController tableView:tableController.tableView canEditRowAtIndexPath:deleteIndexPath]; assertThatBool(delegateCanEdit, is(equalToBool(YES))); [RKTestNotificationObserver waitForNotificationWithName:RKRequestDidLoadResponseNotification usingBlock:^{ [tableController tableView:tableController.tableView commitEditingStyle:UITableViewCellEditingStyleDelete forRowAtIndexPath:deleteIndexPath]; }]; assertThatInt([tableController rowCount], is(equalToInt(1))); assertThat([tableController objectForRowAtIndexPath:deleteIndexPath], is(equalTo(other))); assertThatBool([blake isDeleted], is(equalToBool(YES))); } - (void)testLocallyCommitADeleteWhenTheCanEditRowsPropertyIsSet { [self bootstrapStoreAndCache]; [self stubObjectManagerToOnline]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.canEditRows = YES; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; NSIndexPath *deleteIndexPath = [NSIndexPath indexPathForRow:1 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; blake.railsID = nil; other.railsID = nil; NSError *error = nil; [blake.managedObjectContext save:&error]; assertThat(error, is(nilValue())); assertThatInt([tableController rowCount], is(equalToInt(2))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:deleteIndexPath], is(equalTo(other))); BOOL delegateCanEdit = [tableController tableView:tableController.tableView canEditRowAtIndexPath:deleteIndexPath]; assertThatBool(delegateCanEdit, is(equalToBool(YES))); [tableController tableView:tableController.tableView commitEditingStyle:UITableViewCellEditingStyleDelete forRowAtIndexPath:deleteIndexPath]; assertThatInt([tableController rowCount], is(equalToInt(1))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(blake))); } - (void)testNotCommitADeletionWhenTheCanEditRowsPropertyIsNotSet { [self bootstrapStoreAndCache]; [self stubObjectManagerToOnline]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(2))); BOOL delegateCanEdit = [tableController tableView:tableController.tableView canEditRowAtIndexPath:indexPath]; assertThatBool(delegateCanEdit, is(equalToBool(NO))); [tableController tableView:tableController.tableView commitEditingStyle:UITableViewCellEditingStyleDelete forRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]]; assertThatInt([tableController rowCount], is(equalToInt(2))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(other))); } - (void)testDoNothingToCommitAnInsertionWhenTheCanEditRowsPropertyIsSet { [self bootstrapStoreAndCache]; [self stubObjectManagerToOnline]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.canEditRows = YES; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(2))); BOOL delegateCanEdit = [tableController tableView:tableController.tableView canEditRowAtIndexPath:indexPath]; assertThatBool(delegateCanEdit, is(equalToBool(YES))); [tableController tableView:tableController.tableView commitEditingStyle:UITableViewCellEditingStyleInsert forRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]]; assertThatInt([tableController rowCount], is(equalToInt(2))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(other))); } - (void)testNotMoveARowWhenTheCanMoveRowsPropertyIsSet { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.canMoveRows = YES; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(2))); BOOL delegateCanMove = [tableController tableView:tableController.tableView canMoveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; assertThatBool(delegateCanMove, is(equalToBool(YES))); [tableController tableView:tableController.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]]; assertThatInt([tableController rowCount], is(equalToInt(2))); assertThat([tableController objectForRowAtIndexPath:indexPath], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(other))); } #pragma mark - Header, Footer, and Empty Rows - (void)testDetermineIfASectionIndexIsAHeaderSection { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatBool([tableController isHeaderSection:0], is(equalToBool(YES))); assertThatBool([tableController isHeaderSection:1], is(equalToBool(NO))); assertThatBool([tableController isHeaderSection:2], is(equalToBool(NO))); } - (void)testDetermineIfARowIndexIsAHeaderRow { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isHeaderRow:0], is(equalToBool(YES))); assertThatBool([tableController isHeaderRow:1], is(equalToBool(NO))); assertThatBool([tableController isHeaderRow:2], is(equalToBool(NO))); } - (void)testDetermineIfASectionIndexIsAFooterSectionSingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isFooterSection:0], is(equalToBool(YES))); assertThatBool([tableController isFooterSection:1], is(equalToBool(NO))); assertThatBool([tableController isFooterSection:2], is(equalToBool(NO))); } - (void)testDetermineIfASectionIndexIsAFooterSectionMultipleSections { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isFooterSection:0], is(equalToBool(NO))); assertThatBool([tableController isFooterSection:1], is(equalToBool(YES))); assertThatBool([tableController isFooterSection:2], is(equalToBool(NO))); } - (void)testDetermineIfARowIndexIsAFooterRow { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isFooterRow:0], is(equalToBool(NO))); assertThatBool([tableController isFooterRow:1], is(equalToBool(NO))); assertThatBool([tableController isFooterRow:2], is(equalToBool(YES))); } - (void)testIsFooterRowIsAccurateWhenThereIsAFooterRowWithoutAHeaderRowInASingleSection { RKLogConfigureByName("RestKit/UI", RKLogLevelTrace); [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/empty.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsFooterRowsWhenEmpty = YES; tableController.predicate = [NSPredicate predicateWithValue:NO]; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(YES))); } - (void)testIsFooterRowIsAccurateWhenThereIsAHeaderAndAFooterRowInASingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/empty.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsFooterRowsWhenEmpty = YES; tableController.predicate = [NSPredicate predicateWithValue:NO]; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(NO))); indexPath = [NSIndexPath indexPathForRow:1 inSection:0]; assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(YES))); } - (void)testIsFooterRowIsAccurateWhenThereIsAreContentRowsAndAHeaderAndAFooterRowInASingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; assertThatBool([tableController isHeaderIndexPath:indexPath], is(equalToBool(YES))); assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(NO))); // Check the content rows -- there are 2 of them in the JSON indexPath = [NSIndexPath indexPathForRow:1 inSection:0]; assertThatBool([tableController isHeaderIndexPath:indexPath], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(NO))); indexPath = [NSIndexPath indexPathForRow:2 inSection:0]; assertThatBool([tableController isHeaderIndexPath:indexPath], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(NO))); indexPath = [NSIndexPath indexPathForRow:3 inSection:0]; assertThatBool([tableController isHeaderIndexPath:indexPath], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:indexPath], is(equalToBool(YES))); } - (void)testDetermineIfASectionIndexIsAnEmptySection { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatBool([tableController isEmptySection:0], is(equalToBool(YES))); assertThatBool([tableController isEmptySection:1], is(equalToBool(NO))); assertThatBool([tableController isEmptySection:2], is(equalToBool(NO))); } - (void)testDetermineIfARowIndexIsAnEmptyRow { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController loadTable]; assertThatBool([tableController isEmptyRow:0], is(equalToBool(YES))); assertThatBool([tableController isEmptyRow:1], is(equalToBool(NO))); assertThatBool([tableController isEmptyRow:2], is(equalToBool(NO))); } - (void)testDetermineIfAnIndexPathIsAHeaderIndexPath { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalToBool(YES))); assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isHeaderIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]], is(equalToBool(NO))); } - (void)testDetermineIfAnIndexPathIsAFooterIndexPath { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalToBool(YES))); assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isFooterIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]], is(equalToBool(NO))); } - (void)testDetermineIfAnIndexPathIsAnEmptyIndexPathSingleSectionEmptyItemOnly { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalToBool(YES))); assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalToBool(NO))); assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalToBool(NO))); assertThatBool([tableController isEmptyItemIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]], is(equalToBool(NO))); } - (void)testConvertAnIndexPathForHeaderRows { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); } - (void)testConvertAnIndexPathForFooterRowsSingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); } - (void)testConvertAnIndexPathForFooterRowsMultipleSections { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:1]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:1]))); } - (void)testConvertAnIndexPathForEmptyRow { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:3 inSection:0]))); } - (void)testConvertAnIndexPathForHeaderFooterRowsSingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); } - (void)testConvertAnIndexPathForHeaderFooterRowsMultipleSections { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:1]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:1]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:1]))); } - (void)testConvertAnIndexPathForHeaderFooterEmptyRowsSingleSection { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:4 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:3 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:5 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:4 inSection:0]))); } - (void)testConvertAnIndexPathForHeaderFooterEmptyRowsMultipleSections { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.sectionNameKeyPath = @"name"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:1]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:1]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:1]))); } - (void)testConvertAnIndexPathForHeaderEmptyRows { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController loadTable]; assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:0 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:1 inSection:0]))); assertThat([tableController fetchedResultsIndexPathForIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo([NSIndexPath indexPathForRow:2 inSection:0]))); } - (void)testShowHeaderRows { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *headerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]; [tableController addHeaderRowForItem:headerRow]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(3))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo(headerRow))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo(other))); } - (void)testShowFooterRows { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *footerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]; [tableController addFooterRowForItem:footerRow]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(3))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(other))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo(footerRow))); } - (void)testHideHeaderRowsWhenEmptyWhenPropertyIsNotSet { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(0))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); } - (void)testHideFooterRowsWhenEmptyWhenPropertyIsNotSet { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(0))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); } - (void)testRemoveHeaderAndFooterCountsWhenDeterminingIsEmpty { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(1))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); } - (void)testNotShowTheEmptyItemWhenTheTableIsNotEmpty { [self bootstrapStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *headerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]; [tableController addHeaderRowForItem:headerRow]; RKTableItem *footerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]; [tableController addFooterRowForItem:footerRow]; RKTableItem *emptyItem = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]; [tableController setEmptyItem:emptyItem]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; RKHuman *blake = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"blake"]; RKHuman *other = [self fetchObjectWithEntityForName:@"RKHuman" withAttribute:@"name" equalTo:@"other"]; assertThatInt([tableController rowCount], is(equalToInt(4))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]], is(equalTo(headerRow))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]], is(equalTo(blake))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]], is(equalTo(other))); assertThat([tableController objectForRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]], is(equalTo(footerRow))); } - (void)testShowTheEmptyItemWhenTheTableIsEmpty { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = NO; tableController.showsFooterRowsWhenEmpty = NO; [tableController loadTable]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(1))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); } - (void)testShowTheEmptyItemPlusHeadersAndFootersWhenTheTableIsEmpty { [self bootstrapEmptyStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; [tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Header"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Footer"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; [tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) { tableItem.text = @"Empty"; tableItem.cellMapping = [RKTableViewCellMapping cellMappingUsingBlock:^(RKTableViewCellMapping *cellMapping) { [cellMapping addDefaultMappings]; }]; }]]; tableController.showsHeaderRowsWhenEmpty = YES; tableController.showsFooterRowsWhenEmpty = YES; [tableController loadTable]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(3))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); } - (void)testShowTheEmptyImageAfterLoadingAnEmptyCollectionIntoAnEmptyFetch { [self bootstrapEmptyStoreAndCache]; [self stubObjectManagerToOnline]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; UIImage *image = [RKTestFixture imageWithContentsOfFixture:@"blake.png"]; tableController.imageForEmpty = image; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/empty/array"; tableController.autoRefreshFromNetwork = YES; [tableController.cache invalidateAll]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; assertThatBool(tableController.isLoaded, is(equalToBool(YES))); assertThatInt([tableController rowCount], is(equalToInt(0))); assertThatBool(tableController.isEmpty, is(equalToBool(YES))); assertThat(tableController.stateOverlayImageView.image, is(notNilValue())); } - (void)testPostANotificationWhenObjectsAreLoaded { [self bootstrapNakedObjectStoreAndCache]; UITableView *tableView = [UITableView new]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView viewController:viewController]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/NakedEvents.json"; [tableController setObjectMappingForClass:[RKEvent class]]; id observerMock = [OCMockObject observerMock]; [[NSNotificationCenter defaultCenter] addMockObserver:observerMock name:RKTableControllerDidLoadObjectsNotification object:tableController]; [[observerMock expect] notificationWithName:RKTableControllerDidLoadObjectsNotification object:tableController]; [tableController loadTable]; [observerMock verify]; } #pragma mark - Delegate Methods - (void)testDelegateIsInformedOnInsertSection { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:viewController.tableView viewController:viewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.cacheName = @"allHumansCache"; RKFetchedResultsTableControllerTestDelegate *delegate = [RKFetchedResultsTableControllerTestDelegate tableControllerDelegate]; id mockDelegate = [OCMockObject partialMockForObject:delegate]; [[mockDelegate expect] tableController:tableController didInsertSectionAtIndex:0]; tableController.delegate = mockDelegate; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:viewController]; [tableController loadTable]; assertThatInt([tableController rowCount], is(equalToInt(2))); assertThatInt([tableController sectionCount], is(equalToInt(1))); [mockDelegate verify]; } - (void)testDelegateIsInformedOfDidStartLoad { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:viewController.tableView viewController:viewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.cacheName = @"allHumansCache"; id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(RKFetchedResultsTableControllerDelegate)]; [[mockDelegate expect] tableControllerDidStartLoad:tableController]; tableController.delegate = mockDelegate; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:viewController]; [tableController loadTable]; [mockDelegate verify]; } - (void)testDelegateIsInformedOfDidFinishLoad { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:viewController.tableView viewController:viewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.cacheName = @"allHumansCache"; id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(RKFetchedResultsTableControllerDelegate)]; [[mockDelegate expect] tableControllerDidFinishLoad:tableController]; tableController.delegate = mockDelegate; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:viewController]; [tableController loadTable]; [mockDelegate verify]; } - (void)testDelegateIsInformedOfDidInsertObjectAtIndexPath { [self bootstrapStoreAndCache]; RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new]; RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:viewController.tableView viewController:viewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; tableController.cacheName = @"allHumansCache"; id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(RKFetchedResultsTableControllerDelegate)]; [[mockDelegate expect] tableController:tableController didInsertObject:OCMOCK_ANY atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; [[mockDelegate expect] tableController:tableController didInsertObject:OCMOCK_ANY atIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]]; tableController.delegate = mockDelegate; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:viewController]; [tableController loadTable]; [mockDelegate verify]; } - (void)testRetrievalOfManagedObjectIndexPath { [self bootstrapStoreAndCache]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UITableViewController *tableViewController = [storyboard instantiateInitialViewController]; RKFetchedResultsTableController *tableController; tableController = [RKFetchedResultsTableController tableControllerForTableViewController:tableViewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"]; headerItem.cellMapping.reuseIdentifier = @"HeaderCell"; [headerItem.cellMapping addDefaultMappings]; [tableController addHeaderRowForItem:headerItem]; RKTableItem *footerItem = [RKTableItem tableItemWithText:@"Footer"]; footerItem.cellMapping.reuseIdentifier = @"FooterCell"; [footerItem.cellMapping addDefaultMappings]; [tableController addFooterRowForItem:footerItem]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:tableViewController]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; // Let the table update [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]]; assertThatInt([tableController rowCount], is(equalToInteger(4))); NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RKHuman"]; fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; RKHuman *human = [objects objectAtIndex:0]; NSIndexPath *indexPath = [tableController indexPathForObject:human]; assertThatInteger(indexPath.section, is(equalToInteger(0))); assertThatInteger(indexPath.row, is(equalToInteger(1))); } - (void)testRetrievalOfHeaderItemIndexPath { [self bootstrapStoreAndCache]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UITableViewController *tableViewController = [storyboard instantiateInitialViewController]; RKFetchedResultsTableController *tableController; tableController = [RKFetchedResultsTableController tableControllerForTableViewController:tableViewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"]; headerItem.cellMapping.reuseIdentifier = @"HeaderCell"; [headerItem.cellMapping addDefaultMappings]; [tableController addHeaderRowForItem:headerItem]; RKTableItem *footerItem = [RKTableItem tableItemWithText:@"Footer"]; footerItem.cellMapping.reuseIdentifier = @"FooterCell"; [footerItem.cellMapping addDefaultMappings]; [tableController addFooterRowForItem:footerItem]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:tableViewController]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; // Let the table update [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; assertThatInt([tableController rowCount], is(equalToInteger(4))); NSIndexPath *indexPath = [tableController indexPathForObject:headerItem]; assertThatInteger(indexPath.section, is(equalToInteger(0))); assertThatInteger(indexPath.row, is(equalToInteger(0))); } - (void)testRetrievalOfFooterItemIndexPath { [self bootstrapStoreAndCache]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UITableViewController *tableViewController = [storyboard instantiateInitialViewController]; RKFetchedResultsTableController *tableController; tableController = [RKFetchedResultsTableController tableControllerForTableViewController:tableViewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"]; headerItem.cellMapping.reuseIdentifier = @"HeaderCell"; [headerItem.cellMapping addDefaultMappings]; [tableController addHeaderRowForItem:headerItem]; RKTableItem *footerItem = [RKTableItem tableItemWithText:@"Footer"]; footerItem.cellMapping.reuseIdentifier = @"FooterCell"; [footerItem.cellMapping addDefaultMappings]; [tableController addFooterRowForItem:footerItem]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:tableViewController]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; // Let the table update [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; assertThatInt([tableController rowCount], is(equalToInteger(4))); NSIndexPath *indexPath = [tableController indexPathForObject:footerItem]; assertThatInteger(indexPath.section, is(equalToInteger(0))); assertThatInteger(indexPath.row, is(equalToInteger(3))); } - (void)testRetrievalOfEmptyItemReturnsNilIndexPathWhenNotEmpty { [self bootstrapStoreAndCache]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UITableViewController *tableViewController = [storyboard instantiateInitialViewController]; RKFetchedResultsTableController *tableController; tableController = [RKFetchedResultsTableController tableControllerForTableViewController:tableViewController]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/all.json"; RKTableItem *emptyItem = [RKTableItem tableItemWithText:@"Empty!"]; emptyItem.cellMapping.reuseIdentifier = @"HeaderCell"; [emptyItem.cellMapping addDefaultMappings]; tableController.emptyItem = emptyItem; RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"]; headerItem.cellMapping.reuseIdentifier = @"HeaderCell"; [headerItem.cellMapping addDefaultMappings]; [tableController addHeaderRowForItem:headerItem]; RKTableItem *footerItem = [RKTableItem tableItemWithText:@"Footer"]; footerItem.cellMapping.reuseIdentifier = @"FooterCell"; [footerItem.cellMapping addDefaultMappings]; [tableController addFooterRowForItem:footerItem]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:tableViewController]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; // Let the table update [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; assertThatInt([tableController rowCount], is(equalToInteger(4))); NSIndexPath *indexPath = [tableController indexPathForObject:emptyItem]; assertThat(indexPath, is(nilValue())); } - (void)testRetrievalOfEmptyItemReturnsIndexPathWhenEmpty { [self bootstrapStoreAndCache]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UITableViewController *tableViewController = [storyboard instantiateInitialViewController]; RKFetchedResultsTableController *tableController; tableController = [[RKFetchedResultsTableController tableControllerForTableViewController:tableViewController] retain]; RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping]; [cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"]; [tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping]; tableController.managedObjectContext = self.managedObjectContext; tableController.resourcePath = @"/JSON/humans/empty.json"; tableController.showsFooterRowsWhenEmpty = NO; tableController.showsHeaderRowsWhenEmpty = NO; RKTableItem *emptyItem = [RKTableItem tableItemWithText:@"Empty!"]; emptyItem.cellMapping.reuseIdentifier = @"HeaderCell"; [emptyItem.cellMapping addDefaultMappings]; tableController.emptyItem = emptyItem; RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"]; headerItem.cellMapping.reuseIdentifier = @"HeaderCell"; [headerItem.cellMapping addDefaultMappings]; [tableController addHeaderRowForItem:headerItem]; RKTableItem *footerItem = [RKTableItem tableItemWithText:@"Footer"]; footerItem.cellMapping.reuseIdentifier = @"FooterCell"; [footerItem.cellMapping addDefaultMappings]; [tableController addFooterRowForItem:footerItem]; [[[[UIApplication sharedApplication] windows] objectAtIndex:0] setRootViewController:tableViewController]; [RKTestNotificationObserver waitForNotificationWithName:RKTableControllerDidFinishLoadNotification usingBlock:^{ [tableController loadTable]; }]; // Let the table update [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; assertThatInt([tableController rowCount], is(equalToInteger(1))); NSIndexPath *indexPath = [tableController indexPathForObject:emptyItem]; assertThatInteger(indexPath.section, is(equalToInteger(0))); assertThatInteger(indexPath.row, is(equalToInteger(0))); } @end