diff --git a/spec/ameba/rule/lint/spec_filename_spec.cr b/spec/ameba/rule/lint/spec_filename_spec.cr new file mode 100644 index 00000000..44d51dc8 --- /dev/null +++ b/spec/ameba/rule/lint/spec_filename_spec.cr @@ -0,0 +1,36 @@ +require "../../../spec_helper" + +module Ameba::Rule::Lint + subject = SpecFilename.new + + describe SpecFilename do + it "passes if filename is correct" do + expect_no_issues subject, code: "", path: "spec/foo_spec.cr" + expect_no_issues subject, code: "", path: "spec/foo/bar_spec.cr" + end + + it "fails if filename is wrong" do + expect_issue subject, <<-CRYSTAL, path: "spec/foo.cr" + + # ^{} error: Spec filename should have `_spec` suffix: foo_spec.cr, not foo.cr + CRYSTAL + end + + context "properties" do + context "#ignored_dirs" do + it "provide sane defaults" do + expect_no_issues subject, code: "", path: "spec/support/foo.cr" + expect_no_issues subject, code: "", path: "spec/fixtures/foo.cr" + expect_no_issues subject, code: "", path: "spec/data/foo.cr" + end + end + + context "#ignored_filenames" do + it "ignores spec_helper by default" do + expect_no_issues subject, code: "", path: "spec/spec_helper.cr" + expect_no_issues subject, code: "", path: "spec/foo/spec_helper.cr" + end + end + end + end +end diff --git a/src/ameba/rule/lint/spec_filename.cr b/src/ameba/rule/lint/spec_filename.cr new file mode 100644 index 00000000..9711806f --- /dev/null +++ b/src/ameba/rule/lint/spec_filename.cr @@ -0,0 +1,50 @@ +require "file_utils" + +module Ameba::Rule::Lint + # A rule that enforces spec filenames to have `_spec` suffix. + # + # YAML configuration example: + # + # ``` + # Lint/SpecFilename: + # Enabled: true + # ``` + class SpecFilename < Base + properties do + description "Enforces spec filenames to have `_spec` suffix" + ignored_dirs %w[spec/support spec/fixtures spec/data] + ignored_filenames %w[spec_helper] + end + + MSG = "Spec filename should have `_spec` suffix: %s.cr, not %s.cr" + + private LOCATION = {1, 1} + + # TODO: fix the assumption that *source.path* contains relative path + def test(source : Source) + path_ = Path[source.path].to_posix + name = path_.stem + path = path_.to_s + + # check files only within spec/ directory + return unless path.starts_with?("spec/") + # ignore files having `_spec` suffix + return if name.ends_with?("_spec") + + # ignore known false-positives + ignored_dirs.each do |substr| + return if path.starts_with?("#{substr}/") + end + return if name.in?(ignored_filenames) + + expected = "#{name}_spec" + + issue_for LOCATION, MSG % {expected, name} do + new_path = + path_.sibling(expected + path_.extension) + + FileUtils.mv(path, new_path) + end + end + end +end