ComponentKitTests/CKComponentMountTests.mm (136 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/CKComponentInternal.h>
#import <ComponentKit/CKComponentLayout.h>
#import <ComponentKit/CKComponentSubclass.h>
#import <ComponentKit/CKIterableHelpers.h>
#import <ComponentKit/CKLayoutComponent.h>
#import <ComponentKit/CKMountableHelpers.h>
#import <ComponentKit/CKMountedObjectForView.h>
#import "CKComponentTestCase.h"
@interface CKComponentMountTests : CKComponentTestCase
@end
@interface CKDontMountChildrenComponent : CKLayoutComponent
CK_INIT_UNAVAILABLE;
CK_LAYOUT_COMPONENT_INIT_UNAVAILABLE;
+ (instancetype)newWithChild:(CKComponent *)child;
@end
@implementation CKComponentMountTests
- (void)testThatMountingComponentThatReturnsMountChildrenNoDoesNotMountItsChild
{
CKComponent *viewComponent = CK::ComponentBuilder()
.viewClass([UIView class])
.build();
CKComponent *c = [CKDontMountChildrenComponent newWithChild:viewComponent];
RCLayout layout = [c layoutThatFits:{} parentSize:{NAN, NAN}];
XCTAssertTrue(layout.children->front().layout.component == viewComponent,
@"Expected view component to exist in the layout tree");
UIView *view = [UIView new];
NSSet *mountedComponents = CKMountComponentLayout(layout, view, nil, nil);
XCTAssertEqual([[view subviews] count], 0u,
@"CKDontMountChildrenComponent should have prevented view component from mounting");
CKUnmountComponents(mountedComponents);
}
- (void)testMountingComponentAffectsResponderChain
{
CKComponent *c = CK::ComponentBuilder()
.viewClass([UIView class])
.build();
RCLayout layout = [c layoutThatFits:{} parentSize:{NAN, NAN}];
UIView *container = [UIView new];
NSSet *mountedComponents = CKMountComponentLayout(layout, container, nil, nil);
XCTAssertEqualObjects(mountedComponents, [NSSet setWithObject:c], @"Didn't mount as expected");
XCTAssertEqualObjects([c nextResponder], container, @"Did not setup responder correctly!");
XCTAssertEqualObjects([c nextResponderAfterController], container, @"Did not setup responder correctly!");
}
- (void)testUnmounting
{
CKComponent *a = CK::ComponentBuilder()
.viewClass([UIView class])
.build();
CKComponent *b = CK::ComponentBuilder()
.viewClass([UIView class])
.build();
const RCLayout layoutBoth = {a, CGSizeZero,
{
{CGPointZero, {a, {}, {}}},
{CGPointZero, {b, {}, {}}},
}
};
UIView *container = [UIView new];
NSSet *allMounted = CKMountComponentLayout(layoutBoth, container, nil, nil);
XCTAssertNotNil(a.viewContext.view, @"Didn't create view");
XCTAssertNotNil(b.viewContext.view, @"Didn't create view");
const RCLayout layoutA = {a, CGSizeZero,
{
{CGPointZero, {a, {}, {}}},
}
};
NSSet *someMounted = CKMountComponentLayout(layoutA, container, allMounted, nil);
XCTAssertNotNil(a.viewContext.view, @"Should still be mounted");
XCTAssertNil(b.viewContext.view, @"Should not be mounted");
CKUnmountComponents(someMounted);
XCTAssertNil(a.viewContext.view, @"Should not be mounted");
XCTAssertNil(b.viewContext.view, @"Should not be mounted");
}
- (void)testPerformMount
{
const auto viewConfig = CKComponentViewConfiguration {
[UILabel class],
{{@selector(setText:), @"Hello"}}
};
const auto component = [CKComponent newWithView:viewConfig size:{}];
const auto view = [[UIView alloc] initWithFrame:CGRect {{0, 0}, {10, 10}}];
const auto context = CK::Component::MountContext::RootContext(view, nullptr);
RCLayout layout(component, {5, 5});
std::unique_ptr<CKMountInfo> mountInfo;
const auto result = CKPerformMount(mountInfo, layout, viewConfig, context, nil, nullptr, nullptr, nullptr, nullptr);
const auto label = (UILabel *)view.subviews.firstObject;
XCTAssertTrue(result.mountChildren);
XCTAssertTrue(CGRectEqualToRect(label.frame, CGRect {{0, 0}, {5, 5}}));
XCTAssertTrue(CGRectEqualToRect(mountInfo->viewContext.frame, CGRect {{0, 0}, {5, 5}}));
XCTAssertEqualObjects(label.text, @"Hello");
XCTAssertEqual(CKMountedObjectForView(label), component);
XCTAssertEqual(mountInfo->view, label);
CKPerformUnmount(mountInfo, component, nil);
XCTAssertTrue(mountInfo == nullptr);
XCTAssertNil(CKMountedObjectForView(label));
}
@end
@implementation CKDontMountChildrenComponent
{
CKComponent *_child;
}
+ (instancetype)newWithChild:(CKComponent *)child
{
CKDontMountChildrenComponent *c = [self newWithView:{} size:{}];
c->_child = child;
return c;
}
- (unsigned int)numberOfChildren
{
return RCIterable::numberOfChildren(_child);
}
- (id<CKMountable>)childAtIndex:(unsigned int)index
{
return RCIterable::childAtIndex(self, index, _child);
}
- (RCLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize
{
return {
self,
constrainedSize.clamp({100, 100}),
{{{0,0}, [_child layoutThatFits:{{100, 100}, {100, 100}} parentSize:{100, 100}]}}
};
}
- (CK::Component::MountResult)mountInContext:(const CK::Component::MountContext &)context
layout:(const RCLayout &)layout
supercomponent:(CKComponent *)supercomponent
{
CK::Component::MountResult r = [super mountInContext:context layout:layout supercomponent:supercomponent];
return {
.mountChildren = NO,
.contextForChildren = r.contextForChildren
};
}
@end