Skip to content

Commit 7bba2de

Browse files
committed
add dependencies, linter, test
1 parent e3538d0 commit 7bba2de

10 files changed

Lines changed: 208 additions & 14 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.gem
2+
*.rbc
3+
vendor
4+
Gemfile.lock

.rubocop.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
inherit_gem:
2+
rubocop-github:
3+
- config/default_edge.yml
4+
- config/rails_edge.yml
5+
6+
AllCops:
7+
NewCops: enable
8+
9+
Rails/RefuteMethods:
10+
Enabled: false
11+
12+
Style/Documentation:
13+
Enabled: false

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
gemspec

Rakefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
require "bundler/gem_tasks"
4+
require "rake/testtask"
5+
6+
Rake::TestTask.new do |t|
7+
t.libs << "test"
8+
end
9+
10+
desc "Run tests"
11+
task default: :test

erblint-github.gemspec

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
# frozen_string_literal: true
22

33
Gem::Specification.new do |s|
4-
s.name = "erblint-github"
5-
s.version = "0.0.1"
6-
s.summary = "erblint GitHub"
7-
s.description = "Code style checking for GitHub Ruby repositories"
8-
s.homepage = "https://github.com/github/erblint-github"
9-
s.license = "MIT"
10-
11-
s.files = Dir["README.md", "LICENSE"]
12-
13-
s.required_ruby_version = ">= 2.5.0"
14-
15-
s.email = ["opensource+erblint-github@github.com"]
16-
s.authors = ["GitHub Open Source"]
17-
end
4+
s.name = "erblint-github"
5+
s.version = "0.0.1"
6+
s.summary = "erblint GitHub"
7+
s.description = "Code style checking for GitHub Ruby repositories"
8+
s.homepage = "https://github.com/github/erblint-github"
9+
s.license = "MIT"
10+
11+
s.files = Dir["README.md", "LICENSE", "lib/**/*"]
12+
s.require_paths = ["lib"]
13+
14+
s.required_ruby_version = ">= 2.5.0"
15+
16+
s.email = ["opensource+erblint-github@github.com"]
17+
s.authors = ["GitHub Open Source"]
18+
19+
s.add_runtime_dependency "actionview", ">= 5.0.0"
20+
s.add_runtime_dependency "activesupport", ">= 5.0.0"
21+
22+
s.add_development_dependency "erb_lint"
23+
s.add_development_dependency "minitest", "~> 5.14"
24+
s.add_development_dependency "mocha"
25+
s.add_development_dependency "rake", "~> 12.0"
26+
27+
s.add_development_dependency "rubocop", "= 1.13.0"
28+
s.add_development_dependency "rubocop-github", "~> 0.16.0"
29+
end
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# frozen_string_literal: true
2+
3+
require "json"
4+
require "openssl"
5+
6+
module ERBLint
7+
module Linters
8+
module CustomHelpers
9+
def rule_disabled?(processed_source)
10+
processed_source.parser.ast.descendants(:erb).each do |node|
11+
indicator_node, _, code_node, = *node
12+
indicator = indicator_node&.loc&.source
13+
comment = code_node&.loc&.source&.strip
14+
rule_name = self.class.name.match(/:?:?(\w+)\Z/)[1]
15+
16+
if indicator == "#" && comment.start_with?("erblint:disable") && comment.match(rule_name)
17+
if @offenses.any?
18+
clear_offenses
19+
else
20+
add_offense(processed_source.to_source_range(code_node.loc),
21+
"Unused erblint:disable comment for #{rule_name}")
22+
end
23+
end
24+
end
25+
end
26+
27+
def generate_offense(klass, processed_source, tag, message = nil, replacement = nil)
28+
message ||= klass::MESSAGE
29+
klass_name = klass.name.demodulize
30+
offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
31+
add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
32+
end
33+
34+
def possible_attribute_values(tag, attr_name)
35+
value = tag.attributes[attr_name]&.value || nil
36+
basic_conditional_code_check(value || "") || [value].compact
37+
end
38+
39+
# Map possible values from condition
40+
def basic_conditional_code_check(code)
41+
conditional_match = code.match(/["'](.+)["']\sif|unless\s.+/) || code.match(/.+\s?\s["'](.+)["']\s:\s["'](.+)["']/)
42+
[conditional_match[1], conditional_match[2]].compact if conditional_match
43+
end
44+
45+
def tags(processed_source)
46+
processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
47+
end
48+
end
49+
end
50+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 NoRedundantImageAlt < Linter
10+
include ERBLint::Linters::CustomHelpers
11+
include LinterRegistry
12+
13+
MESSAGE = "<img> alt prop should not contain `image` or `picture` as screen readers already announce the element as an image"
14+
REDUNDANT_ALT_WORDS = %w[image picture].freeze
15+
16+
def run(processed_source)
17+
tags(processed_source).each do |tag|
18+
next if tag.name != "img"
19+
next if tag.closing?
20+
21+
alt = possible_attribute_values(tag, "alt").join
22+
next if alt.empty?
23+
24+
generate_offense(self.class, processed_source, tag) if (alt.downcase.split & REDUNDANT_ALT_WORDS).any?
25+
end
26+
27+
rule_disabled?(processed_source)
28+
end
29+
end
30+
end
31+
end
32+
end
33+
end

test/linter_test_case.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
class LinterTestCase < Minitest::Test
4+
def setup
5+
@linter = linter_class&.new(file_loader, linter_class.config_schema.new)
6+
end
7+
8+
def linter_class
9+
raise NotImplementedError
10+
end
11+
12+
def offenses
13+
@linter.offenses
14+
end
15+
16+
def file_loader
17+
ERBLint::FileLoader.new(".")
18+
end
19+
20+
def processed_source
21+
ERBLint::ProcessedSource.new("file.rb", @file)
22+
end
23+
24+
def tags
25+
processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
26+
end
27+
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
require "erb-lint/linters/github/accessibility/no_redundant_image_alt"
5+
6+
class NoRedundantImageAltTest < LinterTestCase
7+
def linter_class
8+
ERBLint::Linters::GitHub::Accessibility::NoRedundantImageAlt
9+
end
10+
11+
def test_warns_if_alt_contains_image
12+
@file = "<img alt='image of an octopus'></img>"
13+
@linter.run(processed_source)
14+
15+
refute_empty @linter.offenses
16+
end
17+
18+
def test_warns_if_alt_contains_picture
19+
@file = "<img alt='picture of an octopus'></img>"
20+
@linter.run(processed_source)
21+
22+
refute_empty @linter.offenses
23+
end
24+
25+
def test_does_not_warn_if_alt_contains_no_redundant_text
26+
@file = "<img alt='an octopus'></img>"
27+
@linter.run(processed_source)
28+
29+
assert_empty @linter.offenses
30+
end
31+
end

test/test_helper.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
ENV["RAILS_ENV"] = "test"
4+
5+
require "erb_lint/all"
6+
require "minitest"
7+
require "minitest/autorun"
8+
require "mocha/minitest"
9+
require "linter_test_case"

0 commit comments

Comments
 (0)