Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

68: Support for dry-run to print undefined steps #70

Merged
merged 10 commits into from
Jul 18, 2017
1 change: 1 addition & 0 deletions Cucumberish.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Pod::Spec.new do |s|
'Cucumberish/Cucumberish.h',
'Cucumberish/Core/Managers/CCIStepsManager.h',
'Cucumberish/Core/CCIBlockDefinitions.h',
'Cucumberish/Core/CCILogManager.h',
'Cucumberish/Core/Models/CCIScenarioDefinition.h',
'Cucumberish/Core/Models/CCIExample.h',
'Cucumberish/Core/Models/CCIStep.h',
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
56 changes: 44 additions & 12 deletions Cucumberish/Core/Managers/CCIStepsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,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 +60,7 @@ - (instancetype)init
self = [super init];

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

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


- (CCIStepDefinition *)findMatchDefinitionForStep:(CCIStep *)step inTestCase:(id)testCase
{
if(step.keyword == nil){
Expand All @@ -88,14 +88,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,6 +163,16 @@ - (CCIStepDefinition *)findDefinitionForStep:(CCIStep *)step amongDefinitions:(N
return retDefinition;
}

- (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"]) {
Expand All @@ -173,6 +188,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 +197,29 @@ - (void)executeStep:(CCIStep *)step inTestCase:(id)testCase
implementation.type = @"And";
}

implementation.body(implementation.matchedValues, implementation.additionalContent);
id xctContextClass = NSClassFromString(@"XCTContext");
if (xctContextClass) {
SEL aSelector = NSSelectorFromString(@"runActivityNamed:block:");

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

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

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

[inv invoke];
}
} else {
implementation.body(implementation.matchedValues, implementation.additionalContent);
}

//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 +295,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
7 changes: 6 additions & 1 deletion Cucumberish/Core/Models/CCIStep.m
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ -(NSDictionary *)toDictionary

}

- (NSString *)fullName
{
return [NSString stringWithFormat:@"%@ %@", self.contextualKeyword ?: self.keyword, self.text];
}


- (NSString *)description
{
Expand All @@ -95,4 +100,4 @@ - (id)copyWithZone:(NSZone *)zone
{
return [[CCIStep alloc] initWithDictionary:[self toDictionary]];
}
@end
@end
15 changes: 15 additions & 0 deletions Cucumberish/Cucumberish.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
#import "CCIStepsManager.h"
#import "CCIBlockDefinitions.h"

typedef NS_ENUM(NSInteger, CCILanguage) {
CCILanguageSwift = 0,
CCILanguageObjectiveC = 1
};

/**
Cucumberish is the main class you will need to parse your feature files and execute them.
Expand Down Expand Up @@ -97,6 +101,17 @@
*/
@property (nonatomic, strong) NSString * testTargetSrcRoot;

/**
If set to true, Cucumberish will scan all your feature files without actually running them and detect steps that are not yet defined. Before, after and around blocks are not
executed when using this feature. Default is false.
*/
@property (nonatomic, assign) BOOL dryRun;

/**
The language used to write the step definition when using the dryRun feature. Default is set to CCILanguageSwift
*/
@property (nonatomic, assign) CCILanguage dryRunLanguage;

/**
Retuans a singleton instance of Cucumberish

Expand Down
Loading