xcfit 2.0.4 → 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -9
- data/Docs/InitialVersion.md +340 -0
- data/Package.swift +5 -0
- data/README.md +348 -145
- data/XCFit.gemspec +1 -1
- data/XCFit.podspec +17 -9
- data/XCFit/Classes/Cucumberish/Core/CCIBlockDefinitions.h +400 -0
- data/XCFit/Classes/Cucumberish/Core/Managers/CCIFeaturesManager.h +72 -0
- data/XCFit/Classes/Cucumberish/Core/Managers/CCIFeaturesManager.m +141 -0
- data/XCFit/Classes/Cucumberish/Core/Managers/CCIStepsManager.h +51 -0
- data/XCFit/Classes/Cucumberish/Core/Managers/CCIStepsManager.m +245 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIArgument.h +62 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIArgument.m +117 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIAroundHock.h +37 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIAroundHock.m +37 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIBackground.h +46 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIBackground.m +117 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIExample.h +89 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIExample.m +128 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIFeature.h +49 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIFeature.m +174 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIHock.h +36 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIHock.m +34 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCILocation.h +61 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCILocation.m +91 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIScenarioDefinition.h +97 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIScenarioDefinition.m +224 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIStep.h +85 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIStep.m +98 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIStepDefinition.h +43 -0
- data/XCFit/Classes/Cucumberish/Core/Models/CCIStepDefinition.m +65 -0
- data/XCFit/Classes/Cucumberish/Cucumberish.h +133 -0
- data/XCFit/Classes/Cucumberish/Cucumberish.m +604 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstBuilder.h +15 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstBuilder.m +312 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstNode.h +20 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstNode.m +79 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHBackground.h +12 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHBackground.m +18 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHComment.h +11 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHComment.m +27 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDataTable.h +14 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDataTable.m +30 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDocString.h +14 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDocString.m +29 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHExamples.h +24 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHExamples.m +49 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHFeature.h +24 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHFeature.m +50 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialect.h +25 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialect.m +64 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProvider.h +9 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProvider.m +124 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProviderProtocol.h +10 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageConstants.h +9 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageConstants.m +10 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageSetting.h +17 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageSetting.m +18 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLine.h +12 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLine.m +179 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineProtocol.h +64 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineSpan.h +9 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineSpan.m +26 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasDescriptionProtocol.h +8 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasLocationProtocol.h +8 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasRowsProtocol.h +8 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasStepsProtocol.h +8 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasTagsProtocol.h +8 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHLocation.h +12 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHLocation.m +41 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHNode.h +4 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHNode.m +5 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser+Extensions.h +10 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser+Extensions.m +18 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser.h +132 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser.m +2735 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParserException.h +55 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParserException.m +192 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenario.h +14 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenario.m +18 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition.h +23 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition.m +41 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition_Private.h +6 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioOutline.h +15 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioOutline.m +26 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStep.h +16 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStep.m +35 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStepArgument.h +4 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStepArgument.m +5 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableCell.h +13 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableCell.m +28 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableRow.h +13 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableRow.m +29 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTag.h +13 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTag.m +28 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHToken.h +26 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHToken.m +50 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenMatcher.h +29 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenMatcher.m +283 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenScanner.h +12 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenScanner.m +59 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/NSString+Trim.h +7 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/NSString+Trim.m +28 -0
- data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/gherkin-languages.json +2969 -0
- data/XCFit/Classes/Cucumberish/Utils/NSArray+Hashes.h +24 -0
- data/XCFit/Classes/Cucumberish/Utils/NSArray+Hashes.m +44 -0
- data/XCFit/Classes/Cucumberish/Utils/NSObject+Dictionary.h +7 -0
- data/XCFit/Classes/Cucumberish/Utils/NSObject+Dictionary.m +83 -0
- data/XCFit/Classes/Cucumberish/Utils/NSString+Formatter.h +32 -0
- data/XCFit/Classes/Cucumberish/Utils/NSString+Formatter.m +70 -0
- data/Xcode7-Templates/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateInfo.plist +13 -0
- data/Xcode7-Templates/XCFit/iOS UI Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +11 -0
- data/lib/XCFit/version.rb +1 -1
- metadata +108 -38
- data/XCFit/Assets/.gitkeep +0 -0
- data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateIcon.png +0 -0
- data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateIcon@2x.png +0 -0
- data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateInfo.plist +0 -22
- data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/___FILEBASENAME___.feature +0 -20
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/AcceptanceTests-Bridging-Header.h +0 -1
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/FixtureExample.swift +0 -31
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/SlimTables.h +0 -28
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateIcon.png +0 -0
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateIcon@2x.png +0 -0
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateInfo.plist +0 -150
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -1
- data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -15
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateIcon.png +0 -0
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateIcon@2x.png +0 -0
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateInfo.plist +0 -123
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -7
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.feature +0 -11
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.h +0 -14
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -17
- data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.swift +0 -23
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateIcon.png +0 -0
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateIcon@2x.png +0 -0
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateInfo.plist +0 -123
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -7
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.feature +0 -11
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.h +0 -14
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -17
- data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.swift +0 -25
- data/XCFit/Assets/XCFit/Mac Acceptance Tests.xctemplate/Empty.xib +0 -12
- data/XCFit/Assets/XCFit/Mac Acceptance Tests.xctemplate/TemplateInfo.plist +0 -54
- data/XCFit/Assets/XCFit/MacOS API Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
- data/XCFit/Assets/XCFit/MacOS UI Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
- data/XCFit/Assets/XCFit/iOS API Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
- data/XCFit/Assets/XCFit/iOS Acceptance Tests.xctemplate/TemplateInfo.plist +0 -52
- data/XCFit/Assets/XCFit/iOS UI Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
@@ -0,0 +1,98 @@
|
|
1
|
+
//
|
2
|
+
// CCIStep.m
|
3
|
+
//
|
4
|
+
// Created by Ahmed Ali on 2/1/2016
|
5
|
+
// Copyright © 2016 Ahmed Ali. All rights reserved.
|
6
|
+
//
|
7
|
+
//
|
8
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
// of this software and associated documentation files (the "Software"), to deal
|
10
|
+
// in the Software without restriction, including without limitation the rights
|
11
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
// copies of the Software, and to permit persons to whom the Software is
|
13
|
+
// furnished to do so, subject to the following conditions:
|
14
|
+
//
|
15
|
+
// The above copyright notice and this permission notice shall be included in
|
16
|
+
// all copies or substantial portions of the Software.
|
17
|
+
//
|
18
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
// THE SOFTWARE. Model file Generated using JSONExport: https://github.com/Ahmed-Ali/JSONExport
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
#import "CCIStep.h"
|
29
|
+
|
30
|
+
|
31
|
+
@interface CCIStep ()
|
32
|
+
@end
|
33
|
+
@implementation CCIStep
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Instantiate the instance using the passed dictionary values to set the properties values
|
40
|
+
*/
|
41
|
+
|
42
|
+
-(instancetype)initWithDictionary:(NSDictionary *)dictionary
|
43
|
+
{
|
44
|
+
self = [super init];
|
45
|
+
if(dictionary[@"keyword"] != nil && ![dictionary[@"keyword"] isKindOfClass:[NSNull class]]){
|
46
|
+
self.keyword = [dictionary[@"keyword"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
47
|
+
}
|
48
|
+
|
49
|
+
if(dictionary[@"location"] != nil && ![dictionary[@"location"] isKindOfClass:[NSNull class]]){
|
50
|
+
self.location = [[CCILocation alloc] initWithDictionary:dictionary[@"location"]];
|
51
|
+
}
|
52
|
+
|
53
|
+
if(dictionary[@"text"] != nil && ![dictionary[@"text"] isKindOfClass:[NSNull class]]){
|
54
|
+
self.text = dictionary[@"text"];
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
if(dictionary[@"argument"] != nil && ![dictionary[@"argument"] isKindOfClass:[NSNull class]]){
|
59
|
+
self.argument = [[CCIArgument alloc] initWithDictionary:dictionary[@"argument"]];
|
60
|
+
}
|
61
|
+
return self;
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Returns all the available property values in the form of NSDictionary object where the key is the approperiate json key and the value is the value of the corresponding property
|
67
|
+
*/
|
68
|
+
-(NSDictionary *)toDictionary
|
69
|
+
{
|
70
|
+
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
|
71
|
+
if(self.keyword != nil){
|
72
|
+
dictionary[@"keyword"] = self.keyword;
|
73
|
+
}
|
74
|
+
if(self.location != nil){
|
75
|
+
dictionary[@"location"] = [self.location toDictionary];
|
76
|
+
}
|
77
|
+
if(self.text != nil){
|
78
|
+
dictionary[@"text"] = self.text;
|
79
|
+
}
|
80
|
+
|
81
|
+
if(self.argument != nil){
|
82
|
+
dictionary[@"argument"] = [self.argument toDictionary];
|
83
|
+
}
|
84
|
+
return dictionary;
|
85
|
+
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
- (NSString *)description
|
90
|
+
{
|
91
|
+
return [NSString stringWithFormat:@"Step text: %@", self.text];
|
92
|
+
}
|
93
|
+
|
94
|
+
- (id)copyWithZone:(NSZone *)zone
|
95
|
+
{
|
96
|
+
return [[CCIStep alloc] initWithDictionary:[self toDictionary]];
|
97
|
+
}
|
98
|
+
@end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
//
|
2
|
+
// CCIStepDefinition.h
|
3
|
+
|
4
|
+
//
|
5
|
+
// Created by Ahmed Ali on 02/01/16.
|
6
|
+
// Copyright © 2016 Ahmed Ali. All rights reserved.
|
7
|
+
//
|
8
|
+
//
|
9
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
// of this software and associated documentation files (the "Software"), to deal
|
11
|
+
// in the Software without restriction, including without limitation the rights
|
12
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
// copies of the Software, and to permit persons to whom the Software is
|
14
|
+
// furnished to do so, subject to the following conditions:
|
15
|
+
//
|
16
|
+
// The above copyright notice and this permission notice shall be included in
|
17
|
+
// all copies or substantial portions of the Software.
|
18
|
+
//
|
19
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
25
|
+
// THE SOFTWARE.
|
26
|
+
|
27
|
+
#import <Foundation/Foundation.h>
|
28
|
+
#import "CCIBlockDefinitions.h"
|
29
|
+
|
30
|
+
@class CCIStep;
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
@interface CCIStepDefinition : NSObject<NSCopying>
|
35
|
+
|
36
|
+
@property NSString * regexString;
|
37
|
+
@property NSString * type;
|
38
|
+
@property NSArray * matchedValues;
|
39
|
+
@property NSDictionary * additionalContent;
|
40
|
+
@property (nonatomic, copy) CCIStepBody body;
|
41
|
+
|
42
|
+
+ (instancetype)definitionWithType:(NSString *)type regexString:(NSString *)regexString implementationBody:(CCIStepBody)body;
|
43
|
+
@end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
//
|
2
|
+
// CCIStepDefinition.m
|
3
|
+
|
4
|
+
//
|
5
|
+
// Created by Ahmed Ali on 02/01/16.
|
6
|
+
// Copyright © 2016 Ahmed Ali. All rights reserved.
|
7
|
+
//
|
8
|
+
//
|
9
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
// of this software and associated documentation files (the "Software"), to deal
|
11
|
+
// in the Software without restriction, including without limitation the rights
|
12
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
// copies of the Software, and to permit persons to whom the Software is
|
14
|
+
// furnished to do so, subject to the following conditions:
|
15
|
+
//
|
16
|
+
// The above copyright notice and this permission notice shall be included in
|
17
|
+
// all copies or substantial portions of the Software.
|
18
|
+
//
|
19
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
25
|
+
// THE SOFTWARE.
|
26
|
+
|
27
|
+
#import "CCIStepDefinition.h"
|
28
|
+
#import "GHParser+Extensions.h"
|
29
|
+
#import "NSObject+Dictionary.h"
|
30
|
+
#import "CCIFeature.h"
|
31
|
+
#import "CCILocation.h"
|
32
|
+
#include <stdio.h>
|
33
|
+
|
34
|
+
|
35
|
+
@implementation CCIStepDefinition
|
36
|
+
|
37
|
+
|
38
|
+
+ (instancetype)definitionWithType:(NSString *)type regexString:(NSString *)regex implementationBody:(CCIStepBody)body
|
39
|
+
{
|
40
|
+
CCIStepDefinition * definition = [CCIStepDefinition new];
|
41
|
+
definition.type = type;
|
42
|
+
definition.regexString = regex;
|
43
|
+
definition.body = body;
|
44
|
+
return definition;
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
- (NSString *)description
|
50
|
+
{
|
51
|
+
return [NSString stringWithFormat:@"Definition type: %@, regexString: %@, matchedValues: %@", self.type, self.regexString, self.matchedValues];
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
#pragma mark - NSCopying
|
57
|
+
- (id)copyWithZone:(nullable NSZone *)zone
|
58
|
+
{
|
59
|
+
CCIStepDefinition * stepDefinition = [CCIStepDefinition definitionWithType:self.type regexString:self.regexString implementationBody:self.body];
|
60
|
+
stepDefinition.matchedValues = self.matchedValues;
|
61
|
+
|
62
|
+
return stepDefinition;
|
63
|
+
}
|
64
|
+
@end
|
65
|
+
|
@@ -0,0 +1,133 @@
|
|
1
|
+
//
|
2
|
+
// Cucumberish.h
|
3
|
+
|
4
|
+
//
|
5
|
+
// Created by Ahmed Ali on 03/01/16.
|
6
|
+
// Copyright © 2016 Ahmed Ali. All rights reserved.
|
7
|
+
//
|
8
|
+
//
|
9
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
10
|
+
// of this software and associated documentation files (the "Software"), to deal
|
11
|
+
// in the Software without restriction, including without limitation the rights
|
12
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
13
|
+
// copies of the Software, and to permit persons to whom the Software is
|
14
|
+
// furnished to do so, subject to the following conditions:
|
15
|
+
//
|
16
|
+
// The above copyright notice and this permission notice shall be included in
|
17
|
+
// all copies or substantial portions of the Software.
|
18
|
+
//
|
19
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
21
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
22
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
23
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
24
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
25
|
+
// THE SOFTWARE.
|
26
|
+
|
27
|
+
#import <Foundation/Foundation.h>
|
28
|
+
#import <XCTest/XCTest.h>
|
29
|
+
#import "CCIStepsManager.h"
|
30
|
+
#import "CCIBlockDefinitions.h"
|
31
|
+
|
32
|
+
|
33
|
+
/**
|
34
|
+
Cucumberish is the main class you will need to parse your feature files and execute them.
|
35
|
+
You should not create instances of this class directly, instead you need to use the instance method.
|
36
|
+
|
37
|
+
@see +[Cucumberish instance]
|
38
|
+
*/
|
39
|
+
@interface Cucumberish : NSObject
|
40
|
+
|
41
|
+
/**
|
42
|
+
As this being written, there is an issue with Xcode that causes the last scenario to disappear once it is done.
|
43
|
+
If this causes an issue for your test report, change the value of this property to YES before calling beginExecution.
|
44
|
+
|
45
|
+
@Note
|
46
|
+
Thoguh the default value of this property is NO, it is highly recommended to set the value of this proeprty to YES.
|
47
|
+
|
48
|
+
@Note
|
49
|
+
This will cause Cucumberish to execute an additional scenario called cucumberishCleanupScenario which will immediately disappear instead of your real last scenario.
|
50
|
+
Also this will increase the number of executed scenarios by 1. If you only have 6 scenarios, you will see 7 scenarios in the console message, and in your report navigator.
|
51
|
+
|
52
|
+
*/
|
53
|
+
@property (nonatomic) BOOL fixMissingLastScenario;
|
54
|
+
|
55
|
+
/**
|
56
|
+
If you change this property value to YES, feature names and scenarios will appear in Xcode Test Navigator as is (allowing spaces and special characters).
|
57
|
+
However, allowing pretty names might cause some issues with some tools like XCTool
|
58
|
+
*/
|
59
|
+
@property (nonatomic) BOOL prettyNamesAllowed;
|
60
|
+
|
61
|
+
/**
|
62
|
+
If the name of folder that contains your test target files is different than the test target it self, then tell Cucumber the name of the folder through this property.
|
63
|
+
This is important for proper error reporting.
|
64
|
+
*/
|
65
|
+
@property (nonatomic) NSString * testTargetFolderName;
|
66
|
+
|
67
|
+
/**
|
68
|
+
Retuans a singleton instance of Cucumberish
|
69
|
+
|
70
|
+
@return singleton instance of Cucumberish
|
71
|
+
*/
|
72
|
+
+ (instancetype)instance;
|
73
|
+
|
74
|
+
/**
|
75
|
+
Parses any .feature file that is located inside the passed folder name and map it to a test case if the feature inside the file has one or more tags of the passed tags (if any) and it doesn't have a tag from the excluded tags parameter
|
76
|
+
|
77
|
+
@param directory a path to your featuresDirectory relative to your test target main folder.
|
78
|
+
@param bundle the bundle where the directory is located
|
79
|
+
@param includeTags array of strings to filter which features that will be parsed to be executed, if nil then all feature files will be parsed.
|
80
|
+
@param excludeTags array of string to filter which features should not be executed.
|
81
|
+
|
82
|
+
@note The feature directory has to be a real physical folder. Also when adding this folder to your test target, and get the prompt on how you would like to add it from Xcode, choose "Create Folder Reference" and @b NOT to Create Groups.
|
83
|
+
@note tags should not be prefixed with @@ symbole
|
84
|
+
@note tags in includeTags parameter should not exist in the excludedTags parameter as it doesn't make any sense.
|
85
|
+
@note the tags will be also on the scenario level. That's it, if the feature passes the tag check, then we will check each of its scenarios against the tags as well. But if a feature is excluded (because it has a tag from the excludedTags array), none of its scenarios will be executed.
|
86
|
+
|
87
|
+
@return the singleton instance of Cucumberish so you can call beginExecution immediately if you want.
|
88
|
+
*/
|
89
|
+
- (Cucumberish *)parserFeaturesInDirectory:(NSString *)directory
|
90
|
+
fromBundle:(NSBundle *)bundle
|
91
|
+
includeTags:(NSArray<NSString *> *)includeTags
|
92
|
+
excludeTags:(NSArray<NSString *> *)excludeTags;
|
93
|
+
|
94
|
+
/**
|
95
|
+
Parses any .feature file that is located inside the passed folder name and map it to a test case if the feature inside the file has one or more tags of the passed tags (if any) and it doesn't have a tag from the excluded tags parameter
|
96
|
+
|
97
|
+
@param directory a path to your featuresDirectory relative to your test target main folder.
|
98
|
+
@param includeTags array of strings to filter which features that will be parsed to be executed, if nil then all feature files will be parsed.
|
99
|
+
@param excludeTags array of string to filter which features should not be executed.
|
100
|
+
|
101
|
+
@note The feature directory has to be a real physical folder. Also when adding this folder to your test target, and get the prompt on how you would like to add it from Xcode, choose "Create Folder Reference" and @b NOT to Create Groups.
|
102
|
+
@note If you followed the manual installation steps of Cucumberish, then make sure the features folder is the root folder of your project. Otherwise, it is better to use the parserFeaturesInDirectory:fromBundle:includeTags:excludeTags: method to specify the runtime bundle that will include the features folder.
|
103
|
+
|
104
|
+
@note tags should not be prefixed with @@ symbole
|
105
|
+
@note tags in includeTags parameter should not exist in the excludedTags parameter as it doesn't make any sense.
|
106
|
+
@note the tags will be also on the scenario level. That's it, if the feature passes the tag check, then we will check each of its scenarios against the tags as well. But if a feature is excluded (because it has a tag from the excludedTags array), none of its scenarios will be executed.
|
107
|
+
|
108
|
+
@return the singleton instance of Cucumberish so you can call beginExecution immediately if you want.
|
109
|
+
*/
|
110
|
+
- (Cucumberish *)parserFeaturesInDirectory:(NSString *)featuresDirectory
|
111
|
+
includeTags:(NSArray<NSString *> *)tags
|
112
|
+
excludeTags:(NSArray<NSString *> *)excludedTags;
|
113
|
+
|
114
|
+
/**
|
115
|
+
Fire the execution of all the previously parsed features in an alphabetic ascending order.
|
116
|
+
*/
|
117
|
+
- (void)beginExecution;
|
118
|
+
|
119
|
+
/**
|
120
|
+
Conventient method that calls parserFeaturesInDirectory:includeTags:excludeTags: followed by an immediate call to beginExecution
|
121
|
+
|
122
|
+
*/
|
123
|
+
+ (void)executeFeaturesInDirectory:(NSString *)featuresDirectory includeTags:(NSArray<NSString *> *)tags excludeTags:(NSArray<NSString *> *)excludedTags;
|
124
|
+
|
125
|
+
/**
|
126
|
+
Conventient method that calls parserFeaturesInDirectory:fromBundle:includeTags:excludeTags: followed by an immediate call to beginExecution
|
127
|
+
|
128
|
+
*/
|
129
|
+
+ (void)executeFeaturesInDirectory:(NSString *)featuresDirectory fromBundle:(NSBundle *)bundle includeTags:(NSArray *)tags excludeTags:(NSArray *)excludedTags;
|
130
|
+
|
131
|
+
@end
|
132
|
+
|
133
|
+
|
@@ -0,0 +1,604 @@
|
|
1
|
+
//
|
2
|
+
// Cucumberish.m
|
3
|
+
//
|
4
|
+
// Created by Ahmed Ali on 03/01/16.
|
5
|
+
// Copyright © 2016 Ahmed Ali. All rights reserved.
|
6
|
+
//
|
7
|
+
//
|
8
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
// of this software and associated documentation files (the "Software"), to deal
|
10
|
+
// in the Software without restriction, including without limitation the rights
|
11
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
// copies of the Software, and to permit persons to whom the Software is
|
13
|
+
// furnished to do so, subject to the following conditions:
|
14
|
+
//
|
15
|
+
// The above copyright notice and this permission notice shall be included in
|
16
|
+
// all copies or substantial portions of the Software.
|
17
|
+
//
|
18
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
// THE SOFTWARE.
|
25
|
+
|
26
|
+
#import <XCTest/XCTest.h>
|
27
|
+
#import <objc/runtime.h>
|
28
|
+
|
29
|
+
#import "Cucumberish.h"
|
30
|
+
#import "CCIFeature.h"
|
31
|
+
#import "CCIFeaturesManager.h"
|
32
|
+
#import "CCIStepsManager.h"
|
33
|
+
#import "NSString+Formatter.h"
|
34
|
+
#import "CCIStepDefinition.h"
|
35
|
+
#import "CCIScenarioDefinition.h"
|
36
|
+
#import "CCIHock.h"
|
37
|
+
#import "CCIAroundHock.h"
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
@interface CCIExeption : NSException @end
|
42
|
+
@implementation CCIExeption @end
|
43
|
+
|
44
|
+
OBJC_EXTERN void executeScenario(XCTestCase * self, SEL _cmd, CCIScenarioDefinition * scenario, CCIFeature * feature);
|
45
|
+
OBJC_EXTERN void executeSteps(XCTestCase * testCase, NSArray * steps, id parentScenario);
|
46
|
+
OBJC_EXTERN NSString * stepDefinitionLineForStep(CCIStep * step);
|
47
|
+
|
48
|
+
@interface Cucumberish()
|
49
|
+
@property (nonatomic, copy) void(^beforeStartHock)(void);
|
50
|
+
@property (nonatomic, copy) void(^afterFinishHock)(void);
|
51
|
+
|
52
|
+
@property (nonatomic, strong) NSMutableArray<CCIHock *> * beforeHocks;
|
53
|
+
@property (nonatomic, strong) NSMutableArray<CCIHock *> * afterHocks;
|
54
|
+
@property (nonatomic, strong) NSMutableArray<CCIAroundHock *> * aroundHocks;
|
55
|
+
|
56
|
+
@property (nonatomic, strong) NSArray<NSString *> * tags;
|
57
|
+
@property (nonatomic, strong) NSArray<NSString *> * excludedTags;
|
58
|
+
|
59
|
+
@property (nonatomic, assign) NSInteger scenariosRun;
|
60
|
+
@property (nonatomic, assign) NSInteger scenarioCount;
|
61
|
+
|
62
|
+
|
63
|
+
@end
|
64
|
+
@implementation Cucumberish
|
65
|
+
|
66
|
+
+ (instancetype)instance {
|
67
|
+
static Cucumberish * instance = nil;
|
68
|
+
static dispatch_once_t onceToken;
|
69
|
+
dispatch_once(&onceToken, ^{
|
70
|
+
instance = [[Cucumberish alloc] init];
|
71
|
+
});
|
72
|
+
return instance;
|
73
|
+
}
|
74
|
+
|
75
|
+
- (instancetype)init
|
76
|
+
{
|
77
|
+
self = [super init];
|
78
|
+
self.fixMissingLastScenario = NO;
|
79
|
+
|
80
|
+
self.beforeHocks = [NSMutableArray array];
|
81
|
+
self.afterHocks = [NSMutableArray array];
|
82
|
+
self.aroundHocks = [NSMutableArray array];
|
83
|
+
return self;
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
- (Cucumberish *)parserFeaturesInDirectory:(NSString *)directory fromBundle:(NSBundle *)bundle includeTags:(NSArray<NSString *> *)includeTags excludeTags:(NSArray<NSString *> *)excludeTags
|
88
|
+
{
|
89
|
+
NSArray * featureFiles = [bundle URLsForResourcesWithExtension:@".feature" subdirectory:directory];
|
90
|
+
|
91
|
+
[[CCIFeaturesManager instance] parseFeatureFiles:featureFiles bundle:bundle withTags:includeTags execludeFeaturesWithTags:excludeTags];
|
92
|
+
self.tags = includeTags;
|
93
|
+
self.excludedTags = excludeTags;
|
94
|
+
return self;
|
95
|
+
}
|
96
|
+
|
97
|
+
- (Cucumberish *)parserFeaturesInDirectory:(NSString *)featuresDirectory includeTags:(NSArray<NSString *> *)tags excludeTags:(NSArray<NSString *> *)excludedTags
|
98
|
+
{
|
99
|
+
[self parserFeaturesInDirectory:featuresDirectory
|
100
|
+
fromBundle:[NSBundle bundleForClass:[Cucumberish class]]
|
101
|
+
includeTags:tags
|
102
|
+
excludeTags:excludedTags];
|
103
|
+
return self;
|
104
|
+
}
|
105
|
+
|
106
|
+
+ (void)executeFeaturesInDirectory:(NSString *)featuresDirectory includeTags:(NSArray *)tags excludeTags:(NSArray *)excludedTags
|
107
|
+
{
|
108
|
+
[[[Cucumberish instance] parserFeaturesInDirectory:featuresDirectory
|
109
|
+
includeTags:tags
|
110
|
+
excludeTags:excludedTags] beginExecution];
|
111
|
+
}
|
112
|
+
|
113
|
+
+ (void)executeFeaturesInDirectory:(NSString *)featuresDirectory fromBundle:(NSBundle *)bundle includeTags:(NSArray *)tags excludeTags:(NSArray *)excludedTags
|
114
|
+
{
|
115
|
+
[[[Cucumberish instance] parserFeaturesInDirectory:featuresDirectory
|
116
|
+
fromBundle:bundle
|
117
|
+
includeTags:tags
|
118
|
+
excludeTags:excludedTags] beginExecution];
|
119
|
+
}
|
120
|
+
|
121
|
+
- (void)beginExecution
|
122
|
+
{
|
123
|
+
|
124
|
+
for(CCIFeature * feature in [[CCIFeaturesManager instance] features]){
|
125
|
+
Class featureClass = [Cucumberish featureTestCaseClass:feature];
|
126
|
+
[[CCIFeaturesManager instance] setClass:featureClass forFeature:feature];
|
127
|
+
//This swizzling has to happen for each feature class...
|
128
|
+
//If it is created on XCTestCase level, it will not work properly.
|
129
|
+
[Cucumberish swizzleDefaultSuiteImplementationForClass:featureClass];
|
130
|
+
[Cucumberish swizzleFailureRecordingImplementationForClass:featureClass];
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
#pragma mark - Manage hocks
|
135
|
+
/**
|
136
|
+
Adds an after hock to the after hocks chain in LIFO order.
|
137
|
+
@param hock the after hock to be registerd
|
138
|
+
*/
|
139
|
+
- (void)addAfterHock:(CCIHock *)hock
|
140
|
+
{
|
141
|
+
[self.afterHocks insertObject:hock atIndex:0];
|
142
|
+
}
|
143
|
+
/**
|
144
|
+
Adds a before hock to the before hocks chain in FIFO order.
|
145
|
+
@param hock the before hock to be registerd
|
146
|
+
*/
|
147
|
+
- (void)addBeforeHock:(CCIHock *)hock
|
148
|
+
{
|
149
|
+
[self.beforeHocks addObject:hock];
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
Adds an around hock to the around hocks chain in FIFO order.
|
154
|
+
@param hock the before hock to be registerd
|
155
|
+
*/
|
156
|
+
- (void)addAroundHock:(CCIAroundHock *)hock
|
157
|
+
{
|
158
|
+
[self.aroundHocks addObject:hock];
|
159
|
+
}
|
160
|
+
|
161
|
+
/**
|
162
|
+
Executes all the hocks that matches tags with the passed scenario.
|
163
|
+
Hocks may optionally be tagged, if an hock is tagged, then it will only be executed if the scenario has a matching tag.
|
164
|
+
|
165
|
+
@param array of CCIHock to be executed
|
166
|
+
@param scenario the scenario that will be passed to each matching hocks.
|
167
|
+
*/
|
168
|
+
|
169
|
+
- (void)executeMatchingHocksInHocks:(NSArray<CCIHock *> *)hocks forScenario:(CCIScenarioDefinition *)scenario
|
170
|
+
{
|
171
|
+
for(CCIHock * hock in hocks){
|
172
|
+
if(hock.tags.count > 0){
|
173
|
+
if(scenario.tags.count > 0){
|
174
|
+
for (NSString * tag in scenario.tags) {
|
175
|
+
if([hock.tags containsObject:tag]){
|
176
|
+
hock.block(scenario);
|
177
|
+
break;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}else{
|
182
|
+
hock.block(scenario);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
/**
|
189
|
+
Executes all the before hocks that matches tags with the passed scenario.
|
190
|
+
Hocks may optionally be tagged, if an hock is tagged, then it will only be executed if the scenario has a matching tag.
|
191
|
+
|
192
|
+
@param scenario the scenario that will be passed to each matching hocks.
|
193
|
+
*/
|
194
|
+
- (void)executeBeforeHocksWithScenario:(CCIScenarioDefinition *)scenario
|
195
|
+
{
|
196
|
+
[self executeMatchingHocksInHocks:self.beforeHocks forScenario:scenario];
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
Executes all the after hocks that matches tags with the passed scenario.
|
201
|
+
Hocks may optionally be tagged, if an hock is tagged, then it will only be executed if the scenario has a matching tag.
|
202
|
+
|
203
|
+
@param scenario the scenario that will be passed to each matching hocks.
|
204
|
+
*/
|
205
|
+
- (void)executeAfterHocksWithScenario:(CCIScenarioDefinition *)scenario
|
206
|
+
{
|
207
|
+
[self executeMatchingHocksInHocks:self.afterHocks forScenario:scenario];
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
Executes all the around hocks that matches tags with the passed scenario.
|
212
|
+
Hocks may optionally be tagged, if an hock is tagged, then it will only be executed if the scenario has a matching tag.
|
213
|
+
|
214
|
+
@param scenario the scenario that will be passed to each matching hocks.
|
215
|
+
@param executionBlock a block that when called, will execute the scenario. Around hocks are supposed to determine when this block will be executed.
|
216
|
+
*/
|
217
|
+
- (void)executeAroundHocksWithScenario:(CCIScenarioDefinition *)scenario executionBlock:(void(^)(void))executionBlock
|
218
|
+
{
|
219
|
+
|
220
|
+
void(^executionChain)(void) = NULL;
|
221
|
+
if(scenario.tags.count > 0){
|
222
|
+
for(CCIAroundHock * around in self.aroundHocks){
|
223
|
+
for (NSString * tag in scenario.tags) {
|
224
|
+
if([around.tags containsObject:tag]){
|
225
|
+
if(executionChain == NULL){
|
226
|
+
executionChain = ^{
|
227
|
+
around.block(scenario, executionBlock);
|
228
|
+
};
|
229
|
+
}else{
|
230
|
+
executionChain = ^{
|
231
|
+
around.block(scenario, executionChain);
|
232
|
+
};
|
233
|
+
}
|
234
|
+
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
|
241
|
+
if(executionChain != NULL){
|
242
|
+
executionChain();
|
243
|
+
}else{
|
244
|
+
executionBlock();
|
245
|
+
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
|
250
|
+
#pragma mark - Runtime hacks
|
251
|
+
|
252
|
+
+ (void)swizzleOrignalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector originalClass:(Class)originalClass targetClass:(Class)targetClass classMethod:(BOOL)classMethod
|
253
|
+
{
|
254
|
+
Class class = classMethod ? object_getClass((id)originalClass) : originalClass;
|
255
|
+
Method originalMethod = nil;
|
256
|
+
if(classMethod){
|
257
|
+
originalMethod = class_getClassMethod(class, originalSelector);
|
258
|
+
}else{
|
259
|
+
originalMethod = class_getInstanceMethod(class, originalSelector);
|
260
|
+
}
|
261
|
+
Method swizzledMethod = class_getClassMethod(targetClass, swizzledSelector);
|
262
|
+
BOOL didAddMethod =
|
263
|
+
class_addMethod(class,
|
264
|
+
originalSelector,
|
265
|
+
method_getImplementation(swizzledMethod),
|
266
|
+
method_getTypeEncoding(swizzledMethod));
|
267
|
+
|
268
|
+
if (didAddMethod) {
|
269
|
+
class_replaceMethod(class,
|
270
|
+
swizzledSelector,
|
271
|
+
method_getImplementation(originalMethod),
|
272
|
+
method_getTypeEncoding(originalMethod));
|
273
|
+
} else {
|
274
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
|
279
|
+
+ (void)swizzleFailureRecordingImplementationForClass:(Class)class
|
280
|
+
{
|
281
|
+
SEL originalSelector = @selector(recordFailureWithDescription:inFile:atLine:expected:);
|
282
|
+
SEL swizzledSelector = @selector(cucumberish_recordFailureWithDescription:inFile:atLine:expected:);
|
283
|
+
[Cucumberish swizzleOrignalSelector:originalSelector swizzledSelector:swizzledSelector originalClass:class targetClass:[Cucumberish class] classMethod:NO];
|
284
|
+
}
|
285
|
+
|
286
|
+
+ (void)swizzleDefaultSuiteImplementationForClass:(Class)class
|
287
|
+
{
|
288
|
+
SEL originalSelector = @selector(defaultTestSuite);
|
289
|
+
SEL swizzledSelector = @selector(cucumberish_defaultTestSuite);
|
290
|
+
[Cucumberish swizzleOrignalSelector:originalSelector swizzledSelector:swizzledSelector originalClass:class targetClass:[Cucumberish class] classMethod:YES];
|
291
|
+
}
|
292
|
+
|
293
|
+
|
294
|
+
|
295
|
+
|
296
|
+
+ (void)createScenariosForScenarioOutline:(CCIScenarioDefinition *)outline feature:(CCIFeature *)feature class:(Class)klass suite:(XCTestSuite *)suite
|
297
|
+
{
|
298
|
+
for(CCIExample * example in outline.examples){
|
299
|
+
|
300
|
+
//Loop on the example bod(y|ies)
|
301
|
+
NSUInteger numberOfRows = [(NSArray *)example.exampleData[example.exampleData.allKeys.firstObject] count];
|
302
|
+
for(int i = 0; i < numberOfRows; i++){
|
303
|
+
//Scenario for each body
|
304
|
+
CCIScenarioDefinition * scenario = [outline copy];
|
305
|
+
scenario.name = [scenario.name stringByAppendingFormat:@" Example %lu", (unsigned long)(i + 1)];
|
306
|
+
scenario.examples = nil;
|
307
|
+
for(NSString * variable in example.exampleData.allKeys){
|
308
|
+
NSString * replacement = example.exampleData[variable][i];
|
309
|
+
//now loop on each step in the scenario to replace the place holders with their values
|
310
|
+
for(CCIStep * step in scenario.steps){
|
311
|
+
NSString * placeHolder = [NSString stringWithFormat:@"<%@>", variable];
|
312
|
+
step.text = [step.text stringByReplacingOccurrencesOfString:placeHolder withString:replacement];
|
313
|
+
if (step.argument.rows) {
|
314
|
+
NSMutableArray *modifiedRows = [NSMutableArray arrayWithCapacity:step.argument.rows.count];
|
315
|
+
for (NSArray *row in step.argument.rows) {
|
316
|
+
NSMutableArray *array = [row mutableCopy];
|
317
|
+
[row enumerateObjectsUsingBlock:^(NSString *value, NSUInteger idx, BOOL * _Nonnull stop) {
|
318
|
+
if ([value isEqualToString:placeHolder]){
|
319
|
+
array[idx] = replacement;
|
320
|
+
}
|
321
|
+
}];
|
322
|
+
[modifiedRows addObject:array];
|
323
|
+
}
|
324
|
+
step.argument.rows = modifiedRows;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
}
|
328
|
+
|
329
|
+
XCTestCase * testCase = [[klass alloc] initWithInvocation:[Cucumberish invocationForScenario:scenario feature:feature class:klass]];
|
330
|
+
[suite addTest:testCase];
|
331
|
+
[Cucumberish instance].scenarioCount++;
|
332
|
+
}
|
333
|
+
|
334
|
+
|
335
|
+
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
+ (Class)featureTestCaseClass:(CCIFeature *)feature
|
340
|
+
{
|
341
|
+
//Prefix it with CCI to avoit any name collision
|
342
|
+
//Prefix it with CCI to avoit any name collision
|
343
|
+
NSString * className = [@"CCI " stringByAppendingString:feature.name];
|
344
|
+
if(![[Cucumberish instance] prettyNamesAllowed]){
|
345
|
+
className = [@"CCI_" stringByAppendingString:[feature.name camleCaseStringWithFirstUppercaseCharacter:YES]];
|
346
|
+
}
|
347
|
+
Class featureClass = objc_allocateClassPair([XCTestCase class], [className UTF8String], 0);
|
348
|
+
if(featureClass == nil){
|
349
|
+
featureClass = NSClassFromString(className);
|
350
|
+
NSUInteger availableClassesWithTheSameName = 1;
|
351
|
+
while (featureClass == nil) {
|
352
|
+
className = [className stringByAppendingFormat:@"%lu", (long unsigned)availableClassesWithTheSameName];
|
353
|
+
featureClass = objc_allocateClassPair([XCTestCase class], [className UTF8String], 0);
|
354
|
+
}
|
355
|
+
}
|
356
|
+
objc_registerClassPair(featureClass);
|
357
|
+
return featureClass;
|
358
|
+
}
|
359
|
+
|
360
|
+
+ (NSInvocation *)invocationForScenario:(CCIScenarioDefinition *)scenario feature:(CCIFeature *)feature class:(Class)klass
|
361
|
+
{
|
362
|
+
NSString * methodName = scenario.name;
|
363
|
+
|
364
|
+
if(![[Cucumberish instance] prettyNamesAllowed]){
|
365
|
+
methodName = [methodName camleCaseStringWithFirstUppercaseCharacter:NO];
|
366
|
+
}
|
367
|
+
SEL sel = NSSelectorFromString(methodName);
|
368
|
+
|
369
|
+
//Prefered to forward the implementation to a C function instead of Objective-C method, to avoid confusion with the type of "self" object that is being to the implementation
|
370
|
+
class_addMethod(klass, sel, (IMP)executeScenario, [@"v@:@:@" UTF8String]);
|
371
|
+
|
372
|
+
NSMethodSignature *signature = [klass instanceMethodSignatureForSelector:sel];
|
373
|
+
|
374
|
+
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
375
|
+
|
376
|
+
[invocation setSelector:sel];
|
377
|
+
|
378
|
+
|
379
|
+
[invocation setArgument:&scenario atIndex:2];
|
380
|
+
[invocation setArgument:&feature atIndex:3];
|
381
|
+
[invocation retainArguments];
|
382
|
+
return invocation;
|
383
|
+
}
|
384
|
+
|
385
|
+
|
386
|
+
#pragma mark - Swizzled methods
|
387
|
+
/**
|
388
|
+
Swizzled method, inside its implementation @b self does not refer to Cucumberish class.
|
389
|
+
Records a failure in the execution of the test and is used by all test assertions.
|
390
|
+
|
391
|
+
@param description The description of the failure being reported.
|
392
|
+
|
393
|
+
@param filePath The file path to the source file where the failure being reported was encountered.
|
394
|
+
@param lineNumber The line number in the source file at filePath where the failure being reported was encountered.
|
395
|
+
|
396
|
+
@param expected YES if the failure being reported was the result of a failed assertion, NO if it was the result of an uncaught exception.
|
397
|
+
*/
|
398
|
+
|
399
|
+
+ (void)cucumberish_recordFailureWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber expected:(BOOL)expected
|
400
|
+
{
|
401
|
+
//If exception already thrown and handled by executeSteps function, then report it immediately.
|
402
|
+
if([filePath hasSuffix:@".feature"]){
|
403
|
+
[self cucumberish_recordFailureWithDescription:description inFile:filePath atLine:lineNumber expected:expected];
|
404
|
+
}else{
|
405
|
+
//Throw the exception so proper error report takes place.
|
406
|
+
throwCucumberishException(description);
|
407
|
+
}
|
408
|
+
|
409
|
+
}
|
410
|
+
|
411
|
+
/**
|
412
|
+
Swizzled method, inside its implementation @b self does not refer to Cucumberish class.
|
413
|
+
@return a test suite containing test cases for all of the tests in the class.
|
414
|
+
*/
|
415
|
+
|
416
|
+
+ (XCTestSuite *)cucumberish_defaultTestSuite
|
417
|
+
{
|
418
|
+
XCTestSuite * suite = [self cucumberish_defaultTestSuite];
|
419
|
+
CCIFeature * feature = [[CCIFeaturesManager instance] getFeatureForClass:self];
|
420
|
+
|
421
|
+
for (CCIScenarioDefinition * scenario in feature.scenarioDefinitions) {
|
422
|
+
if(![[Cucumberish instance] shouldIncludeScenario:scenario]){
|
423
|
+
continue;
|
424
|
+
}
|
425
|
+
if([scenario.keyword isEqualToString:@"Scenario Outline"]){
|
426
|
+
[Cucumberish createScenariosForScenarioOutline:scenario feature:feature class:self suite:suite];
|
427
|
+
}else{
|
428
|
+
if([scenario.keyword isEqualToString:@"Background"]){
|
429
|
+
//Do not add a scenario for a background steps
|
430
|
+
feature.background = (CCIBackground *)scenario;
|
431
|
+
continue;
|
432
|
+
}
|
433
|
+
XCTestCase * testCase = [[self alloc] initWithInvocation:[Cucumberish invocationForScenario:scenario feature:feature class:self]];
|
434
|
+
[suite addTest:testCase];
|
435
|
+
[Cucumberish instance].scenarioCount++;
|
436
|
+
}
|
437
|
+
|
438
|
+
}
|
439
|
+
CCIFeature * lastFeature = [CCIFeaturesManager instance].features.lastObject;
|
440
|
+
if([Cucumberish instance].fixMissingLastScenario && feature == lastFeature){
|
441
|
+
CCIScenarioDefinition * cleanupScenario = [[CCIScenarioDefinition alloc] init];
|
442
|
+
cleanupScenario.name = @"cucumberishCleanupScenario";
|
443
|
+
XCTestCase * finalCase = [[self alloc] initWithInvocation:[Cucumberish invocationForScenario:cleanupScenario feature:lastFeature class:self]];
|
444
|
+
[suite addTest:finalCase];
|
445
|
+
[Cucumberish instance].scenarioCount++;
|
446
|
+
}
|
447
|
+
|
448
|
+
return suite;
|
449
|
+
}
|
450
|
+
|
451
|
+
- (BOOL)shouldIncludeScenario:(CCIScenarioDefinition *)scenario
|
452
|
+
{
|
453
|
+
BOOL shouldIncludeScenario = YES;
|
454
|
+
if(![scenario.keyword isEqualToString:@"Background"]){
|
455
|
+
if(self.tags.count > 0){
|
456
|
+
shouldIncludeScenario = [self tags:scenario.tags intersectWithTags:self.tags]&& ![self tags:scenario.tags intersectWithTags:self.excludedTags];
|
457
|
+
}else if(self.excludedTags.count > 0){
|
458
|
+
shouldIncludeScenario = ![self tags:scenario.tags intersectWithTags:self.excludedTags];
|
459
|
+
}
|
460
|
+
}
|
461
|
+
return shouldIncludeScenario;
|
462
|
+
}
|
463
|
+
|
464
|
+
|
465
|
+
- (BOOL)tags:(NSArray *)tags intersectWithTags:(NSArray *)intersectionTags
|
466
|
+
{
|
467
|
+
BOOL intersect = NO;
|
468
|
+
if(tags.count == 0 || intersectionTags.count == 0){
|
469
|
+
return intersect;
|
470
|
+
}
|
471
|
+
for(NSString * tag in tags){
|
472
|
+
if([intersectionTags containsObject:tag]){
|
473
|
+
intersect = YES;
|
474
|
+
break;
|
475
|
+
}
|
476
|
+
}
|
477
|
+
return intersect;
|
478
|
+
}
|
479
|
+
@end
|
480
|
+
|
481
|
+
|
482
|
+
#pragma mark - C Functions
|
483
|
+
|
484
|
+
void executeScenario(XCTestCase * self, SEL _cmd, CCIScenarioDefinition * scenario, CCIFeature * feature)
|
485
|
+
{
|
486
|
+
|
487
|
+
self.continueAfterFailure = YES;
|
488
|
+
if([Cucumberish instance].scenariosRun == 0 && [Cucumberish instance].beforeStartHock){
|
489
|
+
[Cucumberish instance].beforeStartHock();
|
490
|
+
}
|
491
|
+
[[Cucumberish instance] executeBeforeHocksWithScenario:scenario];
|
492
|
+
if(feature.background != nil && scenario.steps.count > 0){
|
493
|
+
executeSteps(self, feature.background.steps, feature.background);
|
494
|
+
}
|
495
|
+
|
496
|
+
[[Cucumberish instance] executeAroundHocksWithScenario:scenario executionBlock:^{
|
497
|
+
executeSteps(self, scenario.steps, scenario);
|
498
|
+
}];
|
499
|
+
[Cucumberish instance].scenariosRun++;
|
500
|
+
[[Cucumberish instance] executeAfterHocksWithScenario:scenario];
|
501
|
+
|
502
|
+
if([Cucumberish instance].scenariosRun == [Cucumberish instance].scenarioCount && [Cucumberish instance].afterFinishHock){
|
503
|
+
[Cucumberish instance].afterFinishHock();
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
void executeSteps(XCTestCase * testCase, NSArray * steps, id parentScenario)
|
508
|
+
{
|
509
|
+
|
510
|
+
NSString * targetName = [[Cucumberish instance] testTargetFolderName] ? : [[NSBundle bundleForClass:[Cucumberish class]] infoDictionary][@"CFBundleName"];
|
511
|
+
NSString * srcRoot = SRC_ROOT;
|
512
|
+
//Clean up unwanted /Pods path caused by cocoa pods
|
513
|
+
if([srcRoot hasSuffix:@"/Pods"]){
|
514
|
+
srcRoot = [srcRoot stringByReplacingCharactersInRange:NSMakeRange(srcRoot.length - 5, 5) withString:@""];
|
515
|
+
}
|
516
|
+
|
517
|
+
for (CCIStep * step in steps) {
|
518
|
+
|
519
|
+
@try {
|
520
|
+
[[CCIStepsManager instance] executeStep:step inTestCase:testCase];
|
521
|
+
}
|
522
|
+
@catch (CCIExeption *exception) {
|
523
|
+
NSString * filePath = [NSString stringWithFormat:@"%@/%@%@", srcRoot, targetName, step.location.filePath];
|
524
|
+
[testCase recordFailureWithDescription:exception.reason inFile:filePath atLine:step.location.line expected:YES];
|
525
|
+
if([parentScenario isKindOfClass:[CCIScenarioDefinition class]]){
|
526
|
+
CCIScenarioDefinition * scenario = (CCIScenarioDefinition *)parentScenario;
|
527
|
+
step.status = CCIStepStatusFailed;
|
528
|
+
scenario.success = NO;
|
529
|
+
scenario.failureReason = exception.reason;
|
530
|
+
}
|
531
|
+
break;
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
|
537
|
+
void CCIAssert(BOOL expression, NSString * failureMessage, ...)
|
538
|
+
{
|
539
|
+
if(!expression){
|
540
|
+
va_list args;
|
541
|
+
va_start(args, failureMessage);
|
542
|
+
NSString *description = [[NSString alloc] initWithFormat:failureMessage arguments:args];
|
543
|
+
va_end(args);
|
544
|
+
throwCucumberishException(description);
|
545
|
+
}
|
546
|
+
}
|
547
|
+
|
548
|
+
void CCISAssert(BOOL expression, NSString * failureMessage)
|
549
|
+
{
|
550
|
+
CCIAssert(expression, failureMessage);
|
551
|
+
}
|
552
|
+
|
553
|
+
void throwCucumberishException(NSString *reason, ...)
|
554
|
+
{
|
555
|
+
va_list args;
|
556
|
+
va_start(args, reason);
|
557
|
+
NSString *description = [[NSString alloc] initWithFormat:reason arguments:args];
|
558
|
+
va_end(args);
|
559
|
+
[[CCIExeption exceptionWithName:@"CCIException" reason:description userInfo:nil] raise];
|
560
|
+
}
|
561
|
+
|
562
|
+
void SThrowCucumberishException(NSString * reason)
|
563
|
+
{
|
564
|
+
throwCucumberishException(reason);
|
565
|
+
}
|
566
|
+
#pragma mark - Hooks
|
567
|
+
void beforeStart(void(^beforeStartBlock)(void))
|
568
|
+
{
|
569
|
+
[Cucumberish instance].beforeStartHock = beforeStartBlock;
|
570
|
+
}
|
571
|
+
|
572
|
+
void afterFinish(void(^afterFinishBlock)(void))
|
573
|
+
{
|
574
|
+
[Cucumberish instance].afterFinishHock = afterFinishBlock;
|
575
|
+
}
|
576
|
+
|
577
|
+
void before(CCIScenarioHockBlock beforeEachBlock)
|
578
|
+
{
|
579
|
+
[[Cucumberish instance] addBeforeHock:[CCIHock hockWithTags:nil block:beforeEachBlock]];
|
580
|
+
}
|
581
|
+
|
582
|
+
void after(CCIScenarioHockBlock afterEachBlock)
|
583
|
+
{
|
584
|
+
[[Cucumberish instance] addAfterHock:[CCIHock hockWithTags:nil block:afterEachBlock]];
|
585
|
+
}
|
586
|
+
|
587
|
+
void beforeTagged(NSArray * tags, CCIScenarioHockBlock beforeTaggedBlock)
|
588
|
+
{
|
589
|
+
[[Cucumberish instance] addBeforeHock:[CCIHock hockWithTags:tags block:beforeTaggedBlock]];
|
590
|
+
}
|
591
|
+
|
592
|
+
void afterTagged(NSArray * tags, CCIScenarioHockBlock afterTaggedBlock)
|
593
|
+
{
|
594
|
+
[[Cucumberish instance] addAfterHock:[CCIHock hockWithTags:tags block:afterTaggedBlock]];
|
595
|
+
}
|
596
|
+
|
597
|
+
void around(NSArray * tags, CCIScenarioExecutionHockBlock aroundScenarioBlock)
|
598
|
+
{
|
599
|
+
[[Cucumberish instance] addAroundHock:[CCIAroundHock hockWithTags:tags block:aroundScenarioBlock]];
|
600
|
+
}
|
601
|
+
|
602
|
+
|
603
|
+
|
604
|
+
|