decidim-dev 0.27.4 → 0.28.0.rc5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/commands/decidim/{dummy_resources → dev}/create_dummy_resource.rb +1 -2
- data/app/controllers/decidim/{dummy_resources → dev}/dummy_resources_controller.rb +2 -2
- data/app/events/decidim/dev/dummy_resource_event.rb +10 -0
- data/app/forms/decidim/{dummy_resources → dev}/dummy_resource_form.rb +1 -1
- data/app/jobs/decidim/dev/hide_all_created_by_author_job.rb +13 -0
- data/app/mailers/decidim/{dummy_resources → dev}/dummy_resource_mailer.rb +1 -1
- data/app/models/decidim/dev/application_record.rb +9 -0
- data/app/models/decidim/dev/coauthorable_dummy_resource.rb +10 -0
- data/app/models/decidim/dev/dummy_resource.rb +93 -0
- data/app/models/decidim/dev/nested_dummy_resource.rb +10 -0
- data/app/packs/src/decidim/dev/accessibility.js +3 -3
- data/app/packs/src/decidim/dev/test/custom_map_factory.js +1 -1
- data/app/packs/stylesheets/decidim/dev/_accessibility.scss +24 -24
- data/app/packs/stylesheets/decidim/dev/_map.scss +10 -0
- data/app/packs/stylesheets/decidim/dev.scss +1 -0
- data/app/presenters/decidim/dev/official_author_presenter.rb +33 -0
- data/app/serializers/decidim/dev/dummy_serializer.rb +21 -0
- data/app/views/decidim/dev/dummy_resources/show.html.erb +25 -0
- data/config/environment.rb +3 -0
- data/config/locales/ar.yml +0 -1
- data/config/locales/bg.yml +0 -1
- data/config/locales/cs.yml +4 -4
- data/config/locales/de.yml +2 -2
- data/config/locales/el.yml +0 -1
- data/config/locales/en.yml +1 -1
- data/config/locales/es-MX.yml +1 -1
- data/config/locales/es-PY.yml +1 -1
- data/config/locales/eu.yml +14 -8
- data/config/locales/gl.yml +0 -1
- data/config/locales/hu.yml +0 -1
- data/config/locales/id-ID.yml +0 -1
- data/config/locales/it.yml +0 -1
- data/config/locales/lv.yml +0 -1
- data/config/locales/nl.yml +0 -1
- data/config/locales/no.yml +0 -1
- data/config/locales/pl.yml +0 -1
- data/config/locales/pt-BR.yml +0 -1
- data/config/locales/pt.yml +0 -1
- data/config/locales/ru.yml +0 -1
- data/config/locales/sk.yml +0 -1
- data/config/locales/sq-AL.yml +1 -0
- data/config/locales/sv.yml +1 -1
- data/config/locales/th-TH.yml +1 -0
- data/config/locales/tr-TR.yml +0 -1
- data/config/locales/zh-CN.yml +0 -1
- data/config/rubocop/disabled.yml +11 -0
- data/config/rubocop/faker.yml +480 -0
- data/config/rubocop/rails.yml +105 -0
- data/config/rubocop/rspec.yml +69 -0
- data/config/rubocop/ruby.yml +1207 -0
- data/lib/decidim/dev/admin.rb +8 -0
- data/lib/decidim/dev/admin_engine.rb +43 -0
- data/lib/decidim/dev/assets/import_participatory_space_private_users.csv +2 -2
- data/lib/decidim/dev/assets/import_participatory_space_private_users_invalid_col_sep.csv +2 -0
- data/lib/decidim/dev/assets/import_participatory_space_private_users_nok.csv +2 -2
- data/lib/decidim/dev/assets/import_participatory_space_private_users_with_bom.csv +1 -1
- data/lib/decidim/dev/assets/iso-8859-15.md +1 -1
- data/lib/decidim/dev/assets/participatory_text.md +4 -2
- data/lib/decidim/dev/assets/verify_user_groups.csv +22 -22
- data/lib/decidim/dev/component.rb +94 -0
- data/lib/decidim/dev/engine.rb +21 -3
- data/lib/decidim/dev/test/base_spec_helper.rb +1 -0
- data/lib/decidim/dev/test/factories.rb +50 -0
- data/lib/decidim/dev/test/form_to_param_shared_examples.rb +1 -1
- data/lib/decidim/dev/test/promoted_participatory_processes_shared_examples.rb +9 -9
- data/lib/decidim/dev/test/rspec_support/accessibility_examples.rb +119 -1
- data/lib/decidim/dev/test/rspec_support/attachment_helpers.rb +2 -2
- data/lib/decidim/dev/test/rspec_support/bullet.rb +32 -0
- data/lib/decidim/dev/test/rspec_support/capybara.rb +26 -21
- data/lib/decidim/dev/test/rspec_support/cell_matchers.rb +1 -1
- data/lib/decidim/dev/test/rspec_support/component.rb +7 -317
- data/lib/decidim/dev/test/rspec_support/component_context.rb +10 -10
- data/lib/decidim/dev/test/rspec_support/confirmation_helpers.rb +18 -14
- data/lib/decidim/dev/test/rspec_support/data_consent.rb +2 -2
- data/lib/decidim/dev/test/rspec_support/dynamic_attach.rb +19 -4
- data/lib/decidim/dev/test/rspec_support/editor_context.rb +35 -0
- data/lib/decidim/dev/test/rspec_support/engine_examples.rb +15 -0
- data/lib/decidim/dev/test/rspec_support/filters.rb +11 -0
- data/lib/decidim/dev/test/rspec_support/forms_validations.rb +20 -0
- data/lib/decidim/dev/test/rspec_support/geocoder.rb +7 -7
- data/lib/decidim/dev/test/rspec_support/helpers.rb +187 -34
- data/lib/decidim/dev/test/rspec_support/imports_controller_shared_examples.rb +13 -13
- data/lib/decidim/dev/test/rspec_support/tom_select.rb +26 -0
- data/lib/decidim/dev/test/rspec_support/translation_helpers.rb +8 -8
- data/lib/decidim/dev/test/rspec_support/warden.rb +1 -1
- data/lib/decidim/dev/test/rspec_support/webpacker.rb +10 -0
- data/lib/decidim/dev/test/spec_helper.rb +15 -4
- data/lib/decidim/dev/test/w3c_rspec_validators_overrides.rb +1 -5
- data/lib/decidim/dev/version.rb +1 -1
- data/lib/decidim/dev.rb +22 -0
- data/lib/decidim-dev.rb +1 -1
- data/lib/tasks/lighthouse_report.rake +29 -7
- data/rubocop-decidim.yml +13 -0
- metadata +125 -71
- data/app/views/decidim/dummy_resources/dummy_resources/show.html.erb +0 -15
- data/lib/decidim/dev/test/rspec_support/capybara_data_picker.rb +0 -36
- data/lib/decidim/dev/test/rspec_support/capybara_scopes_picker.rb +0 -92
- data/lib/decidim/dev/test/rspec_support/summary_notification.rb +0 -51
- data/lib/rubocop/cop/decidim/hash_shorthand_syntax_backports.rb +0 -175
- data/lib/rubocop/cop/decidim.rb +0 -9
- /data/app/views/decidim/{dummy_resources → dev}/dummy_resources/foo.html.erb +0 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Dev
|
5
|
+
class AdminEngine < Rails::Engine
|
6
|
+
engine_name "dummy_admin"
|
7
|
+
|
8
|
+
routes do
|
9
|
+
resources :dummy_resources do
|
10
|
+
resources :nested_dummy_resources
|
11
|
+
end
|
12
|
+
|
13
|
+
root to: proc { [200, {}, ["DUMMY ADMIN ENGINE"]] }
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "dummy_admin.imports" do
|
17
|
+
class ::DummyCreator < Decidim::Admin::Import::Creator
|
18
|
+
def self.resource_klass
|
19
|
+
Decidim::Dev::DummyResource
|
20
|
+
end
|
21
|
+
|
22
|
+
def produce
|
23
|
+
resource
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def resource
|
29
|
+
@resource ||= Decidim::Dev::DummyResource.new(
|
30
|
+
title: { en: "Dummy" },
|
31
|
+
author: context[:current_user],
|
32
|
+
component:
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def component
|
37
|
+
context[:current_component]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
john.doe@example.org;John Doe
|
2
|
+
jane.doe@example.org;Jane Doe
|
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
john.doe@example.org;John Doe (Roe)
|
2
|
+
jane.doe@example.org;Jane Doe
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
john.doe@example.org;John Doe
|
@@ -1,3 +1,5 @@
|
|
1
|
+
<!-- markdown-lint-disable-file single-h1 -->
|
2
|
+
|
1
3
|
# The great title for a new law
|
2
4
|
|
3
5
|
## A co-creation process to create creative creations
|
@@ -27,8 +29,8 @@ When an organization finishes a co-creation process, the accountability process
|
|
27
29
|
blah, blah, blah...
|
28
30
|
|
29
31
|
1. one
|
30
|
-
|
31
|
-
|
32
|
+
1. two
|
33
|
+
1. three
|
32
34
|
|
33
35
|
### Following up accounted results
|
34
36
|
|
@@ -1,22 +1,22 @@
|
|
1
|
-
Email
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
1
|
+
Email;;;;;;
|
2
|
+
acme@example.org;;;;;;
|
3
|
+
;;;;;;
|
4
|
+
;;;;;;
|
5
|
+
;;;;;;
|
6
|
+
;;;;;;
|
7
|
+
;;;;;;
|
8
|
+
;;;;;;
|
9
|
+
;;;;;;
|
10
|
+
;;;;;;
|
11
|
+
;;;;;;
|
12
|
+
;;;;;;
|
13
|
+
;;;;;;
|
14
|
+
;;;;;;
|
15
|
+
;;;;;;
|
16
|
+
;;;;;;
|
17
|
+
;;;;;;
|
18
|
+
;;;;;;
|
19
|
+
;;;;;;
|
20
|
+
;;;;;;
|
21
|
+
;;;;;;
|
22
|
+
;;;;;;
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Decidim.register_component(:dummy) do |component|
|
4
|
+
component.engine = Decidim::Dev::Engine
|
5
|
+
component.admin_engine = Decidim::Dev::AdminEngine
|
6
|
+
component.icon = "media/images/decidim_dev_dummy.svg"
|
7
|
+
|
8
|
+
component.actions = %w(foo bar)
|
9
|
+
|
10
|
+
component.newsletter_participant_entities = ["Decidim::Dev::DummyResource"]
|
11
|
+
|
12
|
+
component.settings(:global) do |settings|
|
13
|
+
settings.attribute :scopes_enabled, type: :boolean, default: false
|
14
|
+
settings.attribute :scope_id, type: :scope
|
15
|
+
settings.attribute :comments_enabled, type: :boolean, default: true
|
16
|
+
settings.attribute :comments_max_length, type: :integer, required: false
|
17
|
+
settings.attribute :resources_permissions_enabled, type: :boolean, default: true
|
18
|
+
settings.attribute :dummy_global_attribute1, type: :boolean
|
19
|
+
settings.attribute :dummy_global_attribute2, type: :boolean, readonly: ->(_context) { false }
|
20
|
+
settings.attribute :readonly_attribute, type: :boolean, default: true, readonly: ->(_context) { true }
|
21
|
+
settings.attribute :enable_pads_creation, type: :boolean, default: false
|
22
|
+
settings.attribute :amendments_enabled, type: :boolean, default: false
|
23
|
+
settings.attribute :dummy_global_translatable_text, type: :text, translated: true, editor: true, required: true
|
24
|
+
end
|
25
|
+
|
26
|
+
component.settings(:step) do |settings|
|
27
|
+
settings.attribute :comments_blocked, type: :boolean, default: false
|
28
|
+
settings.attribute :dummy_step_attribute1, type: :boolean
|
29
|
+
settings.attribute :dummy_step_attribute2, type: :boolean, readonly: ->(_context) { false }
|
30
|
+
settings.attribute :dummy_step_translatable_text, type: :text, translated: true, editor: true, required: true
|
31
|
+
settings.attribute :readonly_step_attribute, type: :boolean, default: true, readonly: ->(_context) { true }
|
32
|
+
settings.attribute :amendment_creation_enabled, type: :boolean, default: true
|
33
|
+
settings.attribute :amendment_reaction_enabled, type: :boolean, default: true
|
34
|
+
settings.attribute :amendment_promotion_enabled, type: :boolean, default: true
|
35
|
+
settings.attribute :amendments_visibility, type: :string, default: "all"
|
36
|
+
settings.attribute :endorsements_enabled, type: :boolean, default: false
|
37
|
+
settings.attribute :endorsements_blocked, type: :boolean, default: false
|
38
|
+
end
|
39
|
+
|
40
|
+
component.register_resource(:dummy_resource) do |resource|
|
41
|
+
resource.name = :dummy
|
42
|
+
resource.model_class_name = "Decidim::Dev::DummyResource"
|
43
|
+
resource.template = "decidim/dummy_resource/linked_dummys"
|
44
|
+
resource.actions = %w(foo)
|
45
|
+
resource.searchable = true
|
46
|
+
end
|
47
|
+
|
48
|
+
component.register_resource(:nested_dummy_resource) do |resource|
|
49
|
+
resource.name = :nested_dummy
|
50
|
+
resource.model_class_name = "Decidim::Dev::NestedDummyResource"
|
51
|
+
end
|
52
|
+
|
53
|
+
component.register_resource(:coauthorable_dummy_resource) do |resource|
|
54
|
+
resource.name = :coauthorable_dummy
|
55
|
+
resource.model_class_name = "Decidim::Dev::CoauthorableDummyResource"
|
56
|
+
resource.template = "decidim/coauthorabledummy_resource/linked_dummys"
|
57
|
+
resource.actions = %w(foo-coauthorable)
|
58
|
+
resource.searchable = false
|
59
|
+
end
|
60
|
+
|
61
|
+
component.register_stat :dummies_count_high, primary: true, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, _start_at, _end_at|
|
62
|
+
components.count * 10
|
63
|
+
end
|
64
|
+
|
65
|
+
component.register_stat :dummies_count_medium, primary: true, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, _start_at, _end_at|
|
66
|
+
components.count * 100
|
67
|
+
end
|
68
|
+
|
69
|
+
component.exports :dummies do |exports|
|
70
|
+
exports.collection do
|
71
|
+
[1, 2, 3]
|
72
|
+
end
|
73
|
+
|
74
|
+
exports.serializer Decidim::Dev::DummySerializer
|
75
|
+
end
|
76
|
+
|
77
|
+
component.imports :dummies do |imports|
|
78
|
+
imports.messages do |msg|
|
79
|
+
msg.set(:resource_name) { |count: 1| count == 1 ? "Dummy" : "Dummies" }
|
80
|
+
msg.set(:title) { "Import dummies" }
|
81
|
+
msg.set(:label) { "Import dummies from a file" }
|
82
|
+
end
|
83
|
+
|
84
|
+
imports.creator DummyCreator
|
85
|
+
imports.example do |import_component|
|
86
|
+
locales = import_component.organization.available_locales
|
87
|
+
translated = ->(name) { locales.map { |l| "#{name}/#{l}" } }
|
88
|
+
[
|
89
|
+
translated.call("title") + %w(body) + translated.call("translatable_text") + %w(address latitude longitude),
|
90
|
+
locales.map { "Title text" } + ["Body text"] + locales.map { "Translatable text" } + ["Fake street 1", 1.0, 1.0]
|
91
|
+
]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/decidim/dev/engine.rb
CHANGED
@@ -7,15 +7,33 @@ module Decidim
|
|
7
7
|
isolate_namespace Decidim::Dev
|
8
8
|
engine_name "decidim_dev"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
routes do
|
11
|
+
root to: proc { [200, {}, ["DUMMY ENGINE"]] }
|
12
|
+
|
13
|
+
resources :dummy_resources do
|
14
|
+
resources :nested_dummy_resources
|
15
|
+
get :foo, on: :member
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
19
|
+
initializer "decidim_dev.tools" do
|
20
|
+
# Disable if the boost performance mode is enabled
|
21
|
+
next if Rails.application.config.try(:boost_performance)
|
22
|
+
|
23
|
+
ActiveSupport.on_load(:action_controller) { include Decidim::Dev::NeedsDevelopmentTools } if Rails.env.development? || ENV.fetch("DECIDIM_DEV_ENGINE", nil)
|
24
|
+
end
|
25
|
+
|
16
26
|
initializer "decidim_dev.webpacker.assets_path" do
|
17
27
|
Decidim.register_assets_path File.expand_path("app/packs", root)
|
18
28
|
end
|
29
|
+
|
30
|
+
initializer "decidim_dev.moderation_content" do
|
31
|
+
config.to_prepare do
|
32
|
+
ActiveSupport::Notifications.subscribe("decidim.admin.block_user:after") do |_event_name, data|
|
33
|
+
Decidim::Dev::HideAllCreatedByAuthorJob.perform_later(**data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
19
37
|
end
|
20
38
|
end
|
21
39
|
end
|
@@ -1,8 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "decidim/components/namer"
|
4
|
+
|
3
5
|
FactoryBot.define do
|
4
6
|
factory :dummy_component, parent: :component do
|
5
7
|
name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :surveys).i18n_name }
|
6
8
|
manifest_name { :dummy }
|
7
9
|
end
|
10
|
+
|
11
|
+
factory :dummy_resource, class: "Decidim::Dev::DummyResource" do
|
12
|
+
transient do
|
13
|
+
users { nil }
|
14
|
+
# user_groups correspondence to users is by sorting order
|
15
|
+
user_groups { [] }
|
16
|
+
end
|
17
|
+
title { Decidim::Faker::Localized.localized { generate(:name) } }
|
18
|
+
component { create(:component, manifest_name: "dummy") }
|
19
|
+
author { create(:user, :confirmed, organization: component.organization) }
|
20
|
+
scope { create(:scope, organization: component.organization) }
|
21
|
+
|
22
|
+
trait :published do
|
23
|
+
published_at { Time.current }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :with_endorsements do
|
27
|
+
after :create do |resource|
|
28
|
+
5.times.collect do
|
29
|
+
create(:endorsement, resource:, author: build(:user, organization: resource.component.organization))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
factory :nested_dummy_resource, class: "Decidim::Dev::NestedDummyResource" do
|
36
|
+
title { generate(:name) }
|
37
|
+
dummy_resource { create(:dummy_resource) }
|
38
|
+
end
|
39
|
+
|
40
|
+
factory :coauthorable_dummy_resource, class: "Decidim::Dev::CoauthorableDummyResource" do
|
41
|
+
title { generate(:name) }
|
42
|
+
component { create(:component, manifest_name: "dummy") }
|
43
|
+
|
44
|
+
transient do
|
45
|
+
authors_list { [create(:user, organization: component.organization)] }
|
46
|
+
end
|
47
|
+
|
48
|
+
after :build do |resource, evaluator|
|
49
|
+
evaluator.authors_list.each do |coauthor|
|
50
|
+
resource.coauthorships << if coauthor.is_a?(Decidim::UserGroup)
|
51
|
+
build(:coauthorship, author: coauthor.users.first, user_group: coauthor, coauthorable: resource, organization: evaluator.component.organization)
|
52
|
+
else
|
53
|
+
build(:coauthorship, author: coauthor, coauthorable: resource, organization: evaluator.component.organization)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
8
58
|
end
|
@@ -9,14 +9,14 @@ shared_examples "with promoted participatory processes and groups" do
|
|
9
9
|
it "includes promoted participatory processes and groups placing groups in first place" do
|
10
10
|
create(
|
11
11
|
:participatory_process_group,
|
12
|
-
organization:
|
12
|
+
organization:
|
13
13
|
)
|
14
14
|
|
15
15
|
unpromoted_process = create(
|
16
16
|
:participatory_process,
|
17
17
|
:with_steps,
|
18
18
|
:published,
|
19
|
-
organization:
|
19
|
+
organization:
|
20
20
|
)
|
21
21
|
unpromoted_process.active_step.update!(end_date: Time.current.advance(days: 1))
|
22
22
|
|
@@ -25,14 +25,14 @@ shared_examples "with promoted participatory processes and groups" do
|
|
25
25
|
:with_steps,
|
26
26
|
:published,
|
27
27
|
:promoted,
|
28
|
-
organization:
|
28
|
+
organization:
|
29
29
|
)
|
30
30
|
promoted_process.active_step.update!(end_date: Time.current.advance(days: 2))
|
31
31
|
|
32
32
|
promoted_group = create(
|
33
33
|
:participatory_process_group,
|
34
34
|
:promoted,
|
35
|
-
organization:
|
35
|
+
organization:
|
36
36
|
)
|
37
37
|
|
38
38
|
_external_promoted_group = create(
|
@@ -51,14 +51,14 @@ shared_examples "with promoted participatory processes and groups" do
|
|
51
51
|
:with_steps,
|
52
52
|
:unpublished,
|
53
53
|
:promoted,
|
54
|
-
organization:
|
54
|
+
organization:
|
55
55
|
)
|
56
56
|
|
57
57
|
create(
|
58
58
|
:participatory_process,
|
59
59
|
:with_steps,
|
60
60
|
:unpublished,
|
61
|
-
organization:
|
61
|
+
organization:
|
62
62
|
)
|
63
63
|
|
64
64
|
last =
|
@@ -67,7 +67,7 @@ shared_examples "with promoted participatory processes and groups" do
|
|
67
67
|
:with_steps,
|
68
68
|
:published,
|
69
69
|
:promoted,
|
70
|
-
organization:
|
70
|
+
organization:
|
71
71
|
)
|
72
72
|
|
73
73
|
last.active_step.update!(end_date: nil)
|
@@ -78,7 +78,7 @@ shared_examples "with promoted participatory processes and groups" do
|
|
78
78
|
:with_steps,
|
79
79
|
:published,
|
80
80
|
:promoted,
|
81
|
-
organization
|
81
|
+
organization:,
|
82
82
|
end_date: Time.current.advance(days: 10)
|
83
83
|
)
|
84
84
|
|
@@ -90,7 +90,7 @@ shared_examples "with promoted participatory processes and groups" do
|
|
90
90
|
:with_steps,
|
91
91
|
:published,
|
92
92
|
:promoted,
|
93
|
-
organization
|
93
|
+
organization:,
|
94
94
|
end_date: Time.current.advance(days: 8)
|
95
95
|
)
|
96
96
|
|
@@ -1,5 +1,123 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module AxeMatchers
|
4
|
+
def self.axe_version
|
5
|
+
@axe_version ||= begin
|
6
|
+
package = JSON.load_file(Rails.root.join("node_modules/axe-core/package.json"))
|
7
|
+
package["version"]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.axe_mainline_version
|
12
|
+
@axe_mainline_version ||= axe_version.split(".")[0..1].join(".")
|
13
|
+
end
|
14
|
+
|
15
|
+
class ResultFormatter
|
16
|
+
def initialize(result)
|
17
|
+
@result = result
|
18
|
+
@violations = result["violations"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def format
|
22
|
+
<<~MESSAGE
|
23
|
+
|
24
|
+
Found #{violations.count} accessibility #{violations.count == 1 ? "violation" : "violations"}:
|
25
|
+
|
26
|
+
#{violation_messages.join("\n")}
|
27
|
+
MESSAGE
|
28
|
+
end
|
29
|
+
|
30
|
+
def violation_messages
|
31
|
+
violations.each_with_index.map do |violation, index|
|
32
|
+
nodes = violation["nodes"]
|
33
|
+
[
|
34
|
+
"#{index + 1}) #{violation["id"]}: #{violation["help"]} (#{violation["impact"]})",
|
35
|
+
indent_lines(violation["helpUrl"], 1),
|
36
|
+
indent_lines("The following #{nodes.length} #{nodes.length == 1 ? "node" : "nodes"} violate this rule:", 1),
|
37
|
+
"",
|
38
|
+
indent_lines(node_messages_for(nodes), 2),
|
39
|
+
""
|
40
|
+
]
|
41
|
+
end.flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :result, :violations
|
47
|
+
|
48
|
+
def indent_lines(lines, indent_level = 1)
|
49
|
+
indent = " " * indent_level
|
50
|
+
Array(lines).flatten.map { |line| line.length.positive? ? "#{indent}#{line}" : "" }.join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def node_messages_for(nodes)
|
54
|
+
nodes.map do |node|
|
55
|
+
[
|
56
|
+
"Selector: #{Array(node["target"]).join(", ")}",
|
57
|
+
("HTML: #{node["html"].gsub(/^\s*|\n*/, "")}" unless node["html"].nil?),
|
58
|
+
fix(node["all"], "Fix all of the following:"),
|
59
|
+
fix(node["none"], "Fix all of the following:"),
|
60
|
+
fix(node["any"], "Fix any of the following:")
|
61
|
+
].compact.presence.tap { |messages| messages&.push("") }
|
62
|
+
end.compact
|
63
|
+
end
|
64
|
+
|
65
|
+
def fix(checks, message)
|
66
|
+
valid_checks = checks.compact
|
67
|
+
[
|
68
|
+
(message unless valid_checks.empty?),
|
69
|
+
*valid_checks.map { |check| "- #{check["message"]}" }
|
70
|
+
].compact
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BeAxeClean
|
75
|
+
def matches?(page)
|
76
|
+
@results = execute_axe(page)
|
77
|
+
results["violations"].count.zero?
|
78
|
+
end
|
79
|
+
|
80
|
+
def failure_message
|
81
|
+
ResultFormatter.new(results).format
|
82
|
+
end
|
83
|
+
|
84
|
+
def failure_message_when_negated
|
85
|
+
"Expected to find accessibility violations. None were detected."
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
attr_reader :results
|
91
|
+
|
92
|
+
def execute_axe(page)
|
93
|
+
load_axe(page)
|
94
|
+
|
95
|
+
script = <<-JS
|
96
|
+
var callback = arguments[arguments.length - 1];
|
97
|
+
var context = document;
|
98
|
+
var options = {};
|
99
|
+
axe.run(context, options).then(res => JSON.parse(JSON.stringify(res))).then(callback);
|
100
|
+
JS
|
101
|
+
page = page.driver if page.respond_to?("driver")
|
102
|
+
page = page.browser if page.respond_to?("browser") && !page.browser.is_a?(::Symbol)
|
103
|
+
page.execute_async_script(script)
|
104
|
+
end
|
105
|
+
|
106
|
+
def load_axe(page)
|
107
|
+
jslib = Rails.root.join("node_modules/axe-core/axe.min.js")
|
108
|
+
page.execute_script jslib.read
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def be_axe_clean
|
113
|
+
BeAxeClean.new
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
RSpec.configure do |config|
|
118
|
+
config.include AxeMatchers
|
119
|
+
end
|
120
|
+
|
3
121
|
shared_examples_for "accessible page" do
|
4
122
|
it "passes accessibility tests" do
|
5
123
|
expect(page).to be_axe_clean
|
@@ -7,7 +125,7 @@ shared_examples_for "accessible page" do
|
|
7
125
|
|
8
126
|
it "passes HTML validation" do
|
9
127
|
# Capybara is stripping the doctype out of the HTML which is required for
|
10
|
-
# the validation. If it
|
128
|
+
# the validation. If it does not exist, add it there.
|
11
129
|
html = page.source
|
12
130
|
html = "<!DOCTYPE html>\n#{html}" unless html.strip.match?(/^<!DOCTYPE/i)
|
13
131
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bullet"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
Decidim::ApplicationJob.include(Bullet::ActiveJob) if defined?(Bullet::ActiveJob)
|
7
|
+
|
8
|
+
Rails.application.config.after_initialize do
|
9
|
+
Bullet.enable = Decidim::Env.new("DECIDIM_BULLET_ENABLED", "true").present?
|
10
|
+
Bullet.rails_logger = true
|
11
|
+
Bullet.raise = true
|
12
|
+
Bullet.stacktrace_includes = %w(decidim-)
|
13
|
+
|
14
|
+
# Detect N+1 queries
|
15
|
+
Bullet.n_plus_one_query_enable = Decidim::Env.new("DECIDIM_BULLET_N_PLUS_ONE", "false").present?
|
16
|
+
# Detect eager-loaded associations which are not used
|
17
|
+
Bullet.unused_eager_loading_enable = Decidim::Env.new("DECIDIM_BULLET_UNUSED_EAGER", "false").present?
|
18
|
+
# Detect unnecessary COUNT queries which could be avoided with a counter_cache
|
19
|
+
Bullet.counter_cache_enable = Decidim::Env.new("DECIDIM_BULLET_COUNTER_CACHE", "true").present?
|
20
|
+
end
|
21
|
+
|
22
|
+
if Bullet.enable?
|
23
|
+
config.before(:each) do
|
24
|
+
Bullet.start_request
|
25
|
+
end
|
26
|
+
|
27
|
+
config.after(:each) do
|
28
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
29
|
+
Bullet.end_request
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|