| // | |
| // 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 |