erblint-github 0.0.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e46c63a275b6e7d34b0aab81c0de051625bfbd7fc6c3fedb944a6d10a5ba3cff
4
- data.tar.gz: 33984e9857ae9a998a3ae56a9fd29c0bf89557d41eee3698cb13731327da7988
3
+ metadata.gz: 48eba970607504df53f6b0f2ce5cff3b07051148eb1f4dca2fd78fa8287671a9
4
+ data.tar.gz: 820da77f5a3ad624c32f4de22817e1c3d80ed755ddbe717368ef97e17e87e262
5
5
  SHA512:
6
- metadata.gz: 0404a048ebd068503aa1f7521a9c1f116165e3d9c507bee6e0a04c28ec3871b12fb53dc1141ff57981d013ac0af8d2bb41b21faea545c7741ff28f1d4e4c3b95
7
- data.tar.gz: c64a47d57b7bb87ed7f1437be1430f44f860f58e0d28a6acca5b530e442350e9d1e8f28d83fdf8230d731a0851e812f9ad82278bec964aefffdac16fc250a530
6
+ metadata.gz: 34b69cd67df4ea14698098a1331bd787cc9c653cb616f8020c0767478427a41144d177706397eaedc7f7f3767b60758231a000ab45d3fef2dcd8ee07249b49ad
7
+ data.tar.gz: d34ed13a0a85b146f4b2eff4e6fcbe00bbe7aef41d7168c6dadb2cc78a3fd49b42cc5dc92e6669cffb7b9bb81cf982495862fb46acb166a49dd63c9813d31d60
data/README.md CHANGED
@@ -23,19 +23,31 @@ require "erblint-github/linters"
23
23
  ```yaml
24
24
  ---
25
25
  linters:
26
+ GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled:
27
+ enabled: true
28
+ GitHub::Accessibility::IframeHasTitle:
29
+ enabled: true
26
30
  GitHub::Accessibility::ImageHasAlt:
27
31
  enabled: true
28
- GitHub::Accessibility::NoAriaLabelMisuse:
32
+ GitHub::Accessibility::NoAriaLabelMisuseCounter:
33
+ enabled: true
34
+ GitHub::Accessibility::NoPositiveTabIndex:
29
35
  enabled: true
30
36
  GitHub::Accessibility::NoRedundantImageAlt:
31
37
  enabled: true
38
+ GitHub::Accessibility::NoTitleAttributeCounter:
39
+ enabled: true
32
40
  ```
33
41
 
34
- ### Rules
42
+ ## Rules
35
43
 
36
- - [GitHub::Accessibility::ImageHasAlt](./docs/rules/accessibility/no-aria-label-misuse.md)
37
- - [GitHub::Accessibility::NoAriaLabelMisuse](./docs/rules/accessibility/image-has-alt.md)
44
+ - [GitHub::Accessibility::AvoidBothDisabledAndAriaDisabled](./docs/rules/accessibility/avoid-both-disabled-and-aria-disabled.md)
45
+ - [GitHub::Accessibility::IframeHasTitle](./docs/rules/accessibility/iframe-has-title.md)
46
+ - [GitHub::Accessibility::ImageHasAlt](./docs/rules/accessibility/image-has-alt.md)
47
+ - [GitHub::Accessibility::NoAriaLabelMisuseCounter](./docs/rules/accessibility/no-aria-label-misuse-counter.md)
48
+ - [GitHub::Accessibility::NoPositiveTabIndex](./docs/rules/accessibility/no-positive-tab-index.md)
38
49
  - [GitHub::Accessibility::NoRedundantImageAlt](./docs/rules/accessibility/no-redundant-image-alt.md)
50
+ - [GitHub::Accessibility::NoTitleAttributeCounter](./docs/rules/accessibility/no-title-attribute-counter.md)
39
51
 
40
52
  ## Testing
41
53
 
@@ -11,7 +11,7 @@ module ERBLint
11
11
  indicator_node, _, code_node, = *node
12
12
  indicator = indicator_node&.loc&.source
13
13
  comment = code_node&.loc&.source&.strip
14
- rule_name = self.class.name.gsub("ERBLint::Linters::", "")
14
+ rule_name = simple_class_name
15
15
 
16
16
  if indicator == "#" && comment.start_with?("erblint:disable") && comment.match(rule_name)
17
17
  if @offenses.any?
@@ -24,10 +24,43 @@ module ERBLint
24
24
  end
25
25
  end
26
26
 
27
+ def counter_correct?(processed_source)
28
+ comment_node = nil
29
+ expected_count = 0
30
+ rule_name = simple_class_name
31
+ offenses_count = @offenses.length
32
+
33
+ processed_source.parser.ast.descendants(:erb).each do |node|
34
+ indicator_node, _, code_node, = *node
35
+ indicator = indicator_node&.loc&.source
36
+ comment = code_node&.loc&.source&.strip
37
+
38
+ if indicator == "#" && comment.start_with?("erblint:counter") && comment.match(rule_name)
39
+ comment_node = node
40
+ expected_count = comment.match(/\s(\d+)\s?$/)[1].to_i
41
+ end
42
+ end
43
+
44
+ if offenses_count.zero?
45
+ # have to adjust to get `\n` so we delete the whole line
46
+ add_offense(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:counter comment for #{rule_name}", "") if comment_node
47
+ return
48
+ end
49
+
50
+ first_offense = @offenses[0]
51
+
52
+ if comment_node.nil?
53
+ add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:counter #{rule_name} #{offenses_count} %> to bypass this check.", "<%# erblint:counter #{rule_name} #{offenses_count} %>")
54
+ else
55
+ clear_offenses
56
+ add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", "<%# erblint:counter #{rule_name} #{offenses_count} %>") if expected_count != offenses_count
57
+ end
58
+ end
59
+
27
60
  def generate_offense(klass, processed_source, tag, message = nil, replacement = nil)
28
61
  message ||= klass::MESSAGE
29
- klass_name = klass.name.demodulize
30
- offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
62
+ message += "\nLearn more at https://github.com/github/erblint-github#rules.\n"
63
+ offense = ["#{simple_class_name}:#{message}", tag.node.loc.source].join("\n")
31
64
  add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
32
65
  end
33
66
 
@@ -45,6 +78,10 @@ module ERBLint
45
78
  def tags(processed_source)
46
79
  processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
47
80
  end
81
+
82
+ def simple_class_name
83
+ self.class.name.gsub("ERBLint::Linters::", "")
84
+ end
48
85
  end
49
86
  end
50
87
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class AvoidBothDisabledAndAriaDisabled < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT = %w[button fieldset input optgroup option select textarea].freeze
14
+ MESSAGE = "[aria-disabled] may be used in place of native HTML [disabled] to allow tab-focus on an otherwise ignored element. Setting both attributes is contradictory."
15
+
16
+ def run(processed_source)
17
+ tags(processed_source).each do |tag|
18
+ next if tag.closing?
19
+ next unless ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT.include?(tag.name)
20
+ next unless tag.attributes["disabled"] && tag.attributes["aria-disabled"]
21
+
22
+ generate_offense(self.class, processed_source, tag)
23
+ end
24
+
25
+ rule_disabled?(processed_source)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class IframeHasTitle < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "<iframe> with meaningful content should have a title attribute that identifies the content."\
14
+ " If <iframe> has no meaningful content, hide it from assistive technology with `aria-hidden='true'`."\
15
+
16
+ def run(processed_source)
17
+ tags(processed_source).each do |tag|
18
+ next if tag.name != "iframe"
19
+ next if tag.closing?
20
+
21
+ title = possible_attribute_values(tag, "title")
22
+
23
+ generate_offense(self.class, processed_source, tag) if title.empty? && !aria_hidden?(tag)
24
+ end
25
+
26
+ rule_disabled?(processed_source)
27
+ end
28
+
29
+ private
30
+
31
+ def aria_hidden?(tag)
32
+ tag.attributes["aria-hidden"]&.value&.present?
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -6,7 +6,7 @@ module ERBLint
6
6
  module Linters
7
7
  module GitHub
8
8
  module Accessibility
9
- class NoAriaLabelMisuse < Linter
9
+ class NoAriaLabelMisuseCounter < Linter
10
10
  include ERBLint::Linters::CustomHelpers
11
11
  include LinterRegistry
12
12
 
@@ -34,7 +34,21 @@ module ERBLint
34
34
  end
35
35
  end
36
36
  end
37
- rule_disabled?(processed_source)
37
+ counter_correct?(processed_source)
38
+ end
39
+
40
+ def autocorrect(processed_source, offense)
41
+ return unless offense.context
42
+
43
+ lambda do |corrector|
44
+ if processed_source.file_content.include?("erblint:counter #{simple_class_name}")
45
+ # update the counter if exists
46
+ corrector.replace(offense.source_range, offense.context)
47
+ else
48
+ # add comment with counter if none
49
+ corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
50
+ end
51
+ end
38
52
  end
39
53
  end
40
54
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class NoPositiveTabIndex < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "Do not use positive tabindex as it is error prone and can severely disrupt navigation experience for keyboard users"
14
+
15
+ def run(processed_source)
16
+ tags(processed_source).each do |tag|
17
+ next if tag.closing?
18
+ next unless tag.attributes["tabindex"]&.value.to_i.positive?
19
+
20
+ generate_offense(self.class, processed_source, tag)
21
+ end
22
+
23
+ rule_disabled?(processed_source)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../custom_helpers"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module GitHub
8
+ module Accessibility
9
+ class NoTitleAttributeCounter < Linter
10
+ include ERBLint::Linters::CustomHelpers
11
+ include LinterRegistry
12
+
13
+ MESSAGE = "The title attribute should never be used unless for an `<iframe>` as it is inaccessible for several groups of users."
14
+
15
+ def run(processed_source)
16
+ tags(processed_source).each do |tag|
17
+ next if tag.name == "iframe"
18
+ next if tag.closing?
19
+
20
+ title = possible_attribute_values(tag, "title")
21
+ generate_offense(self.class, processed_source, tag) if title.present?
22
+ end
23
+
24
+ counter_correct?(processed_source)
25
+ end
26
+
27
+ def autocorrect(processed_source, offense)
28
+ return unless offense.context
29
+
30
+ lambda do |corrector|
31
+ if processed_source.file_content.include?("erblint:counter #{simple_class_name}")
32
+ # update the counter if exists
33
+ corrector.replace(offense.source_range, offense.context)
34
+ else
35
+ # add comment with counter if none
36
+ corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :docs do
4
+ task :coverage do
5
+ require "erb_lint/all"
6
+ require "erblint-github/linters"
7
+
8
+ Dir[File.join(__dir__, "linters", "github/**/*.rb")].sort.each do |file|
9
+ rule_documentation_path = file
10
+ .gsub("#{__dir__}linters/github/", "docs/rules/")
11
+ .gsub(".rb", ".md")
12
+ .tr("_", "-")
13
+ raise "Missing rule documentation. Please document rule in #{rule_documentation_path}" unless File.file?(rule_documentation_path.to_s)
14
+ end
15
+ puts "All rules have been properly documented."
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erblint-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-01 00:00:00.000000000 Z
11
+ date: 2022-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erb_lint
@@ -105,9 +105,14 @@ files:
105
105
  - README.md
106
106
  - lib/erblint-github/linters.rb
107
107
  - lib/erblint-github/linters/custom_helpers.rb
108
+ - lib/erblint-github/linters/github/accessibility/avoid_both_disabled_and_aria_disabled.rb
109
+ - lib/erblint-github/linters/github/accessibility/iframe_has_title.rb
108
110
  - lib/erblint-github/linters/github/accessibility/image_has_alt.rb
109
- - lib/erblint-github/linters/github/accessibility/no_aria_label_misuse.rb
111
+ - lib/erblint-github/linters/github/accessibility/no_aria_label_misuse_counter.rb
112
+ - lib/erblint-github/linters/github/accessibility/no_positive_tab_index.rb
110
113
  - lib/erblint-github/linters/github/accessibility/no_redundant_image_alt.rb
114
+ - lib/erblint-github/linters/github/accessibility/no_title_attribute_counter.rb
115
+ - lib/tasks/docs.rake
111
116
  homepage: https://github.com/github/erblint-github
112
117
  licenses:
113
118
  - MIT
@@ -127,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
132
  - !ruby/object:Gem::Version
128
133
  version: '0'
129
134
  requirements: []
130
- rubygems_version: 3.0.3
135
+ rubygems_version: 3.2.32
131
136
  signing_key:
132
137
  specification_version: 4
133
138
  summary: erblint GitHub