Module: Marso
- Defined in:
- lib/marso/config.rb,
lib/marso/version.rb,
lib/marso/launcher.rb,
lib/marso/factories.rb,
lib/marso/messages/errors.rb,
lib/marso/domain/step/step.rb,
lib/marso/domain/story/story.rb,
lib/marso/helpers/texthelper.rb,
lib/marso/helpers/statushelper.rb,
lib/marso/toolbelt/fiberpiping.rb,
lib/marso/domain/feature/feature.rb,
lib/marso/domain/story/story_load.rb,
lib/marso/helpers/componenthelper.rb,
lib/marso/domain/scenario/scenario.rb,
lib/marso/domain/step/step_publish.rb,
lib/marso/domain/story/story_publish.rb,
lib/marso/domain/feature/feature_load.rb,
lib/marso/domain/feature/feature_publish.rb,
lib/marso/domain/scenario/scenario_publish.rb
Defined Under Namespace
Modules: FeatureLoad, FeaturePublish, Messages, ScenarioPublish, StepPublish, StoryLoad, StoryPublish, TextHelper Classes: Config, Enumerate, Feature, FiberFilter, FiberProjection, FiberProjectionMany, Scenario, ScenarioContext, Step, Story
Constant Summary collapse
- VERSION =
"0.1.29059"
Class Method Summary collapse
-
.components(component_type, file_path_pattern, ctx = {}) ⇒ Object
component_type: :feature :story :scenario_context.
-
.core_components_query(component_type, options = {}) ⇒ Object
options: Hash defined as follow :rootpath => Path of the folder containing the ‘stories’ folder.
- .item_with_stronger_status(x, y) ⇒ Object
-
.load_components(component_type, file_path_pattern, ctx = {}) ⇒ Object
component_type: :feature :story :scenario_context.
- .openNewBrowser(browser = nil) ⇒ Object
-
.reorganize_scenariocontexts_into_components(scenario_contexts, origin_stories, origin_features) ⇒ Object
This method reorganized a collection of scenario contexts so they are regrouped under their parent components(origin_stories or origin_features).
-
.run_features(options = {}) ⇒ Object
options: Hash defined as follow :rootpath => Path of the folder containing the ‘stories’ folder.
- .run_features_async(options = {}) ⇒ Object
-
.show_features_text(options = {}) ⇒ Object
options: Hash defined as follow :rootpath => Path of the folder containing the ‘stories’ folder.
-
.show_scenarios_text(options = {}) ⇒ Object
options: Hash defined as follow :rootpath => Path of the folder containing the ‘stories’ folder.
-
.show_stories_text(options = {}) ⇒ Object
options: Hash defined as follow :rootpath => Path of the folder containing the ‘stories’ folder.
Class Method Details
.components(component_type, file_path_pattern, ctx = {}) ⇒ Object
component_type:
:feature
:story
:scenario_context
35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/marso/helpers/componenthelper.rb', line 35 def self.components(component_type, file_path_pattern, ctx={}) Enumerate.from(Dir[file_path_pattern]) .where { |file| load file Object.const_defined?("MarsoContext") && Object.const_get("MarsoContext").respond_to?(component_type) } .select { |file| component = Object.const_get("MarsoContext").send(component_type, ctx) class_name = component_type.to_s.split("_").map { |x| x.capitalize }.join raise ArgumentError, "Method MarsoContext.#{component_type} cannot return nil" if component.nil? raise ArgumentError, "Method MarsoContext.#{component_type} must return an object of class Marso::Story" unless component.class.to_s == "Marso::#{class_name}" component } end |
.core_components_query(component_type, options = {}) ⇒ Object
options: Hash defined as follow
:rootpath => Path of the folder containing the 'stories' folder. If not
specified, then the default is to set it to the current
caller's location
:select => Single story's id or Array of story's ids. The execution
of stories will be restricted to those ids only
:include_mode => Symbol that defines what should be included in the
feature's description. Possible values are:
=> :none - (Default) Only display the feature's description
=> :with_stories - Display the feature description as well as all its
stories' description
=> :with_stories_scenarios - Display the feature description as well
as all its stories' description
(including their scenarios)
=> :with_scenarios - Display the feature description as well as all its
scenarios' description
=> :with_all - Display the feature description as well as both all its
stories(including their scenarios) and scenarios descriptions
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/marso/launcher.rb', line 297 def self.core_components_query(component_type, ={}) file_pattern = nil case component_type when :feature file_pattern = 'features/*/*.rb' when :story file_pattern = 'stories/*/*.rb' when :scenario_context file_pattern = 'scenarios/*.rb' else raise ArgumentError, ":#{component_type} is not a valid component_type. " + "Valid types are #{[:feature, :story, :scenario_context].join(', ')}" end file_path_pattern = File.join(.rootpath, file_pattern) load_mode = .include_mode.to_load_mode components = Marso.components(component_type, file_path_pattern) ids_selection = .ids_selection query = ids_selection.any? ? components.where { |x| ids_selection.include?(x.id) } : components if component_type == :scenario_context return query else return query.select { |c| c.load(load_mode) } end end |
.item_with_stronger_status(x, y) ⇒ Object
172 173 174 |
# File 'lib/marso/helpers/statushelper.rb', line 172 def self.item_with_stronger_status(x, y) return x.status>=y.status ? x : y end |
.load_components(component_type, file_path_pattern, ctx = {}) ⇒ Object
component_type:
:feature
:story
:scenario_context
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/marso/helpers/componenthelper.rb', line 9 def self.load_components(component_type, file_path_pattern, ctx={}) components = [] Dir[file_path_pattern].each { |file| load file file_contains_marso_component = Object.const_defined?("MarsoContext") && Object.const_get("MarsoContext").respond_to?(component_type) if file_contains_marso_component component = Object.const_get("MarsoContext").send(component_type, ctx) class_name = component_type.to_s.split("_").map { |x| x.capitalize }.join raise ArgumentError, "Method MarsoContext.#{component_type} cannot return nil" if component.nil? raise ArgumentError, "Method MarsoContext.#{component_type} must return an object of class Marso::Story" unless component.class.to_s == "Marso::#{class_name}" components << component end } return components end |
.openNewBrowser(browser = nil) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/marso/factories.rb', line 7 def openNewBrowser(browser=nil) browser = !browser.nil? && browser.class == Symbol ? browser : :firefox profile = nil case browser when :firefox profile = Selenium::WebDriver::Firefox::Profile.new else raise "Marso does not support '#{browser}' yet. The only supported browser is currently firefox" end profile['browser.cache.disk.enable'] = false b = Watir::Browser.new browser, :profile => profile return b end |
.reorganize_scenariocontexts_into_components(scenario_contexts, origin_stories, origin_features) ⇒ Object
This method reorganized a collection of scenario contexts so they are regrouped under their parent components(origin_stories or origin_features). The stories that results from that reorganization will also be regrouped under their respective features Arguments:
- scenario_contexts: Array of scenarios contexts that need to be
reorganized
- origin_stories: Array of stories OR single story, that are a known
parent of some of the scenario_contexts
- origin_features: Array of features OR single feature, that are a known
parent of some of the scenario_contexts
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/marso/helpers/componenthelper.rb', line 62 def self.reorganize_scenariocontexts_into_components(scenario_contexts, origin_stories, origin_features) original_features = origin_features.is_a?(Array) ? origin_features : [origin_features] original_stories = origin_stories.is_a?(Array) ? origin_stories : [origin_stories] updated_stories = [] scenario_contexts_per_feature_ids = [] features_updated_with_stories = [] updated_features = [] scenario_contexts .group_by { |x| x.story_id } .each { |k,v| if k != nil # scenarios which belong to stories story = original_stories.detect { |x| x.id == k } updated_story_description = story.description.clone updated_story_description[:scenario_contexts] = v updated_story_ctx = story.ctx.clone updated_story = Story.new(updated_story_description, updated_story_ctx) updated_stories << updated_story else # scenarios which do not belong to any stories, and therefore probably belong straight to a feature scenario_contexts_per_feature_ids = v.group_by { |y| y.feature_id } end } # Update original stories that seem to not have any scenario_contexts associated with them original_stories.each { |s| unless updated_stories.any? { |x| x.id == s.id } new_description = s.description.clone new_description[:status] = :failed_no_scenarios updated_stories << Marso::Story.new(new_description, s.ctx) end } # Regroup updated stories under their respective features updated_stories .group_by { |x| x.feature_id } .each { |k,v| if k != nil # stories which belong to features feat = original_features.detect { |x| x.id == k } updated_feat_description = feat.description.clone updated_feat_description[:stories] = v updated_feat_ctx = feat.ctx.clone updated_feat = Feature.new(updated_feat_description, updated_feat_ctx) features_updated_with_stories << updated_feat else # not sure what to do yet. Normally, scenario_contexts with no feature # or story associated with them should not happen end } # Add all the original features that do not contain any updated stories # to the list of updated features. This is needed so that the updated features # list contain all the orignial features so that we can use it for the next step original_features.each { |x| (features_updated_with_stories << x) unless features_updated_with_stories.any? { |y| y.id == x.id } } # Regroup scenario contexts that are not associated with any stories under # their respective feature updated_features = features_updated_with_stories.map { |x| if scenario_contexts_per_feature_ids.key?(x.id) # found scenarios for this feature new_description = x.description.clone new_description[:scenario_contexts] = scenario_contexts_per_feature_ids[x.id] new_ctx = x.ctx.clone Feature.new(new_description, new_ctx) else # no scenarios for this feature. Check if it has stories, otherwise flag it as failed if x.stories.any? x else new_description = x.description.clone new_description[:status] = :failed_no_component new_ctx = x.ctx.clone Feature.new(new_description, new_ctx) end end } return [updated_features, updated_stories] end |
.run_features(options = {}) ⇒ Object
options: Hash defined as follow
:rootpath => Path of the folder containing the 'stories' folder. If not
specified, then the default is to set it to the current
caller's location
:select => Single story's id or Array of story's ids. The execution
of stories will be restricted to those ids only
:include_mode => Symbol that defines what should be included in the
feature's description. Possible values are:
=> :none - (Default) Only display the feature's description
=> :with_stories - Display the feature description as well as all its
stories' description
=> :with_stories_scenarios - Display the feature description as well
as all its stories' description
(including their scenarios)
=> :with_scenarios - Display the feature description as well as all its
scenarios' description
=> :with_all - Display the feature description as well as both all its
stories(including their scenarios) and scenarios descriptions
45 46 47 48 49 50 51 52 |
# File 'lib/marso/launcher.rb', line 45 def self.run_features(={}) [:include_mode] = :with_all Marso.core_components_query(:feature, ) .select_many { |f| f.all_scenario_contexts } .select { |scn| scn.run } .execute end |
.run_features_async(options = {}) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/marso/launcher.rb', line 54 def self.run_features_async(={}) [:include_mode] = :with_all all_features = Marso.core_components_query(:feature, ).to_a _all_feat = Enumerate.from(all_features) all_stories = _all_feat.select_many { |f| f.stories }.to_a all_scenario_ctxs = _all_feat.select_many { |f| f.all_scenario_contexts }.to_a scenarios_per_features = Hash[all_features .reject { |f| f.scenario_contexts.empty? } .group_by { |f| f.id } .map { |f_id, f| [ f_id, # Hash key { # Hash value :scenarios => f[0].scenario_contexts.map { |scn| {:processed => false, :id => scn.id, :original_scn => scn}}, :original_feature => f[0], :processed => false } ]}] scenarios_per_stories = Hash[all_stories .reject { |s| s.scenario_contexts.empty? } .group_by { |s| s.id } .map { |s_id, s| [ s_id, # Hash key { # Hash value :scenarios => s[0].scenario_contexts.map { |scn| {:processed => false, :id => scn.id, :original_scn => scn}}, :original_story => s[0], :processed => false } ]}] stories_per_features = Hash[all_features .reject { |f| f.stories.empty? } .group_by { |f| f.id } .map { |f_id, f| [ f_id, # Hash key { # Hash value :stories => f[0].stories.map { |s| {:processed => false, :id => s.id }}, :processed => false } ]}] features_with_status = Hash[all_features.map { |f| [ f.id, # Hash key { # Hash value :original_feature => f, :processed => false } ]}] task_size = all_scenario_ctxs.size updated_scenario_ctxs = [] EM.run do all_scenario_ctxs.each { |scn| EM.defer( proc { scn.run }, lambda { |updated_scenario_ctx| updated_scenario_ctxs << updated_scenario_ctx task_size -= 1 EM.stop if task_size < 1 puts updated_scenario_ctx.indented_colorized_text scn_id = updated_scenario_ctx.id story_id = updated_scenario_ctx.story_id feat_id = updated_scenario_ctx.feature_id scenarios_per_story = scenarios_per_stories[story_id] scenarios_per_feat = scenarios_per_features[feat_id] stories_per_feat = stories_per_features[feat_id] feature_with_status = features_with_status[feat_id] story_completed = false feature_completed = false unless scenarios_per_story.nil? item = scenarios_per_story[:scenarios].detect { |scn| scn[:id] == scn_id } unless item.nil? item[:processed] = true item[:updated_scn] = updated_scenario_ctx story_completed = scenarios_per_story[:scenarios].all? { |scn| scn[:processed] } if story_completed # 1. Update the story scenarios_per_story[:processed] = true original_story = scenarios_per_story[:original_story] udt_story_desc = original_story.description.clone udt_story_ctx = original_story.ctx.clone udt_story_desc[:scenario_contexts] = scenarios_per_story[:scenarios].map { |scn| scn[:updated_scn]} updated_story = Marso::Story.new(udt_story_desc, udt_story_ctx) puts updated_story.indented_colorized_text scenarios_per_story[:updated_story] = updated_story unless stories_per_feat.nil? # 2. Cascade the new story status upon the feature # 2.1. Update the status of the story under the feature story_item = stories_per_feat[:stories].detect { |s| s[:id] == story_id } unless story_item.nil? story_item[:processed] = true # 2.2. Check all stories under that feature are noew completed all_stories_for_that_feature_completed = stories_per_feat[:stories].all? { |s| s[:processed] } if all_stories_for_that_feature_completed stories_per_feat[:processed] = true # 2.3. Check if that feature is now fully completed feature_completed = scenarios_per_feat.nil? || scenarios_per_feat[:processed] if feature_completed feature_with_status[:processed] = true original_feature = feature_with_status[:original_feature] udt_feat_desc = original_feature.description.clone udt_feat_ctx = original_feature.ctx.clone # 2.3.1. Update feature with all updated stories unless stories_per_feat.nil? feat_story_ids = stories_per_feat[:stories].map { |s| s[:id]} upt_stories = scenarios_per_stories .select { |s_id| feat_story_ids.include?(s_id) } .values.map { |x| x[:updated_story] } udt_feat_desc[:stories] = upt_stories end # 2.3.2. Update feature with all updated scenarios unless scenarios_per_feat.nil? udt_feat_desc[:scenario_contexts] = scenarios_per_feat[:scenarios].map { |scn| scn[:updated_scn]} end updated_feature = Marso::Feature.new(udt_feat_desc, udt_feat_ctx) puts updated_feature.indented_colorized_text feature_with_status[:updated_feature] = updated_feature end end end end end end end unless scenarios_per_feat.nil? item = scenarios_per_feat[:scenarios].detect { |scn| scn[:id] == scn_id } unless item.nil? item[:processed] = true item[:updated_scn] = updated_scenario_ctx all_scenarios_for_that_feature_completed = scenarios_per_feat[:scenarios].all? { |scn| scn[:processed] } if all_scenarios_for_that_feature_completed # 1. Update the feature scenarios_per_feat[:processed] = true # 2. Check if that feature is now fully completed feature_completed = stories_per_feat.nil? || stories_per_feat[:processed] if feature_completed feature_with_status[:processed] = true original_feature = feature_with_status[:original_feature] udt_feat_desc = original_feature.description.clone udt_feat_ctx = original_feature.ctx.clone # 2.2.1. Update feature with all updated stories unless stories_per_feat.nil? feat_story_ids = stories_per_feat[:stories].map { |s| s[:id]} upt_stories = scenarios_per_stories .select { |s_id| feat_story_ids.include?(s_id) } .values.map { |x| x[:updated_story] } udt_feat_desc[:stories] = upt_stories end # 2.2.2. Update feature with all updated scenarios unless scenarios_per_feat.nil? udt_feat_desc[:scenario_contexts] = scenarios_per_feat[:scenarios].map { |scn| scn[:updated_scn]} end updated_feature = Marso::Feature.new(udt_feat_desc, udt_feat_ctx) puts updated_feature.indented_colorized_text feature_with_status[:updated_feature] = updated_feature end end end end }) } end end |
.show_features_text(options = {}) ⇒ Object
options: Hash defined as follow
:rootpath => Path of the folder containing the 'stories' folder. If not
specified, then the default is to set it to the current
caller's location
:select => Single story's id or Array of story's ids. The execution
of stories will be restricted to those ids only
:include_mode => Symbol that defines what should be included in the
feature's description. Possible values are:
=> :none - (Default) Only display the feature's description
=> :with_stories - Display the feature description as well as all its
stories' description
=> :with_stories_scenarios - Display the feature description as well
as all its stories' description
(including their scenarios)
=> :with_scenarios - Display the feature description as well as all its
scenarios' description
=> :with_all - Display the feature description as well as both all its
stories(including their scenarios) and scenarios descriptions
244 245 246 247 248 |
# File 'lib/marso/launcher.rb', line 244 def self.show_features_text(={}) Marso.core_components_query(:feature, ) .select { |f| puts f.indented_colorized_details(.include_mode) } .execute end |
.show_scenarios_text(options = {}) ⇒ Object
options: Hash defined as follow
:rootpath => Path of the folder containing the 'stories' folder. If not
specified, then the default is to set it to the current
caller's location
:select => Single story's id or Array of story's ids. The execution
of stories will be restricted to those ids only
273 274 275 276 277 |
# File 'lib/marso/launcher.rb', line 273 def self.show_scenarios_text(={}) Marso.core_components_query(:scenario_context, ) .select { |s| puts s.indented_colorized_text } .execute end |
.show_stories_text(options = {}) ⇒ Object
options: Hash defined as follow
:rootpath => Path of the folder containing the 'stories' folder. If not
specified, then the default is to set it to the current
caller's location
:select => Single story's id or Array of story's ids. The execution
of stories will be restricted to those ids only
:include_mode => Symbol that defines what should be included in the
feature's description. Possible values are:
=> :none - (Default) Only display the feature's description
=> :with_scenarios - Display the feature description as well as all its
scenarios' description
261 262 263 264 265 |
# File 'lib/marso/launcher.rb', line 261 def self.show_stories_text(={}) Marso.core_components_query(:story, ) .select { |s| puts s.indented_colorized_details(.include_mode) } .execute end |