vident 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 +4 -4
- data/Gemfile +2 -0
- data/lib/vident/test_case.rb +8 -0
- data/lib/vident/testing/attributes_tester.rb +176 -0
- data/lib/vident/testing/auto_test.rb +70 -0
- data/lib/vident/version.rb +1 -1
- data/lib/vident.rb +6 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2f2d014f440bdd33f9910c04ba79846825afb4c8a61bd6e5079079fd07c0473
|
4
|
+
data.tar.gz: fb730836ba6c63a582f97afcaceed12467a1b8ac92f2f5d9a0d871a4791c3de4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da7e90adb10d5301803cde48f06685983478c01eec44bfe0c8db9a0909734572ccc1ee72490850655bc915f4c2de17d27b1191d3d649c53d7a23d2d1e40b83a2
|
7
|
+
data.tar.gz: 2626db1242b53e3abe20c75a8269125e564e1bc89a178f6e76feccda869ef2c41d671d0ece1a48f55441d6e4483e4962c76118ba48beba991529fe2e0ed322cc
|
data/Gemfile
CHANGED
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vident
|
4
|
+
module Testing
|
5
|
+
class AttributesTester
|
6
|
+
def initialize(test_configurations)
|
7
|
+
@test_configurations = test_configurations
|
8
|
+
end
|
9
|
+
|
10
|
+
# Generates attribute hashes for all permutations of the given valid values.
|
11
|
+
def valid_configurations
|
12
|
+
# Expands any auto generated values and returns all attributes and their values
|
13
|
+
test_attrs = prepare_attributes_to_test
|
14
|
+
|
15
|
+
# The first permutation is the initial state
|
16
|
+
initial_state = prepare_initial_test_state(test_attrs)
|
17
|
+
|
18
|
+
# Create remaining permutations
|
19
|
+
test_attrs.flat_map do |attr_name, values|
|
20
|
+
values.map { |v| initial_state.merge(attr_name => v) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Generates attribute hashes for all permutations of the given invalid values.
|
25
|
+
def invalid_configurations
|
26
|
+
return [] unless invalid_configured?
|
27
|
+
|
28
|
+
# prepare a valid initial state, then add any attrs that have :invalid
|
29
|
+
initial_state = prepare_initial_test_state(prepare_attributes_to_test)
|
30
|
+
|
31
|
+
# Merge in the invalid permutations
|
32
|
+
test_configurations.inject([]) do |memo, attr|
|
33
|
+
key, opts = attr
|
34
|
+
next memo += nil if opts == :strict_boolean
|
35
|
+
if opts.is_a?(Hash)
|
36
|
+
values = if opts[:invalid].nil? && opts[:valid].is_a?(Hash)
|
37
|
+
# If no invalid key specified we generate based on whats valid
|
38
|
+
config = invalid_attribute_test_values_for(opts[:valid][:type], opts[:valid])
|
39
|
+
(config&.fetch(:invalid, []) || []) + (config&.fetch(:converts, []) || [])
|
40
|
+
elsif opts[:invalid].is_a?(Array)
|
41
|
+
opts[:invalid]
|
42
|
+
end
|
43
|
+
|
44
|
+
memo += values.map { |i| initial_state.merge(key => i) } if values
|
45
|
+
end
|
46
|
+
memo
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :test_configurations
|
53
|
+
|
54
|
+
def invalid_configured?
|
55
|
+
test_configurations.values.any? { |v| v.respond_to?(:key?) && v.key?(:invalid) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def prepare_attributes_to_test
|
59
|
+
test_configurations.transform_values do |attr_config|
|
60
|
+
next [true, false, nil] if attr_config == :boolean
|
61
|
+
next [true, false] if attr_config == :strict_boolean
|
62
|
+
valid = attr_config[:valid]
|
63
|
+
raise "Ensure :valid attributes configuration is provided" unless valid
|
64
|
+
next valid if valid.is_a?(Array)
|
65
|
+
attribute_test_values_for(valid)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def prepare_initial_test_state(test_attrs)
|
70
|
+
initial_state = {}
|
71
|
+
test_attrs.each { |attr_name, values| initial_state[attr_name] = values.first }
|
72
|
+
initial_state
|
73
|
+
end
|
74
|
+
|
75
|
+
def attribute_test_values_for(options)
|
76
|
+
type = parse_type(options[:type])
|
77
|
+
return options[:in] if options[:in]
|
78
|
+
values =
|
79
|
+
case type
|
80
|
+
when :string, "String"
|
81
|
+
s = (1..8).map { |l| ::Faker::String.random(length: l) }
|
82
|
+
s.prepend "test string"
|
83
|
+
s = s.select(&:present?)
|
84
|
+
s << "" if options[:allow_blank]
|
85
|
+
s
|
86
|
+
when :boolean
|
87
|
+
[false, true]
|
88
|
+
when :float, "Float"
|
89
|
+
(1..3).map { Faker::Number.positive } + (1..3).map { Faker::Number.negative }
|
90
|
+
when :numeric, "Numeric"
|
91
|
+
(1..3).map { Faker::Number.positive } + [1, 5]
|
92
|
+
when :integer, "Integer"
|
93
|
+
min = options[:min] || -10_000
|
94
|
+
max = options[:max] || 10_000
|
95
|
+
(1..3).map { Kernel.rand(min..max) }
|
96
|
+
when :array, "Array"
|
97
|
+
a =
|
98
|
+
if options[:sub_type] == Numeric
|
99
|
+
[[1, 2, 3], [0.3, 2, 0.002]]
|
100
|
+
elsif options[:sub_type]
|
101
|
+
[[options[:sub_type].new]]
|
102
|
+
else
|
103
|
+
[%i[a b c], [1, 2, 3], %w[a b]]
|
104
|
+
end
|
105
|
+
a << [] if options[:allow_blank]
|
106
|
+
a
|
107
|
+
when :any
|
108
|
+
[false, 1245, {}, :df, "hi"]
|
109
|
+
when :hash, "Hash"
|
110
|
+
a = [{a: 1}]
|
111
|
+
a << {} if options[:allow_blank]
|
112
|
+
a
|
113
|
+
when :symbol, "Symbol"
|
114
|
+
%i[a b c]
|
115
|
+
else
|
116
|
+
raise StandardError, "Attribute type not understood (#{type})"
|
117
|
+
end
|
118
|
+
|
119
|
+
if options[:allow_nil] || !options[:default].nil? || (options[:allow_blank] && options[:allow_nil].nil?)
|
120
|
+
values << nil
|
121
|
+
end
|
122
|
+
values
|
123
|
+
end
|
124
|
+
|
125
|
+
def invalid_attribute_test_values_for(type, options)
|
126
|
+
values = case parse_type(type)
|
127
|
+
when :string, "String"
|
128
|
+
# All values are also convertable to string
|
129
|
+
a = {converts: [false, 1245, 1.0, {}, :df, []], invalid: []}
|
130
|
+
a[:invalid] << "" if options[:allow_blank] == false
|
131
|
+
a
|
132
|
+
when :boolean
|
133
|
+
# All values are also convertable to boolean with !!
|
134
|
+
{converts: ["sdf", 234, 3.5, {}, :sdf, []], invalid: []}
|
135
|
+
when :float, "Float"
|
136
|
+
# Not all values are convertable to float
|
137
|
+
{converts: [234, "12.2"], invalid: ["sdf", 234, {}, :sdf, [], false]}
|
138
|
+
when :numeric, "Numeric"
|
139
|
+
{converts: ["12.2"], invalid: ["sdf", {}, :sdf, [], false]}
|
140
|
+
when :integer, "Integer"
|
141
|
+
# Not all values are convertable to integer
|
142
|
+
{converts: [234.0, "123", "sdf"], invalid: [{}, :sdf, [], false]}
|
143
|
+
when :array, "Array"
|
144
|
+
# Not all values are convertable to array
|
145
|
+
a = if options[:sub_type]
|
146
|
+
{converts: [{}, [{}]], invalid: ["sdf", [123], [Class.new]]}
|
147
|
+
else
|
148
|
+
{converts: [{}], invalid: ["sdf", 234, 3.5, :sdf, false]}
|
149
|
+
end
|
150
|
+
a[:invalid] << [] if options[:allow_blank] == false
|
151
|
+
a
|
152
|
+
when :any
|
153
|
+
# There are no invalid values
|
154
|
+
{converts: [], invalid: []}
|
155
|
+
when :hash, "Hash"
|
156
|
+
a = {converts: [[], [[:a, 1], [:b, 2]]], invalid: [:sdf, false, "boo", 123]}
|
157
|
+
a[:invalid] << {} if options[:allow_blank] == false
|
158
|
+
a
|
159
|
+
when :symbol, "Symbol"
|
160
|
+
{converts: ["foo"], invalid: [{}, false, [], 123]}
|
161
|
+
else
|
162
|
+
raise StandardError, "Attribute type not understood (#{type})"
|
163
|
+
end
|
164
|
+
|
165
|
+
if options[:default].nil? && (!options[:allow_nil] || (!options[:allow_blank] && options[:allow_nil] != true))
|
166
|
+
values[:invalid] << nil
|
167
|
+
end
|
168
|
+
values
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse_type(type)
|
172
|
+
type.is_a?(Symbol) ? type : type.name
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/hooks"
|
4
|
+
|
5
|
+
module Vident
|
6
|
+
module Testing
|
7
|
+
module AutoTest
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include Minitest::Hooks
|
12
|
+
|
13
|
+
def before_all
|
14
|
+
@results_content = []
|
15
|
+
::Vident::StableId.set_current_sequence_generator
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_all
|
19
|
+
html = <<~HTML
|
20
|
+
<!doctype html>
|
21
|
+
<html lang="en">
|
22
|
+
<head>
|
23
|
+
<meta charset="UTF-8">
|
24
|
+
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
25
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
26
|
+
<title>Test run output</title>
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
#{@results_content.map(&:to_s).join("<hr>\n").html_safe}
|
30
|
+
</body>
|
31
|
+
</html>
|
32
|
+
HTML
|
33
|
+
|
34
|
+
# TODO: configurable layout
|
35
|
+
filename = self.class.name.gsub("::", "_")
|
36
|
+
File.write("render_results_#{filename}.html", html) # TODO: path for outputs (eg default tmp/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class_methods do
|
41
|
+
def auto_test(class_under_test, **param_config)
|
42
|
+
attribute_tester = Vident::Testing::AttributesTester.new(param_config)
|
43
|
+
attribute_tester.valid_configurations.each_with_index do |test, index|
|
44
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
45
|
+
def test_renders_with_valid_attrs_#{index}
|
46
|
+
test_attrs = #{test}
|
47
|
+
begin
|
48
|
+
render_inline(#{class_under_test}.new(**test_attrs))
|
49
|
+
rescue => error
|
50
|
+
assert(false, "Should not raise with #{test.to_s.tr("\"", "'")} but did raise \#{error}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
|
56
|
+
attribute_tester.invalid_configurations.each_with_index do |test, index|
|
57
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
58
|
+
def test_raises_with_invalid_attrs_#{index}
|
59
|
+
test_attrs = #{test}
|
60
|
+
assert_raises(StandardError, "Should raise with #{test.to_s.tr("\"", "'")}") do
|
61
|
+
render_inline(#{class_under_test}.new(**test_attrs))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/vident/version.rb
CHANGED
data/lib/vident.rb
CHANGED
@@ -32,3 +32,9 @@ require_relative "vident/base"
|
|
32
32
|
require_relative "vident/component"
|
33
33
|
require_relative "vident/typed_component"
|
34
34
|
require_relative "vident/caching/cache_key"
|
35
|
+
|
36
|
+
require_relative "vident/testing/attributes_tester"
|
37
|
+
require_relative "vident/testing/auto_test"
|
38
|
+
|
39
|
+
# TODO: what if not using view_component?
|
40
|
+
require_relative "vident/test_case"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vident
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Ierodiaconou
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -60,6 +60,9 @@ files:
|
|
60
60
|
- lib/vident/root_component/using_phlex_html.rb
|
61
61
|
- lib/vident/root_component/using_view_component.rb
|
62
62
|
- lib/vident/stable_id.rb
|
63
|
+
- lib/vident/test_case.rb
|
64
|
+
- lib/vident/testing/attributes_tester.rb
|
65
|
+
- lib/vident/testing/auto_test.rb
|
63
66
|
- lib/vident/typed_component.rb
|
64
67
|
- lib/vident/version.rb
|
65
68
|
- sig/vident.rbs
|