• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MushroomObserver / mushroom-observer / 26221504187

21 May 2026 10:52AM UTC coverage: 96.348% (-0.1%) from 96.482%
26221504187

Pull #4315

github

nimmolo
Fix integration + system tests for new obs/project checkbox ID format

Missed in the obs/list array-shape refactor — Capybara tests still
looked for the old `project_id_<id>` / `list_id_<id>` checkbox IDs.
After the wire-shape change, Superform auto-generates IDs from the
field DOM:

- `project_id_<id>` → `observation_project_ids_<id>`
- `list_id_<id>` → `observation_species_list_ids_<id>`

Files updated:
- test/integration/capybara/observations_integration_test.rb (4 sites)
- test/integration/capybara/field_slips_integration_test.rb (1 site)
- test/system/observation_form_system_test.rb (1 site)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull Request #4315: Project + species_list membership, `ImagesEditForm` Phlex

152 of 154 new or added lines in 11 files covered. (98.7%)

54 existing lines in 3 files now uncovered.

38675 of 40141 relevant lines covered (96.35%)

664.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.86
/app/controllers/observations_controller/validators.rb
1
# frozen_string_literal: true
2

3
#  :section: Validators
4
#  These validators return Boolean values, and also set the @any_errors ivar.
5
#
6
#    validate_name
7
#      name_params
8
#      resolve_name(...)
9
#
10
#    validate_place_name
11
#
12
#    validate_projects
13
#      checked_project_conflicts
14

15
# Included in both ObservationsController and NamingsController
16
module ObservationsController::Validators
1✔
17
  private
1✔
18

19
  def validate_name
1✔
20
    success = resolve_name
103✔
21
    if @name
103✔
22
      @naming.name = @name
54✔
23
    elsif !success
49✔
24
      @naming.errors.add(:name,
9✔
25
                         :form_observations_there_is_a_problem_with_name.t)
26
      flash_object_errors(@naming)
9✔
27
    end
28
    return true if success
103✔
29

30
    @any_errors = true
9✔
31
    false
9✔
32
  end
33

34
  # Set the ivars for the form: @given_name, @name - and potentially ivars for
35
  # form_name_feedback in the case the name is not resolved unambiguously:
36
  # @names, @valid_names, @parent_deprecated, @suggest_corrections.
37
  # Returns true if the name is resolved unambiguously.
38
  def resolve_name
1✔
39
    resolver = Naming::NameResolver.new(@user, **name_params)
135✔
40
    success = false
135✔
41
    resolver.results.each do |ivar, value|
135✔
42
      if ivar == :success
945✔
43
        success = value
135✔
44
      else
45
        instance_variable_set(:"@#{ivar}", value)
810✔
46
      end
47
    end
48
    success
135✔
49
  end
50

51
  # given_name, given_id from observation/naming/fields. Note: nil.to_i == 0
52
  # approved_name, chosen_name from form_name_feedback
53
  # also used in namings_controller
54
  def name_params
1✔
55
    {
56
      given_name: naming_params_dig(:name).to_s,
135✔
57
      # given_id: naming_params_dig(:name_id).to_i,
58
      approved_name: params[:approved_name].to_s,
59
      chosen_name: params.dig(:chosen_name, :name_id).to_s
60
    }
61
  end
62

63
  # Dig into naming params, checking both old and new param structures
64
  # Old: params[:naming][:key], New: params[:observation][:naming][:key]
65
  def naming_params_dig(*keys)
1✔
66
    params.dig(:observation, :naming, *keys) || params.dig(:naming, *keys)
341✔
67
  end
68

69
  # Helper methods for nested form params (Superform nests under :observation)
70
  def collection_number_params
1✔
71
    params.dig(:observation, :collection_number) || params[:collection_number]
76✔
72
  end
73

74
  def herbarium_record_params
1✔
75
    params.dig(:observation, :herbarium_record) || params[:herbarium_record]
106✔
76
  end
77

78
  # Submitted project_ids array (post-Phlex shape:
79
  # `observation[project_ids][]=<id>`). `compact_blank` strips the
80
  # form's sentinel hidden input (value=""), leaving the integer-
81
  # string IDs the user checked.
82
  def submitted_project_ids
1✔
83
    params.dig(:observation, :project_ids)&.compact_blank
156✔
84
  end
85

86
  def submitted_list_ids
1✔
NEW
87
    params.dig(:observation, :species_list_ids)&.compact_blank
×
88
  end
89

90
  # The form may be in a state where it has an existing MO Location name in the
91
  # `place_name` field, but not the corresponding MO location_id. It could be
92
  # because of user trying to create a duplicate, or because the user had a
93
  # prefilled location, but clicked on the "Create Location" button - this keeps
94
  # the place_name, but clears the location_id field. Either way, we need to
95
  # check if we already have a location by this name. If so, find the existing
96
  # location and use that for the obs.
97
  def validate_place_name
1✔
98
    place_name = @observation.place_name
141✔
99
    lat = @observation.lat
141✔
100
    lng = @observation.lng
141✔
101
    if !lat && !lng && place_name.blank?
141✔
102
      @any_errors = true
4✔
103
      return false
4✔
104
    end
105

106
    # Set location to unknown if place_name blank && lat/lng are present
107
    if Location.is_unknown?(place_name) || (lat && lng && place_name.blank?)
137✔
108
      @observation.location = Location.unknown
9✔
109
      @observation.where = nil
9✔
110
      # If it's unknown, we're good. don't need to check for duplicates.
111
      return true
9✔
112
    end
113

114
    name = Location.user_format(@user, @observation.place_name)
128✔
115
    @dubious_where_reasons = Location.dubious_name?(name, true)
128✔
116
    return true if @dubious_where_reasons.empty?
128✔
117

118
    @any_errors = true
18✔
119
    false
18✔
120
  end
121

122
  def validate_projects
1✔
123
    ids = submitted_project_ids
141✔
124
    return true if ids.blank?
141✔
125

126
    conflicting_projects = checked_project_conflicts - @observation.projects
15✔
127
    @error_checked_projects = conflicting_projects.reject do |proj|
15✔
128
      proj.is_admin?(@user)
7✔
129
    end
130
    if @error_checked_projects.any?
15✔
131
      flash_error(:form_observations_there_is_a_problem_with_projects.t)
1✔
132
      @any_errors = true
1✔
133
      return false
1✔
134
    end
135

136
    return true if params.dig(:observation, :ignore_proj_conflicts) == "1"
14✔
137

138
    @suspect_checked_projects = conflicting_projects - @error_checked_projects
12✔
139
    if @suspect_checked_projects.any?
12✔
140
      flash_warning(:form_observations_there_is_a_problem_with_projects.t)
4✔
141
    end
142
    return true if @suspect_checked_projects.empty?
12✔
143

144
    @any_errors = true
4✔
145
    false
4✔
146
  end
147

148
  def checked_project_conflicts
1✔
149
    ids = submitted_project_ids
15✔
150
    return [] if ids.blank?
15✔
151

152
    Project.where(id: ids).includes(:location).select do |proj|
15✔
153
      proj.violates_constraints?(@observation)
17✔
154
    end
155
  end
156

157
  def validate_observation
1✔
158
    return true if validate_object(@observation)
103✔
159

160
    @any_errors = true
×
161
    false
×
162
  end
163

164
  def validate_naming
1✔
165
    return true if !@name || validate_object(@naming)
54✔
166

167
    @any_errors = true
×
168
    false
×
169
  end
170

171
  def validate_vote
1✔
172
    return true if !@name || @vote.value.nil? || validate_object(@vote)
54✔
173

174
    @any_errors = true
×
175
    false
×
176
  end
177

178
  def validate_images
1✔
179
    return true if @bad_images.empty?
103✔
180

181
    @any_errors = true
1✔
182
    false
1✔
183
  end
184
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc