quby 4.0.3 → 5.0.0.pre1

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/quby/questionnaires.rb +1 -0
  3. data/lib/quby/questionnaires/api.rb +5 -1
  4. data/lib/quby/questionnaires/deserializer.rb +433 -0
  5. data/lib/quby/questionnaires/dsl.rb +10 -15
  6. data/lib/quby/questionnaires/entities.rb +1 -0
  7. data/lib/quby/questionnaires/entities/charting/line_chart.rb +23 -0
  8. data/lib/quby/questionnaires/entities/charting/overview_chart.rb +3 -1
  9. data/lib/quby/questionnaires/entities/definition.rb +3 -5
  10. data/lib/quby/questionnaires/entities/fields.rb +0 -15
  11. data/lib/quby/questionnaires/entities/question.rb +9 -32
  12. data/lib/quby/questionnaires/entities/questionnaire.rb +4 -15
  13. data/lib/quby/questionnaires/entities/questions/checkbox_question.rb +0 -24
  14. data/lib/quby/questionnaires/entities/questions/date_question.rb +0 -8
  15. data/lib/quby/questionnaires/entities/score_calculation.rb +36 -3
  16. data/lib/quby/questionnaires/repos.rb +1 -0
  17. data/lib/quby/questionnaires/repos/bundle_disk_repo.rb +51 -0
  18. data/lib/quby/version.rb +1 -1
  19. data/spec/internal/log/test-events.log +166 -0
  20. data/spec/internal/log/test.log +6170 -0
  21. data/spec/internal/tmp/capybara/screenshot_2020-10-27-14-25-21.063.html +207 -0
  22. data/spec/internal/tmp/capybara/screenshot_2020-10-27-14-25-21.063.png +0 -0
  23. data/spec/quby/answers/services/answer_validations_spec.rb +8 -8
  24. data/spec/quby/questionnaires/deserializer/questionnaire_spec.rb +237 -0
  25. data/spec/quby/questionnaires/dsl_spec.rb +0 -9
  26. data/spec/quby/questionnaires/entities/fields_spec.rb +3 -3
  27. data/spec/quby/questionnaires/entities/question_spec.rb +0 -8
  28. data/spec/quby/questionnaires/entities/questionnaire_spec.rb +2 -26
  29. data/spec/quby/table_backend/range_tree_spec.rb +7 -0
  30. data/spec/spec_helper.rb +1 -0
  31. metadata +25 -46
  32. data/lib/quby/questionnaires/dsl/base.rb +0 -20
  33. data/lib/quby/questionnaires/dsl/calls_custom_methods.rb +0 -29
  34. data/lib/quby/questionnaires/dsl/charting/bar_chart_builder.rb +0 -18
  35. data/lib/quby/questionnaires/dsl/charting/chart_builder.rb +0 -91
  36. data/lib/quby/questionnaires/dsl/charting/line_chart_builder.rb +0 -57
  37. data/lib/quby/questionnaires/dsl/charting/overview_chart_builder.rb +0 -31
  38. data/lib/quby/questionnaires/dsl/charting/radar_chart_builder.rb +0 -18
  39. data/lib/quby/questionnaires/dsl/helpers.rb +0 -51
  40. data/lib/quby/questionnaires/dsl/panel_builder.rb +0 -80
  41. data/lib/quby/questionnaires/dsl/question_builder.rb +0 -40
  42. data/lib/quby/questionnaires/dsl/questionnaire_builder.rb +0 -252
  43. data/lib/quby/questionnaires/dsl/questions/base.rb +0 -179
  44. data/lib/quby/questionnaires/dsl/questions/checkbox_question_builder.rb +0 -20
  45. data/lib/quby/questionnaires/dsl/questions/date_question_builder.rb +0 -18
  46. data/lib/quby/questionnaires/dsl/questions/deprecated_question_builder.rb +0 -18
  47. data/lib/quby/questionnaires/dsl/questions/float_question_builder.rb +0 -21
  48. data/lib/quby/questionnaires/dsl/questions/integer_question_builder.rb +0 -21
  49. data/lib/quby/questionnaires/dsl/questions/radio_question_builder.rb +0 -20
  50. data/lib/quby/questionnaires/dsl/questions/select_question_builder.rb +0 -18
  51. data/lib/quby/questionnaires/dsl/questions/string_question_builder.rb +0 -20
  52. data/lib/quby/questionnaires/dsl/questions/text_question_builder.rb +0 -22
  53. data/lib/quby/questionnaires/dsl/score_builder.rb +0 -22
  54. data/lib/quby/questionnaires/dsl/standardized_panel_generators.rb +0 -33
  55. data/lib/quby/questionnaires/dsl/table_builder.rb +0 -48
  56. data/lib/quby/questionnaires/services/definition_validator.rb +0 -298
  57. data/spec/quby/questionnaires/dsl/calls_custom_methods_spec.rb +0 -38
  58. data/spec/quby/questionnaires/dsl/charting/bar_chart_builder_spec.rb +0 -41
  59. data/spec/quby/questionnaires/dsl/charting/chart_builder_spec.rb +0 -127
  60. data/spec/quby/questionnaires/dsl/charting/line_chart_builder_spec.rb +0 -66
  61. data/spec/quby/questionnaires/dsl/charting/radar_chart_builder_spec.rb +0 -41
  62. data/spec/quby/questionnaires/dsl/helpers_spec.rb +0 -80
  63. data/spec/quby/questionnaires/dsl/questionnaire_builder_spec.rb +0 -480
  64. data/spec/quby/questionnaires/services/definition_validator_spec.rb +0 -793
  65. data/spec/support/examples_for_chart_builders.rb +0 -59
@@ -1,298 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'nokogumbo'
3
- require 'active_model'
4
- require 'quby/questionnaires/entities/questionnaire'
5
-
6
- module Quby
7
- module Questionnaires
8
- module Services
9
- class DefinitionValidator < ActiveModel::Validator
10
- MAX_KEY_LENGTH = 19
11
- KEY_PREFIX = 'v_'
12
-
13
- attr_reader :definition
14
- attr_reader :questionnaire
15
-
16
- def validate(definition)
17
- questionnaire = DSL.build_from_definition(definition)
18
- validate_fields(questionnaire)
19
- validate_title(questionnaire)
20
- validate_questions(questionnaire)
21
- validate_scores(questionnaire)
22
- validate_table_edgecases(questionnaire)
23
- validate_flags(questionnaire)
24
- validate_respondent_types(questionnaire)
25
- validate_outcome_tables(questionnaire)
26
- validate_markdown_fields(questionnaire) if questionnaire.validate_html
27
- validate_raw_content_items(questionnaire) if questionnaire.validate_html
28
- # Some compilation errors are Exceptions (pure syntax errors) and some StandardErrors (NameErrors)
29
- rescue Exception => exception # rubocop:disable Lint/RescueException
30
- definition.errors.add(:sourcecode, {message: "Questionnaire error: #{definition.key}\n" \
31
- "#{exception.message}",
32
- backtrace: exception.backtrace[0..5].join("<br/>")})
33
- end
34
-
35
- def validate_fields(questionnaire)
36
- questionnaire.fields.input_keys
37
- .find { |k| !k.is_a?(Symbol) }
38
- &.tap { |k| fail "Input key #{k} is not a symbol" }
39
- questionnaire.fields.answer_keys
40
- .find { |k| !k.is_a?(Symbol) }
41
- &.tap { |k| fail "Answer key #{k} is not a symbol" }
42
- end
43
-
44
- def validate_title(questionnaire)
45
- if questionnaire.title.blank?
46
- fail "Questionnaire title is missing."
47
- end
48
- end
49
-
50
- def validate_questions(questionnaire)
51
- questionnaire.answer_keys.each do |key|
52
- validate_key_format(key)
53
- end
54
-
55
- questionnaire.question_hash.each_value do |question|
56
- validate_question(question)
57
- subquestions_cant_have_default_invisible question
58
- validate_subquestion_absence_in_select question
59
- validate_placeholder_options_nil_values question
60
- validate_values_unique question
61
-
62
- validate_question_options(questionnaire, question)
63
- validate_presence_of_titles question
64
- validate_no_spaces_before_question_nr_in_title question
65
- end
66
- end
67
-
68
- def validate_question(question)
69
- unless question.valid?
70
- fail "Question #{question.key} is invalid: #{question.errors.full_messages.join(', ')}"
71
- end
72
- end
73
-
74
- def validate_scores(questionnaire)
75
- questionnaire.scores.each do |score|
76
- validate_score_key_length(score)
77
- validate_score_label_present(score)
78
- end
79
- end
80
-
81
- def validate_question_options(questionnaire, question)
82
- question.options.each do |option|
83
- msg_base = "Question #{option.question.key} option #{option.key}"
84
- to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
85
- to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
86
- end
87
- end
88
-
89
- def validate_presence_of_titles(question)
90
- return if question.allow_blank_titles
91
- if !question.subquestion? && question.title.blank? && question.context_free_title.blank?
92
- fail "Question #{question.key} must define either `:title` or `:context_free_title`."
93
- end
94
- end
95
-
96
- def validate_table_edgecases(questionnaire)
97
- questionnaire.panels.each do |panel|
98
- tables = panel.items.select { |item| item.is_a?(Entities::Table) }
99
- tables.each do |table|
100
- questions = table.items.select { |item| item.is_a?(Entities::Question) }
101
- questions.each { |question| validate_table_question(question) }
102
- end
103
- end
104
- end
105
-
106
- def validate_flags(questionnaire)
107
- questionnaire.flags.each_value do |flag|
108
- validate_flag_shows(questionnaire, flag)
109
- validate_flag_hides(questionnaire, flag)
110
- validate_flag_depends_on(questionnaire, flag)
111
- end
112
- end
113
-
114
- def validate_flag_shows(questionnaire, flag)
115
- unknown_questions = flag.shows_questions.select { |key| !questionnaire.key_in_use?(key) }
116
- return if unknown_questions.blank?
117
-
118
- fail ArgumentError, "Flag '#{key}' has unknown shows_questions keys #{unknown_questions}"
119
- end
120
-
121
- def validate_flag_hides(questionnaire, flag)
122
- unknown_questions = flag.hides_questions.select { |key| !questionnaire.key_in_use?(key) }
123
- return if unknown_questions.blank?
124
-
125
- fail ArgumentError, "Flag '#{key}' has unknown hides_questions keys #{unknown_questions}"
126
- end
127
-
128
- def validate_flag_depends_on(questionnaire, flag)
129
- return if flag.depends_on.blank? || questionnaire.flags.key?(flag.depends_on)
130
-
131
- fail ArgumentError, "Flag #{flag.key} depends_on nonexistent flag '#{flag.depends_on}'"
132
- end
133
-
134
- def validate_respondent_types(questionnaire)
135
- valid_respondent_types = Entities::Questionnaire::RESPONDENT_TYPES
136
-
137
- invalid_types = questionnaire.respondent_types - valid_respondent_types
138
-
139
- if invalid_types.present?
140
- fail "Invalid respondent types: :#{invalid_types.join(', :')}\n"\
141
- "Choose one or more from: :#{valid_respondent_types.join(', :')}"
142
- end
143
- end
144
-
145
- def validate_outcome_tables(questionnaire)
146
- questionnaire.outcome_tables.each do |table|
147
- next if table.valid?
148
- fail "Outcome table #{table.errors.full_messages}"
149
- end
150
- end
151
-
152
- def to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:)
153
- return if option.hides_questions.blank?
154
- msg_base += " hides_questions"
155
- option.hides_questions.each do |key|
156
- validate_question_key_exists?(questionnaire, key, msg_base: msg_base)
157
- validate_not_subquestion(questionnaire, key, msg_base: msg_base)
158
- end
159
- end
160
-
161
- def to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:)
162
- return if option.shows_questions.blank?
163
- msg_base += " shows_questions"
164
- option.shows_questions.each do |key|
165
- validate_question_key_exists?(questionnaire, key, msg_base: msg_base)
166
- validate_not_subquestion(questionnaire, key, msg_base: msg_base)
167
- end
168
- end
169
-
170
- def subquestions_cant_have_default_invisible(question)
171
- if question.subquestion? && question.default_invisible
172
- fail "Question #{question.key} is a subquestion with default_invisible."
173
- end
174
- end
175
-
176
- private
177
-
178
- # Don't write question numbers as " 1. Title", but as "1\\. Title".
179
- def validate_no_spaces_before_question_nr_in_title(question)
180
- if question.title && question.title.match(/^\s{2,}\d+\\\./)
181
- fail "Question with number does not need leading spaces."
182
- end
183
- end
184
-
185
- def validate_question_key_exists?(questionnaire, key, msg_base:)
186
- unless questionnaire.question_hash[key]
187
- fail msg_base + " references nonexistent question #{key}"
188
- end
189
- end
190
-
191
- def validate_not_subquestion(questionnaire, key, msg_base:)
192
- if questionnaire.question_hash[key].subquestion?
193
- fail msg_base + " references subquestion #{key}"
194
- end
195
- end
196
-
197
- def validate_key_format(key)
198
- if key.to_s.length > MAX_KEY_LENGTH
199
- fail "Key '#{key}' should contain at most #{MAX_KEY_LENGTH} characters."
200
- end
201
- unless key.to_s.start_with?(KEY_PREFIX)
202
- fail "Key '#{key}' should start with '#{KEY_PREFIX}'."
203
- end
204
- end
205
-
206
- def validate_score_key_length(score)
207
- if score.key.to_s.length > MAX_KEY_LENGTH
208
- fail "Score key `#{score.key}` should contain at most #{MAX_KEY_LENGTH} characters."
209
- end
210
- end
211
-
212
- def validate_score_label_present(score)
213
- fail "Score #{score.key} label must be passed in as an option." unless score.label.present?
214
- end
215
-
216
- def validate_subquestion_absence_in_select(question)
217
- return unless question.type == :select
218
- question.options.each do |option|
219
- unless option.questions.empty?
220
- fail "Question '#{question.key}' of type ':select' may not include other questions."
221
- end
222
- end
223
- end
224
-
225
- def validate_placeholder_options_nil_values(question)
226
- question.options.each do |question_option|
227
- if question_option.placeholder && question_option.value.present?
228
- fail "#{question.key}:#{question_option.key}: Placeholder options should not have values defined."
229
- end
230
- end
231
- end
232
-
233
- def validate_values_unique(question)
234
- return if question.type == :check_box || question.allow_duplicate_option_values
235
-
236
- question.options.each_with_object([]) do |question_option, seen_values|
237
- next if question_option.placeholder || question_option.inner_title
238
-
239
- fail "#{question.key}:#{question_option.key}: Has no option value defined." if question_option.value.blank?
240
- if seen_values.include?(question_option.value)
241
- fail "#{question.key}:#{question_option.key}: " \
242
- "Another option with value #{question_option.value} is already defined."
243
- end
244
- seen_values << question_option.value
245
- end
246
- end
247
-
248
- def validate_table_question(question)
249
- question.subquestions.each do |subquestion|
250
- if subquestion.presentation != :next_to_title
251
- fail "Question #{question.key} is inside a table, but has a subquestion #{subquestion.key}, " \
252
- "which is not allowed."
253
- end
254
- end
255
- end
256
-
257
- def validate_markdown_fields(questionnaire)
258
- questionnaire.panels.each do |panel|
259
- panel.items.select { |item| item.is_a?(Entities::Text) }.each do |text_item|
260
- validate_markdown(text_item.str, text_item.str)
261
- end
262
- end
263
- questionnaire.questions.each do |question|
264
- Entities::Question::MARKDOWN_ATTRIBUTES.each do |attr|
265
- validate_markdown(question.send(attr), "#{question.key}.#{attr}")
266
- question.options.each do |option|
267
- Entities::QuestionOption::MARKDOWN_ATTRIBUTES.each do |option_attr|
268
- validate_markdown(option.send(option_attr), "#{question.key}:#{option.key}.#{option_attr}")
269
- end
270
- end
271
- end
272
- end
273
- end
274
-
275
- def validate_raw_content_items(questionnaire)
276
- questionnaire.panels.each do |panel|
277
- panel.items.each do |item|
278
- next if item.raw_content.blank?
279
-
280
- validate_html(item.raw_content)
281
- end
282
- end
283
- end
284
-
285
- def validate_markdown(markdown, key)
286
- validate_html(MarkdownParser.new(markdown).to_html, key)
287
- end
288
-
289
- def validate_html(html, key = nil)
290
- fragment = Nokogiri::HTML5.fragment(html, max_errors: 3)
291
- return unless fragment.errors.present?
292
-
293
- fail "#{key || html} contains invalid html: #{fragment.errors.map(&:to_s).join(', ')}."
294
- end
295
- end
296
- end
297
- end
298
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Quby::Questionnaires::DSL
6
- describe CallsCustomMethods do
7
-
8
- class TestCustomMethod
9
- prepend CallsCustomMethods
10
-
11
- attr_reader :test
12
- def initialize(*args)
13
- @test = 'attribute'
14
- end
15
- end
16
-
17
- let(:custom_methods) { {custom: -> { 'custom_method' }} }
18
- let(:test_object) { TestCustomMethod.new(custom_methods: custom_methods) }
19
-
20
- describe 'prepending' do
21
- it 'does not skip the actual initializer' do
22
- expect(test_object.test).to eq('attribute')
23
- end
24
- end
25
-
26
- describe '#method_missing' do
27
- it 'allows the class to receive messages defined in custom_methods' do
28
- expect(test_object.custom).to eq('custom_method')
29
- end
30
- end
31
-
32
- describe '#respond_to_missing?' do
33
- it 'also considers custom methods' do
34
- expect(test_object.send(:respond_to_missing?, :custom)).to eq(true)
35
- end
36
- end
37
- end
38
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Quby::Questionnaires::DSL
6
- describe BarChartBuilder do
7
- it_behaves_like ChartBuilder
8
-
9
- let(:questionnaire) { double(key: 'questionnaire_key') }
10
-
11
- it 'makes a bar chart' do
12
- expect(dsl { }).to be_an_instance_of(::Quby::Questionnaires::Entities::Charting::BarChart)
13
- end
14
-
15
- def dsl(key = :test, options = {}, &block)
16
- builder = BarChartBuilder.new(questionnaire, key, options)
17
- builder.build(&block)
18
- end
19
-
20
- it 'sets y-axis range' do
21
- expect(dsl { range 0..40 }.y_range).to eq (0..40)
22
- end
23
-
24
- it 'sets y-axis tick interval' do
25
- expect(dsl { tick_interval 1 }.tick_interval).to eq 1
26
- end
27
-
28
- it 'sets plotlines' do
29
- plotlines =
30
- dsl do
31
- plotline 40, :orange
32
- plotline 60, :red
33
- end.plotlines
34
-
35
- expect(plotlines).to eq [
36
- {value: 40, color: :orange, width: 1, zIndex: 3},
37
- {value: 60, color: :red, width: 1, zIndex: 3}
38
- ]
39
- end
40
- end
41
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- module Quby::Questionnaires::DSL
6
- describe ChartBuilder do
7
-
8
- let(:plottable_key) { :some_key }
9
- let(:plottable) { Quby::Questionnaires::Entities::ScoreCalculation.new plottable_key, {} }
10
- let(:questionnaire) do
11
- double(key: 'questionnaire_key').tap do |questionnaire|
12
- allow(questionnaire).to receive(:find_plottable).with(plottable_key).and_return plottable
13
- end
14
- end
15
- let(:chart_plottables) { [] }
16
- let(:chart_class) { Quby::Questionnaires::Entities::Charting::Chart }
17
- before { ChartBuilder.set_chart_class(chart_class) }
18
- let(:chart_builder) { ChartBuilder.new questionnaire, 'chart_key' }
19
-
20
- def dsl(key = :test, options = {}, &block)
21
- builder = ChartBuilder.new(questionnaire, key, options)
22
- builder.build(&block)
23
- end
24
-
25
- describe '#plot' do
26
- it 'fetches a plottable by key from the questionnaire' do
27
- expect(questionnaire).to receive(:find_plottable).with(plottable_key)
28
- chart_builder.plot plottable_key
29
- end
30
-
31
- it 'creates a plottable with the options merged into the plottable options' do
32
- allow(plottable).to receive(:options).and_return({some: 'options', other: 'options'})
33
- expect(Quby::Questionnaires::Entities::Charting::Plottable).to receive(:new)
34
- .with(plottable.key,
35
- some: 'different_options',
36
- other: 'options',
37
- questionnaire_key: 'questionnaire_key')
38
- chart_builder.plot plottable_key, some: 'different_options'
39
- end
40
-
41
- it 'sets the label from a question title when no label is present' do
42
- plottable = Quby::Questionnaires::Entities::Question.new plottable_key, title: 'some_title'
43
- expect(questionnaire).to receive(:find_plottable).with(plottable_key).and_return plottable
44
- expect(Quby::Questionnaires::Entities::Charting::Plottable).to receive(:new)
45
- .with(plottable_key, questionnaire_key: 'questionnaire_key', label: 'some_title')
46
- chart_builder.plot plottable_key
47
- end
48
-
49
- it 'does not set the label when it is given in the options' do
50
- plottable = Quby::Questionnaires::Entities::Question.new plottable_key, title: 'some_title'
51
- expect(questionnaire).to receive(:find_plottable).with(plottable_key).and_return plottable
52
- expect(Quby::Questionnaires::Entities::Charting::Plottable).to receive(:new)
53
- .with(plottable_key, questionnaire_key: 'questionnaire_key', label: 'some_label')
54
- chart_builder.plot plottable_key, label: 'some_label'
55
- end
56
- end
57
-
58
- describe '#y_categories' do
59
- it 'assigns y_categories' do
60
- categories = %w(Bad Great Best)
61
- chart_builder.y_categories categories
62
- expect(chart_builder.build { }.y_categories).to eq(categories)
63
- end
64
- end
65
-
66
- describe '#plotband' do
67
- it 'can set plotbands' do
68
- plotbands =
69
- dsl do
70
- plotband 2, 5, :yellow
71
- plotband 5, 8, :red
72
- end.plotbands
73
-
74
- expect(plotbands).to eq [
75
- {from: 2, to: 5, color: :yellow},
76
- {from: 5, to: 8, color: :red}
77
- ]
78
- end
79
- end
80
-
81
- describe '#y_range_categories' do
82
- it 'sets RangeCategories.new(*parameters).as_range_hash to the chart y_range_categories' do
83
- expect(RangeCategories).to receive(:new).with(0, 'Zeer laag', 30).and_return(double(as_range_hash: 'foo'))
84
- chart_builder.y_range_categories 0, 'Zeer laag', 30
85
- expect(chart_builder.build { }.y_range_categories).to eq('foo')
86
- end
87
-
88
- it 'converts the parameters to a range hash' do
89
- expected_categories = {(0.0...30.0) => "Zeer laag", (30.0...40.0) => "Laag", (40.0...60.0) => "Gemiddeld",
90
- (60.0...70.0) => "Hoog", (70.0..100.0) => "Zeer hoog"}
91
- chart_builder.y_range_categories 0, 'Zeer laag',
92
- 30, 'Laag',
93
- 40, 'Gemiddeld',
94
- 60, 'Hoog',
95
- 70, 'Zeer hoog', 100
96
- expect(chart_builder.build { }.y_range_categories).to eq(expected_categories)
97
- end
98
- end
99
-
100
- describe '#range' do
101
- it 'assigns y_range' do
102
- chart_builder.range 0..5
103
- expect(chart_builder.build { }.y_range).to eq(0..5)
104
- end
105
- end
106
- describe '#validate' do
107
- describe 'when there is no range defined but y_categories are present' do
108
- it 'uses the 0..y_categories.count-1 as a range' do
109
- chart_builder.y_categories %w(Bad Great Best)
110
- expect(chart_builder.build { }.y_range).to eq(0..2)
111
- end
112
- end
113
- describe 'when range and y_categories are present' do
114
- it 'raises if the range does not correspond to the y_categories length' do
115
- chart_builder.range 0..3
116
- chart_builder.y_categories %w(Bad Great Best)
117
- expect { chart_builder.build { } }.to raise_error(ArgumentError)
118
- end
119
- it 'does not raise if the range matches the y_categories' do
120
- chart_builder.range 0..2
121
- chart_builder.y_categories %w(Bad Great Best)
122
- expect { chart_builder.build { } }.not_to raise_error
123
- end
124
- end
125
- end
126
- end
127
- end