snapshot_testing 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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