diff --git a/lib/rubocop/cop/discourse/fabricator_shorthand.rb b/lib/rubocop/cop/discourse/fabricator_shorthand.rb new file mode 100644 index 0000000..744793a --- /dev/null +++ b/lib/rubocop/cop/discourse/fabricator_shorthand.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Discourse + # When fabricating a record without custom attributes, we can use the + # fabricator shorthand as long as the identifier matches the fabricator + # name. + # + # @example + # + # # bad + # fab!(:user) { Fabricate(:user) } + # + # # good + # fab!(:user) + # + # When using custom attributes or the identifier doesn't match, the + # shorthand can't be used. + # + # @example + # + # # good + # fab!(:user) { Fabricate(:user, trust_level: TrustLevel[0]) } + # + # # good + # fab!(:another_user) { Fabricate(:user) } + class FabricatorShorthand < Base + def_node_matcher :offending_fabricator?, <<-MATCHER + (block + (send nil? :fab! + (sym $_identifier)) + (args) + (send nil? :Fabricate + (sym $_identifier))) + MATCHER + + def on_block(node) + offending_fabricator?(node) do |identifier| + add_offense(node, message: message(identifier)) + end + end + + private + + def message(identifier) + "Use the fabricator shorthand: `fab!(:#{identifier})`" + end + end + end + end +end diff --git a/spec/lib/rubocop/cop/fabricator_shorthand_spec.rb b/spec/lib/rubocop/cop/fabricator_shorthand_spec.rb new file mode 100644 index 0000000..203cf23 --- /dev/null +++ b/spec/lib/rubocop/cop/fabricator_shorthand_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe RuboCop::Cop::Discourse::FabricatorShorthand, :config do + subject(:cop) { described_class.new(config) } + + let(:config) { RuboCop::Config.new } + + it "registers an offense when not using the fabricator shorthand" do + expect_offense(<<~RUBY) + RSpec.describe "Foo" do + fab!(:foo) { Fabricate(:foo) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Discourse/FabricatorShorthand: Use the fabricator shorthand: `fab!(:foo)` + end + RUBY + end + + it "does not register an offense when the fabricator has attributes" do + expect_no_offenses(<<~RUBY) + RSpec.describe "Foo" do + fab!(:foo) { Fabricate(:foo, bar: 1) } + end + RUBY + end + + it "does not register an offense when the identifier doesn't match" do + expect_no_offenses(<<~RUBY) + RSpec.describe "Foo" do + fab!(:bar) { Fabricate(:foo) } + end + RUBY + end +end diff --git a/stree-compat.yml b/stree-compat.yml index 70a7d6f..91d39e8 100644 --- a/stree-compat.yml +++ b/stree-compat.yml @@ -22,3 +22,8 @@ AllCops: Discourse: Enabled: true + +Discourse/FabricatorShorthand: + Enabled: true + Include: + - 'spec/**/*_spec.rb'