/** * Copyright (c) 2016-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import #import #import "IGTestDiffingDataSource.h" #import "IGTestDiffingObject.h" #import "IGTestDiffingSectionController.h" #import "IGTestStringBindableCell.h" #import "IGTestNumberBindableCell.h" #import "IGListAdapterInternal.h" #import "IGTestObject.h" #import "IGTestCell.h" @interface IGListBindingSectionControllerTests : XCTestCase @property (nonatomic, strong) IGListCollectionView *collectionView; @property (nonatomic, strong) IGListAdapter *adapter; @property (nonatomic, strong) IGTestDiffingDataSource *dataSource; @property (nonatomic, strong) UICollectionViewFlowLayout *layout; @property (nonatomic, strong) UIWindow *window; @end @implementation IGListBindingSectionControllerTests - (void)setUp { [super setUp]; self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 1000)]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; self.collectionView = [[IGListCollectionView alloc] initWithFrame:self.window.bounds collectionViewLayout:layout]; [self.window addSubview:self.collectionView]; self.dataSource = [IGTestDiffingDataSource new]; self.adapter = [[IGListAdapter alloc] initWithUpdater:[IGListAdapterUpdater new] viewController:nil workingRangeSize:0]; } - (void)tearDown { [super tearDown]; self.window = nil; self.collectionView = nil; self.adapter = nil; self.dataSource = nil; } - (void)setupWithObjects:(NSArray *)objects { self.dataSource.objects = objects; self.adapter.collectionView = self.collectionView; self.adapter.dataSource = self.dataSource; [self.collectionView layoutIfNeeded]; } - (id)cellAtSection:(NSInteger)section item:(NSInteger)item { return [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:section]]; } - (void)test_whenInitialLoad_withEmptyViewModels_thatCollectionViewEmpty { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[]] ]]; XCTAssertEqual([self.collectionView numberOfSections], 1); XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 0); } - (void)test_whenInitialLoad_withMultipleViewModels_thatCellsMappedAndConfigured { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], [[IGTestDiffingObject alloc] initWithKey:@2 objects:@[@"foo", @"bar", @42]], [[IGTestDiffingObject alloc] initWithKey:@3 objects:@[]], ]]; XCTAssertEqual([self.collectionView numberOfSections], 3); XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2); XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 3); XCTAssertEqual([self.collectionView numberOfItemsInSection:2], 0); IGTestNumberBindableCell *cell00 = [self cellAtSection:0 item:0]; IGTestStringBindableCell *cell01 = [self cellAtSection:0 item:1]; IGTestStringBindableCell *cell10 = [self cellAtSection:1 item:0]; IGTestStringBindableCell *cell11 = [self cellAtSection:1 item:1]; IGTestNumberBindableCell *cell12 = [self cellAtSection:1 item:2]; XCTAssertEqualObjects(cell00.textField.text, @"7"); XCTAssertEqualObjects(cell01.label.text, @"seven"); XCTAssertEqualObjects(cell10.label.text, @"foo"); XCTAssertEqualObjects(cell11.label.text, @"bar"); XCTAssertEqualObjects(cell12.textField.text, @"42"); } - (void)test_whenUpdating_withAddedModels_thatCellsCorrectAndConfigured { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], ]]; self.dataSource.objects = @[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven", @8, @"eight"]], ]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self.adapter performUpdatesAnimated:YES completion:^(BOOL finished) { XCTAssertEqual([self.collectionView numberOfSections], 1); XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 4); IGTestNumberBindableCell *cell00 = [self cellAtSection:0 item:0]; IGTestStringBindableCell *cell01 = [self cellAtSection:0 item:1]; IGTestNumberBindableCell *cell02 = [self cellAtSection:0 item:2]; IGTestStringBindableCell *cell03 = [self cellAtSection:0 item:3]; XCTAssertEqualObjects(cell00.textField.text, @"7"); XCTAssertEqualObjects(cell01.label.text, @"seven"); XCTAssertEqualObjects(cell02.textField.text, @"8"); XCTAssertEqualObjects(cell03.label.text, @"eight"); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:15 handler:nil]; } - (void)test_whenSelectingCell_thatCorrectViewModelSelected { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], ]]; [self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0]]; IGTestDiffingSectionController *section = [self.adapter sectionControllerForObject:self.dataSource.objects.firstObject]; XCTAssertEqualObjects(section.selectedViewModel, @"seven"); } - (void)test_whenAdapterReloadsObjects_thatSectionUpdated { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], ]]; [self.adapter reloadObjects:@[[[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@"four", @4, @"seven", @7]]]]; IGTestNumberBindableCell *cell00 = [self cellAtSection:0 item:0]; IGTestStringBindableCell *cell01 = [self cellAtSection:0 item:1]; XCTAssertEqualObjects(cell00.textField.text, @"7"); XCTAssertEqualObjects(cell01.label.text, @"seven"); XCTAssertNil([self cellAtSection:0 item:2]); XCTAssertNil([self cellAtSection:0 item:3]); // "fake" batch updates to make sure that calling reload triggers a diffed batch update XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self.adapter performBatchAnimated:YES updates:^(id batchContext){} completion:^(BOOL finished) { IGTestStringBindableCell *batchedCell00 = [self cellAtSection:0 item:0]; IGTestNumberBindableCell *batchedCell01 = [self cellAtSection:0 item:1]; IGTestStringBindableCell *batchedCell02 = [self cellAtSection:0 item:2]; IGTestNumberBindableCell *batchedCell03 = [self cellAtSection:0 item:3]; XCTAssertEqualObjects(batchedCell00.label.text, @"four"); XCTAssertEqualObjects(batchedCell01.textField.text, @"4"); XCTAssertEqualObjects(batchedCell02.label.text, @"seven"); XCTAssertEqualObjects(batchedCell03.textField.text, @"7"); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:16 handler:nil]; } - (void)test_whenUpdating_withViewModelMovesAndReloads_thatCellUpdatedAndInstanceSame { NSArray *initObjects = @[ @"foo", @"bar", [[IGTestObject alloc] initWithKey:@42 value:@"baz"] ]; [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:initObjects] ]]; XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 3); IGTestStringBindableCell *cell00 = [self cellAtSection:0 item:0]; IGTestStringBindableCell *cell01 = [self cellAtSection:0 item:1]; IGTestCell *cell02 = [self cellAtSection:0 item:2]; XCTAssertEqualObjects(cell00.label.text, @"foo"); XCTAssertEqualObjects(cell01.label.text, @"bar"); XCTAssertEqualObjects(cell02.label.text, @"baz"); NSArray *newObjects = @[ [[IGTestObject alloc] initWithKey:@42 value:@"bang"], // moved to section 0 and value changed @"foo", @"bar", ]; self.dataSource.objects = @[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:newObjects] ]; XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self.adapter performUpdatesAnimated:YES completion:^(BOOL finished) { IGTestCell *batchedCell00 = [self cellAtSection:0 item:0]; IGTestStringBindableCell *batchedCell01 = [self cellAtSection:0 item:1]; IGTestStringBindableCell *batchedCell02 = [self cellAtSection:0 item:2]; XCTAssertEqualObjects(batchedCell00.label.text, @"bang"); XCTAssertEqualObjects(batchedCell01.label.text, @"foo"); XCTAssertEqualObjects(batchedCell02.label.text, @"bar"); XCTAssertEqual(cell00, batchedCell01); XCTAssertEqual(cell01, batchedCell02); XCTAssertEqual(cell02, batchedCell00); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:16 handler:nil]; } - (void)test_whenUpdatingManually_with2Updates_thatBothCompletionBlocksCalled { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], ]]; IGTestDiffingSectionController *section = [self.adapter sectionControllerForObject:self.dataSource.objects[0]]; XCTestExpectation *expectation1 = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [section updateAnimated:YES completion:^(BOOL updated) { XCTAssertTrue(updated); [expectation1 fulfill]; }]; XCTestExpectation *expectation2 = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [section updateAnimated:YES completion:^(BOOL updated) { // queued second, shouldn't execute update block XCTAssertFalse(updated); [expectation2 fulfill]; }]; [self waitForExpectationsWithTimeout:15 handler:nil]; } @end