From 1ed40df30326dbaa27ec604f46a50c6ffad360de Mon Sep 17 00:00:00 2001 From: Matthew Stanton Date: Wed, 3 Jun 2026 13:03:49 -0400 Subject: [PATCH] Disable color for file formatter output Signed-off-by: Matthew Stanton --- CHANGELOG.md | 1 + lib/cucumber/cli/options.rb | 2 +- lib/cucumber/formatter/console.rb | 69 +++++++++++++++++------- lib/cucumber/formatter/console_counts.rb | 1 + spec/cucumber/formatter/console_spec.rb | 48 +++++++++++++++++ 5 files changed, 102 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5dc1464..19ca2ad1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo - Show failed step error details in the summary formatter output - Fixed up JRuby examples which weren't running due to anglicisation issues (Pivoted to use English step definitions to help JRuby testing) - Fixed up Arabic example which had some incorrect logic for step definition matching (Due to RTL nature of the language) +- Disable ANSI color by default for console formatter output written with `--out`. ## [11.1.0] - 2026-06-02 ### Added diff --git a/lib/cucumber/cli/options.rb b/lib/cucumber/cli/options.rb index ff278a186..a2edb0df4 100644 --- a/lib/cucumber/cli/options.rb +++ b/lib/cucumber/cli/options.rb @@ -435,7 +435,7 @@ def add_tag_limit(tag_limits, tag_name, limit) end def color(color) - Cucumber::Term::ANSIColor.coloring = color + Cucumber::Term::ANSIColor.coloring = (@options[:color] = color) end def initialize_project diff --git a/lib/cucumber/formatter/console.rb b/lib/cucumber/formatter/console.rb index 1c6f5fb53..56ce1148d 100644 --- a/lib/cucumber/formatter/console.rb +++ b/lib/cucumber/formatter/console.rb @@ -31,27 +31,31 @@ module Console include Duration def format_step(keyword, step_match, status, source_indent) - comment = if source_indent - c = indent("# #{step_match.location}", source_indent) - format_string(c, :comment) - else - '' - end - - format = format_for(status, :param) - line = keyword + step_match.format_args(format) + comment - format_string(line, status) + with_formatter_coloring do + comment = if source_indent + c = indent("# #{step_match.location}", source_indent) + format_string(c, :comment) + else + '' + end + + format = format_for(status, :param) + line = keyword + step_match.format_args(format) + comment + format_string(line, status) + end end def format_string(input, status) - fmt = format_for(status) - input.to_s.split("\n").map do |line| - if fmt.instance_of?(Proc) - fmt.call(line) - else - fmt % line - end - end.join("\n") + with_formatter_coloring do + fmt = format_for(status) + input.to_s.split("\n").map do |line| + if fmt.instance_of?(Proc) + fmt.call(line) + else + fmt % line + end + end.join("\n") + end end def print_elements(elements, status, kind) @@ -229,6 +233,35 @@ def indent(string, padding) FORMATS = Hash.new { |hash, format| hash[format] = method(format).to_proc } + def with_formatter_coloring + original_coloring = Cucumber::Term::ANSIColor.coloring? + Cucumber::Term::ANSIColor.coloring = formatter_coloring? + yield + ensure + Cucumber::Term::ANSIColor.coloring = original_coloring + end + + def formatter_coloring? + options = if instance_variable_defined?(:@options) && @options + @options + elsif instance_variable_defined?(:@config) && @config.respond_to?(:to_hash) + @config.to_hash + else + {} + end + return options[:color] if options.key?(:color) + + io = if instance_variable_defined?(:@io) + @io + elsif instance_variable_defined?(:@config) && @config.respond_to?(:out_stream) + @config.out_stream + end + return Cucumber::Term::ANSIColor.coloring? if io.nil? + return false unless io.respond_to?(:tty?) + + io.tty? + end + def format_for(*keys) key = keys.join('_').to_sym fmt = FORMATS[key] diff --git a/lib/cucumber/formatter/console_counts.rb b/lib/cucumber/formatter/console_counts.rb index 8a7b37dea..db16cd810 100644 --- a/lib/cucumber/formatter/console_counts.rb +++ b/lib/cucumber/formatter/console_counts.rb @@ -8,6 +8,7 @@ class ConsoleCounts include Console def initialize(config) + @config = config @summary = Core::Report::Summary.new(config.event_bus) end diff --git a/spec/cucumber/formatter/console_spec.rb b/spec/cucumber/formatter/console_spec.rb index 5b7e65487..d2968ce70 100644 --- a/spec/cucumber/formatter/console_spec.rb +++ b/spec/cucumber/formatter/console_spec.rb @@ -2,6 +2,7 @@ require 'cucumber/configuration' require 'cucumber/formatter/console' +require 'cucumber/step_match' module Cucumber module Formatter @@ -21,6 +22,53 @@ module Formatter res = indent(' a line', -10) expect(res).to eq 'a line' end + + context 'when coloring console output' do + around do |example| + original_coloring = Cucumber::Term::ANSIColor.coloring? + Cucumber::Term::ANSIColor.coloring = true + example.run + ensure + Cucumber::Term::ANSIColor.coloring = original_coloring + end + + it 'uses color by default when the formatter output is a tty' do + @io = instance_double(IO, tty?: true) + @options = {} + + expect(format_string('undefined', :undefined)).to include("\e[33m") + end + + it 'disables color by default when the formatter output is a file' do + @io = instance_double(File, tty?: false) + @options = {} + + expect(format_string('undefined', :undefined)).to eq 'undefined' + end + + it 'allows explicit color to override the formatter output destination' do + @io = instance_double(File, tty?: false) + @options = { color: true } + + expect(format_string('undefined', :undefined)).to include("\e[33m") + end + + it 'allows explicit no-color to override a tty output destination' do + @io = instance_double(IO, tty?: true) + @options = { color: false } + + expect(format_string('undefined', :undefined)).to eq 'undefined' + end + + it 'disables step argument color by default when the formatter output is a file' do + @io = instance_double(File, tty?: false) + @options = {} + step_match = instance_double(Cucumber::StepMatch) + allow(step_match).to receive(:format_args) { |format| "I have #{format.call('3')} cukes" } + + expect(format_step('Given ', step_match, :passed, nil)).to eq 'Given I have 3 cukes' + end + end end end end