diff --git a/BeKindRewind.podspec b/BeKindRewind.podspec index 921e43a..d12de2e 100644 --- a/BeKindRewind.podspec +++ b/BeKindRewind.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "BeKindRewind" - s.version = "0.9.3" + s.version = "0.9.4" s.summary = "A simple way to record and replay network requests for testing and debugging." s.description = <<-DESC Provides an XCTestCase subclass for easily diff --git a/BeKindRewind/Core/BKRRequestMatching.h b/BeKindRewind/Core/BKRRequestMatching.h index 92d75dc..d28f273 100644 --- a/BeKindRewind/Core/BKRRequestMatching.h +++ b/BeKindRewind/Core/BKRRequestMatching.h @@ -59,6 +59,12 @@ @optional +/** + * If the matcher class stores information between recordings, then this can be implemented to reset matcher state. + * This can be expected to be called whenever the VCR calls reset + */ +- (void)reset; + /** * Convenience callback for testing the scheme of a request for possible stubbing * diff --git a/BeKindRewind/Core/Editor/BKREditor.h b/BeKindRewind/Core/Editor/BKREditor.h index eff1201..9c676e9 100644 --- a/BeKindRewind/Core/Editor/BKREditor.h +++ b/BeKindRewind/Core/Editor/BKREditor.h @@ -80,4 +80,15 @@ typedef void (^BKRCassetteEditingBlock)(BOOL updatedEnabled, BKRCassette *casset */ - (NSArray *)allScenes; +/** + * Resets the receiver if it contains any internal state. This should + * usually be called at the end of a session. This method is non-blocking + * and asynchronously called on the receiver's internal custom queue. This + * method sets the receiver's enabled property to NO + * + * @param completionBlock run on the receiver's queue after the reset + * actions are performed + */ +- (void)resetWithCompletionBlock:(void(^)(void))completionBlock; + @end diff --git a/BeKindRewind/Core/Editor/BKREditor.m b/BeKindRewind/Core/Editor/BKREditor.m index 3cccb2c..9ef9199 100644 --- a/BeKindRewind/Core/Editor/BKREditor.m +++ b/BeKindRewind/Core/Editor/BKREditor.m @@ -98,4 +98,12 @@ - (BKRCassette *)currentCassette { return self.currentCassette.allScenes; } +- (void)resetWithCompletionBlock:(void (^)(void))completionBlock { + [self setEnabled:NO withCompletionHandler:^(BOOL updatedEnabled, BKRCassette *cassette) { + if (completionBlock) { + completionBlock(); + } + }]; +} + @end diff --git a/BeKindRewind/Core/Editor/BKRPlayingEditor.m b/BeKindRewind/Core/Editor/BKRPlayingEditor.m index 31012ee..b074af6 100644 --- a/BeKindRewind/Core/Editor/BKRPlayingEditor.m +++ b/BeKindRewind/Core/Editor/BKRPlayingEditor.m @@ -51,6 +51,19 @@ - (void)setEnabled:(BOOL)enabled withCompletionHandler:(BKRCassetteEditingBlock) }]; } +- (void)resetWithCompletionBlock:(void (^)(void))completionBlock { + BKRWeakify(self); + [super resetWithCompletionBlock:^void (void){ + BKRStrongify(self); + if ([self->_matcher respondsToSelector:@selector(reset)]) { + [self->_matcher reset]; + } + if (completionBlock) { + completionBlock(); + } + }]; +} + - (void)_removeAllStubs { [BKROHHTTPStubsWrapper removeAllStubs]; } diff --git a/BeKindRewind/Core/Editor/BKRRecordingEditor.h b/BeKindRewind/Core/Editor/BKRRecordingEditor.h index b1088f4..fcfdeef 100644 --- a/BeKindRewind/Core/Editor/BKRRecordingEditor.h +++ b/BeKindRewind/Core/Editor/BKRRecordingEditor.h @@ -27,12 +27,6 @@ */ @property (nonatomic, assign, readonly) BOOL handledRecording; -/** - * This resets the BKRRecordingEditor since it interacts with a singleton BKRRecorder. This should be called before - * releasing the instance. - */ -- (void)reset; - /** * Add raw recordable frame representing a component of a network request to the current cassette * diff --git a/BeKindRewind/Core/Editor/BKRRecordingEditor.m b/BeKindRewind/Core/Editor/BKRRecordingEditor.m index 6fed7af..5583c61 100644 --- a/BeKindRewind/Core/Editor/BKRRecordingEditor.m +++ b/BeKindRewind/Core/Editor/BKRRecordingEditor.m @@ -31,13 +31,19 @@ - (instancetype)init { return self; } -- (void)reset { +// This resets the BKRRecordingEditor since it interacts with a +// singleton BKRRecorder. This should be called before +// releasing the instance. +- (void)resetWithCompletionBlock:(void (^)(void))completionBlock { BKRWeakify(self); - dispatch_barrier_async(self.editingQueue, ^{ + [super resetWithCompletionBlock:^void (void){ BKRStrongify(self); self->_handledRecording = NO; self->_recordingStartTime = nil; - }); + if (completionBlock) { + completionBlock(); + } + }]; } diff --git a/BeKindRewind/Core/Player/BKRPlayer.h b/BeKindRewind/Core/Player/BKRPlayer.h index 7759d92..0d11d4e 100644 --- a/BeKindRewind/Core/Player/BKRPlayer.h +++ b/BeKindRewind/Core/Player/BKRPlayer.h @@ -63,7 +63,9 @@ /** * Reset the player's enabled state along with before - * and after playback blocks + * and after playback blocks. If the matcher saves any state, + * that can be reset here by implemented the optional `reset` method + * in the BKRRequestMatching protocol */ - (void)resetWithCompletionBlock:(void (^)(void))completionBlock; diff --git a/BeKindRewind/Core/Player/BKRPlayer.m b/BeKindRewind/Core/Player/BKRPlayer.m index e767c41..df63a9b 100644 --- a/BeKindRewind/Core/Player/BKRPlayer.m +++ b/BeKindRewind/Core/Player/BKRPlayer.m @@ -62,7 +62,7 @@ - (BOOL)isEnabled { - (void)resetWithCompletionBlock:(void (^)(void))completionBlock { self.currentCassette = nil; - [self.editor setEnabled:NO withCompletionHandler:^(BOOL updatedEnabled, BKRCassette *cassette) { + [self.editor resetWithCompletionBlock:^{ if (completionBlock) { completionBlock(); } diff --git a/BeKindRewind/Core/Recorder/BKRRecorder.m b/BeKindRewind/Core/Recorder/BKRRecorder.m index 2bd3bb9..fc94c2e 100644 --- a/BeKindRewind/Core/Recorder/BKRRecorder.m +++ b/BeKindRewind/Core/Recorder/BKRRecorder.m @@ -80,8 +80,7 @@ - (void)resetWithCompletionBlock:(void (^)(void))completionBlock { self.currentCassette = nil; self.beginRecordingBlock = nil; self.endRecordingBlock = nil; - [self.editor reset]; - [self.editor editCassette:^(BOOL updatedEnabled, BKRCassette *cassette) { + [self.editor resetWithCompletionBlock:^{ if (completionBlock) { completionBlock(); } diff --git a/Example/Tests/Helpers/XCTestCase+BKRHelpers.h b/Example/Tests/Helpers/XCTestCase+BKRHelpers.h index fd2a335..ec8670b 100644 --- a/Example/Tests/Helpers/XCTestCase+BKRHelpers.h +++ b/Example/Tests/Helpers/XCTestCase+BKRHelpers.h @@ -123,6 +123,8 @@ typedef void (^BKRTestBatchNetworkTimeoutCompletionHandler)(BKRTestExpectedResul - (BKRTestExpectedResult *)HTTPBinCancelledRequestWithRecording:(BOOL)isRecording; - (BKRTestExpectedResult *)HTTPBinGetRequestWithQueryString:(NSString *)queryString withRecording:(BOOL)isRecording; - (BKRTestExpectedResult *)HTTPBinPostRequestWithRecording:(BOOL)isRecording; +- (BKRTestExpectedResult *)HTTPBinRedirectWithRecording:(BOOL)isRecording; +- (BKRTestExpectedResult *)HTTPBinDripDataWithRecording:(BOOL)isRecording; #pragma mark - PN Helpers diff --git a/Example/Tests/Helpers/XCTestCase+BKRHelpers.m b/Example/Tests/Helpers/XCTestCase+BKRHelpers.m index 213680d..6b755ef 100644 --- a/Example/Tests/Helpers/XCTestCase+BKRHelpers.m +++ b/Example/Tests/Helpers/XCTestCase+BKRHelpers.m @@ -769,6 +769,12 @@ - (NSDictionary *)_HTTPBinResponseAllHeaderFieldsWithContentLength:(NSString *)c }; } +- (NSDictionary *)_HTTPBinChunkedResponseAllHeaderFieldsWithContentLength:(NSString *)contentLengthString { + NSMutableDictionary *mutableOriginalDictionary = [[self _HTTPBinResponseAllHeaderFieldsWithContentLength:contentLengthString] mutableCopy]; + mutableOriginalDictionary[@"Content-Type"] = @"application/octet-stream"; + return mutableOriginalDictionary.copy; +} + - (NSDictionary *)_PNResponseAllHeaderFieldsWithContentLength:(NSString *)contentLengthString { return @{ @"Access-Control-Allow-Methods": @"GET", @@ -868,6 +874,27 @@ - (BKRTestExpectedResult *)HTTPBinGetRequestWithQueryString:(NSString *)queryStr return expectedResult; } +- (BKRTestExpectedResult *)HTTPBinDripDataWithRecording:(BOOL)isRecording { + BKRTestExpectedResult *expectedResult = [BKRTestExpectedResult result]; + expectedResult.isRecording = isRecording; + expectedResult.hasCurrentRequest = YES; + expectedResult.URLString = @"https://httpbin.org/drip?numbytes=30000&duration=0&code=200"; + expectedResult.currentRequestAllHTTPHeaderFields = [self _expectedGETCurrentRequestAllHTTPHeaderFields]; + expectedResult.responseCode = 200; + expectedResult.responseAllHeaderFields = [self _HTTPBinChunkedResponseAllHeaderFieldsWithContentLength:@"30000"]; + expectedResult.expectedNumberOfFrames = 4; + + return expectedResult; +} + +- (BKRTestExpectedResult *)HTTPBinRedirectWithRecording:(BOOL)isRecording { + BKRTestExpectedResult *expectedResult = [BKRTestExpectedResult result]; + expectedResult.isRecording = isRecording; + expectedResult.URLString = @"http://httpbin.org/redirect/6"; + + return expectedResult; +} + - (BKRTestExpectedResult *)HTTPBinPostRequestWithRecording:(BOOL)isRecording { BKRTestExpectedResult *result = [BKRTestExpectedResult result]; result.isRecording = isRecording; diff --git a/Example/Tests/TestCases/BKRRecorderTestCase.m b/Example/Tests/TestCases/BKRRecorderTestCase.m index a8c8fce..090ce12 100644 --- a/Example/Tests/TestCases/BKRRecorderTestCase.m +++ b/Example/Tests/TestCases/BKRRecorderTestCase.m @@ -152,4 +152,15 @@ - (void)testRecordingTwoSimultaneousGETRequests { }]; } +- (void)DISABLE_testRecordingChunkedResponseRequest { + BKRTestExpectedResult *expectedResult = [self HTTPBinDripDataWithRecording:YES]; + + [self BKRTest_executeHTTPBinNetworkCallsForExpectedResults:@[expectedResult] simultaneously:NO withTaskCompletionAssertions:^(BKRTestExpectedResult *result, NSURLSessionTask *task, NSData *data, NSURLResponse *response, NSError *error) { + NSLog(@"result: %@", result); + NSLog(@"task: %@", task); + } taskTimeoutHandler:^(BKRTestExpectedResult *result, NSURLSessionTask *task, NSError *error, BKRTestBatchSceneAssertionHandler batchSceneAssertions) { + batchSceneAssertions([BKRRecorder sharedInstance].allScenes); + }]; +} + @end diff --git a/Podfile.lock b/Podfile.lock index 360ce5a..cdb10d5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - BeKindRewind (0.9.3): + - BeKindRewind (0.9.4): - OHHTTPStubs (~> 4.7.1) - OHHTTPStubs (4.7.1): - OHHTTPStubs/Default (= 4.7.1) @@ -23,7 +23,7 @@ EXTERNAL SOURCES: :path: . SPEC CHECKSUMS: - BeKindRewind: 0f322f16427cf9305821c9a2e80e3f5d3386cc0f + BeKindRewind: 2794c487e9bc4eed434469abec60d207acdad1c9 OHHTTPStubs: f7d1604d04d37d055460c0ef9af01bb249ded0fa COCOAPODS: 0.39.0 diff --git a/README.md b/README.md index c9fa339..7c459b9 100644 --- a/README.md +++ b/README.md @@ -144,13 +144,15 @@ Jordan Zucker, jordan.zucker@gmail.com BeKindRewind is available under the MIT license. See the LICENSE file for more info. ## Release criteria -* Rewrite README instructions * proper support for redirects * handle multi-part data -* proper header file +* tests for matcher classes +* tests for OSX, tvOS ## Future features -* swift tests (at least basic) +* swift tests (at least basic)/Swift Package Manager +* Code example for playing back in the README (not just recording) +* explain fixture write directory hack for easy recording * Separate into subspecs * add code coverage * test different types of errors (timeout, invalid url) for playing/recording