Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more unit tests to stack section controller #286

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions IGListKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -455,12 +455,12 @@
8285404F1DE40D2D00118B94 /* IGListTestAdapterHorizontalDataSource.m */,
8240C7F91DC2F6CF00B3AAE7 /* IGListTestAdapterStoryboardDataSource.h */,
8240C7FA1DC2F6CF00B3AAE7 /* IGListTestAdapterStoryboardDataSource.m */,
8285404A1DE40C6E00118B94 /* IGListTestHorizontalSection.h */,
8285404B1DE40C6E00118B94 /* IGListTestHorizontalSection.m */,
88144EF31D870EDC007C7F66 /* IGListTestOffsettingLayout.h */,
88144EF41D870EDC007C7F66 /* IGListTestOffsettingLayout.m */,
88144EF51D870EDC007C7F66 /* IGListTestSection.h */,
88144EF61D870EDC007C7F66 /* IGListTestSection.m */,
8285404A1DE40C6E00118B94 /* IGListTestHorizontalSection.h */,
8285404B1DE40C6E00118B94 /* IGListTestHorizontalSection.m */,
8240C7F61DC2F3FB00B3AAE7 /* IGListTestStoryboardSection.h */,
8240C7F71DC2F3FB00B3AAE7 /* IGListTestStoryboardSection.m */,
88144EF71D870EDC007C7F66 /* IGListTestUICollectionViewDataSource.h */,
Expand Down
42 changes: 9 additions & 33 deletions Source/IGListStackedSectionController.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,13 @@

#import "IGListSectionControllerInternal.h"

@interface UICollectionViewCell (IGListStackedSectionController)
@end
@implementation UICollectionViewCell (IGListStackedSectionController)

static void * kStackedSectionControllerKey = &kStackedSectionControllerKey;

- (void)ig_setStackedSectionController:(id)stackedSectionController {
objc_setAssociatedObject(self, kStackedSectionControllerKey, stackedSectionController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)ig_stackedSectionController {
return objc_getAssociatedObject(self, kStackedSectionControllerKey);
}

static void * kStackedSectionControllerIndexKey = &kStackedSectionControllerIndexKey;

- (void)ig_setStackedSectionControllerIndex:(NSInteger)stackedSectionControllerIndex {
objc_setAssociatedObject(self, kStackedSectionControllerIndexKey, @(stackedSectionControllerIndex), OBJC_ASSOCIATION_ASSIGN);
}

- (NSInteger)ig_stackedSectionControllerIndex {
return [objc_getAssociatedObject(self, kStackedSectionControllerIndexKey) integerValue];
}

@end

@implementation IGListStackedSectionController

- (instancetype)initWithSectionControllers:(NSArray <IGListSectionController<IGListSectionType> *> *)sectionControllers {
if (self = [super init]) {
for (IGListSectionController<IGListSectionType> *sectionController in sectionControllers) {
sectionController.collectionContext = self;
sectionController.viewController = self.viewController;

if (self.supplementaryViewSource == nil) {
self.supplementaryViewSource = sectionController.supplementaryViewSource;
}
}

_visibleSectionControllers = [[NSCountedSet alloc] init];
Expand Down Expand Up @@ -123,6 +93,15 @@ - (NSIndexSet *)itemIndexesForSectionController:(IGListSectionController<IGListS
return itemIndexes;
}

- (id<IGListSupplementaryViewSource>)supplementaryViewSource {
for (IGListSectionController *sectionController in self.sectionControllers) {
id<IGListSupplementaryViewSource> supplementaryViewSource = sectionController.supplementaryViewSource;
if (supplementaryViewSource != nil) {
return supplementaryViewSource;
}
}
return nil;
}

#pragma mark - IGListSectionType

Expand Down Expand Up @@ -305,9 +284,6 @@ - (void)listAdapter:(IGListAdapter *)listAdapter willDisplaySectionController:(I
IGListSectionController<IGListSectionType> *childSectionController = [self sectionControllerForObjectIndex:index];
const NSUInteger localIndex = [self localIndexForSectionController:childSectionController index:index];

[cell ig_setStackedSectionController:childSectionController];
[cell ig_setStackedSectionControllerIndex:localIndex];

NSCountedSet *visibleSectionControllers = self.visibleSectionControllers;
id<IGListDisplayDelegate> displayDelegate = [childSectionController displayDelegate];

Expand Down
243 changes: 240 additions & 3 deletions Tests/IGListStackSectionControllerTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
#import "IGListDisplayHandler.h"
#import "IGListStackedSectionControllerInternal.h"
#import "IGListTestSection.h"
#import "IGTestCell.h"
#import "IGTestStackedDataSource.h"
#import "IGTestStoryboardCell.h"
#import "IGTestStoryboardViewController.h"
#import "IGTestSupplementarySource.h"
#import "IGTestSupplementarySource.h"
#import "IGTestStoryboardSupplementarySource.h"

static const CGRect kStackTestFrame = (CGRect){{0.0, 0.0}, {100.0, 100.0}};

Expand All @@ -37,9 +43,15 @@ - (void)setUp {

self.window = [[UIWindow alloc] initWithFrame:kStackTestFrame];

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
self.collectionView = [[IGListCollectionView alloc] initWithFrame:kStackTestFrame collectionViewLayout:layout];
[self.window addSubview:self.collectionView];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"IGTestStoryboard" bundle:[NSBundle bundleForClass:self.class]];
IGTestStoryboardViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"testVC"];
self.window.rootViewController = vc;
[self.window addSubview:vc.view];
[vc performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
self.collectionView = vc.collectionView;

vc.view.frame = kStackTestFrame;
self.collectionView.frame = kStackTestFrame;

self.dataSource = [[IGTestStackedDataSource alloc] init];
self.adapter = [[IGListAdapter alloc] initWithUpdater:[IGListAdapterUpdater new] viewController:nil workingRangeSize:0];
Expand Down Expand Up @@ -431,4 +443,229 @@ - (void)test_whenPerformingItemUpdates_thatMutationsMapToSectionControllers {
[self waitForExpectationsWithTimeout:15 handler:nil];
}

- (void)test_whenSelectingItems_thatChildSectionControllersSelected {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@1 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
]];

[self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
[self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:[NSIndexPath indexPathForItem:2 inSection:1]];
[self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:2]];

IGListStackedSectionController *stack0 = [self.adapter sectionControllerForObject:self.dataSource.objects[0]];
IGListStackedSectionController *stack1 = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];
IGListStackedSectionController *stack2 = [self.adapter sectionControllerForObject:self.dataSource.objects[2]];

XCTAssertTrue([stack0.sectionControllers[0] wasSelected]);
XCTAssertFalse([stack0.sectionControllers[1] wasSelected]);
XCTAssertFalse([stack0.sectionControllers[2] wasSelected]);
XCTAssertFalse([stack1.sectionControllers[0] wasSelected]);
XCTAssertTrue([stack1.sectionControllers[1] wasSelected]);
XCTAssertFalse([stack1.sectionControllers[2] wasSelected]);
XCTAssertFalse([stack2.sectionControllers[0] wasSelected]);
XCTAssertTrue([stack2.sectionControllers[1] wasSelected]);
}

- (void)test_whenUsingNibs_withStoryboards_thatCellsAreConfigured {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @"nib", @"storyboard"]],
]];

UICollectionViewCell *cell0 = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
IGTestCell *cell1 = (IGTestCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0]];
IGTestStoryboardCell *cell2 = (IGTestStoryboardCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:2 inSection:0]];

XCTAssertEqualObjects(cell0.class, [UICollectionViewCell class]);
XCTAssertEqualObjects(cell1.class, [IGTestCell class]);
XCTAssertEqualObjects(cell2.class, [IGTestStoryboardCell class]);

XCTAssertEqualObjects(cell1.label.text, @"nib");
XCTAssertEqualObjects(cell2.label.text, @"storyboard");
}

- (void)test_whenForwardingDidScrollEvent_thatChildSectionControllersReceiveEvent {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
]];

id mockScrollDelegate = [OCMockObject mockForProtocol:@protocol(IGListScrollDelegate)];

IGListStackedSectionController *stack0 = [self.adapter sectionControllerForObject:self.dataSource.objects[0]];
IGListStackedSectionController *stack1 = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];

stack0.scrollDelegate = mockScrollDelegate;
stack1.scrollDelegate = mockScrollDelegate;

[[mockScrollDelegate expect] listAdapter:self.adapter didScrollSectionController:stack0];
[[mockScrollDelegate expect] listAdapter:self.adapter didScrollSectionController:stack1];

[self.adapter scrollViewDidScroll:self.collectionView];

[mockScrollDelegate verify];
}

- (void)test_whenForwardingWillBeginDraggingEvent_thatChildSectionControllersReceiveEvent {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
]];

id mockScrollDelegate = [OCMockObject mockForProtocol:@protocol(IGListScrollDelegate)];

IGListStackedSectionController *stack0 = [self.adapter sectionControllerForObject:self.dataSource.objects[0]];
IGListStackedSectionController *stack1 = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];

stack0.scrollDelegate = mockScrollDelegate;
stack1.scrollDelegate = mockScrollDelegate;

[[mockScrollDelegate expect] listAdapter:self.adapter willBeginDraggingSectionController:stack0];
[[mockScrollDelegate expect] listAdapter:self.adapter willBeginDraggingSectionController:stack1];

[self.adapter scrollViewWillBeginDragging:self.collectionView];

[mockScrollDelegate verify];
}

- (void)test_whenForwardingDidEndDraggingEvent_thatChildSectionControllersReceiveEvent {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
]];

id mockScrollDelegate = [OCMockObject mockForProtocol:@protocol(IGListScrollDelegate)];

IGListStackedSectionController *stack0 = [self.adapter sectionControllerForObject:self.dataSource.objects[0]];
IGListStackedSectionController *stack1 = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];

stack0.scrollDelegate = mockScrollDelegate;
stack1.scrollDelegate = mockScrollDelegate;

[[mockScrollDelegate expect] listAdapter:self.adapter didEndDraggingSectionController:stack0 willDecelerate:NO];
[[mockScrollDelegate expect] listAdapter:self.adapter didEndDraggingSectionController:stack1 willDecelerate:NO];

[self.adapter scrollViewDidEndDragging:self.collectionView willDecelerate:NO];

[mockScrollDelegate verify];
}

- (void)test_whenUsingSupplementary_withCode_thatSupplementaryViewExists {
// updater that uses reloadData so we can rebuild all views/sizes
IGListAdapter *adapter = [[IGListAdapter alloc] initWithUpdater:[IGListReloadDataUpdater new] viewController:nil workingRangeSize:0];

self.dataSource.objects = @[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
];

adapter.collectionView = self.collectionView;
adapter.dataSource = self.dataSource;
[self.collectionView layoutIfNeeded];

IGListStackedSectionController *stack = [adapter sectionControllerForObject:self.dataSource.objects[1]];
IGListTestSection *section = stack.sectionControllers.lastObject;

IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new];
// the stack acts as the collection context. manually assign it.
supplementarySource.collectionContext = stack;
// however the actual section controller the supplementary serves is a child of the stack
supplementarySource.sectionController = section;
supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionFooter];

section.supplementaryViewSource = supplementarySource;

[adapter performUpdatesAnimated:NO completion:nil];

XCTAssertNotNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionFooter
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]]);
XCTAssertNotNil(supplementarySource);
}

- (void)test_whenUsingSupplementary_withNib_thatSupplementaryViewExists {
// updater that uses reloadData so we can rebuild all views/sizes
IGListAdapter *adapter = [[IGListAdapter alloc] initWithUpdater:[IGListReloadDataUpdater new] viewController:nil workingRangeSize:0];

self.dataSource.objects = @[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
];

adapter.collectionView = self.collectionView;
adapter.dataSource = self.dataSource;
[self.collectionView layoutIfNeeded];

IGListStackedSectionController *stack = [adapter sectionControllerForObject:self.dataSource.objects[1]];
IGListTestSection *section = stack.sectionControllers.lastObject;

IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new];
// the stack acts as the collection context. manually assign it.
supplementarySource.collectionContext = stack;
// however the actual section controller the supplementary serves is a child of the stack
supplementarySource.sectionController = section;
supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionFooter];
supplementarySource.dequeueFromNib = YES;

section.supplementaryViewSource = supplementarySource;

[adapter performUpdatesAnimated:NO completion:nil];

XCTAssertNotNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionFooter
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]]);
XCTAssertNotNil(supplementarySource);
}

- (void)test_whenUsingSupplementary_withStoryboard_thatSupplementaryViewExists {
// updater that uses reloadData so we can rebuild all views/sizes
IGListAdapter *adapter = [[IGListAdapter alloc] initWithUpdater:[IGListReloadDataUpdater new] viewController:nil workingRangeSize:0];

self.dataSource.objects = @[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
];

adapter.collectionView = self.collectionView;
adapter.dataSource = self.dataSource;
[self.collectionView layoutIfNeeded];

IGListStackedSectionController *stack = [adapter sectionControllerForObject:self.dataSource.objects[1]];
IGListTestSection *section = stack.sectionControllers.lastObject;

IGTestStoryboardSupplementarySource *supplementarySource = [IGTestStoryboardSupplementarySource new];
// the stack acts as the collection context. manually assign it.
supplementarySource.collectionContext = stack;
// however the actual section controller the supplementary serves is a child of the stack
supplementarySource.sectionController = section;

// the "section header" property of the parent collection view must be checked
supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionHeader];

section.supplementaryViewSource = supplementarySource;

[adapter performUpdatesAnimated:NO completion:nil];

XCTAssertNotNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader
atIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]]);
XCTAssertNotNil(supplementarySource);
}

- (void)test_whenScrollingFromChildSectionController_thatScrollsToCorrectPosition {
// pad with enough items that we can freely scroll to the middle without accounting for content size
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@4, @5, @6]],
[[IGTestObject alloc] initWithKey:@1 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@4, @5, @6]]
]];

IGListStackedSectionController *stack = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];
IGListTestSection *section = stack.sectionControllers[1];

[section.collectionContext scrollToSectionController:section atIndex:1 scrollPosition:UICollectionViewScrollPositionTop animated:NO];

// IGListTestSection cells are 100x10
XCTAssertEqual(self.collectionView.contentOffset.x, 0);
XCTAssertEqual(self.collectionView.contentOffset.y, 170);
}

@end
Loading