MASViewConstraintSpec.m 13 KB
//
//  MASViewConstraintSpec.m
//  Masonry
//
//  Created by Jonas Budelmann on 21/07/13.
//  Copyright (c) 2013 cloudling. All rights reserved.
//

#import "MASViewConstraint.h"
#import "MASConstraint.h"
#import "View+MASAdditions.h"
#import "MASConstraintDelegateMock.h"
#import "MASCompositeConstraint.h"

@interface MASViewConstraint ()

@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint;
@property (nonatomic, assign) NSLayoutRelation layoutRelation;
@property (nonatomic, assign) MASLayoutPriority layoutPriority;
@property (nonatomic, assign) CGFloat layoutMultiplier;
@property (nonatomic, assign) CGFloat layoutConstant;
@property (nonatomic, assign) BOOL hasLayoutRelation;

@end

@interface MASCompositeConstraint () <MASConstraintDelegate>

@property (nonatomic, strong) NSMutableArray *childConstraints;

@end

SpecBegin(MASViewConstraint) {
    MASConstraintDelegateMock *delegate;
    MAS_VIEW *superview;
    MASViewConstraint *constraint;
    MAS_VIEW *otherView;
}

- (void)setUp {
    superview = MAS_VIEW.new;
    delegate = MASConstraintDelegateMock.new;

    MAS_VIEW *view = MAS_VIEW.new;
    constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:view.mas_width];
    constraint.delegate = delegate;

    [superview addSubview:view];

    otherView = MAS_VIEW.new;
    [superview addSubview:otherView];
}


- (void)testRelationCreateEqualConstraint {
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    MASViewConstraint *newConstraint = (id)constraint.equalTo(secondViewAttribute);

    expect(newConstraint).to.beIdenticalTo(constraint);
    expect(constraint.secondViewAttribute).to.beIdenticalTo(secondViewAttribute);
    expect(constraint.layoutRelation).to.equal(NSLayoutRelationEqual);
}

- (void)testRelationCreateGreaterThanOrEqualConstraint {
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    MASViewConstraint *newConstraint = (id)constraint.greaterThanOrEqualTo(secondViewAttribute);

    expect(newConstraint).to.beIdenticalTo(constraint);
    expect(constraint.secondViewAttribute).to.beIdenticalTo(secondViewAttribute);
    expect(constraint.layoutRelation).to.equal(NSLayoutRelationGreaterThanOrEqual);
}

- (void)testRelationCreateLessThanOrEqualConstraint {
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    MASViewConstraint *newConstraint = (id)constraint.lessThanOrEqualTo(secondViewAttribute);

    expect(newConstraint).to.beIdenticalTo(constraint);
    expect(constraint.secondViewAttribute).to.beIdenticalTo(secondViewAttribute);
    expect(constraint.layoutRelation).to.equal(NSLayoutRelationLessThanOrEqual);
}

- (void)testRelationNotAllowUpdateOfEqual {
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    constraint.lessThanOrEqualTo(secondViewAttribute);

    expect(^{
        constraint.equalTo(secondViewAttribute);
    }).to.raise(@"NSInternalInconsistencyException");
}

- (void)testRelationNotAllowUpdateOfLessThanOrEqual{
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    constraint.equalTo(secondViewAttribute);

    expect(^{
        constraint.lessThanOrEqualTo(secondViewAttribute);
    }).to.raise(@"NSInternalInconsistencyException");
}

- (void)testRelationNotAllowUpdateOfGreaterThanOrEqual {
    MASViewAttribute *secondViewAttribute = otherView.mas_top;
    constraint.greaterThanOrEqualTo(secondViewAttribute);

    expect(^{
        constraint.greaterThanOrEqualTo(secondViewAttribute);
    }).to.raise(@"NSInternalInconsistencyException");
}


- (void)testRelationAcceptsView {
    MAS_VIEW *view = MAS_VIEW.new;
    constraint.equalTo(view);

    expect(constraint.secondViewAttribute.view).to.beIdenticalTo(view);
    expect(constraint.firstViewAttribute.layoutAttribute).to.equal(constraint.secondViewAttribute.layoutAttribute);
}


- (void)testRelationCreatesCompositeWithArrayOfViews {
    NSArray *views = @[MAS_VIEW.new, MAS_VIEW.new, MAS_VIEW.new];
    [delegate.constraints addObject:constraint];

    MASCompositeConstraint *composite = (id)constraint.equalTo(views).priorityMedium().offset(-10);

    expect(delegate.constraints).to.haveCountOf(1);
    expect(delegate.constraints[0]).to.beKindOf(MASCompositeConstraint.class);
    for (MASViewConstraint *childConstraint in composite.childConstraints) {
        NSUInteger index = [composite.childConstraints indexOfObject:childConstraint];
        expect(childConstraint.secondViewAttribute.view).to.beIdenticalTo((MAS_VIEW *)views[index]);
        expect(childConstraint.firstViewAttribute.layoutAttribute).to.equal(NSLayoutAttributeWidth);
        expect(childConstraint.secondViewAttribute.layoutAttribute).to.equal(NSLayoutAttributeWidth);
        expect(childConstraint.layoutPriority).to.equal(MASLayoutPriorityDefaultMedium);
        expect(childConstraint.layoutConstant).to.equal(-10);
    }
}

- (void)testRelationCreatesCompositeWithArrayOfAttributes {
    NSArray *viewAttributes = @[MAS_VIEW.new.mas_height, MAS_VIEW.new.mas_left];
    [delegate.constraints addObject:constraint];

    MASCompositeConstraint *composite = (id)constraint.equalTo(viewAttributes).priority(60).offset(10);

    expect(delegate.constraints).to.haveCountOf(1);
    expect(delegate.constraints[0]).to.beKindOf(MASCompositeConstraint.class);
    for (MASViewConstraint *childConstraint in composite.childConstraints) {
        NSUInteger index = [composite.childConstraints indexOfObject:childConstraint];
        expect(childConstraint.secondViewAttribute.view).to.beIdenticalTo([viewAttributes[index] view]);
        expect(childConstraint.firstViewAttribute.layoutAttribute).to.equal(NSLayoutAttributeWidth);
        expect(childConstraint.secondViewAttribute.layoutAttribute).to.equal([viewAttributes[index] layoutAttribute]);
        expect(childConstraint.layoutPriority).to.equal(60);
        expect(childConstraint.layoutConstant).to.equal(10);
    }
}

- (void)testRelationComplainsWithUnsupportedArgument {
    expect(^{
        constraint.equalTo(@{});
    }).to.raise(@"NSInternalInconsistencyException");
}

- (void)testPriorityHigh {
    constraint.equalTo(otherView);
    constraint.with.priorityHigh();
    [constraint install];

    expect(constraint.layoutPriority).to.equal(MASLayoutPriorityDefaultHigh);
    expect(constraint.layoutConstraint.priority).to.equal(MASLayoutPriorityDefaultHigh);
}


- (void)testPriorityLow {
    constraint.equalTo(otherView);
    constraint.with.priorityLow();
    [constraint install];

    expect(constraint.layoutPriority).to.equal(MASLayoutPriorityDefaultLow);
    expect(constraint.layoutConstraint.priority).to.equal(MASLayoutPriorityDefaultLow);
}


- (void)testPriorityMedium {
    constraint.equalTo(otherView);
    constraint.with.priorityMedium();
    [constraint install];

    expect(constraint.layoutPriority).to.equal(MASLayoutPriorityDefaultMedium);
    expect(constraint.layoutConstraint.priority).to.equal(MASLayoutPriorityDefaultMedium);
}


- (void)testMultiplierNotUpdate {
    [constraint install];

    expect(^{
        constraint.multipliedBy(0.9);
    }).to.raise(@"NSInternalInconsistencyException");
}


- (void)testMultiplierWithMultipliedBy {
    constraint.equalTo(otherView);
    constraint.multipliedBy(5);
    [constraint install];

    expect(constraint.layoutMultiplier).to.equal(5);
    expect(constraint.layoutConstraint.multiplier).to.equal(5);
}

- (void)testMultiplierWithDividedBy {
    constraint.equalTo(otherView);
    constraint.dividedBy(10);
    [constraint install];

    expect(constraint.layoutMultiplier).to.equal(0.1);
    expect(constraint.layoutConstraint.multiplier).to.beCloseTo(0.1);
}

- (void)testConstantUpdateAfterConstraintCreated {
    [constraint install];
    constraint.offset(10);

    expect(constraint.layoutConstant).to.equal(10);
    expect(constraint.layoutConstraint.constant).to.equal(10);
}


- (void)testConstantUpdateSidesOffset {
    MASViewConstraint *centerY = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_centerY];
    centerY.with.insets((MASEdgeInsets){10, 10, 10, 10});
    expect(centerY.layoutConstant).to.equal(0);

    MASViewConstraint *top = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_top];
    top.insets((MASEdgeInsets){15, 10, 10, 10});
    expect(top.layoutConstant).to.equal(15);

    MASViewConstraint *left = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_left];
    left.insets((MASEdgeInsets){10, 15, 10, 10});
    expect(left.layoutConstant).to.equal(15);

    MASViewConstraint *bottom = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_bottom];
    bottom.insets((MASEdgeInsets){10, 10, 15, 10});
    expect(bottom.layoutConstant).to.equal(-15);

    MASViewConstraint *right = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_right];
    right.insets((MASEdgeInsets){10, 10, 10, 15});
    expect(right.layoutConstant).to.equal(-15);
}

- (void)testConstantUpdateCenterOffsetOnly {
    MASViewConstraint *width = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_width];
    width.centerOffset(CGPointMake(-20, -10));
    expect(width.layoutConstant).to.equal(0);

    MASViewConstraint *centerX = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_centerX];
    centerX.centerOffset(CGPointMake(-20, -10));
    expect(centerX.layoutConstant).to.equal(-20);

    MASViewConstraint *centerY = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_centerY];
    centerY.centerOffset(CGPointMake(-20, -10));
    expect(centerY.layoutConstant).to.equal(-10);
}

- (void)testConstantUpdateSizeOffsetOnly {
    MASViewConstraint *bottom = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_bottom];
    bottom.sizeOffset(CGSizeMake(-40, 55));
    expect(bottom.layoutConstant).to.equal(0);

    MASViewConstraint *width = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_width];
    width.sizeOffset(CGSizeMake(-40, 55));
    expect(width.layoutConstant).to.equal(-40);

    MASViewConstraint *height = [[MASViewConstraint alloc] initWithFirstViewAttribute:otherView.mas_height];
    height.sizeOffset(CGSizeMake(-40, 55));
    expect(height.layoutConstant).to.equal(55);
}

- (void)testInstallLayoutConstraintOnCommit {
    MASViewAttribute *secondViewAttribute = otherView.mas_height;
    constraint.equalTo(secondViewAttribute);
    constraint.multipliedBy(0.5);
    constraint.offset(10);
    constraint.priority(345);
    [constraint install];

    expect(constraint.layoutConstraint.firstAttribute).to.equal(NSLayoutAttributeWidth);
    expect(constraint.layoutConstraint.secondAttribute).to.equal(NSLayoutAttributeHeight);
    expect(constraint.layoutConstraint.firstItem).to.beIdenticalTo(constraint.firstViewAttribute.view);
    expect(constraint.layoutConstraint.secondItem).to.beIdenticalTo(constraint.secondViewAttribute.view);
    expect(constraint.layoutConstraint.relation).to.equal(NSLayoutRelationEqual);
    expect(constraint.layoutConstraint.constant).to.equal(10);
    expect(constraint.layoutConstraint.priority).to.equal(345);
    expect(constraint.layoutConstraint.multiplier).to.equal(0.5);

    expect(superview.constraints[0]).to.beIdenticalTo(constraint.layoutConstraint);
}


- (void)testInstallRelativeToSuperview {
    MAS_VIEW *view = MAS_VIEW.new;
    constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:view.mas_baseline];
    constraint.delegate = delegate;
    [superview addSubview:view];

    constraint.equalTo(@10);
    [constraint install];

    expect(constraint.layoutConstraint.firstAttribute).to.equal(NSLayoutAttributeBaseline);
    expect(constraint.layoutConstraint.secondAttribute).to.equal(NSLayoutAttributeBaseline);
    expect(constraint.layoutConstraint.firstItem).to.beIdenticalTo(constraint.firstViewAttribute.view);
    expect(constraint.layoutConstraint.secondItem).to.beIdenticalTo(superview);
    expect(constraint.layoutConstraint.relation).to.equal(NSLayoutRelationEqual);
    expect(constraint.layoutConstraint.constant).to.equal(10);
    expect(superview.constraints[0]).to.beIdenticalTo(constraint.layoutConstraint);
}


- (void)testInstallSizeAsConstant {
    constraint.equalTo(@10);
    [constraint install];

    expect(constraint.layoutConstraint.firstAttribute).to.equal(NSLayoutAttributeWidth);
    expect(constraint.layoutConstraint.secondAttribute).to.equal(NSLayoutAttributeNotAnAttribute);
    expect(constraint.layoutConstraint.firstItem).to.beIdenticalTo(constraint.firstViewAttribute.view);
    expect(constraint.layoutConstraint.secondItem).to.beNil();
    expect(constraint.layoutConstraint.relation).to.equal(NSLayoutRelationEqual);
    expect(constraint.layoutConstraint.constant).to.equal(10);
    expect(constraint.firstViewAttribute.view.constraints[0]).to.beIdenticalTo(constraint.layoutConstraint);
}


- (void)testUninstallConstraint {
    MASViewAttribute *secondViewAttribute = otherView.mas_height;
    constraint.equalTo(secondViewAttribute);
    [constraint install];

    expect(superview.constraints).to.haveCountOf(1);
    expect(superview.constraints[0]).to.equal(constraint.layoutConstraint);

    [constraint uninstall];
    expect(superview.constraints).to.haveCountOf(0);
}

SpecEnd