blob: 627d634f7b44258cb789dcafc07fb129628107cf [file] [log] [blame] [edit]
//
// RACPropertySignalExamples.m
// ReactiveCocoa
//
// Created by Josh Abernathy on 9/28/12.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "RACTestObject.h"
#import "EXTKeyPathCoding.h"
#import "NSObject+RACDeallocating.h"
#import "NSObject+RACPropertySubscribing.h"
#import "NSObject+RACSelectorSignal.h"
#import "RACCompoundDisposable.h"
#import "RACDisposable.h"
#import "RACSubject.h"
NSString * const RACPropertySignalExamples = @"RACPropertySignalExamples";
NSString * const RACPropertySignalExamplesSetupBlock = @"RACPropertySignalExamplesSetupBlock";
SharedExampleGroupsBegin(RACPropertySignalExamples)
sharedExamplesFor(RACPropertySignalExamples, ^(NSDictionary *data) {
__block RACTestObject *testObject = nil;
__block void (^setupBlock)(RACTestObject *, NSString *keyPath, id nilValue, RACSignal *);
beforeEach(^{
setupBlock = data[RACPropertySignalExamplesSetupBlock];
testObject = [[RACTestObject alloc] init];
});
it(@"should set the value of the property with the latest value from the signal", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.objectValue), nil, subject);
expect(testObject.objectValue).to.beNil();
[subject sendNext:@1];
expect(testObject.objectValue).to.equal(@1);
[subject sendNext:@2];
expect(testObject.objectValue).to.equal(@2);
[subject sendNext:nil];
expect(testObject.objectValue).to.beNil();
});
it(@"should set the given nilValue for an object property", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.objectValue), @"foo", subject);
expect(testObject.objectValue).to.beNil();
[subject sendNext:@1];
expect(testObject.objectValue).to.equal(@1);
[subject sendNext:@2];
expect(testObject.objectValue).to.equal(@2);
[subject sendNext:nil];
expect(testObject.objectValue).to.equal(@"foo");
});
it(@"should leave the value of the property alone after the signal completes", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.objectValue), nil, subject);
expect(testObject.objectValue).to.beNil();
[subject sendNext:@1];
expect(testObject.objectValue).to.equal(@1);
[subject sendCompleted];
expect(testObject.objectValue).to.equal(@1);
});
it(@"should set the value of a non-object property with the latest value from the signal", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.integerValue), nil, subject);
expect(testObject.integerValue).to.equal(0);
[subject sendNext:@1];
expect(testObject.integerValue).to.equal(1);
[subject sendNext:@2];
expect(testObject.integerValue).to.equal(2);
[subject sendNext:@0];
expect(testObject.integerValue).to.equal(0);
});
it(@"should set the given nilValue for a non-object property", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.integerValue), @42, subject);
expect(testObject.integerValue).to.equal(0);
[subject sendNext:@1];
expect(testObject.integerValue).to.equal(@1);
[subject sendNext:@2];
expect(testObject.integerValue).to.equal(@2);
[subject sendNext:nil];
expect(testObject.integerValue).to.equal(@42);
});
it(@"should not invoke -setNilValueForKey: with a nilValue", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.integerValue), @42, subject);
__block BOOL setNilValueForKeyInvoked = NO;
[[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) {
setNilValueForKeyInvoked = YES;
}];
[subject sendNext:nil];
expect(testObject.integerValue).to.equal(@42);
expect(setNilValueForKeyInvoked).to.beFalsy();
});
it(@"should invoke -setNilValueForKey: without a nilValue", ^{
RACSubject *subject = [RACSubject subject];
setupBlock(testObject, @keypath(testObject.integerValue), nil, subject);
[subject sendNext:@1];
expect(testObject.integerValue).to.equal(@1);
testObject.catchSetNilValueForKey = YES;
__block BOOL setNilValueForKeyInvoked = NO;
[[testObject rac_signalForSelector:@selector(setNilValueForKey:)] subscribeNext:^(NSString *key) {
setNilValueForKeyInvoked = YES;
}];
[subject sendNext:nil];
expect(testObject.integerValue).to.equal(@1);
expect(setNilValueForKeyInvoked).to.beTruthy();
});
it(@"should retain intermediate signals when binding", ^{
RACSubject *subject = [RACSubject subject];
expect(subject).notTo.beNil();
__block BOOL deallocd = NO;
@autoreleasepool {
@autoreleasepool {
RACSignal *intermediateSignal = [subject map:^(NSNumber *num) {
return @(num.integerValue + 1);
}];
expect(intermediateSignal).notTo.beNil();
[intermediateSignal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
deallocd = YES;
}]];
setupBlock(testObject, @keypath(testObject.integerValue), nil, intermediateSignal);
}
// Spin the run loop to account for RAC magic that retains the
// signal for a single iteration.
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
}
expect(deallocd).to.beFalsy();
[subject sendNext:@5];
expect(testObject.integerValue).to.equal(6);
[subject sendNext:@6];
expect(testObject.integerValue).to.equal(7);
expect(deallocd).to.beFalsy();
[subject sendCompleted];
// Can't test deallocd again, because it's legal for the chain to be
// retained until the object or the original signal is destroyed.
});
});
SharedExampleGroupsEnd