├── .rspec ├── Rakefile ├── lib ├── rspec_tap │ ├── version.rb │ └── formatter.rb └── rspec_tap.rb ├── Gemfile ├── .gitignore ├── .travis.yml ├── bin ├── setup └── console ├── spec ├── spec_helper.rb └── lib │ └── formatter_spec.rb ├── rspec_tap.gemspec ├── LICENSE └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | task :default => :spec 3 | -------------------------------------------------------------------------------- /lib/rspec_tap/version.rb: -------------------------------------------------------------------------------- 1 | module RspecTap 2 | VERSION = "0.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in rspec_tap.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/rspec_tap.rb: -------------------------------------------------------------------------------- 1 | require "rspec_tap/version" 2 | require "rspec_tap/formatter" 3 | 4 | module RspecTap 5 | end 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.3.1 4 | before_install: gem install bundler -v 1.12.5 5 | script: bundle exec rspec -f RspecTap::Formatter 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "rspec_tap" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.expect_with :rspec do |expectations| 3 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 4 | end 5 | 6 | config.mock_with :rspec do |mocks| 7 | mocks.verify_partial_doubles = true 8 | end 9 | 10 | config.shared_context_metadata_behavior = :apply_to_host_groups 11 | 12 | config.filter_run_when_matching :focus 13 | config.disable_monkey_patching! 14 | config.order = :random 15 | Kernel.srand config.seed 16 | end 17 | -------------------------------------------------------------------------------- /rspec_tap.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'rspec_tap/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "rspec_tap" 8 | spec.version = RspecTap::VERSION 9 | spec.authors = ["Andrew Appleton"] 10 | spec.email = ["andysapple@gmail.com"] 11 | 12 | spec.summary = %q{A TAP formatter for rspec} 13 | 14 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 15 | spec.bindir = "exe" 16 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 17 | spec.require_paths = ["lib"] 18 | 19 | spec.add_development_dependency "bundler", "~> 2.1" 20 | spec.add_development_dependency "rake", ">= 12.3.3" 21 | spec.add_development_dependency "rspec", "~> 3.4" 22 | spec.add_development_dependency "pry" 23 | end 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andrew Appleton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RSpec TAP 2 | 3 | [![Build Status](https://travis-ci.org/appleton/rspec_tap.svg?branch=master)](https://travis-ci.org/appleton/rspec_tap) 4 | 5 | A [TAP](https://testanything.org/) formatter for rspec. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'rspec_tap' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install rspec_tap 22 | 23 | ## Usage 24 | 25 | ```bash 26 | bundle exec rspec -f RspecTap::Formatter 27 | ``` 28 | 29 | ## Development 30 | 31 | After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 32 | 33 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 34 | 35 | ## Contributing 36 | 37 | Bug reports and pull requests are welcome on GitHub at https://github.com/appleton/rspec_tap. 38 | -------------------------------------------------------------------------------- /lib/rspec_tap/formatter.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "rspec/core/formatters/base_text_formatter" 3 | 4 | module RspecTap 5 | class Formatter < RSpec::Core::Formatters::BaseTextFormatter 6 | RSpec::Core::Formatters.register self, 7 | :start, 8 | :example_group_started, 9 | :example_started, 10 | :example_passed, 11 | :example_failed 12 | 13 | def initialize(stdout) 14 | @progress_count = 0 15 | super(stdout) 16 | end 17 | 18 | def example_started(notification) 19 | @progress_count += 1 20 | end 21 | 22 | def start(notification) 23 | super(notification) 24 | output.puts "1..#{notification.count}" 25 | end 26 | 27 | def example_passed(notification) 28 | example_line(description: notification.example.description) 29 | end 30 | 31 | def example_failed(notification) 32 | example_line( 33 | status: "not ok", 34 | description: notification.example.description 35 | ) 36 | diagnostic(notification) 37 | end 38 | 39 | def example_pending(notification) 40 | example_line( 41 | description: notification.example.description, 42 | directive: "# SKIP" 43 | ) 44 | end 45 | 46 | def dump_failures(*args) 47 | # No-op. We already output failure info inline 48 | end 49 | 50 | private 51 | 52 | def example_line(status: "ok", description: "", directive: "") 53 | output.puts([ 54 | status, 55 | @progress_count, 56 | "-", 57 | @example_group.parent_groups.reverse.map(&:description).join(" "), 58 | description, 59 | directive 60 | ].compact.join(" ")) 61 | end 62 | 63 | def diagnostic(notification) 64 | message = notification.fully_formatted(nil) 65 | output.puts(prefix(message, with: "#")) 66 | end 67 | 68 | def prefix(str, with: " ") 69 | str.split("\n").map { |substr| "#{with}#{substr}" }.join("\n") 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/lib/formatter_spec.rb: -------------------------------------------------------------------------------- 1 | require "ostruct" 2 | require "spec_helper" 3 | require "rspec_tap/formatter" 4 | 5 | RSpec.describe RspecTap::Formatter do 6 | let(:output) { StringIO.new } 7 | subject(:formatter) { described_class.new(output) } 8 | 9 | describe "#start" do 10 | subject(:start) { formatter.start(OpenStruct.new(count: 2)) } 11 | 12 | it "outputs a test plan" do 13 | start 14 | expect(output.string).to include("1..2") 15 | end 16 | end 17 | 18 | shared_examples_for "a test line" do 19 | it { is_expected.to include(notification.example.description) } 20 | it do 21 | is_expected.to include(formatter.instance_variable_get(:@progress_count).to_s) 22 | end 23 | 24 | context "a nested example group" do 25 | let(:parent_groups) do 26 | [ 27 | OpenStruct.new(description: "my subgroup"), 28 | OpenStruct.new(description: "My group") 29 | ] 30 | end 31 | 32 | it { is_expected.to include("My group my subgroup") } 33 | end 34 | end 35 | 36 | describe "#example_passed" do 37 | before do 38 | formatter.instance_variable_set( 39 | :@example_group, OpenStruct.new(parent_groups: parent_groups) 40 | ) 41 | formatter.example_passed(notification) 42 | end 43 | 44 | subject(:output_string) { output.string } 45 | 46 | let(:parent_groups) { [] } 47 | 48 | let(:notification) do 49 | OpenStruct.new( 50 | example: OpenStruct.new(description: "An test") 51 | ) 52 | end 53 | 54 | it { is_expected.to start_with("ok") } 55 | it_behaves_like "a test line" 56 | end 57 | 58 | describe "#example_failed" do 59 | before do 60 | formatter.instance_variable_set( 61 | :@example_group, OpenStruct.new(parent_groups: parent_groups) 62 | ) 63 | formatter.example_failed(notification) 64 | end 65 | 66 | subject(:output_string) { output.string } 67 | 68 | let(:parent_groups) { [] } 69 | 70 | let(:notification) do 71 | Class.new do 72 | def example 73 | OpenStruct.new(description: "An test") 74 | end 75 | 76 | def fully_formatted(number = nil) 77 | "A full test error message" 78 | end 79 | end.new 80 | end 81 | 82 | it { is_expected.to start_with("not ok") } 83 | it_behaves_like "a test line" 84 | 85 | it "prints the failure message" do 86 | expect(output_string).to include(notification.fully_formatted) 87 | end 88 | end 89 | 90 | describe "#example_pending" do 91 | before do 92 | formatter.instance_variable_set( 93 | :@example_group, OpenStruct.new(parent_groups: parent_groups) 94 | ) 95 | formatter.example_pending(notification) 96 | end 97 | 98 | subject(:output_string) { output.string } 99 | 100 | let(:parent_groups) { [] } 101 | 102 | let(:notification) do 103 | OpenStruct.new( 104 | example: OpenStruct.new(description: "An test") 105 | ) 106 | end 107 | 108 | it { is_expected.to start_with("ok") } 109 | it { is_expected.to end_with("# SKIP\n") } 110 | it_behaves_like "a test line" 111 | end 112 | 113 | describe "#dump_failures" do 114 | it "no-ops" do 115 | expect { formatter.dump_failures }.to_not change { output.string } 116 | end 117 | end 118 | end 119 | --------------------------------------------------------------------------------