def multiline_indentation(self): if self._multiline_indentation is None: offset = 0 if self.show_aligned_keywords: offset = 2 indentation = make_indentation(3 * self.indent_size + offset) self._multiline_indentation = indentation if self.current_rule: indent_extra = make_indentation(self.indent_size) return self._multiline_indentation + indent_extra return self._multiline_indentation
def result(self, result): """ Process the result of a step (after step execution). :param result: """ step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = '%s%6s %s ... ' % (indent, step.keyword, step.name) else: text = '%s%s %s ... ' % (indent, step.keyword, step.name) self.stream.write(text) status = result.status if self.show_timings: status += " in %0.3fs" % step.duration if result.error_message: self.stream.write('%s\n%s\n' % (status, result.error_message)) else: self.stream.write('%s\n' % status) if self.show_multiline: if step.text: self.doc_string(step.text) if step.table: self.table(step.table)
def result(self, result): """ Process the result of a step (after step execution). :param result: """ step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = u"%s%6s %s ... " % (indent, step.keyword, step.name) else: text = u"%s%s %s ... " % (indent, step.keyword, step.name) self.stream.write(text) status_text = result.status.name if self.show_timings: status_text = "started at %s, %s in %0.3fs," % ( datetime.fromtimestamp(step.start).strftime("%H:%M:%S.%f"), status_text, step.duration) if result.error_message: self.stream.write(u"%s\n%s\n" % (status_text, result.error_message)) else: self.stream.write(u"%s\n" % status_text) if self.show_multiline: if step.text: self.doc_string(step.text) if step.table: self.table(step.table)
def result(self, result): """ Process the result of a step (after step execution). :param result: """ step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = u'%s%6s %s ... ' % (indent, step.keyword, step.name) else: text = u'%s%s %s ... ' % (indent, step.keyword, step.name) self.stream.write(text) status = result.status if self.show_timings: status += " in %0.3fs" % step.duration if result.error_message: self.stream.write(u'%s\n%s\n' % (status, result.error_message)) else: self.stream.write(u'%s\n' % status) if self.show_multiline: if step.text: self.doc_string(step.text) if step.table: self.table(step.table)
def scenario(self, scenario): self.reset_steps() self.stream.write(u"\n") indent = make_indentation(self.indent_size) text = u"%s%s: %s\n" % (indent, scenario.keyword, scenario.name) self.write_tags(scenario.tags, indent) self.stream.write(text)
def multiline_indentation(self): if self._multiline_indentation is None: offset = 0 if self.show_aligned_keywords: offset = 2 indentation = make_indentation(3 * self.indent_size + offset) self._multiline_indentation = indentation return self._multiline_indentation
class StepsCatalogFormatter(StepsDocFormatter): """ Provides formatter class that shows the documentation of all registered step definitions. The primary purpose is to provide help for a test writer. In order to ease work for non-programmer testers, the technical details of the steps (i.e. function name, source location) are ommited and the steps are shown as they would apprear in a feature file (no noisy '@', or '(', etc.). Also, the output is sorted by step type (Given, When, Then) Generic step definitions are listed with all three step types. EXAMPLE: $ behave --dry-run -f steps.catalog features/ Given a file named "{filename}" with Creates a textual file with the content provided as docstring. When I run "{command}" Run a command as subprocess, collect its output and returncode. Given a file named "{filename}" exists When a file named "{filename}" exists Then a file named "{filename}" exists Verifies that a file with this filename exists. .. code-block:: gherkin Given a file named "abc.txt" exists When a file named "abc.txt" exists ... .. note:: Supports behave dry-run mode. """ name = "steps.catalog" description = "Shows non-technical documentation for step definitions." shows_location = False shows_function_name = False ordered_by_location = False doc_prefix = make_indentation(4) def describe_step_definition(self, step_definition, step_type=None): if not step_type: step_type = step_definition.step_type assert step_type desc = [] if step_type == "step": for step_type1 in self.step_types[:-1]: text = u"%5s %s" % (step_type1.title(), step_definition.pattern) desc.append(text) else: desc.append(u"%s %s" % (step_type.title(), step_definition.pattern)) return '\n'.join(desc)
def scenario(self, scenario): indent_extra = 0 if self.current_rule: indent_extra = self.indent_size self.reset_steps() self.stream.write("\n") indent = make_indentation(self.indent_size + indent_extra) self.write_entity(scenario, indent)
def scenario(self, scenario): indent_extra = 0 if self.current_rule: indent_extra = self.indent_size self.reset_steps() self.stream.write(u"\n") indent = make_indentation(self.indent_size + indent_extra) self.write_entity(scenario, indent)
def background(self, background): self.reset_steps() if not self.SHOW_BACKGROUNDS: return indent_extra = 0 if self.current_rule: indent_extra = self.indent_size indent = make_indentation(self.indent_size + indent_extra) self.write_entity(background, indent, has_tags=False)
def result(self, step): """ Process the result of a step (after step execution). :param step: Step object with result to process. """ step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = u"%s%6s %s" % (indent, step.keyword, step.name) else: text = u"%s%s %s" % (indent, step.keyword, step.name) text = escapes[step.status.name] + textwrap.shorten( text, width=self.LINE_WIDTH - 30) + escapes['reset'] + ' ' self.stream.write(text) status_text = ': ' status_text += { 'passed': u'\u2713', 'failed': u'\u26A0' }[step.status.name] if self.show_timings: status_text += " in %0.3fs" % step.duration unicode_errors = 0 if step.error_message: try: self.stream.write(u"%s\n%s\n" % (status_text, step.error_message)) except UnicodeError as e: unicode_errors += 1 self.stream.write(u"%s\n" % status_text) self.stream.write(u"%s while writing error message: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise else: self.stream.write(u"%s\n" % status_text) if self.show_multiline: if step.text: try: self.doc_string(step.text) except UnicodeError as e: unicode_errors += 1 self.stream.write(u"%s while writing docstring: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise if step.table: self.table(step.table)
def feature(self, feature): super(PlainColorFormatter, self).feature(feature) if feature.description: self.stream.write('\n') for line in feature.description: self.stream.write(make_indentation(self.indent_size)) self.stream.write(line) self.stream.write('\n') self.stream.write('\n')
def describe_step(cls, step): status = str(step.status) if cls.show_timings: status += u" in %0.3fs" % step.duration text = u'%s %s ... ' % (step.keyword, step.name) text += u'%s\n' % status if cls.show_multiline: prefix = make_indentation(2) if step.text: text += ModelDescriptor.describe_docstring(step.text, prefix) elif step.table: text += ModelDescriptor.describe_table(step.table, prefix) return text
def describe_step(self, step): status_text = _text(step.status.name) if self.show_timings: status_text += " in %0.3fs" % step.duration text = '%s %s ... ' % (step.keyword, step.name) text += '%s\n' % status_text if self.show_multiline: prefix = make_indentation(2) if step.text: text += ModelDescriptor.describe_docstring(step.text, prefix) elif step.table: text += ModelDescriptor.describe_table(step.table, prefix) return text
def print_step(self, step, executed=True): indent = make_indentation(2 * self.indent_size) plain_text = self.step_format % (indent, step.keyword, step.name) # pretty formater prints not executed steps as skipped status = step.status if executed else Status.skipped status_text = status.name self.stream.write(escapes[status_text] + plain_text + escapes['reset']) if self.show_timings: if executed: status_text += " in %0.3fs" % step.duration else: status_text += " in -.---s" # creates nice indentation between end of step description and status whitespace_width = 80 whitespace_width -= len(plain_text) whitespace_width -= len(status_text) whitespace_width -= len('... ') self.stream.write(u" " * max(1, whitespace_width)) self.stream.write(u"... ") self.stream.write(status_text) self.stream.write('\n') if step.error_message: for line in step.error_message.split('\n'): self.stream.write(make_indentation(self.indent_size * 4)) self.stream.write(line) self.stream.write('\n') if self.show_multiline: if step.text: self.doc_string(step.text) if step.table: self.table(step.table)
def result(self, step): """Process the result of a step (after step execution). :param step: Step object with result to process. """ indent_extra = 0 if self.current_rule: indent_extra = self.indent_size step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size + indent_extra) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = "%s%6s %s ... " % (indent, step.keyword, step.name) else: text = "%s%s %s ... " % (indent, step.keyword, step.name) self.stream.write(text) status_text = step.status.name if self.show_timings: status_text += " in %0.3fs" % step.duration unicode_errors = 0 if step.error_message: try: self.stream.write("%s\n%s\n" % (status_text, step.error_message)) except UnicodeError as e: unicode_errors += 1 self.stream.write("%s\n" % status_text) self.stream.write("%s while writing error message: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise else: self.stream.write("%s\n" % status_text) if self.show_multiline: if step.text: try: self.doc_string(step.text) except UnicodeError as e: unicode_errors += 1 self.stream.write("%s while writing docstring: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise if step.table: self.table(step.table)
def result(self, step): """Process the result of a step (after step execution). :param step: Step object with result to process. """ indent_extra = 0 if self.current_rule: indent_extra = self.indent_size step = self.steps.pop(0) indent = make_indentation(2 * self.indent_size + indent_extra) if self.show_aligned_keywords: # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): text = u"%s%6s %s ... " % (indent, step.keyword, step.name) else: text = u"%s%s %s ... " % (indent, step.keyword, step.name) self.stream.write(text) status_text = step.status.name if self.show_timings: status_text += " in %0.3fs" % step.duration unicode_errors = 0 if step.error_message: try: self.stream.write(u"%s\n%s\n" % (status_text, step.error_message)) except UnicodeError as e: unicode_errors += 1 self.stream.write(u"%s\n" % status_text) self.stream.write(u"%s while writing error message: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise else: self.stream.write(u"%s\n" % status_text) if self.show_multiline: if step.text: try: self.doc_string(step.text) except UnicodeError as e: unicode_errors += 1 self.stream.write(u"%s while writing docstring: %s\n" % \ (e.__class__.__name__, e)) if self.RAISE_OUTPUT_ERRORS: raise if step.table: self.table(step.table)
def describe_scenario(cls, scenario): """ Describe the scenario and the test status. NOTE: table, multiline text is missing in description. :param scenario: Scenario that was tested. :return: Textual description of the scenario. """ header_line = u'\[email protected]\n' header_line += ' %s: %s\n' % (scenario.keyword, scenario.name) footer_line = u'\[email protected]\n' + u'-' * 80 + '\n' text = u'' for step in scenario: text += cls.describe_step(step) step_indentation = make_indentation(4) return header_line + indent(text, step_indentation) + footer_line
def describe_scenario(self, scenario): """Describe the scenario and the test status. NOTE: table, multiline text is missing in description. :param scenario: Scenario that was tested. :return: Textual description of the scenario. """ header_line = '\[email protected]\n' if self.show_tags and scenario.tags: header_line += '\n %s\n' % self.describe_tags(scenario.tags) header_line += ' %s: %s\n' % (scenario.keyword, scenario.name) footer_line = '\[email protected]\n' + '-' * 80 + '\n' text = '' for step in scenario: text += self.describe_step(step) step_indentation = make_indentation(4) return header_line + indent(text, step_indentation) + footer_line
def describe_scenario(cls, scenario): """ Describe the scenario and the test status. NOTE: table, multiline text is missing in description. :param scenario: Scenario that was tested. :return: Textual description of the scenario. """ header_line = u"\[email protected]\n" if cls.show_tags and scenario.tags: header_line += u"\n %s\n" % cls.describe_tags(scenario.tags) header_line += u" %s: %s\n" % (scenario.keyword, scenario.name) footer_line = u"\[email protected]\n" + u"-" * 80 + "\n" text = u"" for step in scenario: text += cls.describe_step(step) step_indentation = make_indentation(4) return header_line + indent(text, step_indentation) + footer_line
def print_statement_head(self, statement, steps=None): self.writer.write(u"\n") #self.print_comments(self.statement.comments, ' ') indentation = self.indentations[self.INDENT_SCENARIO] if hasattr(statement, 'tags'): self.print_tags(statement.tags, indentation) text = u"%s%s: %s " % (indentation, statement.keyword, statement.name) self.writer.write(text, style="scenario") if self.show_location: steps = steps or [] self.calculate_location_indentations(statement, steps) if self.show_source: assert self.show_location indent_size = self.location_indentations.pop(0) location = six.text_type(statement.location) text = self.location_text(location, indent_size) self.writer.write(text, style="comments") self.writer.write("\n") description = getattr(statement, "description", None) if self.show_scenario_description and description: indentation = make_indentation(self.indent_size * 2) self.print_description(description, indentation)
def print_statement_head(self, statement, steps=None): self.writer.write(u"\n") #self.print_comments(self.statement.comments, ' ') indentation = self.indentations[self.INDENT_SCENARIO] if hasattr(statement, 'tags'): self.print_tags(statement.tags, indentation) text = u"%s%s: %s " % (indentation, statement.keyword, statement.name) self.writer.write(text, style="scenario") if self.show_location: steps = steps or [] self.calculate_location_indentations(statement, steps) if self.show_source: assert self.show_location indent_size = self.location_indentations.pop(0) location = six.text_type(statement.location) text = self.location_text(location, indent_size) self.writer.write(text, style="comments") self.writer.write("\n") description = getattr(statement, "description", None) if self.show_scenario_description and description: indentation = make_indentation(self.indent_size*2) self.print_description(description, indentation)
def location_text(self, text, indent_size=0): if not text: return u'' indentation = make_indentation(indent_size) return u'%s # %s' % (indentation, text)
def build_indentations(self, levels): return [ make_indentation(level * self.indent_size) for level in levels]
def scenario_outline(self, outline): self.reset_steps() indent = make_indentation(self.indent_size) text = u'%s%s: %s\n' % (indent, outline.keyword, outline.name) self.stream.write(text)
def rule(self, rule): self.current_rule = rule self.reset_steps() indent = make_indentation(self.indent_size) self.stream.write(u"\n") self.write_entity(rule, indent)
class StepsDocFormatter(AbstractStepsFormatter): """ Provides formatter class that shows the documentation of all registered step definitions. The primary purpose is to provide help for a test writer. EXAMPLE: $ behave --dry-run -f steps.doc features/ @given('a file named "{filename}" with') Function: step_a_file_named_filename_with() Location: behave4cmd0/command_steps.py:50 Creates a textual file with the content provided as docstring. @when('I run "{command}"') Function: step_i_run_command() Location: behave4cmd0/command_steps.py:80 Run a command as subprocess, collect its output and returncode. @step('a file named "{filename}" exists') Function: step_file_named_filename_exists() Location: behave4cmd0/command_steps.py:305 Verifies that a file with this filename exists. .. code-block:: gherkin Given a file named "abc.txt" exists When a file named "abc.txt" exists ... .. note:: Supports behave dry-run mode. """ name = "steps.doc" description = "Shows documentation for step definitions." shows_location = True shows_function_name = True ordered_by_location = True doc_prefix = make_indentation(4) # -- REPORT SPECIFIC-API: def report(self): self.report_step_definition_docs() self.stream.write("\n") def report_step_definition_docs(self): step_definitions = [] for step_type in self.step_types: for step_definition in self.step_registry.steps[step_type]: # step_definition.step_type = step_type assert step_definition.step_type is not None step_definitions.append(step_definition) if self.ordered_by_location: compare = lambda x, y: cmp(x.location, y.location) step_definitions = sorted(step_definitions, compare) for step_definition in step_definitions: self.write_step_definition(step_definition) def write_step_definition(self, step_definition): step_definition_text = self.describe_step_definition(step_definition) self.stream.write(u"%s\n" % step_definition_text) doc = inspect.getdoc(step_definition.func) func_name = step_definition.func.__name__ if self.shows_function_name and func_name not in ("step", "impl"): self.stream.write(u" Function: %s()\n" % func_name) if self.shows_location: self.stream.write(u" Location: %s\n" % step_definition.location) if doc: doc = doc.strip() self.stream.write(indent(doc, self.doc_prefix)) self.stream.write("\n") self.stream.write("\n")
def scenario_outline(self, outline): self.reset_steps() indent = make_indentation(self.indent_size) text = '%s%s: %s\n' % (indent, outline.keyword, outline.name) self.stream.write(text)
def scenario(self, scenario): self.reset_steps() self.stream.write('\n') indent = make_indentation(self.indent_size) text = '%s%s: %s\n' % (indent, scenario.keyword, scenario.name) self.stream.write(text)
def background(self, background): self.reset_steps() indent = make_indentation(self.indent_size) text = '%s%s: %s\n' % (indent, background.keyword, background.name) self.stream.write(text)
class StepsUsageFormatter(AbstractStepsFormatter): """ Provides formatter class that shows how step definitions are used by steps. EXAMPLE: $ behave --dry-run -f steps.usage features/ ... .. note:: Supports behave dry-run mode. """ name = "steps.usage" description = "Shows how step definitions are used by steps." doc_prefix = make_indentation(4) min_location_column = 40 def __init__(self, stream_opener, config): super(StepsUsageFormatter, self).__init__(stream_opener, config) self.step_usage_database = {} self.undefined_steps = [] def reset(self): super(StepsUsageFormatter, self).reset() self.step_usage_database = {} self.undefined_steps = [] def get_step_type_for_step_definition(self, step_definition): step_type = step_definition.step_type if not step_type: # -- DETERMINE STEP-TYPE FROM STEP-REGISTRY: assert self.step_registry for step_type, values in self.step_registry.steps.items(): if step_definition in values: return step_type # -- OTHERWISE: step_type = "step" return step_type def select_unused_step_definitions(self): step_definitions = set() for step_type, values in self.step_registry.steps.items(): step_definitions.update(values) used_step_definitions = set(self.step_usage_database.keys()) unused_step_definitions = step_definitions - used_step_definitions return unused_step_definitions def update_usage_database(self, step_definition, step): matching_steps = self.step_usage_database.get(step_definition, None) if matching_steps is None: assert step_definition.step_type is not None matching_steps = self.step_usage_database[step_definition] = [] # -- AVOID DUPLICATES: From Scenario Outlines if not steps_contain(matching_steps, step): matching_steps.append(step) def update_usage_database_for_step(self, step): step_definition = self.step_registry.find_step_definition(step) if step_definition: self.update_usage_database(step_definition, step) # elif step not in self.undefined_steps: elif not steps_contain(self.undefined_steps, step): # -- AVOID DUPLICATES: From Scenario Outlines self.undefined_steps.append(step) def update_usage_database_for_feature(self, feature): # -- PROCESS BACKGROUND (if exists): Use Background steps only once. if feature.background: for step in feature.background.steps: self.update_usage_database_for_step(step) # -- PROCESS SCENARIOS: Without background steps. for scenario in feature.walk_scenarios(): for step in scenario.steps: self.update_usage_database_for_step(step) # -- FORMATTER API: def feature(self, feature): super(StepsUsageFormatter, self).feature(feature) self.update_usage_database_for_feature(feature) # -- REPORT API: def report(self): self.report_used_step_definitions() self.report_unused_step_definitions() self.report_undefined_steps() self.stream.write("\n") # -- REPORT SPECIFIC-API: def report_used_step_definitions(self): # -- STEP: Used step definitions. # ORDERING: Sort step definitions by file location. compare = lambda x, y: cmp(x[0].location, y[0].location) step_definition_items = self.step_usage_database.items() step_definition_items = sorted(step_definition_items, compare) for step_definition, steps in step_definition_items: stepdef_text = self.describe_step_definition(step_definition) steps_text = [ u" %s %s" % (step.keyword, step.name) for step in steps ] steps_text.append(stepdef_text) max_size = compute_words_maxsize(steps_text) if max_size < self.min_location_column: max_size = self.min_location_column schema = u"%-" + str(max_size) + "s # %s\n" self.stream.write(schema % (stepdef_text, step_definition.location)) schema = u"%-" + str(max_size) + "s # %s\n" for step, step_text in zip(steps, steps_text): self.stream.write(schema % (step_text, step.location)) self.stream.write("\n") def report_unused_step_definitions(self): unused_step_definitions = self.select_unused_step_definitions() if not unused_step_definitions: return # -- STEP: Prepare report for unused step definitions. # ORDERING: Sort step definitions by file location. compare = lambda x, y: cmp(x.location, y.location) step_definitions = sorted(unused_step_definitions, compare) step_texts = [ self.describe_step_definition(step_definition) for step_definition in step_definitions ] max_size = compute_words_maxsize(step_texts) if max_size < self.min_location_column - 2: max_size = self.min_location_column - 2 # -- STEP: Write report. schema = u" %-" + str(max_size) + "s # %s\n" self.stream.write("UNUSED STEP DEFINITIONS[%d]:\n" % len(step_texts)) for step_definition, text in zip(step_definitions, step_texts): self.stream.write(schema % (text, step_definition.location)) def report_undefined_steps(self): if not self.undefined_steps: return # -- STEP: Undefined steps. compare = lambda x, y: cmp(x.location, y.location) undefined_steps = sorted(self.undefined_steps, compare) steps_text = [ u" %s %s" % (step.keyword, step.name) for step in undefined_steps ] max_size = compute_words_maxsize(steps_text) if max_size < self.min_location_column: max_size = self.min_location_column self.stream.write("\nUNDEFINED STEPS[%d]:\n" % len(steps_text)) schema = u"%-" + str(max_size) + "s # %s\n" for step, step_text in zip(undefined_steps, steps_text): self.stream.write(schema % (step_text, step.location))
def build_indentations(self, levels): return [make_indentation(level * self.indent_size) for level in levels]
def scenario(self, scenario): self.reset_steps() self.stream.write(u'\n') indent = make_indentation(self.indent_size) text = u'%s%s: %s\n' % (indent, scenario.keyword, scenario.name) self.stream.write(text)
def rule(self, rule): self.current_rule = rule self.reset_steps() indent = make_indentation(self.indent_size) self.stream.write("\n") self.write_entity(rule, indent)
def background(self, background): self.reset_steps() indent = make_indentation(self.indent_size) text = u"%s%s: %s\n" % (indent, background.keyword, background.name) self.stream.write(text)