vident-typed-minitest 0.13.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 +7 -0
- data/CHANGELOG.md +215 -0
- data/LICENSE.txt +21 -0
- data/README.md +866 -0
- data/lib/vident/typed/minitest/attributes_tester.rb +214 -0
- data/lib/vident/typed/minitest/auto_test.rb +73 -0
- data/lib/vident/typed/minitest/engine.rb +8 -0
- data/lib/vident/typed/minitest/test_case.rb +12 -0
- data/lib/vident/typed/minitest/version.rb +7 -0
- data/lib/vident/typed/minitest.rb +14 -0
- metadata +166 -0
@@ -0,0 +1,214 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vident
|
4
|
+
module Typed
|
5
|
+
module Minitest
|
6
|
+
class AttributesTester
|
7
|
+
def initialize(test_configurations)
|
8
|
+
@test_configurations = test_configurations
|
9
|
+
end
|
10
|
+
|
11
|
+
# Generates attribute hashes for all permutations of the given valid values.
|
12
|
+
def valid_configurations
|
13
|
+
# Expands any auto generated values and returns all attributes and their values
|
14
|
+
test_attrs = prepare_attributes_to_test
|
15
|
+
|
16
|
+
# The first permutation is the initial state
|
17
|
+
initial_state = prepare_initial_test_state(test_attrs)
|
18
|
+
|
19
|
+
# Create remaining permutations
|
20
|
+
test_attrs.flat_map do |attr_name, values|
|
21
|
+
values.map { |v| initial_state.merge(attr_name => v) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generates attribute hashes for all permutations of the given invalid values.
|
26
|
+
def invalid_configurations
|
27
|
+
return [] unless invalid_configured?
|
28
|
+
|
29
|
+
# prepare a valid initial state, then add any attrs that have :invalid
|
30
|
+
initial_state = prepare_initial_test_state(prepare_attributes_to_test)
|
31
|
+
|
32
|
+
# Merge in the invalid permutations
|
33
|
+
test_configurations.inject([]) do |memo, attr|
|
34
|
+
key, opts = attr
|
35
|
+
next memo += nil if opts == :strict_boolean
|
36
|
+
if opts.is_a?(Hash)
|
37
|
+
values = if opts[:invalid].nil? && opts[:valid].is_a?(Hash)
|
38
|
+
# If no invalid key specified we generate based on whats valid
|
39
|
+
config = invalid_attribute_test_values_for(opts[:valid][:type], opts[:valid])
|
40
|
+
(config&.fetch(:invalid, []) || []) + (config&.fetch(:converts, []) || [])
|
41
|
+
elsif opts[:invalid].is_a?(Array)
|
42
|
+
opts[:invalid]
|
43
|
+
end
|
44
|
+
|
45
|
+
memo += values.map { |i| initial_state.merge(key => i) } if values
|
46
|
+
end
|
47
|
+
memo
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :test_configurations
|
54
|
+
|
55
|
+
def invalid_configured?
|
56
|
+
test_configurations.values.any? { |v| v.respond_to?(:key?) && v.key?(:invalid) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def prepare_attributes_to_test
|
60
|
+
test_configurations.transform_values do |attr_config|
|
61
|
+
next [true, false, nil] if attr_config == :boolean
|
62
|
+
next [true, false] if attr_config == :strict_boolean
|
63
|
+
valid = attr_config[:valid]
|
64
|
+
raise "Ensure :valid attributes configuration is provided" unless valid
|
65
|
+
next valid if valid.is_a?(Array)
|
66
|
+
attribute_test_values_for(valid)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_initial_test_state(test_attrs)
|
71
|
+
initial_state = {}
|
72
|
+
test_attrs.each { |attr_name, values| initial_state[attr_name] = values.first }
|
73
|
+
initial_state
|
74
|
+
end
|
75
|
+
|
76
|
+
def attribute_test_values_for(options)
|
77
|
+
type = parse_type(options[:type])
|
78
|
+
return options[:in] if options[:in]
|
79
|
+
values =
|
80
|
+
case type
|
81
|
+
when :string, "String"
|
82
|
+
s = (1..8).map { |l| ::Faker::String.random(length: l) }
|
83
|
+
s.prepend "test string"
|
84
|
+
s = s.select(&:present?)
|
85
|
+
s << "" if options[:allow_blank]
|
86
|
+
s
|
87
|
+
when :boolean
|
88
|
+
[false, true]
|
89
|
+
when :float, "Float"
|
90
|
+
(1..3).map { Faker::Number.positive } + (1..3).map { Faker::Number.negative }
|
91
|
+
when :numeric, "Numeric"
|
92
|
+
(1..3).map { Faker::Number.positive } + [1, 5]
|
93
|
+
when :integer, "Integer"
|
94
|
+
min = options[:min] || -10_000
|
95
|
+
max = options[:max] || 10_000
|
96
|
+
(1..3).map { Kernel.rand(min..max) }
|
97
|
+
when :array, "Array"
|
98
|
+
a =
|
99
|
+
if options[:sub_type] == Numeric
|
100
|
+
[[1, 2, 3], [0.3, 2, 0.002]]
|
101
|
+
elsif options[:sub_type]
|
102
|
+
[[options[:sub_type].new]]
|
103
|
+
else
|
104
|
+
[%i[a b c], [1, 2, 3], %w[a b]]
|
105
|
+
end
|
106
|
+
a << [] if options[:allow_blank]
|
107
|
+
a
|
108
|
+
when :any
|
109
|
+
[false, 1245, {}, :df, "hi"]
|
110
|
+
when :hash, "Hash"
|
111
|
+
a = [{a: 1}]
|
112
|
+
a << {} if options[:allow_blank]
|
113
|
+
a
|
114
|
+
when :symbol, "Symbol"
|
115
|
+
%i[a b c]
|
116
|
+
else
|
117
|
+
raise StandardError, "Attribute type not understood (#{type})"
|
118
|
+
end
|
119
|
+
|
120
|
+
if options[:allow_nil] || !options[:default].nil? || (options[:allow_blank] && options[:allow_nil].nil?)
|
121
|
+
values << nil
|
122
|
+
end
|
123
|
+
values
|
124
|
+
end
|
125
|
+
|
126
|
+
def invalid_attribute_test_values_for(type, options)
|
127
|
+
values = case parse_type(type)
|
128
|
+
when :string, "String"
|
129
|
+
# All values are also convertable to string
|
130
|
+
string_values(options)
|
131
|
+
when :boolean
|
132
|
+
# All values are also convertable to boolean with !!
|
133
|
+
boolean_values
|
134
|
+
when :float, "Float"
|
135
|
+
# Not all values are convertable to float
|
136
|
+
float_values
|
137
|
+
when :numeric, "Numeric"
|
138
|
+
numeric_values
|
139
|
+
when :integer, "Integer"
|
140
|
+
# Not all values are convertable to integer
|
141
|
+
integer_values
|
142
|
+
when :array, "Array"
|
143
|
+
# Not all values are convertable to array
|
144
|
+
array_values(options)
|
145
|
+
when :any
|
146
|
+
# There are no invalid values
|
147
|
+
any_values
|
148
|
+
when :hash, "Hash"
|
149
|
+
hash_values(options)
|
150
|
+
when :symbol, "Symbol"
|
151
|
+
symbol_values
|
152
|
+
else
|
153
|
+
raise StandardError, "Attribute type not understood (#{type})"
|
154
|
+
end
|
155
|
+
|
156
|
+
if options[:default].nil? && (!options[:allow_nil] || (!options[:allow_blank] && options[:allow_nil] != true))
|
157
|
+
values[:invalid] << nil
|
158
|
+
end
|
159
|
+
values
|
160
|
+
end
|
161
|
+
|
162
|
+
def parse_type(type)
|
163
|
+
type.is_a?(Symbol) ? type : type.name
|
164
|
+
end
|
165
|
+
|
166
|
+
def string_values(options)
|
167
|
+
a = {converts: [false, 1245, 1.0, {}, :df, []], invalid: []}
|
168
|
+
a[:invalid] << "" if options[:allow_blank] == false
|
169
|
+
a
|
170
|
+
end
|
171
|
+
|
172
|
+
def boolean_values
|
173
|
+
{converts: ["sdf", 234, 3.5, {}, :sdf, []], invalid: []}
|
174
|
+
end
|
175
|
+
|
176
|
+
def float_values
|
177
|
+
{converts: [234, "12.2"], invalid: ["sdf", 234, {}, :sdf, [], false]}
|
178
|
+
end
|
179
|
+
|
180
|
+
def numeric_values
|
181
|
+
{converts: ["12.2"], invalid: ["sdf", {}, :sdf, [], false]}
|
182
|
+
end
|
183
|
+
|
184
|
+
def integer_values
|
185
|
+
{converts: [234.0, "123", "sdf"], invalid: [{}, :sdf, [], false]}
|
186
|
+
end
|
187
|
+
|
188
|
+
def array_values(options)
|
189
|
+
a = if options[:sub_type]
|
190
|
+
{converts: [{}, [{}]], invalid: ["sdf", [123], [Class.new]]}
|
191
|
+
else
|
192
|
+
{converts: [{}], invalid: ["sdf", 234, 3.5, :sdf, false]}
|
193
|
+
end
|
194
|
+
a[:invalid] << [] if options[:allow_blank] == false
|
195
|
+
a
|
196
|
+
end
|
197
|
+
|
198
|
+
def any_values
|
199
|
+
{converts: [], invalid: []}
|
200
|
+
end
|
201
|
+
|
202
|
+
def hash_values(options)
|
203
|
+
a = {converts: [[], [[:a, 1], [:b, 2]]], invalid: [:sdf, false, "boo", 123]}
|
204
|
+
a[:invalid] << {} if options[:allow_blank] == false
|
205
|
+
a
|
206
|
+
end
|
207
|
+
|
208
|
+
def symbol_values
|
209
|
+
{converts: ["foo"], invalid: [{}, false, [], 123]}
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/hooks"
|
4
|
+
|
5
|
+
module Vident
|
6
|
+
module Typed
|
7
|
+
module Minitest
|
8
|
+
module AutoTest
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
include ::Minitest::Hooks
|
13
|
+
|
14
|
+
def before_all
|
15
|
+
@results_content = []
|
16
|
+
::Vident::StableId.set_current_sequence_generator
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_all
|
20
|
+
html = <<~HTML
|
21
|
+
<!doctype html>
|
22
|
+
<html lang="en">
|
23
|
+
<head>
|
24
|
+
<meta charset="UTF-8">
|
25
|
+
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
26
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
27
|
+
<title>Test run output</title>
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
#{@results_content.map(&:to_s).join("<hr>\n").html_safe}
|
31
|
+
</body>
|
32
|
+
</html>
|
33
|
+
HTML
|
34
|
+
|
35
|
+
# TODO: configurable layout
|
36
|
+
filename = self.class.name.gsub("::", "_")
|
37
|
+
File.write("render_results_#{filename}.html", html) # TODO: path for outputs (eg default tmp/)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class_methods do
|
42
|
+
def auto_test(class_under_test, **param_config)
|
43
|
+
attribute_tester = AttributesTester.new(param_config)
|
44
|
+
attribute_tester.valid_configurations.each_with_index do |test, index|
|
45
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
46
|
+
def test_renders_with_valid_attrs_#{index}
|
47
|
+
test_attrs = #{test}
|
48
|
+
begin
|
49
|
+
@results_content << render_inline(#{class_under_test}.new(**test_attrs))
|
50
|
+
assert @results_content.present?, "Should render with #{test.to_s.tr("\"", "'")}"
|
51
|
+
rescue => error
|
52
|
+
assert(false, "Should not raise with #{test.to_s.tr("\"", "'")} but did raise \#{error}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
end
|
57
|
+
|
58
|
+
attribute_tester.invalid_configurations.each_with_index do |test, index|
|
59
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
60
|
+
def test_raises_with_invalid_attrs_#{index}
|
61
|
+
test_attrs = #{test}
|
62
|
+
assert_raises(StandardError, "Should raise with #{test.to_s.tr("\"", "'")}") do
|
63
|
+
@results_content << render_inline(#{class_under_test}.new(**test_attrs))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
RUBY
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "vident/typed"
|
2
|
+
require "vident/typed/minitest/version"
|
3
|
+
require "vident/typed/minitest/attributes_tester"
|
4
|
+
require "vident/typed/minitest/auto_test"
|
5
|
+
require "vident/typed/minitest/test_case"
|
6
|
+
require "vident/typed/minitest/engine" if defined?(Rails)
|
7
|
+
|
8
|
+
module Vident
|
9
|
+
module Typed
|
10
|
+
module Minitest
|
11
|
+
# Your code goes here...
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vident-typed-minitest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.13.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Ierodiaconou
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-04-07 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: railties
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '7.2'
|
19
|
+
- - "<"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '9'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '7.2'
|
29
|
+
- - "<"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '9'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: activesupport
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '7.2'
|
39
|
+
- - "<"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '9'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '7.2'
|
49
|
+
- - "<"
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '9'
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: vident-typed
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - "~>"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 0.13.0
|
59
|
+
type: :runtime
|
60
|
+
prerelease: false
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - "~>"
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 0.13.0
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: minitest
|
68
|
+
requirement: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 5.14.4
|
73
|
+
- - "<"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '6.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 5.14.4
|
83
|
+
- - "<"
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '6.0'
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: minitest-hooks
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.5.0
|
93
|
+
- - "<"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.0'
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.5.0
|
103
|
+
- - "<"
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '2.0'
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: faker
|
108
|
+
requirement: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.22.0
|
113
|
+
- - "<"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '4.0'
|
116
|
+
type: :runtime
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 2.22.0
|
123
|
+
- - "<"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '4.0'
|
126
|
+
description: Vident test helper for Minitest
|
127
|
+
email:
|
128
|
+
- stevegeek@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- CHANGELOG.md
|
134
|
+
- LICENSE.txt
|
135
|
+
- README.md
|
136
|
+
- lib/vident/typed/minitest.rb
|
137
|
+
- lib/vident/typed/minitest/attributes_tester.rb
|
138
|
+
- lib/vident/typed/minitest/auto_test.rb
|
139
|
+
- lib/vident/typed/minitest/engine.rb
|
140
|
+
- lib/vident/typed/minitest/test_case.rb
|
141
|
+
- lib/vident/typed/minitest/version.rb
|
142
|
+
homepage: https://github.com/stevegeek/vident
|
143
|
+
licenses:
|
144
|
+
- MIT
|
145
|
+
metadata:
|
146
|
+
homepage_uri: https://github.com/stevegeek/vident
|
147
|
+
source_code_uri: https://github.com/stevegeek/vident
|
148
|
+
changelog_uri: https://github.com/stevegeek/vident/blob/main/CHANGELOG.md
|
149
|
+
rdoc_options: []
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: 3.1.0
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
requirements: []
|
163
|
+
rubygems_version: 3.6.2
|
164
|
+
specification_version: 4
|
165
|
+
summary: Vident test helper for Minitest
|
166
|
+
test_files: []
|