snapshot_testing 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa10b4d611599255fcbb0ddfea02d05c400c951a
4
- data.tar.gz: d794383a14439bbcaaec7fa4be0016563ba4090d
3
+ metadata.gz: a09c878dab9b37cfb19478f2647d404fc1091840
4
+ data.tar.gz: 83d05b1d4a199a08f44e0c4a3d5a01b3e07f9754
5
5
  SHA512:
6
- metadata.gz: a74f16d1f1a897d94aa2bf67308d6e693ba464348e711ac176511c6870836698bd1a4c7bc17d277dcf79084fb4c8addd8f4be213018d0b426b92b3661364ae6b
7
- data.tar.gz: 6e96bc7657a8bdcc29746dc87fdb29232aecc7d94843c5af02175ae10290121f04cedcf492801f1c38ff3af51d577dea4f3cb1a8a585b8d026df81587b530b43
6
+ metadata.gz: 3d630db5ee68d5dcb9df88b29f6991eacbdee0f150c03282f6e6121eb434bf9476985111e71438e5b8ae8d66af1c29d12d50078f6fd18b558ecd3bf77281ed0a
7
+ data.tar.gz: 44402a6591987118f01cdc357e5b089625a0406850da4e8e2e16c8134a7571102d02f3594dc3d0212dc423115abf20b3b0a3e54acd8ee270e705904e66a8976a
@@ -5,3 +5,7 @@ cache: bundler
5
5
  rvm:
6
6
  - 2.6.3
7
7
  before_install: gem install bundler -v 2.0.2
8
+
9
+ script:
10
+ - bundle exec rspec
11
+ - bundle exec rake examples
data/Gemfile CHANGED
@@ -2,4 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in snapshot_testing.gemspec
4
4
  gemspec
5
- gem 'pry'
data/README.md CHANGED
@@ -47,6 +47,7 @@ RSpec.describe "Example" do
47
47
  it "takes a snapshot" do
48
48
  expect("hello").to match_snapshot
49
49
  expect("goodbye").to match_snapshot
50
+ expect("hello").to match_snapshot("hello.txt")
50
51
  end
51
52
  end
52
53
  ```
@@ -63,6 +64,7 @@ class ExampleTest < Minitest::Test
63
64
  def test_takes_a_snapshot
64
65
  assert_snapshot "hello"
65
66
  assert_snapshot "goodbye"
67
+ assert_snapshot "hello.txt", "hello"
66
68
  end
67
69
  end
68
70
 
@@ -70,8 +72,9 @@ class ExampleSpec < Minitest::Spec
70
72
  include SnapshotTesting::Minitest
71
73
 
72
74
  it "takes a snapshot" do
73
- "hello".must_match_snapshot
74
- "goodbye".must_match_snapshot
75
+ _("hello").must_match_snapshot
76
+ _("goodbye").must_match_snapshot
77
+ _("hello").must_match_snapshot "hello.txt"
75
78
  end
76
79
  end
77
80
  ```
@@ -88,6 +91,7 @@ class ExampleTest < Test::Unit::TestCase
88
91
  def test_snapshot
89
92
  assert_snapshot "hello"
90
93
  assert_snapshot "goodbye"
94
+ assert_snapshot "hello.txt", "hello"
91
95
  end
92
96
  end
93
97
  ```
data/Rakefile CHANGED
@@ -3,10 +3,19 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
+ def info(message)
7
+ puts "\033[1m#{message}\033[0m"
8
+ end
9
+
6
10
  task :examples do
7
- ruby 'examples/minitest.rb'
8
- ruby 'examples/test_unit.rb'
9
- ruby 'examples/rspec.rb'
11
+ info "==>> Running RSpec..."
12
+ ruby "examples/rspec.rb -f progress"
13
+
14
+ info "\n\n==>> Running Minitest..."
15
+ ruby "examples/minitest.rb"
16
+
17
+ info "\n\n==>> Running TestUnit..."
18
+ ruby "examples/test_unit.rb"
10
19
  end
11
20
 
12
21
  task :default => :spec
@@ -9,13 +9,21 @@ class ExampleTest < Minitest::Test
9
9
  assert_snapshot "hello"
10
10
  assert_snapshot "goodbye"
11
11
  end
12
+
13
+ def test_named_snapshot
14
+ assert_snapshot "named.minitest.test.txt", "named"
15
+ end
12
16
  end
13
17
 
14
18
  class ExampleSpec < Minitest::Spec
15
19
  include SnapshotTesting::Minitest
16
20
 
17
21
  it "takes a snapshot" do
18
- "hello".must_match_snapshot
19
- "goodbye".must_match_snapshot
22
+ _("hello").must_match_snapshot
23
+ _("goodbye").must_match_snapshot
24
+ end
25
+
26
+ it "takes a named snapshot" do
27
+ _("named").must_match_snapshot "named.minitest.spec.txt"
20
28
  end
21
29
  end
@@ -11,4 +11,8 @@ RSpec.describe "Example" do
11
11
  expect("hello").to match_snapshot
12
12
  expect("goodbye").to match_snapshot
13
13
  end
14
+
15
+ it "takes a named snapshot" do
16
+ expect("named").to match_snapshot("named.rspec.txt")
17
+ end
14
18
  end
@@ -9,4 +9,8 @@ class ExampleTest < Test::Unit::TestCase
9
9
  assert_snapshot "hello"
10
10
  assert_snapshot "goodbye"
11
11
  end
12
+
13
+ def test_named_snapshot
14
+ assert_snapshot "named.unit.txt", "named"
15
+ end
12
16
  end
@@ -3,4 +3,7 @@ require "snapshot_testing/snapshot"
3
3
  require "snapshot_testing/recorder"
4
4
 
5
5
  module SnapshotTesting
6
+ def self.update?
7
+ !ENV["UPDATE_SNAPSHOTS"].nil?
8
+ end
6
9
  end
@@ -2,17 +2,10 @@ require "snapshot_testing"
2
2
 
3
3
  module SnapshotTesting
4
4
  module Minitest
5
- def self.included(_)
6
- return unless defined?(::Minitest::Expectations)
7
- return if ::Minitest::Expectations.method_defined?(:must_match_snapshot)
8
- ::Minitest::Expectations.infect_an_assertion(:assert_snapshot, :must_match_snapshot, true)
9
- end
10
-
11
5
  def before_setup
12
6
  @__snapshot_recorder__ = SnapshotTesting::Recorder.new(
13
7
  name: name,
14
- path: method(name).source_location.first,
15
- update: !ENV['UPDATE_SNAPSHOTS'].nil?
8
+ path: method(name).source_location.first
16
9
  )
17
10
  super
18
11
  end
@@ -22,8 +15,16 @@ module SnapshotTesting
22
15
  @__snapshot_recorder__.commit
23
16
  end
24
17
 
25
- def assert_snapshot(actual)
26
- assert_equal(@__snapshot_recorder__.record(actual), actual)
18
+ def assert_snapshot(name = nil, actual)
19
+ if name.nil?
20
+ assert_equal(@__snapshot_recorder__.record(actual), actual)
21
+ else
22
+ assert_equal(@__snapshot_recorder__.record_file(name, actual), actual)
23
+ end
24
+ end
25
+
26
+ if respond_to? :infect_an_assertion
27
+ infect_an_assertion :assert_snapshot, :must_match_snapshot
27
28
  end
28
29
  end
29
30
  end
@@ -2,80 +2,125 @@ require "fileutils"
2
2
 
3
3
  module SnapshotTesting
4
4
  class Recorder
5
- def initialize(name:, path:, update:)
5
+ def initialize(name:, path:, update: SnapshotTesting.update?)
6
6
  @name = name
7
7
  @path = path
8
8
  @update = update
9
- @state = {}
9
+ @visited = []
10
+ @added = 0
11
+ @updated = 0
10
12
  end
11
13
 
12
- def snapshot_dir
13
- File.join(File.dirname(@path), "__snapshots__")
14
- end
15
-
16
- def snapshot_file
17
- File.join(snapshot_dir, "#{File.basename(@path)}.snap")
14
+ def snapshot_path
15
+ File.join(snapshots_path, "#{File.basename(path)}.snap")
18
16
  end
19
17
 
20
18
  def snapshots
21
19
  @snapshots ||= begin
22
- Snapshot.load_file(snapshot_file)
20
+ Snapshot.load_file(snapshot_path)
23
21
  rescue Errno::ENOENT
24
22
  {}
25
23
  end
26
24
  end
27
25
 
26
+ def record_file(name, actual)
27
+ expected = begin
28
+ read(name)
29
+ rescue Errno::ENOENT
30
+ write(name, actual)
31
+ log(1, :written)
32
+ actual
33
+ end
34
+
35
+ if update? && actual != expected
36
+ write(name, actual)
37
+ log(1, :updated)
38
+ actual
39
+ else
40
+ expected
41
+ end
42
+ end
43
+
28
44
  def record(actual)
29
- key = "#{@name} #{@state.length + 1}"
45
+ key = "#{name} #{visited.length + 1}"
30
46
 
31
- # keep track each encounter, so we can diff later
32
- @state[key] = actual
47
+ self.visited << key
33
48
 
34
- # pass the test when updating snapshots
35
- return actual if @update
49
+ unless snapshots.key?(key)
50
+ self.added += 1
51
+ self.snapshots[key] = actual
52
+ end
36
53
 
37
- # pass the test when the snapshot does not exist
38
- return actual unless snapshots.key?(key)
54
+ if update? && actual != snapshots[key]
55
+ self.updated += 1
56
+ self.snapshots[key] = actual
57
+ end
39
58
 
40
- # otherwise, compare actual to the snapshot
41
59
  snapshots[key]
42
60
  end
43
61
 
44
62
  def commit
45
- added = @state.select { |k, _| !snapshots.key?(k) }
46
- changed = @state.select { |k, v| snapshots.key?(k) && snapshots[k] != v }
47
- removed = snapshots.keys.select do |k|
48
- k.match?(/^#{@name}\s\d+$/) && !@state.key?(k)
49
- end
63
+ removed = snapshots.keys - visited
64
+ removed = removed.grep(/^#{name}\s\d+$/)
65
+ removed.each { |key| snapshots.delete(key) }
66
+
67
+ write_snapshots(snapshots) if write?
68
+ log(added, :written) unless added.zero?
69
+ log(updated, :updated) unless updated.zero?
70
+ log(removed.length, :removed) if update? && !removed.empty?
71
+ log(removed.length, :obsolete) if !update? && !removed.empty?
72
+ end
50
73
 
51
- result = snapshots.merge(added)
52
- result = result.merge(changed) if @update
53
- result = result.reject { |k, _| removed.include?(k) } if @update
74
+ protected
54
75
 
55
- write(result) if result != snapshots
56
- log(added.length, :written, :green) if added.any?
57
- log(changed.length, :updated, :green) if @update && changed.any?
58
- log(removed.length, :removed, :green) if @update && removed.any?
59
- log(removed.length, :obsolete, :yellow) if !@update && removed.any?
60
- end
76
+ # the number of added snapshots
77
+ attr_accessor :added
78
+
79
+ # the number of updated snapshots
80
+ attr_accessor :updated
81
+
82
+ # all snapshots that have been compared
83
+ attr_reader :visited
61
84
 
62
85
  private
63
86
 
64
- def log(count, status, color)
87
+ # the name of the current test
88
+ attr_reader :name
89
+
90
+ # the file location of the current test
91
+ attr_reader :path
92
+
93
+ # should we update failing snapshots?
94
+ def update?
95
+ @update
96
+ end
97
+
98
+ # should we write to the filesystem?
99
+ def write?
100
+ update? || !added.zero?
101
+ end
102
+
103
+ def snapshots_path
104
+ File.join(File.dirname(path), "__snapshots__")
105
+ end
106
+
107
+ def log(count, status)
65
108
  label = count == 1 ? "snapshot" : "snapshots"
66
- message = "#{count} #{label} #{status}."
109
+ warn "\e[33m#{count} #{label} #{status}\e[0m"
110
+ end
67
111
 
68
- case color
69
- when :yellow
70
- warn "\e[33m#{message}\e[0m"
71
- when :green
72
- warn "\e[32m#{message}\e[0m"
73
- end
112
+ def read(name)
113
+ File.read(File.join(snapshots_path, name))
114
+ end
115
+
116
+ def write(name, data)
117
+ FileUtils.mkdir_p(snapshots_path)
118
+ File.write(File.join(snapshots_path, name), data)
74
119
  end
75
120
 
76
- def write(snapshots)
77
- FileUtils.mkdir_p(snapshot_dir)
78
- File.write(snapshot_file, Snapshot.dump(snapshots))
121
+ def write_snapshots(snapshots)
122
+ FileUtils.mkdir_p(snapshots_path)
123
+ File.write(snapshot_path, Snapshot.dump(snapshots))
79
124
  end
80
125
  end
81
126
  end
@@ -2,12 +2,13 @@ require "snapshot_testing"
2
2
 
3
3
  module SnapshotTesting
4
4
  module RSpec
5
+ extend ::RSpec::Matchers::DSL
6
+
5
7
  def self.included(base)
6
8
  base.let :__snapshot_recorder__ do |example|
7
9
  SnapshotTesting::Recorder.new(
8
10
  name: example.description,
9
- path: example.metadata[:absolute_file_path],
10
- update: !ENV['UPDATE_SNAPSHOTS'].nil?
11
+ path: example.metadata[:absolute_file_path]
11
12
  )
12
13
  end
13
14
 
@@ -16,35 +17,30 @@ module SnapshotTesting
16
17
  end
17
18
  end
18
19
 
19
- def match_snapshot
20
- SnapshotTesting::RSpec::MatchSnapshot.new(recorder: __snapshot_recorder__)
21
- end
22
-
23
- class MatchSnapshot
24
- attr_reader :expected, :actual
20
+ matcher :match_snapshot do |name|
21
+ match do |actual|
22
+ @expected = if name.nil?
23
+ __snapshot_recorder__.record(actual)
24
+ else
25
+ __snapshot_recorder__.record_file(name, actual)
26
+ end
25
27
 
26
- def initialize(recorder:)
27
- @recorder = recorder
28
+ @expected == actual
28
29
  end
29
30
 
30
- def matches?(actual)
31
- @actual = actual
32
- @expected = @recorder.record(@actual)
33
- @actual == @expected
34
- end
31
+ diffable
32
+ description { "match snapshot #{expected_formatted}"}
35
33
 
36
- def failure_message
37
- expected = ::RSpec::Support::ObjectFormatter.format(@expected)
38
- actual = ::RSpec::Support::ObjectFormatter.format(@actual)
39
- "\nexpected: #{expected}\n got: #{actual}\n\n(compared using ==)\n"
34
+ failure_message do |actual|
35
+ "\nexpected: #{expected_formatted}\n got: #{actual_formatted}\n\n(compared using ==)\n"
40
36
  end
41
37
 
42
- def diffable?
43
- true
38
+ def expected_formatted
39
+ ::RSpec::Support::ObjectFormatter.format(@expected)
44
40
  end
45
41
 
46
- def supports_block_expectations?
47
- false
42
+ def actual_formatted
43
+ ::RSpec::Support::ObjectFormatter.format(@actual)
48
44
  end
49
45
  end
50
46
  end
@@ -5,8 +5,7 @@ module SnapshotTesting
5
5
  def setup
6
6
  @__snapshot_recorder__ = SnapshotTesting::Recorder.new(
7
7
  name: method_name,
8
- path: method(method_name).source_location.first,
9
- update: !ENV['UPDATE_SNAPSHOTS'].nil?
8
+ path: method(method_name).source_location.first
10
9
  )
11
10
  super
12
11
  end
@@ -16,8 +15,12 @@ module SnapshotTesting
16
15
  @__snapshot_recorder__.commit
17
16
  end
18
17
 
19
- def assert_snapshot(actual)
20
- assert_equal(@__snapshot_recorder__.record(actual), actual)
18
+ def assert_snapshot(name = nil, actual)
19
+ if name.nil?
20
+ assert_equal(@__snapshot_recorder__.record(actual), actual)
21
+ else
22
+ assert_equal(@__snapshot_recorder__.record_file(name, actual), actual)
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -1,3 +1,3 @@
1
1
  module SnapshotTesting
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snapshot_testing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ray Zane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-22 00:00:00.000000000 Z
11
+ date: 2019-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -110,6 +110,10 @@ files:
110
110
  - bin/console
111
111
  - bin/setup
112
112
  - examples/__snapshots__/minitest.rb.snap
113
+ - examples/__snapshots__/named.minitest.spec.txt
114
+ - examples/__snapshots__/named.minitest.test.txt
115
+ - examples/__snapshots__/named.rspec.txt
116
+ - examples/__snapshots__/named.unit.txt
113
117
  - examples/__snapshots__/rspec.rb.snap
114
118
  - examples/__snapshots__/test_unit.rb.snap
115
119
  - examples/minitest.rb