xcfit 2.0.4 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -9
  3. data/Docs/InitialVersion.md +340 -0
  4. data/Package.swift +5 -0
  5. data/README.md +348 -145
  6. data/XCFit.gemspec +1 -1
  7. data/XCFit.podspec +17 -9
  8. data/XCFit/Classes/Cucumberish/Core/CCIBlockDefinitions.h +400 -0
  9. data/XCFit/Classes/Cucumberish/Core/Managers/CCIFeaturesManager.h +72 -0
  10. data/XCFit/Classes/Cucumberish/Core/Managers/CCIFeaturesManager.m +141 -0
  11. data/XCFit/Classes/Cucumberish/Core/Managers/CCIStepsManager.h +51 -0
  12. data/XCFit/Classes/Cucumberish/Core/Managers/CCIStepsManager.m +245 -0
  13. data/XCFit/Classes/Cucumberish/Core/Models/CCIArgument.h +62 -0
  14. data/XCFit/Classes/Cucumberish/Core/Models/CCIArgument.m +117 -0
  15. data/XCFit/Classes/Cucumberish/Core/Models/CCIAroundHock.h +37 -0
  16. data/XCFit/Classes/Cucumberish/Core/Models/CCIAroundHock.m +37 -0
  17. data/XCFit/Classes/Cucumberish/Core/Models/CCIBackground.h +46 -0
  18. data/XCFit/Classes/Cucumberish/Core/Models/CCIBackground.m +117 -0
  19. data/XCFit/Classes/Cucumberish/Core/Models/CCIExample.h +89 -0
  20. data/XCFit/Classes/Cucumberish/Core/Models/CCIExample.m +128 -0
  21. data/XCFit/Classes/Cucumberish/Core/Models/CCIFeature.h +49 -0
  22. data/XCFit/Classes/Cucumberish/Core/Models/CCIFeature.m +174 -0
  23. data/XCFit/Classes/Cucumberish/Core/Models/CCIHock.h +36 -0
  24. data/XCFit/Classes/Cucumberish/Core/Models/CCIHock.m +34 -0
  25. data/XCFit/Classes/Cucumberish/Core/Models/CCILocation.h +61 -0
  26. data/XCFit/Classes/Cucumberish/Core/Models/CCILocation.m +91 -0
  27. data/XCFit/Classes/Cucumberish/Core/Models/CCIScenarioDefinition.h +97 -0
  28. data/XCFit/Classes/Cucumberish/Core/Models/CCIScenarioDefinition.m +224 -0
  29. data/XCFit/Classes/Cucumberish/Core/Models/CCIStep.h +85 -0
  30. data/XCFit/Classes/Cucumberish/Core/Models/CCIStep.m +98 -0
  31. data/XCFit/Classes/Cucumberish/Core/Models/CCIStepDefinition.h +43 -0
  32. data/XCFit/Classes/Cucumberish/Core/Models/CCIStepDefinition.m +65 -0
  33. data/XCFit/Classes/Cucumberish/Cucumberish.h +133 -0
  34. data/XCFit/Classes/Cucumberish/Cucumberish.m +604 -0
  35. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstBuilder.h +15 -0
  36. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstBuilder.m +312 -0
  37. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstNode.h +20 -0
  38. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHAstNode.m +79 -0
  39. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHBackground.h +12 -0
  40. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHBackground.m +18 -0
  41. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHComment.h +11 -0
  42. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHComment.m +27 -0
  43. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDataTable.h +14 -0
  44. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDataTable.m +30 -0
  45. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDocString.h +14 -0
  46. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHDocString.m +29 -0
  47. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHExamples.h +24 -0
  48. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHExamples.m +49 -0
  49. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHFeature.h +24 -0
  50. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHFeature.m +50 -0
  51. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialect.h +25 -0
  52. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialect.m +64 -0
  53. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProvider.h +9 -0
  54. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProvider.m +124 -0
  55. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinDialectProviderProtocol.h +10 -0
  56. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageConstants.h +9 -0
  57. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageConstants.m +10 -0
  58. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageSetting.h +17 -0
  59. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLanguageSetting.m +18 -0
  60. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLine.h +12 -0
  61. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLine.m +179 -0
  62. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineProtocol.h +64 -0
  63. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineSpan.h +9 -0
  64. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHGherkinLineSpan.m +26 -0
  65. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasDescriptionProtocol.h +8 -0
  66. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasLocationProtocol.h +8 -0
  67. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasRowsProtocol.h +8 -0
  68. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasStepsProtocol.h +8 -0
  69. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHHasTagsProtocol.h +8 -0
  70. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHLocation.h +12 -0
  71. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHLocation.m +41 -0
  72. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHNode.h +4 -0
  73. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHNode.m +5 -0
  74. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser+Extensions.h +10 -0
  75. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser+Extensions.m +18 -0
  76. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser.h +132 -0
  77. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParser.m +2735 -0
  78. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParserException.h +55 -0
  79. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHParserException.m +192 -0
  80. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenario.h +14 -0
  81. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenario.m +18 -0
  82. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition.h +23 -0
  83. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition.m +41 -0
  84. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioDefinition_Private.h +6 -0
  85. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioOutline.h +15 -0
  86. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHScenarioOutline.m +26 -0
  87. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStep.h +16 -0
  88. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStep.m +35 -0
  89. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStepArgument.h +4 -0
  90. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHStepArgument.m +5 -0
  91. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableCell.h +13 -0
  92. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableCell.m +28 -0
  93. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableRow.h +13 -0
  94. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTableRow.m +29 -0
  95. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTag.h +13 -0
  96. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTag.m +28 -0
  97. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHToken.h +26 -0
  98. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHToken.m +50 -0
  99. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenMatcher.h +29 -0
  100. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenMatcher.m +283 -0
  101. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenScanner.h +12 -0
  102. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/GHTokenScanner.m +59 -0
  103. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/NSString+Trim.h +7 -0
  104. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/NSString+Trim.m +28 -0
  105. data/XCFit/Classes/Cucumberish/Dependencies/Gherkin/gherkin-languages.json +2969 -0
  106. data/XCFit/Classes/Cucumberish/Utils/NSArray+Hashes.h +24 -0
  107. data/XCFit/Classes/Cucumberish/Utils/NSArray+Hashes.m +44 -0
  108. data/XCFit/Classes/Cucumberish/Utils/NSObject+Dictionary.h +7 -0
  109. data/XCFit/Classes/Cucumberish/Utils/NSObject+Dictionary.m +83 -0
  110. data/XCFit/Classes/Cucumberish/Utils/NSString+Formatter.h +32 -0
  111. data/XCFit/Classes/Cucumberish/Utils/NSString+Formatter.m +70 -0
  112. data/Xcode7-Templates/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateInfo.plist +13 -0
  113. data/Xcode7-Templates/XCFit/iOS UI Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +11 -0
  114. data/lib/XCFit/version.rb +1 -1
  115. metadata +108 -38
  116. data/XCFit/Assets/.gitkeep +0 -0
  117. data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateIcon.png +0 -0
  118. data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateIcon@2x.png +0 -0
  119. data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/TemplateInfo.plist +0 -22
  120. data/XCFit/Assets/Gherkin/Cucumber Feature.xctemplate/___FILEBASENAME___.feature +0 -20
  121. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/AcceptanceTests-Bridging-Header.h +0 -1
  122. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/FixtureExample.swift +0 -31
  123. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/SlimTables.h +0 -28
  124. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateIcon.png +0 -0
  125. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateIcon@2x.png +0 -0
  126. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/TemplateInfo.plist +0 -150
  127. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -1
  128. data/XCFit/Assets/XCFit/Base Acceptance Tests.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -15
  129. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateIcon.png +0 -0
  130. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateIcon@2x.png +0 -0
  131. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/TemplateInfo.plist +0 -123
  132. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -7
  133. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.feature +0 -11
  134. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.h +0 -14
  135. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -17
  136. data/XCFit/Assets/XCFit/Cucumberish API Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.swift +0 -23
  137. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateIcon.png +0 -0
  138. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateIcon@2x.png +0 -0
  139. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/TemplateInfo.plist +0 -123
  140. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___-Bridging-Header.h +0 -7
  141. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.feature +0 -11
  142. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.h +0 -14
  143. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.m +0 -17
  144. data/XCFit/Assets/XCFit/Cucumberish UI Test Bundle Base.xctemplate/___PACKAGENAMEASIDENTIFIER___.swift +0 -25
  145. data/XCFit/Assets/XCFit/Mac Acceptance Tests.xctemplate/Empty.xib +0 -12
  146. data/XCFit/Assets/XCFit/Mac Acceptance Tests.xctemplate/TemplateInfo.plist +0 -54
  147. data/XCFit/Assets/XCFit/MacOS API Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
  148. data/XCFit/Assets/XCFit/MacOS UI Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
  149. data/XCFit/Assets/XCFit/iOS API Cucumberish Testing Bundle.xctemplate/TemplateInfo.plist +0 -39
  150. data/XCFit/Assets/XCFit/iOS Acceptance Tests.xctemplate/TemplateInfo.plist +0 -52
  151. 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
+