class FlakeySpecCatcher::ChangeContext

ChangeContext class

Identifies the conditions in which a code change occurs.

At a high level, a ChangeContext captures (A) the line number, and (B) the block of code in which a change exists. This identifies both the code change's context and its parent context.

FSC uses parent contexts to eliminate re-running tests too many times. For example, if changes are detected in both a 'describe' block and a child test case inside of that 'describe' block, FSC will re-run the contents of the 'describe' block only.

Attributes

ancestor_contexts[R]
description[R]
file_name[R]
line_number[R]
tags[R]

Public Class Methods

new(description:, line_number:, file_name:, ancestor_contexts: [], tags: {}) click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 20
def initialize(description:, line_number:, file_name:, ancestor_contexts: [], tags: {})
  @description = description
  @line_number = line_number
  @file_name = file_name
  @ancestor_contexts = ancestor_contexts
  @tags = tags
  update_descriptions
  identify_tags_and_values
end

Public Instance Methods

==(other) click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 67
def ==(other)
  @description == other.description &&
    @line_number == other.line_number &&
    @file_name == other.file_name &&
    @ancestor_contexts == other.ancestor_contexts
end
ancestor_present_in_reruns(reruns) click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 82
def ancestor_present_in_reruns(reruns)
  reruns.each do |rerun|
    return true if @ancestor_contexts.map(&:rerun_info).include? rerun
  end
  false
end
example_group?() click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 37
def example_group?
  # If the description starts with 'it' or 'scenario' then the test
  #  can provide assertions/expectations and is not an example group
  if @description =~ /^\s*(it|scenario)\s+/
    false
  else
    true
  end
end
identify_tags_and_values() click to toggle source

Get tag strings which optionally may continue tags with their values

and return a hash
# File lib/flakey_spec_catcher/change_context.rb, line 49
def identify_tags_and_values
  tags_with_values = {}
  tag_strings = identify_tag_strings
  return if tag_strings.nil? || tag_strings.empty?

  # Since tags can have a value represented as strings or symbols
  #  we'll only remove the hash rockets and not colons
  #  Example: ":tag => 'special'" => tags_with_values[:tag] = 'special'
  #  Example: ":tag => :special" => tags_with_values[:tag] = :special

  tag_strings.each do |str|
    tag_and_value = str.sub(/=>/, ' ').split(' ')
    tags_with_values[tag_and_value[0]] = tag_and_value[1]
  end

  @tags = tags_with_values
end
rerun_info() click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 74
def rerun_info
  if @line_number.nil?
    @file_name
  else
    "#{@file_name}:#{@line_number}"
  end
end
update_descriptions() click to toggle source
# File lib/flakey_spec_catcher/change_context.rb, line 30
def update_descriptions
  return unless @description.nil? || @description.empty?

  @description = @file_name
  @line_number = nil
end

Private Instance Methods

identify_tag_strings() click to toggle source

Since RSpec doesn't allow for tags to be excluded for runs when explicitly

specifying an example group via `rspec test:line_number`, we have to
do the filtering ourselves which requires us to also identify tags

Assume here that tags are comma delimited following the initial description

# File lib/flakey_spec_catcher/change_context.rb, line 95
def identify_tag_strings
  return if @description.empty?

  tags = @description.split(',')
  return if tags.count == 1

  # Remove the description
  tags.shift
  # Remove the trailing ' do' from the last tag
  tags.last.sub!(/ do$/, '')

  # Remove trailing and leading whitespace
  tags.map!(&:strip)

  # Grab tags which are represented as symbols
  tags.select! { |tag| tag.start_with? ':' }
  tags
end