ComponentKitTests/CKComponentControllerTests.mm (458 lines of code) (raw):
/*
* Copyright (c) 2014-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 <XCTest/XCTest.h>
#import <ComponentKit/CKComponent.h>
#import <ComponentKit/CKComponentController.h>
#import <ComponentKit/CKComponentControllerEvents.h>
#import <ComponentKit/CKComponentControllerHelper.h>
#import <ComponentKit/CKComponentScope.h>
#import <ComponentKit/CKComponentSubclass.h>
#import <ComponentKit/CKComponentHostingView.h>
#import <ComponentKit/CKThreadLocalComponentScope.h>
#import <ComponentKit/CKComponentControllerInternal.h>
#import <ComponentKitTestHelpers/CKComponentLifecycleTestHelper.h>
#import <ComponentKitTestHelpers/CKComponentTestRootScope.h>
#import <ComponentKitTestHelpers/CKLifecycleTestComponent.h>
#import "CKComponentTestCase.h"
using namespace CKComponentControllerHelper;
// Used for testing component controller that doesn't have lifecycle methods implemented.
@interface CKEmptyComponentController: CKComponentController
@end
@interface CKComponentControllerTests : CKComponentTestCase
@end
@implementation CKComponentControllerTests
static CKComponent *componentProvider(id<NSObject> model, id<NSObject>context)
{
return [CKLifecycleTestComponent new];
}
- (void)testThatCreatingComponentCreatesAController
{
CKComponentTestRootScope scope;
CKLifecycleTestComponent *fooComponent = [CKLifecycleTestComponent new];
XCTAssertNotNil(fooComponent.controller);
}
- (void)testThatAttachingManagerInstantiatesComponentController
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertNotNil(fooComponent.controller, @"Expected mounting a component to create controller");
}
- (void)testThatRemountingUnchangedComponentDoesNotCallDidUpdateComponent
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc]
initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
CKLifecycleTestComponentController *controller = fooComponent.controller;
[componentLifecycleTestController detachFromView];
controller.calledDidUpdateComponent = NO; // Reset to NO
[componentLifecycleTestController attachToView:view];
XCTAssertFalse(controller.calledDidUpdateComponent, @"Component did not update so should not call didUpdateComponent");
}
- (void)testThatUpdatingManagerUpdatesComponentController
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
UIView *view = [UIView new];
const CKComponentLifecycleTestHelperState state1 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state1];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent1 = (CKLifecycleTestComponent *)state1.componentLayout.component;
const CKComponentLifecycleTestHelperState state2 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state2];
CKLifecycleTestComponent *fooComponent2 = (CKLifecycleTestComponent *)state1.componentLayout.component;
XCTAssertTrue(fooComponent1.controller == fooComponent2.controller,
@"Expected controller %@ to match %@",
fooComponent1.controller, fooComponent2.controller);
}
- (void)testThatAttachingManagerCallsDidAcquireView
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertTrue(fooComponent.controller.counts.didAcquireView > 0, @"Expected mounting to acquire view");
XCTAssertNotNil(fooComponent.controller.view, @"Expected mounting to acquire view");
}
- (void)testThatDetachingManagerCallsDidRelinquishView
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertFalse(fooComponent.controller.counts.willRelinquishView > 0, @"Did not expect view to be released before detach");
[componentLifecycleTestController detachFromView];
XCTAssertTrue(fooComponent.controller.counts.willRelinquishView > 0, @"Expected detach to call release view");
XCTAssertNil(fooComponent.controller.view, @"Expected detach to release view");
}
- (void)testThatUpdatingStateWhileAttachedRelinquishesOldViewAndAcquiresNewOne
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertTrue(fooComponent.controller.counts.didAcquireView > 0, @"Expected mounting to acquire view");
XCTAssertNotNil(fooComponent.controller.view, @"Expected mounting to acquire view");
UIView *originalView = fooComponent.controller.view;
fooComponent.controller.counts = {}; // reset
[fooComponent updateStateToIncludeNewAttribute];
XCTAssertTrue(fooComponent.controller.counts.willRelinquishView > 0, @"Expected state update to relinquish old view");
XCTAssertTrue(fooComponent.controller.counts.didAcquireView > 0, @"Expected state update to relinquish old view");
XCTAssertTrue(originalView != fooComponent.controller.view, @"Expected different view");
}
- (void)testThatAttachingWithDifferentViewRelinquishesOldViewAndAcquiresNewOne
{
const auto componentLifecycleTestController =
[[CKComponentLifecycleTestHelper alloc]
initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const auto state =
[componentLifecycleTestController
prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
const auto view1 = [UIView new];
[componentLifecycleTestController attachToView:view1];
const auto fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
// Attaching first view should only trigger `didAcquireView` because there is no previous view
// to be relinquished.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 0);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 1);
const auto view2 = [UIView new];
[componentLifecycleTestController attachToView:view2];
// Attaching second view should trigger both `willRelinquishView` and `didAcquireView` because
// we need to relinquish the previous view it acquired.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 1);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 2);
}
- (void)testThatAttachingWithDifferentViewAfterUpdatingStateRelinquishesOldViewAndAcquiresNewOne
{
const auto componentLifecycleTestController =
[[CKComponentLifecycleTestHelper alloc]
initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const auto state =
[componentLifecycleTestController
prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
const auto view1 = [UIView new];
[componentLifecycleTestController attachToView:view1];
const auto fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
// Attaching first view should only trigger `didAcquireView` because there is no previous view
// to be relinquished.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 0);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 1);
[fooComponent updateState:^id(id currentState) {
return @NO;
} mode:CKUpdateModeSynchronous];
const auto view2 = [UIView new];
[componentLifecycleTestController attachToView:view2];
// Attaching second view should trigger both `willRelinquishView` and `didAcquireView` because
// we need to relinquish the previous view it acquired.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 1);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 2);
}
- (void)testThatAttachingWithSameViewAfterUpdatingStateDoesNotRelinquishesOldView
{
const auto componentLifecycleTestController =
[[CKComponentLifecycleTestHelper alloc]
initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const auto state =
[componentLifecycleTestController
prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
const auto view = [UIView new];
[componentLifecycleTestController attachToView:view];
const auto fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
// Attaching first view should only trigger `didAcquireView` because there is no previous view
// to be relinquished.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 0);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 1);
[fooComponent updateState:^id(id currentState) {
return @NO;
} mode:CKUpdateModeSynchronous];
[componentLifecycleTestController attachToView:view];
// Attaching the same view should not trigger `willRelinquishView` or `didAcquireView` after
// state of component is updated.
XCTAssertEqual(fooComponent.controller.counts.willRelinquishView, 0);
XCTAssertEqual(fooComponent.controller.counts.didAcquireView, 1);
}
- (void)testThatResponderChainIsInOrderComponentThenControllerThenRootView
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertEqualObjects([fooComponent nextResponder], fooComponent.controller,
@"Component's nextResponder should be component controller");
XCTAssertEqualObjects([fooComponent.controller nextResponder], view,
@"Root component's controller's nextResponder should be root view");
}
- (void)testThatResponderChainTargetsCorrectResponder
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state];
UIView *view = [UIView new];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertEqualObjects([fooComponent targetForAction:nil withSender:fooComponent], fooComponent, @"Component should respond to this action");
XCTAssertEqualObjects([fooComponent targetForAction:nil withSender:nil], fooComponent.controller, @"Component's controller should respond to this action");
}
- (void)testThatEarlyReturnNew_fromFirstComponent_allowsComponentCreation_whenNotEarlyReturning_onStateUpdate
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
UIView *view = [UIView new];
CKLifecycleTestComponentSetShouldEarlyReturnNew(YES);
const CKComponentLifecycleTestHelperState state1 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state1];
[componentLifecycleTestController attachToView:view];
CKLifecycleTestComponentSetShouldEarlyReturnNew(NO);
const CKComponentLifecycleTestHelperState state2 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController updateWithState:state2];
CKLifecycleTestComponent *fooComponent2 = (CKLifecycleTestComponent *)state2.componentLayout.component;
XCTAssertTrue([fooComponent2.controller isKindOfClass:[CKLifecycleTestComponentController class]],
@"Expected controller %@ to exist and be of type CKLifecycleTestComponentController",
fooComponent2.controller);
}
- (void)testThatComponentControllerReceivesComponentTreeWillAppearEvent
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state];
CKComponentScopeRootAnnounceControllerAppearance([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)[componentLifecycleTestController state].componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledComponentTreeWillAppear,
@"Expected controller %@ to have received component tree will appear event", fooComponent.controller);
}
- (void)testThatComponentControllerReceivesComponentTreeDidDisappearEvent
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state];
CKComponentScopeRootAnnounceControllerAppearance([componentLifecycleTestController state].scopeRoot);
[componentLifecycleTestController detachFromView];
CKComponentScopeRootAnnounceControllerDisappearance([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)[componentLifecycleTestController state].componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledComponentTreeDidDisappear,
@"Expected controller %@ to have received component tree did disappear event", fooComponent.controller);
}
- (void)testThatComponentControllerReceivesComponentTreeWillAppearEventAfterAdditionalStateUpdates
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state1 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
const CKComponentLifecycleTestHelperState state2 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state1];
[componentLifecycleTestController updateWithState:state2];
CKComponentScopeRootAnnounceControllerAppearance([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)[componentLifecycleTestController state].componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledComponentTreeWillAppear,
@"Expected controller %@ to have received component tree will appear event", fooComponent.controller);
}
- (void)testThatComponentControllerReceivesComponentTreeDidDisappearEventAfterAdditionalStateUpdates
{
CKComponentLifecycleTestHelper *componentLifecycleTestController = [[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider
sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state1 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
const CKComponentLifecycleTestHelperState state2 = [componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state1];
[componentLifecycleTestController updateWithState:state2];
CKComponentScopeRootAnnounceControllerAppearance([componentLifecycleTestController state].scopeRoot);
[componentLifecycleTestController detachFromView];
CKComponentScopeRootAnnounceControllerDisappearance([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)[componentLifecycleTestController state].componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledComponentTreeDidDisappear,
@"Expected controller %@ to have received component tree did disappear event", fooComponent.controller);
}
- (void)testComponentControllerReceivesDidInitEvent
{
CKComponentLifecycleTestHelper *componentLifecycleTestController =
[[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state =
[componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state];
CKComponentScopeRootAnnounceControllerInitialization([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledDidInit,
@"Expected component controller to get did init event");
}
- (void)testComponentControllerReceivesInvalidateEvent
{
CKComponentLifecycleTestHelper *componentLifecycleTestController =
[[CKComponentLifecycleTestHelper alloc] initWithComponentProvider:componentProvider sizeRangeProvider:nil];
const CKComponentLifecycleTestHelperState state =
[componentLifecycleTestController prepareForUpdateWithModel:nil
constrainedSize:{{0,0}, {100, 100}}
context:nil];
[componentLifecycleTestController attachToView:[UIView new]];
[componentLifecycleTestController updateWithState:state];
[componentLifecycleTestController detachFromView];
CKComponentScopeRootAnnounceControllerInvalidation([componentLifecycleTestController state].scopeRoot);
CKLifecycleTestComponent *fooComponent = (CKLifecycleTestComponent *)state.componentLayout.component;
XCTAssertTrue(fooComponent.controller.calledInvalidateController,
@"Expected component controller to get invalidation event");
}
- (void)testRemovedComponentControllersFromPreviousScopeRootMatchingPredicate
{
// `CKLifecycleTestComponentController` has `invalidateController` implemented.
const auto componentController1 = [[CKLifecycleTestComponentController alloc] initWithComponent:nil];
const auto componentController2 = [[CKLifecycleTestComponentController alloc] initWithComponent:nil];
const auto previousScopeRoot =
[CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&CKComponentControllerInvalidateEventPredicate}];
[previousScopeRoot registerComponentController:componentController1];
[previousScopeRoot registerComponentController:componentController2];
const auto newScopeRoot = [previousScopeRoot newRoot];
[newScopeRoot registerComponentController:componentController1];
// New scope root doesn't have `componentController2` registered.
const auto removedComponentControllers =
removedControllersFromPreviousScopeRootMatchingPredicate(newScopeRoot,
previousScopeRoot,
&CKComponentControllerInvalidateEventPredicate);
XCTAssertEqual(removedComponentControllers[0], componentController2);
}
- (void)testRemovedComponentControllersAreEmptyFromPreviousScopeRootMatchingPredicate
{
// `CKLifecycleTestComponentController` has `invalidateController` implemented.
const auto componentController1 = [[CKLifecycleTestComponentController alloc] initWithComponent:nil];
const auto componentController2 = [[CKLifecycleTestComponentController alloc] initWithComponent:nil];
const auto previousScopeRoot =
[CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&CKComponentControllerInvalidateEventPredicate}];
[previousScopeRoot registerComponentController:componentController1];
[previousScopeRoot registerComponentController:componentController2];
const auto newScopeRoot = [previousScopeRoot newRoot];
[newScopeRoot registerComponentController:componentController1];
[newScopeRoot registerComponentController:componentController2];
// Both component controllers are presented in new scope root.
const auto removedComponentControllers =
removedControllersFromPreviousScopeRootMatchingPredicate(newScopeRoot,
previousScopeRoot,
&CKComponentControllerInvalidateEventPredicate);
XCTAssertTrue(removedComponentControllers.empty());
}
- (void)testRemovedComponentControllersAreEmptyWhenPreviousScopeRootIsNil
{
const auto scopeRoot =
[CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&CKComponentControllerInvalidateEventPredicate}];
const auto removedComponentControllers =
removedControllersFromPreviousScopeRootMatchingPredicate(scopeRoot,
nil,
&CKComponentControllerInvalidateEventPredicate);
XCTAssertTrue(removedComponentControllers.empty());
}
- (void)testRemovedComponentControllersFromPreviousScopeRootNotMatchingPredicate
{
const auto componentController = [[CKEmptyComponentController alloc] initWithComponent:nil];
const auto previousScopeRoot =
[CKComponentScopeRoot
rootWithListener:nil
analyticsListener:nil
componentPredicates:{}
componentControllerPredicates:{&CKComponentControllerInvalidateEventPredicate}];
[previousScopeRoot registerComponentController:componentController];
const auto newScopeRoot = [previousScopeRoot newRoot];
// `componentController` doesn't match predicate.
const auto removedComponentControllers =
removedControllersFromPreviousScopeRootMatchingPredicate(newScopeRoot,
previousScopeRoot,
&CKComponentControllerInvalidateEventPredicate);
XCTAssertTrue(removedComponentControllers.empty());
}
- (void)testDidUpdateComponentIsCalledOnMountIfCommponentIsNotUpdatedWithSetter
{
const auto component1 = [CKLifecycleTestComponent new];
const auto component2 = [CKLifecycleTestComponent new];
const auto controller = [[CKLifecycleTestComponentController alloc] initWithComponent:component1];
XCTAssertEqual(controller.calledWillUpdateComponent, 0);
XCTAssertEqual(controller.calledDidUpdateComponent, 0);
[controller componentWillMount:component2];
XCTAssertEqual(controller.calledWillUpdateComponent, 1);
XCTAssertEqual(controller.calledDidUpdateComponent, 0);
[controller componentDidMount:component2];
XCTAssertEqual(controller.calledWillUpdateComponent, 1);
XCTAssertEqual(controller.calledDidUpdateComponent, 1);
}
- (void)testDidUpdateComponentIsCalledOnMountIfCommponentIsUpdatedWithSetter
{
const auto component1 = [CKLifecycleTestComponent new];
const auto component2 = [CKLifecycleTestComponent new];
const auto controller = [[CKLifecycleTestComponentController alloc] initWithComponent:component1];
XCTAssertEqual(controller.calledWillUpdateComponent, 0);
XCTAssertEqual(controller.calledDidUpdateComponent, 0);
controller.latestComponent = component2;
XCTAssertEqual(controller.calledWillUpdateComponent, 1);
XCTAssertEqual(controller.calledDidUpdateComponent, 0);
[controller componentWillMount:component2];
XCTAssertEqual(controller.calledWillUpdateComponent, 1);
XCTAssertEqual(controller.calledDidUpdateComponent, 0);
[controller componentDidMount:component2];
XCTAssertEqual(controller.calledWillUpdateComponent, 1);
XCTAssertEqual(controller.calledDidUpdateComponent, 1);
}
@end
@implementation CKEmptyComponentController
@end