diff options
-rw-r--r-- | app/assets/stylesheets/pages/issuable.scss | 4 | ||||
-rw-r--r-- | app/models/dashboard_group_milestone.rb | 1 | ||||
-rw-r--r-- | app/views/layouts/devise.html.haml | 2 | ||||
-rw-r--r-- | changelogs/unreleased/42125-extend-override-check-to-also-check-arity.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/55484-fix-edit-button.yml | 4 | ||||
-rw-r--r-- | db/fixtures/development/24_forks.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/utils/override.rb | 109 | ||||
-rw-r--r-- | qa/qa/page/base.rb | 8 | ||||
-rw-r--r-- | qa/qa/page/main/login.rb | 12 | ||||
-rw-r--r-- | qa/qa/page/main/menu.rb | 8 | ||||
-rw-r--r-- | qa/qa/page/project/issue/show.rb | 6 | ||||
-rw-r--r-- | qa/qa/support/page/logging.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab/utils/override_spec.rb | 32 |
13 files changed, 147 insertions, 52 deletions
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 5b5f486ea63..a1069aa9783 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -60,6 +60,10 @@ padding: 0; margin-bottom: $gl-padding; border-bottom: 0; + word-wrap: break-word; + overflow-wrap: break-word; + min-width: 0; + width: 100%; } .btn-edit { diff --git a/app/models/dashboard_group_milestone.rb b/app/models/dashboard_group_milestone.rb index 32e8104125c..ad0bb55f0a7 100644 --- a/app/models/dashboard_group_milestone.rb +++ b/app/models/dashboard_group_milestone.rb @@ -5,7 +5,6 @@ class DashboardGroupMilestone < GlobalMilestone attr_reader :group_name - override :initialize def initialize(milestone) super(milestone.title, Array(milestone)) diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 4f8db74382f..6003d973c88 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,7 +1,7 @@ !!! 5 %html.devise-layout-html{ class: system_message_class } = render "layouts/head" - %body.ui-indigo.login-page.application.navless{ data: { page: body_data_page } } + %body.ui-indigo.login-page.application.navless.qa-login-page{ data: { page: body_data_page } } .page-wrap = render "layouts/header/empty" .login-page-broadcast diff --git a/changelogs/unreleased/42125-extend-override-check-to-also-check-arity.yml b/changelogs/unreleased/42125-extend-override-check-to-also-check-arity.yml new file mode 100644 index 00000000000..9892466ca50 --- /dev/null +++ b/changelogs/unreleased/42125-extend-override-check-to-also-check-arity.yml @@ -0,0 +1,5 @@ +--- +title: Extend override check to also check arity +merge_request: 23498 +author: Jacopo Beschi @jacopo-beschi +type: added diff --git a/changelogs/unreleased/55484-fix-edit-button.yml b/changelogs/unreleased/55484-fix-edit-button.yml new file mode 100644 index 00000000000..c8998cba248 --- /dev/null +++ b/changelogs/unreleased/55484-fix-edit-button.yml @@ -0,0 +1,4 @@ +title: Fix edit button disappearing in issue title +merge_request: 23948 +author: Ruben Moya +type: fixed diff --git a/db/fixtures/development/24_forks.rb b/db/fixtures/development/24_forks.rb index 61e39c871e6..5eb5956ec74 100644 --- a/db/fixtures/development/24_forks.rb +++ b/db/fixtures/development/24_forks.rb @@ -4,6 +4,12 @@ Sidekiq::Testing.inline! do Gitlab::Seeder.quiet do User.all.sample(10).each do |user| source_project = Project.public_only.sample + + ## + # 04_project.rb might not have created a public project because + # we use randomized approach (e.g. `Array#sample`). + return unless source_project + fork_project = Projects::ForkService.new(source_project, user, namespace: user.namespace).execute if fork_project.valid? diff --git a/lib/gitlab/utils/override.rb b/lib/gitlab/utils/override.rb index c412961ea3f..c87e97d0213 100644 --- a/lib/gitlab/utils/override.rb +++ b/lib/gitlab/utils/override.rb @@ -4,16 +4,11 @@ module Gitlab module Utils module Override class Extension - def self.verify_class!(klass, method_name) - instance_method_defined?(klass, method_name) || - raise( - NotImplementedError.new( - "#{klass}\##{method_name} doesn't exist!")) - end - - def self.instance_method_defined?(klass, name, include_super: true) - klass.instance_methods(include_super).include?(name) || - klass.private_instance_methods(include_super).include?(name) + def self.verify_class!(klass, method_name, arity) + extension = new(klass) + parents = extension.parents_for(klass) + extension.verify_method!( + klass: klass, parents: parents, method_name: method_name, sub_method_arity: arity) end attr_reader :subject @@ -22,35 +17,77 @@ module Gitlab @subject = subject end - def add_method_name(method_name) - method_names << method_name - end - - def add_class(klass) - classes << klass + def parents_for(klass) + index = klass.ancestors.index(subject) + klass.ancestors.drop(index + 1) end def verify! classes.each do |klass| - index = klass.ancestors.index(subject) - parents = klass.ancestors.drop(index + 1) - - method_names.each do |method_name| - parents.any? do |parent| - self.class.instance_method_defined?( - parent, method_name, include_super: false) - end || - raise( - NotImplementedError.new( - "#{klass}\##{method_name} doesn't exist!")) + parents = parents_for(klass) + + method_names.each_pair do |method_name, arity| + verify_method!( + klass: klass, + parents: parents, + method_name: method_name, + sub_method_arity: arity) end end end + def verify_method!(klass:, parents:, method_name:, sub_method_arity:) + overridden_parent = parents.find do |parent| + instance_method_defined?(parent, method_name) + end + + raise NotImplementedError.new("#{klass}\##{method_name} doesn't exist!") unless overridden_parent + + super_method_arity = find_direct_method(overridden_parent, method_name).arity + + unless arity_compatible?(sub_method_arity, super_method_arity) + raise NotImplementedError.new("#{subject}\##{method_name} has arity of #{sub_method_arity}, but #{overridden_parent}\##{method_name} has arity of #{super_method_arity}") + end + end + + def add_method_name(method_name, arity = nil) + method_names[method_name] = arity + end + + def add_class(klass) + classes << klass + end + + def verify_override?(method_name) + method_names.has_key?(method_name) + end + private + def instance_method_defined?(klass, name) + klass.instance_methods(false).include?(name) || + klass.private_instance_methods(false).include?(name) + end + + def find_direct_method(klass, name) + method = klass.instance_method(name) + method = method.super_method until method && klass == method.owner + method + end + + def arity_compatible?(sub_method_arity, super_method_arity) + if sub_method_arity >= 0 && super_method_arity >= 0 + # Regular arguments + sub_method_arity == super_method_arity + else + # It's too complex to check this case, just allow sub-method having negative arity + # But we don't allow sub_method_arity > 0 yet super_method_arity < 0 + sub_method_arity < 0 + end + end + def method_names - @method_names ||= [] + @method_names ||= {} end def classes @@ -80,11 +117,21 @@ module Gitlab def override(method_name) return unless ENV['STATIC_VERIFICATION'] + Override.extensions[self] ||= Extension.new(self) + Override.extensions[self].add_method_name(method_name) + end + + def method_added(method_name) + super + + return unless ENV['STATIC_VERIFICATION'] + return unless Override.extensions[self]&.verify_override?(method_name) + + method_arity = instance_method(method_name).arity if is_a?(Class) - Extension.verify_class!(self, method_name) + Extension.verify_class!(self, method_name, method_arity) else # We delay the check for modules - Override.extensions[self] ||= Extension.new(self) - Override.extensions[self].add_method_name(method_name) + Override.extensions[self].add_method_name(method_name, method_arity) end end diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index d6b763ed5fc..5788dceaaae 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -80,8 +80,8 @@ module QA page.evaluate_script('xhr.status') == 200 end - def find_element(name, wait: Capybara.default_max_wait_time) - find(element_selector_css(name), wait: wait) + def find_element(name, text_filter = nil, wait: Capybara.default_max_wait_time) + find(element_selector_css(name), wait: wait, text: text_filter) end def all_elements(name) @@ -108,8 +108,8 @@ module QA element.select value.to_s.capitalize end - def has_element?(name) - has_css?(element_selector_css(name)) + def has_element?(name, wait: Capybara.default_max_wait_time) + has_css?(element_selector_css(name), wait: wait) end def has_no_text?(text) diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index 97ffe0e5716..cb83ace20b6 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -35,13 +35,21 @@ module QA element :saml_login_button end + view 'app/views/layouts/devise.html.haml' do + element :login_page + end + def initialize # The login page is usually the entry point for all the scenarios so # we need to wait for the instance to start. That said, in some cases # we are already logged-in so we check both cases here. + # Check if we're already logged in first. If we don't then we have to + # wait 10 seconds for the check for the login page to fail every time + # we use this class when we're already logged in (E.g., whenever we + # create a personal access token to use for API access). wait(max: 500) do - has_css?('.login-page') || - Page::Main::Menu.act { has_personal_area?(wait: 0) } + Page::Main::Menu.act { has_personal_area?(wait: 0) } || + has_element?(:login_page) end end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index cc2724618e9..6804cc8fb20 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -63,15 +63,11 @@ module QA end def has_personal_area?(wait: Capybara.default_max_wait_time) - using_wait_time(wait) do - page.has_selector?(element_selector_css(:user_avatar)) - end + has_element?(:user_avatar, wait: wait) end def has_admin_area_link?(wait: Capybara.default_max_wait_time) - using_wait_time(wait) do - page.has_selector?(element_selector_css(:admin_area_link)) - end + has_element?(:admin_area_link, wait: wait) end private diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index 23def93c7dd..9ec6d90719e 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -37,17 +37,17 @@ module QA def select_comments_only_filter click_element :discussion_filter - all_elements(:filter_options)[1].click + find_element(:filter_options, "Show comments only").click end def select_history_only_filter click_element :discussion_filter - all_elements(:filter_options).last.click + find_element(:filter_options, "Show history only").click end def select_all_activities_filter click_element :discussion_filter - all_elements(:filter_options).first.click + find_element(:filter_options, "Show all activity").click end end end diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb index 5765a8041cc..df3b794b14b 100644 --- a/qa/qa/support/page/logging.rb +++ b/qa/qa/support/page/logging.rb @@ -77,7 +77,7 @@ module QA super end - def has_element?(name) + def has_element?(name, wait: Capybara.default_max_wait_time) found = super log("has_element? :#{name} returned #{found}") diff --git a/spec/lib/gitlab/utils/override_spec.rb b/spec/lib/gitlab/utils/override_spec.rb index fc08ebcfc6d..9e7c97f8095 100644 --- a/spec/lib/gitlab/utils/override_spec.rb +++ b/spec/lib/gitlab/utils/override_spec.rb @@ -25,11 +25,21 @@ describe Gitlab::Utils::Override do let(:klass) { subject } - def good(mod) + def good(mod, bad_arity: false, negative_arity: false) mod.module_eval do override :good - def good - super.succ + + if bad_arity + def good(num) + end + elsif negative_arity + def good(*args) + super.succ + end + else + def good + super.succ + end end end @@ -56,6 +66,14 @@ describe Gitlab::Utils::Override do described_class.verify! end + it 'checks ok for overriding method using negative arity' do + good(subject, negative_arity: true) + result = instance.good + + expect(result).to eq(1) + described_class.verify! + end + it 'raises NotImplementedError when it is not overriding anything' do expect do bad(subject) @@ -63,6 +81,14 @@ describe Gitlab::Utils::Override do described_class.verify! end.to raise_error(NotImplementedError) end + + it 'raises NotImplementedError when overriding a method with different arity' do + expect do + good(subject, bad_arity: true) + instance.good(1) + described_class.verify! + end.to raise_error(NotImplementedError) + end end shared_examples 'checking as intended, nothing was overridden' do |