Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
brentleyjones committed Aug 15, 2017
2 parents e719953 + 722ba9f commit b1d42ae
Show file tree
Hide file tree
Showing 30 changed files with 652 additions and 109 deletions.
13 changes: 8 additions & 5 deletions Cucumberish.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "Cucumberish"
s.version = "1.2.0"
s.version = "1.3.0"
s.summary = "Cucumberish is the native Objective-C implementation of Cucumber BDD automation test framework"
s.description = <<-DESC
Cucumberish is Objective-C and Swift framework for Behaviour Driven Development inspired by the amazing way of writing automated test cases introduced originally by Cucumber. With Cucumberish, you write your test cases in almost plain English language.
Expand All @@ -18,13 +18,16 @@ Pod::Spec.new do |s|
s.source_files = 'Cucumberish/*.{h,m}', 'Cucumberish/Core/Managers/*.{h,m}', 'Cucumberish/Core/Models/*.{h,m}', 'Cucumberish/Utils/*.{h,m}', 'Cucumberish/Dependencies/Gherkin', 'Cucumberish/Core/CCIBlockDefinitions.h'
s.public_header_files =
'Cucumberish/Cucumberish.h',
'Cucumberish/Core/Managers/CCIStepsManager.h',
'Cucumberish/Core/CCIBlockDefinitions.h',
'Cucumberish/Core/Models/CCIScenarioDefinition.h',
'Cucumberish/Core/CCILogManager.h',
'Cucumberish/Core/Managers/CCIStepsManager.h',
'Cucumberish/Core/Models/CCIArgument.h',
'Cucumberish/Core/Models/CCIBackground.h',
'Cucumberish/Core/Models/CCIExample.h',
'Cucumberish/Core/Models/CCIStep.h',
'Cucumberish/Core/Models/CCIFeature.h',
'Cucumberish/Core/Models/CCILocation.h',
'Cucumberish/Core/Models/CCIArgument.h'
'Cucumberish/Core/Models/CCIScenarioDefinition.h',
'Cucumberish/Core/Models/CCIStep.h'
s.resource_bundles = {
'GherkinLanguages' => ['Cucumberish/Dependencies/Gherkin/gherkin-languages.json'],
}
Expand Down
35 changes: 23 additions & 12 deletions Cucumberish/Core/CCIBlockDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,37 +332,48 @@ OBJC_EXTERN void afterTagged(NSArray * tags, CCIScenarioHockBlock afterTaggedBlo
C function that registers a code block to be used to call the scenario execution block.
Code blocks registerd with this function will receive two parameters: scenario instance and scenario execution block as a parameter.
If more than one code block matches the scenario, you are still required to call the scenario execution from each registered code block. However, your scenario will be executed once as it is supposed to be.
@Note
This function should not be called after calling @a beginExecution.
@param aroundScenarioBlock code block that will be executed for each scenario, this block receives an instance of the scenario and the scenario execution block.
*/
OBJC_EXTERN void around(CCIScenarioExecutionHockBlock aroundScenarioBlock);

/**
C function that registers a code block to be used to call the scenario execution block.
Code blocks registerd with this function will receive two parameters: scenario instance and scenario execution block as a parameter.
If more than one code block matches the scenario, you are still required to call the scenario execution from each registered code block. However, your scenario will be executed once as it is supposed to be.
Matching against around blocks happens in FIFO (First In First Out) order; in case more than one block has matched the same scenario, then they are nested.
@Note
Failing to call the scenario execution block, will prevent the scenario from being executed.
@b Example of more than one match
There are three registerd blocks with tags that matches the same scenario, the followin nesting calls will happen:
@code
Third Around Match
Block = contains code block that executes the Second Around Match
Second Around Match
Block = contains code block that executes the First Around Match
First Around Match
Block = A call Scenario Exection Block@endcode
@Note
Do not prefix any tag you pass with @@ symbol
@Note
This function should not be called after calling @a beginExecution.
@param tags array of strings that will be used to match specific scenarios
@param aroundScenarioBlock code block that will be executed for each scneario, this block receives an instance of the scenario and the scenario execution block.
@param aroundScenarioBlock code block that will be executed for each scenario, this block receives an instance of the scenario and the scenario execution block.
*/
OBJC_EXTERN void around(NSArray * tags, CCIScenarioExecutionHockBlock aroundScenarioBlock);
OBJC_EXTERN void aroundTagged(NSArray * tags, CCIScenarioExecutionHockBlock aroundScenarioBlock);

#pragma mark - Assertion and Errors
/**
Expand Down
27 changes: 27 additions & 0 deletions Cucumberish/Core/Managers/CCILoggingManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// CCILoggingManager.h
// CucumberishLibrary
//
// Created by Titouan van Belle on 15.07.17.
// Copyright © 2017 Ahmed Ali. All rights reserved.
//

#import <Foundation/Foundation.h>

void CCILog(NSString *format, ...);


@protocol CCILogger<NSObject>

- (void)logFormat:(NSString *)format arguments:(va_list)arguments;

@end


@interface CCILoggingManager : NSObject

+ (CCILoggingManager *)sharedInstance;
- (void)addLogger:(id<CCILogger>)logger;
- (void)logFormat:(NSString *)format arguments:(va_list)arguments;

@end
78 changes: 78 additions & 0 deletions Cucumberish/Core/Managers/CCILoggingManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// CCILoggingManager.h
// CucumberishLibrary
//
// Created by Titouan van Belle on 15.07.17.
// Copyright © 2017 Ahmed Ali. All rights reserved.
//

#import "CCILoggingManager.h"

void CCILog(NSString *format, ...)
{
va_list args;
va_start(args, format);
[[CCILoggingManager sharedInstance] logFormat:format arguments:args];
va_end(args);
}


@interface CCIConsoleLogger : NSObject<CCILogger>

@end

@implementation CCIConsoleLogger

- (void)logFormat:(NSString *)format arguments:(va_list)arguments
{
NSLogv(format, arguments);
}

@end



@interface CCILoggingManager ()

@property (nonatomic, strong) NSSet<CCILogger> *loggers;

@end

@implementation CCILoggingManager

+ (CCILoggingManager *)sharedInstance
{
static CCILoggingManager *sharedInstance;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
sharedInstance = [[CCILoggingManager alloc] init];
sharedInstance.loggers = (NSSet<CCILogger> *)[NSSet new];
id<CCILogger> consoleLogger = [[CCIConsoleLogger alloc] init];
[sharedInstance addLogger:consoleLogger];
});

return sharedInstance;
}

- (void)addLogger:(id<CCILogger>)logger
{
NSMutableSet *mutableLoggers = [self.loggers mutableCopy];
[mutableLoggers addObject:logger];
self.loggers = [mutableLoggers copy];
}

- (void)logFormat:(NSString *)format arguments:(va_list)arguments
{
NSSet *loggers = [self.loggers copy];
for (id<CCILogger> logger in loggers) {
va_list args_copy;
va_copy(args_copy, arguments);

[logger logFormat:format arguments:args_copy];
va_end(args_copy);
};
}


@end
7 changes: 7 additions & 0 deletions Cucumberish/Core/Managers/CCIStepsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
*/
@interface CCIStepsManager : NSObject

/**
A set containing all the steps that are not defined when dry run is enabled
*/
@property (nonatomic, strong) NSMutableSet<CCIStep *> *undefinedSteps;

/**
Returns the singleton class of CCIStepsManager
*/
Expand All @@ -48,4 +53,6 @@
*/
- (void)executeStep:(CCIStep *)step inTestCase:(id)testCase;

- (BOOL)executeStepInDryRun:(CCIStep *)step inTestCase:(id)testCase;

@end
60 changes: 47 additions & 13 deletions Cucumberish/Core/Managers/CCIStepsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#import "CCIStep.h"
#import "CCIStepDefinition.h"

typedef void (^XCTContextActivityBlock)(id _Nullable activity);

static CCIStepsManager * instance = nil;


Expand All @@ -37,14 +39,14 @@
const NSString * kXCTestCaseKey = @"XCTestCase";

@interface CCIStepsManager()

@property NSMutableDictionary * definitions;
@property (copy) NSString *currentContextKeyword;

@end

@implementation CCIStepsManager



+ (instancetype)instance {

@synchronized(self) {
Expand All @@ -60,6 +62,7 @@ - (instancetype)init
self = [super init];

self.definitions = [NSMutableDictionary dictionary];
self.undefinedSteps = [NSMutableSet new];

return self;
}
Expand All @@ -76,7 +79,6 @@ - (NSMutableArray *)definitionsCluster:(NSString *)type
return cluster;
}


- (CCIStepDefinition *)findMatchDefinitionForStep:(CCIStep *)step inTestCase:(id)testCase
{
if(step.keyword == nil){
Expand All @@ -88,14 +90,19 @@ - (CCIStepDefinition *)findMatchDefinitionForStep:(CCIStep *)step inTestCase:(id
return [self findDefinitionForStep:step amongDefinitions:allDefinitions inTestCase:testCase];
}

return [self findDefinitionForStep:step amongDefinitions:[self definitionGroupForStep:step] inTestCase:testCase];
}

- (NSArray *)definitionGroupForStep:(CCIStep *)step
{
NSArray *definitionGroup = self.definitions[step.keyword] ?: @[];
if ([step.keyword isEqualToString:@"And"]) {
step.contextualKeyword = self.currentContextKeyword;
NSArray *contextDefinitionGroup = self.definitions[self.currentContextKeyword];
definitionGroup = [definitionGroup arrayByAddingObjectsFromArray:contextDefinitionGroup];
}

return [self findDefinitionForStep:step amongDefinitions:definitionGroup inTestCase:testCase];

return definitionGroup;
}

- (CCIStepDefinition *)findDefinitionForStep:(CCIStep *)step amongDefinitions:(NSArray *)definitions inTestCase:(id)testCase
Expand Down Expand Up @@ -158,12 +165,22 @@ - (CCIStepDefinition *)findDefinitionForStep:(CCIStep *)step amongDefinitions:(N
return retDefinition;
}

- (void)executeStep:(CCIStep *)step inTestCase:(id)testCase
- (BOOL)executeStepInDryRun:(CCIStep *)step inTestCase:(id)testCase
{
if (![step.keyword isEqualToString:@"And"]) {
self.currentContextKeyword = step.keyword;
}

return [self findMatchDefinitionForStep:step inTestCase:testCase] != nil;
}


- (void)executeStep:(CCIStep *)step inTestCase:(id)testCase
{
if (step.keyword && ![step.keyword isEqualToString:@"And"]) {
self.currentContextKeyword = step.keyword;
}

CCIStepDefinition * implementation = [self findMatchDefinitionForStep:step inTestCase:testCase];
NSString * errorMessage = nil;
if(step.keyword.length > 0){
Expand All @@ -173,6 +190,7 @@ - (void)executeStep:(CCIStep *)step inTestCase:(id)testCase
errorMessage = [NSString stringWithFormat:@"The implementation of this step, calls another step that is not implemented: %@", [step.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
CCIAssert(implementation != nil, errorMessage);

if(step.keyword.length > 0){
NSLog(@"Currently executing: \"%@ %@\"", step.keyword, step.text);
}
Expand All @@ -181,7 +199,29 @@ - (void)executeStep:(CCIStep *)step inTestCase:(id)testCase
implementation.type = @"And";
}

implementation.body(implementation.matchedValues, implementation.additionalContent);
XCTContextActivityBlock activityBlock = ^(id activity) {
implementation.body(implementation.matchedValues, implementation.additionalContent);
};

id xctContextClass = NSClassFromString(@"XCTContext");
if (xctContextClass) {
SEL aSelector = NSSelectorFromString(@"runActivityNamed:block:");

if ([xctContextClass respondsToSelector:aSelector]) {
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[xctContextClass methodSignatureForSelector:aSelector]];
[inv setSelector:aSelector];
[inv setTarget:xctContextClass];

NSString *name = [NSString stringWithFormat:@"%@ %@", implementation.type, step.text];
[inv setArgument:&(name) atIndex:2];
[inv setArgument:&(activityBlock) atIndex:3];

[inv invoke];
}
} else {
activityBlock(nil);
}

//Clean up the step additional content to avoid keeping unwanted objects in memory
implementation.additionalContent = nil;
if(step.keyword.length > 0){
Expand Down Expand Up @@ -257,9 +297,3 @@ void SStep(id testCase, NSString * stepLine)
step(testCase, stepLine);
}







1 change: 0 additions & 1 deletion Cucumberish/Core/Models/CCIScenarioDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ extern const NSString * kBackgroundKeyword;
*/
@property (nonatomic, copy) NSString * type;


/**
In case the execution of this scenario is failed, this property will have the failure message
*/
Expand Down
12 changes: 11 additions & 1 deletion Cucumberish/Core/Models/CCIStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ typedef NS_ENUM(NSInteger,CCIStepStatus) {
*/
@property (nonatomic, strong) CCIArgument * argument;

/**
Set to the keyword of the previous step when the keyword for this step is And
*/
@property (nonatomic, copy) NSString * contextualKeyword;

/**
Can be When, Then, Given, etc...
*/
Expand Down Expand Up @@ -82,4 +87,9 @@ typedef NS_ENUM(NSInteger,CCIStepStatus) {
@return the created dictionary
*/
-(NSDictionary *)toDictionary;
@end

/**
@return a string composed of the keyword and the text of the step
*/
- (NSString *)fullName;
@end
Loading

0 comments on commit b1d42ae

Please sign in to comment.