From cceae0fdcddc0043737ddb9f0b809b659639d7ed Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 11 Mar 2025 03:13:16 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../dashboard/projects_controller.rb | 86 +-- app/controllers/root_controller.rb | 22 +- app/helpers/preferences_helper.rb | 15 +- app/views/dashboard/projects/index.html.haml | 16 +- .../projects/shared/_common.html.haml | 17 +- .../beta/your_work_projects_vue.yml | 9 - config/routes/dashboard.rb | 4 +- doc/user/profile/preferences.md | 9 +- doc/user/project/working_with_projects.md | 3 + lib/gitlab/git/commit.rb | 2 +- lib/gitlab/github_import/markdown_text.rb | 10 +- locale/gitlab.pot | 3 - qa/qa/page/dashboard/projects.rb | 10 +- .../dashboard/projects_controller_spec.rb | 210 +----- spec/controllers/root_controller_spec.rb | 20 +- .../dashboard/archived_projects_spec.rb | 77 +- spec/features/dashboard/projects_spec.rb | 671 ++++++------------ .../dashboard/user_filters_projects_spec.rb | 39 +- ...er_visits_profile_preferences_page_spec.rb | 89 +-- .../show/user_interacts_with_stars_spec.rb | 60 +- spec/helpers/preferences_helper_spec.rb | 81 +-- .../github_import/markdown_text_spec.rb | 14 +- .../projects/index.html.haml_spec.rb | 99 +-- .../projects/shared/_common.html.haml_spec.rb | 90 +-- 24 files changed, 369 insertions(+), 1287 deletions(-) delete mode 100644 config/feature_flags/beta/your_work_projects_vue.yml diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index fd279f93bef..cac650364bb 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -11,98 +11,30 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) } before_action :set_non_archived_param, only: [:index, :starred] before_action :set_sorting - # When your_work_projects_vue FF is enabled we load the projects via GraphQL query - # so we don't want to preload the projects at the controller level to avoid duplicate queries. - before_action :projects, only: [:index], unless: :your_work_projects_vue_feature_flag_enabled? skip_cross_project_access_check :index, :starred feature_category :groups_and_projects urgency :low, [:starred, :index] def index - if your_work_projects_vue_feature_flag_enabled? - return redirect_to personal_dashboard_projects_path if params[:personal] == "true" - return redirect_to inactive_dashboard_projects_path if params[:archived] == "only" - end + return redirect_to personal_dashboard_projects_path if params[:personal] == "true" + return redirect_to inactive_dashboard_projects_path if params[:archived] == "only" respond_to do |format| format.html do - render_projects + render end format.atom do load_events render layout: 'xml' end - format.json do - render json: { - html: view_to_html_string("dashboard/projects/_projects", projects: @projects) - } - end end end - # rubocop: disable CodeReuse/ActiveRecord - def starred - unless your_work_projects_vue_feature_flag_enabled? - @projects = load_projects(params.merge(starred: true, not_aimed_for_deletion: true)) - .includes(:forked_from_project, :topics) - end - - @groups = [] - - respond_to do |format| - format.html - format.json do - render json: { - html: view_to_html_string("dashboard/projects/_projects", projects: @projects) - } - end - end - end - # rubocop: enable CodeReuse/ActiveRecord + def starred; end private - def projects - @projects ||= load_projects(params.merge(non_public: true, not_aimed_for_deletion: true)) - end - - def render_projects - # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260 - Gitlab::GitalyClient.allow_n_plus_1_calls do - render - end - end - - def load_projects(finder_params) - @all_user_projects = ProjectsFinder.new( - params: { non_public: true, archived: false, - not_aimed_for_deletion: true }, current_user: current_user).execute - @all_starred_projects = ProjectsFinder.new( - params: { starred: true, archived: false, - not_aimed_for_deletion: true }, current_user: current_user).execute - - finder_params[:use_cte] = true if use_cte_for_finder? - - projects = ProjectsFinder.new(params: finder_params, current_user: current_user).execute - - projects = preload_associations(projects) - projects = projects.page(finder_params[:page]) - - prepare_projects_for_rendering(projects) - end - - # rubocop: disable CodeReuse/ActiveRecord - def preload_associations(projects) - projects.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]).preload(:project_feature) - end - # rubocop: enable CodeReuse/ActiveRecord - - def use_cte_for_finder? - # The starred action loads public projects, which causes the CTE to be less efficient - action_name == 'index' - end - def load_events projects = ProjectsFinder .new(params: params.merge(non_public: true, not_aimed_for_deletion: true), current_user: current_user) @@ -115,11 +47,6 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?) end - def set_sorting - params[:sort] = set_sort_order - @sort = params[:sort] - end - def default_sort_order sort_value_name end @@ -128,8 +55,9 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController Project::SORTING_PREFERENCE_FIELD end - def your_work_projects_vue_feature_flag_enabled? - Feature.enabled?(:your_work_projects_vue, current_user) + def set_sorting + params[:sort] = set_sort_order + @sort = params[:sort] end end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index bf38dade1ae..18df4512693 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -13,23 +13,11 @@ class RootController < Dashboard::ProjectsController before_action :redirect_unlogged_user, if: -> { current_user.nil? } before_action :redirect_logged_user, if: -> { current_user.present? } - # We only need to load the projects when the user is logged in but did not - # configure a dashboard. In which case we render projects. We can do that straight - # from the #index action. - skip_before_action :projects CACHE_CONTROL_HEADER = 'no-store' - def index - # When your_work_projects_vue FF is enabled we load the projects via GraphQL query - # so we don't want to preload the projects at the controller level to avoid duplicate queries. - return if Feature.enabled?(:your_work_projects_vue, current_user) - - # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260 - Gitlab::GitalyClient.allow_n_plus_1_calls do - projects - super - end + def index # rubocop:disable Lint/UselessMethodDefinition -- we need to explicitly define this action for the `skip_before_action` + super end private @@ -52,10 +40,8 @@ class RootController < Dashboard::ProjectsController flash.keep redirect_to(starred_dashboard_projects_path) when 'member_projects' - if Feature.enabled?(:your_work_projects_vue, current_user) - flash.keep - redirect_to(member_dashboard_projects_path) - end + flash.keep + redirect_to(member_dashboard_projects_path) when 'your_activity' redirect_to(activity_dashboard_path) when 'project_activity' diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index b915140eea6..f611bfacc52 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -10,10 +10,6 @@ module PreferencesHelper end def dashboard_value - return current_user.dashboard if Feature.enabled?(:your_work_projects_vue, current_user) - - return 'projects' if current_user.dashboard == 'member_projects' - current_user.dashboard end @@ -21,7 +17,6 @@ module PreferencesHelper def dashboard_choices dashboards = User.dashboards.keys - dashboards -= ['member_projects'] unless Feature.enabled?(:your_work_projects_vue, current_user) validate_dashboard_choices!(dashboards) dashboards -= excluded_dashboard_choices @@ -36,16 +31,10 @@ module PreferencesHelper # Maps `dashboard` values to more user-friendly option text def localized_dashboard_choices - projects = if Feature.enabled?(:your_work_projects_vue, current_user) - _("Your Contributed Projects (default)") - else - _("Your Projects (default)") - end - { - projects: projects, + projects: _("Your Contributed Projects (default)"), stars: _("Starred Projects"), - member_projects: (_("Member Projects") if Feature.enabled?(:your_work_projects_vue, current_user)), + member_projects: _("Member Projects"), your_activity: _("Your Activity"), project_activity: _("Your Projects' Activity"), starred_project_activity: _("Starred Projects' Activity"), diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 15662064a51..ecdb2d1e793 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -9,16 +9,8 @@ = render "projects/last_push" -- if Feature.enabled?(:your_work_projects_vue, current_user) - - if show_dashboard_projects_welcome_page? - = render "zero_authorized_projects" - - else - = render 'dashboard/projects_head' - #js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } } +- if show_dashboard_projects_welcome_page? + = render "zero_authorized_projects" - else - - if show_projects?(@projects, params) - = render 'dashboard/projects_head' - = render 'dashboard/projects_nav' - = render 'projects' - - else - = render "zero_authorized_projects" + = render 'dashboard/projects_head' + #js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } } diff --git a/app/views/dashboard/projects/shared/_common.html.haml b/app/views/dashboard/projects/shared/_common.html.haml index 81776dee428..b99327ba33d 100644 --- a/app/views/dashboard/projects/shared/_common.html.haml +++ b/app/views/dashboard/projects/shared/_common.html.haml @@ -1,5 +1,5 @@ --# TODO: This file can be removed in favor of 'dashboard/projects/index.html.haml' as part of `:your_work_projects_vue` FF rollout --# https://gitlab.com/gitlab-org/gitlab/-/issues/465889 +-# TODO: This file can be removed in favor of 'dashboard/projects/index.html.haml' +-# Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/523698 - page_title _("Projects") @@ -7,14 +7,5 @@ = render "projects/last_push" -- if Feature.enabled?(:your_work_projects_vue, current_user) - = render 'dashboard/projects_head' - #js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } } -- else - = render 'dashboard/projects_head', project_tab_filter: :starred - = render 'dashboard/projects_nav' - - - if params[:filter_projects] || any_projects?(@projects) - = render 'projects' - - else - = render empty_page += render 'dashboard/projects_head' +#js-your-work-projects-app{ data: { app_data: dashboard_projects_app_data } } diff --git a/config/feature_flags/beta/your_work_projects_vue.yml b/config/feature_flags/beta/your_work_projects_vue.yml deleted file mode 100644 index 00a1f159a82..00000000000 --- a/config/feature_flags/beta/your_work_projects_vue.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: your_work_projects_vue -feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/13066 -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155472 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/465889 -milestone: '17.1' -type: beta -group: group::organizations -default_enabled: false diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb index 314952866d5..6e533dcd34b 100644 --- a/config/routes/dashboard.rb +++ b/config/routes/dashboard.rb @@ -20,8 +20,8 @@ resource :dashboard, controller: 'dashboard', only: [] do resources :projects, only: [:index] do collection do - ## TODO: Migrate `starred` route to 'projects#index' when removing `:your_work_projects_vue` FF - ## https://gitlab.com/gitlab-org/gitlab/-/issues/465889 + ## TODO: Migrate `starred` route to 'projects#index' + ## Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/523698 get :starred get :contributed, :personal, :member, :inactive, to: 'projects#index' end diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md index e6f73c43610..8fd80f0d667 100644 --- a/doc/user/profile/preferences.md +++ b/doc/user/profile/preferences.md @@ -144,16 +144,11 @@ To choose your home organization: {{< history >}} - [Homepage options changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default. +- [Homepage option changes generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed. {{< /history >}} -{{< alert type="flag" >}} - -When the `your_work_projects_vue` feature flag is enabled, the **Your Contributed Projects** view becomes the default option, and an additional **Member Projects** option is available in the dropdown list. For more information, see the history. - -{{< /alert >}} - -Control what page you view when you select the GitLab logo ({{< icon name="tanuki" >}}). You can set your homepage to be Your Projects (default), Your Groups, Your Activity, and other content. +Control what page you view when you select the GitLab logo ({{< icon name="tanuki" >}}). You can set your homepage to be Your Contributed Projects (default), Your Groups, Your Activity, and other content. To choose your homepage view: diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md index 2ff05bd53b7..9938cebf33d 100644 --- a/doc/user/project/working_with_projects.md +++ b/doc/user/project/working_with_projects.md @@ -98,6 +98,7 @@ If you are not authenticated, the list shows public projects only. {{< history >}} - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/13066) in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default. +- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed. {{< /history >}} @@ -127,6 +128,7 @@ To view projects you have contributed to: {{< history >}} - [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Yours" to "Member" in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default. +- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed. {{< /history >}} @@ -359,6 +361,7 @@ To immediately delete a project marked for deletion: {{< history >}} - [Changed](https://gitlab.com/groups/gitlab-org/-/epics/13066) tab label from "Pending deletion" to "Inactive" in GitLab 17.9 [with a flag](../../administration/feature_flags.md) named `your_work_projects_vue`. Disabled by default. +- [Changed tab label generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/465889) in GitLab 17.10. Feature flag `your_work_projects_vue` removed. {{< /history >}} diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index bc4e13e70f8..1b43bd8ba08 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -364,7 +364,7 @@ module Gitlab Gitlab::AppLogger.info( event: 'mrdc_message_method_git', message: - "mrdc#message called via #{caller_locations.reject { |line| line.path.include?('/gems/') }.first(4)}" + "mrdc#message called via #{caller_locations.reject { |line| line.path.include?('/gems/') }.first(10)}" ) end diff --git a/lib/gitlab/github_import/markdown_text.rb b/lib/gitlab/github_import/markdown_text.rb index 029e189a59a..3d22552269c 100644 --- a/lib/gitlab/github_import/markdown_text.rb +++ b/lib/gitlab/github_import/markdown_text.rb @@ -17,7 +17,7 @@ module Gitlab class << self def format(...) - new(...).to_s + new(...).perform end def fetch_attachments(text) @@ -53,17 +53,17 @@ module Gitlab # exists - Boolean that indicates the user exists in the GitLab database. # project - An instance of `Project`. def initialize(text, author = nil, exists = false, project: nil) - @text = text.to_s + @text = text @author = author @exists = exists @project = project end - def to_s + def perform return if text.blank? # Gitlab::EncodingHelper#clean remove `null` chars from the string - text = clean(format) + text = clean(formatted_text) text = convert_ref_links(text, project) if project.present? wrap_mentions_in_backticks(text) end @@ -72,7 +72,7 @@ module Gitlab attr_reader :text, :author, :exists, :project - def format + def formatted_text login = author.respond_to?(:fetch) ? author.fetch(:login, nil) : author.try(:login) return "*Created by: #{login}*\n\n#{text}" if login.present? && !exists diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a66fa25e53b..f349c22b1a2 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -68099,9 +68099,6 @@ msgstr "" msgid "Your Personal Access Token was revoked" msgstr "" -msgid "Your Projects (default)" -msgstr "" - msgid "Your Projects' Activity" msgstr "" diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb index bbba8ac3409..35a40d07058 100644 --- a/qa/qa/page/dashboard/projects.rb +++ b/qa/qa/page/dashboard/projects.rb @@ -48,16 +48,8 @@ module QA click_element('new-project-button', Page::Project::New) end - # The tab names of the HAML and Vue version of the UI differ slightly. - # The Vue version defaults to a "Contributed" tab where-as the HAML version - # defaults to a "Yours" tab. Here we conditionally click the "Member" tab - # if it exists (only on the Vue version). This allows the tests to - # pass in both versions. - # We can remove this conditional check when code behind the - # your_work_projects_vue feature flag becomes the default. def click_member_tab - text = 'Member' - click_link(text) if has_link?(text) + click_link('Member') wait_for_requests end diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb index c3f85e48902..29274d4b366 100644 --- a/spec/controllers/dashboard/projects_controller_spec.rb +++ b/spec/controllers/dashboard/projects_controller_spec.rb @@ -21,7 +21,6 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ end before do - stub_feature_flags(your_work_projects_vue: false) sign_in(user) end @@ -35,191 +34,24 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ end end - it 'orders the projects by name by default' do - get :index - - expect(assigns(:projects)).to eq(projects) - end - - it 'assigns the correct all_user_projects' do - get :index - all_user_projects = assigns(:all_user_projects) - - expect(all_user_projects.count).to eq(2) - end - - it 'assigns the correct all_starred_projects' do - get :index - all_starred_projects = assigns(:all_starred_projects) - - expect(all_starred_projects.count).to eq(1) - expect(all_starred_projects).to include(project2) - end - - context 'project sorting' do - it_behaves_like 'set sort order from user preference' do - let(:sorting_param) { 'created_asc' } - end - end - - context 'with search and sort parameters' do - render_views - - shared_examples 'search and sort parameters' do |sort| - it 'returns a single project with no ambiguous column errors' do - get :index, params: { name: project2.name, sort: sort } - - expect(response).to have_gitlab_http_status(:ok) - expect(assigns(:projects)).to eq([project2]) - end - end - - %w[latest_activity_desc latest_activity_asc stars_desc stars_asc created_desc].each do |sort| - it_behaves_like 'search and sort parameters', sort - end - end - - context 'with archived project' do - let_it_be(:archived_project) do - project2.tap { |p| p.update!(archived: true) } - end - - it 'does not display archived project' do - get :index - projects_result = assigns(:projects) - - expect(projects_result).not_to include(archived_project) - expect(projects_result).to include(project) - end - - it 'excludes archived project from all_user_projects' do - get :index - all_user_projects = assigns(:all_user_projects) - - expect(all_user_projects.count).to eq(1) - expect(all_user_projects).not_to include(archived_project) - end - - it 'excludes archived project from all_starred_projects' do - get :index - all_starred_projects = assigns(:all_starred_projects) - - expect(all_starred_projects.count).to eq(0) - expect(all_starred_projects).not_to include(archived_project) - end - end - - context 'with deleted project' do - let!(:pending_delete_project) do - project.tap { |p| p.update!(pending_delete: true) } - end - - it 'does not display deleted project' do - get :index - projects_result = assigns(:projects) - - expect(projects_result).not_to include(pending_delete_project) - expect(projects_result).to include(project2) - end - end - context 'with redirects' do - context 'when feature flag your_work_projects_vue is true' do - before do - stub_feature_flags(your_work_projects_vue: true) - end - - it 'redirects ?personal=true to /personal' do - get :index, params: { personal: true } - - expect(response).to redirect_to(personal_dashboard_projects_path) - end - - it 'redirects ?archived=only to /inactive' do - get :index, params: { archived: 'only' } - - expect(response).to redirect_to(inactive_dashboard_projects_path) - end - end - - it 'does not redirect ?personal=true to /personal' do + it 'redirects ?personal=true to /personal' do get :index, params: { personal: true } - expect(response).not_to redirect_to(personal_dashboard_projects_path) + expect(response).to redirect_to(personal_dashboard_projects_path) end - it 'does not ?archived=only to /inactive' do + it 'redirects ?archived=only to /inactive' do get :index, params: { archived: 'only' } - expect(response).not_to redirect_to(inactive_dashboard_projects_path) + expect(response).to redirect_to(inactive_dashboard_projects_path) end end end end - context 'json requests' do - render_views - - before do - stub_feature_flags(your_work_projects_vue: false) - sign_in(user) - end - - describe 'GET /projects.json' do - before do - get :index, format: :json - end - - it { is_expected.to respond_with(:success) } - end - - describe 'GET /starred.json' do - subject { get :starred, format: :json } - - let(:projects) { create_list(:project, 2, creator: user) } - let(:aimed_for_deletion_project) { create_list(:project, 2, :archived, creator: user, marked_for_deletion_at: 3.days.ago) } - - before do - projects.each do |project| - project.add_developer(user) - create(:users_star_project, project_id: project.id, user_id: user.id) - end - - aimed_for_deletion_project.each do |project| - project.add_developer(user) - create(:users_star_project, project_id: project.id, user_id: user.id) - end - end - - it 'returns success' do - subject - - expect(response).to have_gitlab_http_status(:ok) - end - - context "pagination" do - before do - allow(Kaminari.config).to receive(:default_per_page).and_return(1) - end - - it 'paginates the records' do - subject - - expect(assigns(:projects).count).to eq(1) - end - end - - it 'does not include projects aimed for deletion' do - subject - - expect(assigns(:projects).count).to eq(2) - end - end - end - context 'atom requests' do before do - stub_feature_flags(your_work_projects_vue: false) sign_in(user) end @@ -298,38 +130,10 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_categ end describe '#starred' do - let_it_be(:project) { create(:project, name: 'Project 1') } - let_it_be(:project2) { create(:project, name: 'Project Two') } + it 'does not assign all_starred_projects' do + get :starred - before_all do - project.add_developer(user) - project2.add_developer(user) - user.toggle_star(project2) - end - - context 'when your_work_projects_vue feature flag is enabled' do - before do - sign_in(user) - end - - it 'does not assign all_starred_projects' do - get :starred - - expect(assigns(:all_starred_projects)).to be_nil - end - end - - context 'when your_work_projects_vue feature flag is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - sign_in(user) - end - - it 'assigns all_starred_projects' do - get :starred - - expect(assigns(:all_starred_projects)).to contain_exactly(project2) - end + expect(assigns(:all_starred_projects)).to be_nil end end end diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 0dabc3441f1..a49d242a5c3 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -79,24 +79,10 @@ RSpec.describe RootController, feature_category: :shared do user.dashboard = 'member_projects' end - context 'when feature flag your_work_projects_vue is enabled' do - it 'redirects to their member projects list' do - get :index + it 'redirects to their member projects list' do + get :index - expect(response).to redirect_to member_dashboard_projects_path - end - end - - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end - - it 'does not redirect' do - get :index - - expect(response).not_to redirect_to member_dashboard_projects_path - end + expect(response).to redirect_to member_dashboard_projects_path end end diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb index e63ce2f1c60..7f384f917e2 100644 --- a/spec/features/dashboard/archived_projects_spec.rb +++ b/spec/features/dashboard/archived_projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Dashboard Archived Project', feature_category: :groups_and_projects do +RSpec.describe 'Dashboard Archived Project', :js, feature_category: :groups_and_projects do let_it_be(:user) { create :user } let_it_be(:project) { create :project } let_it_be(:archived_project) { create(:project, :archived) } @@ -14,70 +14,35 @@ RSpec.describe 'Dashboard Archived Project', feature_category: :groups_and_proje archived_project_2.add_maintainer(user) end - context 'when feature flag your_work_projects_vue is enabled', :js do - before do - sign_in(user) + before do + sign_in(user) - visit member_dashboard_projects_path - wait_for_requests - end - - it 'renders non archived projects' do - expect(page).to have_link(project.name) - expect(page).not_to have_link(archived_project.name) - end - - it 'renders only archived projects' do - click_link 'Inactive' - - expect(page).to have_content(archived_project.name) - expect(page).not_to have_content(project.name) - end - - it 'searches archived projects', :js do - click_link 'Inactive' - - expect(page).to have_link(archived_project.name) - expect(page).to have_link(archived_project_2.name) - - search(archived_project.name) - - expect(page).not_to have_link(archived_project_2.name) - expect(page).to have_link(archived_project.name) - end + visit member_dashboard_projects_path + wait_for_requests end - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - sign_in(user) + it 'renders non archived projects' do + expect(page).to have_link(project.name) + expect(page).not_to have_link(archived_project.name) + end - visit dashboard_projects_path - end + it 'renders only archived projects' do + click_link 'Inactive' - it 'renders non archived projects' do - expect(page).to have_link(project.name) - expect(page).not_to have_link(archived_project.name) - end + expect(page).to have_content(archived_project.name) + expect(page).not_to have_content(project.name) + end - it 'renders only archived projects' do - click_link 'Inactive' + it 'searches archived projects', :js do + click_link 'Inactive' - expect(page).to have_content(archived_project.name) - expect(page).not_to have_content(project.name) - end + expect(page).to have_link(archived_project.name) + expect(page).to have_link(archived_project_2.name) - it 'searches archived projects', :js do - click_link 'Inactive' + search(archived_project.name) - expect(page).to have_link(archived_project.name) - expect(page).to have_link(archived_project_2.name) - - search(archived_project.name) - - expect(page).not_to have_link(archived_project_2.name) - expect(page).to have_link(archived_project.name) - end + expect(page).not_to have_link(archived_project_2.name) + expect(page).to have_link(archived_project.name) end def search(term) diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 530db1b3bf6..14e443cbf46 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -6,497 +6,246 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :repository, creator: build(:user)) } # ensure creator != owner to avoid N+1 false-positive let_it_be(:project2) { create(:project, :public) } + let_it_be(:personal_project) { create(:project, namespace: user.namespace) } + let_it_be(:personal_project_with_stars) { create(:project, namespace: user.namespace, star_count: 10) } + let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha, ref: project.default_branch) } before do project.add_developer(user) sign_in(user) end - context 'when feature :your_work_projects_vue is enabled' do - let_it_be(:personal_project) { create(:project, namespace: user.namespace) } - let_it_be(:personal_project_with_stars) { create(:project, namespace: user.namespace, star_count: 10) } - let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha, ref: project.default_branch) } + it 'mounts JS app and defaults to contributed tab' do + visit dashboard_projects_path + wait_for_requests - it 'mounts JS app and defaults to contributed tab' do + expect(page).to have_content('Projects') + expect(page).to have_selector('a[aria-selected="true"]', text: 'Contributed') + end + + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do + before do visit dashboard_projects_path wait_for_requests - - expect(page).to have_content('Projects') - expect(page).to have_selector('a[aria-selected="true"]', text: 'Contributed') end + end - it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do - before do - visit dashboard_projects_path - wait_for_requests - end - end + it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects + it 'links to the "Explore projects" page' do + visit dashboard_projects_path + wait_for_requests - it 'links to the "Explore projects" page' do - visit dashboard_projects_path - wait_for_requests + expect(page).to have_link("Explore projects", href: starred_explore_projects_path) + end - expect(page).to have_link("Explore projects", href: starred_explore_projects_path) - end - - context 'when user has access to the project' do - it 'shows role badge' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project.id}") do - expect(find_by_testid('user-access-role')).to have_content('Developer') - end - end - end - - context 'when last_activity_at and update_at are present', time_travel_to: '2025-01-27T09:44:07Z' do - let_it_be(:project_with_last_activity) do - create( - :project, - namespace: user.namespace, - last_repository_updated_at: 1.hour.ago, - last_activity_at: Time.current - ) - end - - it 'shows the last_activity_at attribute as the update date', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/514342' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project_with_last_activity.id}") do - expect(page).to have_xpath("//time[@datetime='#{project_with_last_activity.last_activity_at.iso8601}']") - end - end - end - - context 'when last_activity_at is missing', time_travel_to: '2025-01-27T09:44:07Z' do - it 'shows the updated_at attribute as the update date' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project.id}") do - expect(page).to have_xpath("//time[@datetime='#{project.updated_at.iso8601}']") - end - end - end - - it 'shows personal projects on personal projects tab' do - visit personal_dashboard_projects_path - wait_for_requests - - expect(page).not_to have_content(project.name) - expect(page).to have_content(personal_project.name) - end - - it 'sorts projects by most stars when sorting by most stars' do - visit personal_dashboard_projects_path(sort: :stars_desc) - wait_for_requests - - expect(first('[data-testid*="projects-list-item"]')).to have_content(personal_project_with_stars.title) - end - - context 'when on Member projects tab' do - it 'shows all projects you are a member of' do - visit member_dashboard_projects_path - wait_for_requests - - expect(page).to have_content(project.name) - expect(page).to have_content(personal_project.name) - expect(page).to have_content(personal_project_with_stars.name) - expect(find('a[aria-selected="true"]')).to have_content('3') - end - end - - context 'when on Starred projects tab' do - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects - - it 'shows the empty state when there are no starred projects' do - visit(starred_dashboard_projects_path) - wait_for_requests - - expect(page).to have_text("You haven't starred any projects yet.") - end - - it 'shows only starred projects' do - user.toggle_star(project2) - - visit(starred_dashboard_projects_path) - wait_for_requests - - expect(page).not_to have_content(project.name) - expect(page).to have_content(project2.name) - expect(find('a[aria-selected="true"]')).to have_content('1') - end - end - - describe 'with a pipeline' do - it 'shows that the last pipeline passed' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project.id}") do - expect(page).to have_css("[data-testid='ci-icon']") - expect(page).to have_css('[data-testid="status_success_borderless-icon"]') - expect(page).to have_link('Status: Passed') - end - end - - context 'guest user of project and project has private pipelines' do - let_it_be(:guest_user) { create(:user) } - let_it_be(:project_with_private_pipelines) { create(:project, namespace: user.namespace, public_builds: false) } - - before_all do - project_with_private_pipelines.add_guest(guest_user) - end - - before do - sign_in(guest_user) - end - - it 'does not show the pipeline status' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project_with_private_pipelines.id}") do - expect(page).not_to have_css("[data-testid='ci-icon']") - end - end - end - - context "when last_pipeline is missing" do - it 'does not show the pipeline status' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{personal_project.id}") do - expect(page).not_to have_css("[data-testid='ci-icon']") - end - end - end - end - - context 'when project has topics' do - let_it_be(:project_with_topics) { create(:project, namespace: user.namespace, topic_list: 'topic1') } - - it 'shows project topics' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project_with_topics.id}") do - expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) - end - end - end - - context 'when project does not have topics' do - it 'does not show project topics' do - visit member_dashboard_projects_path - wait_for_requests - - within_testid("projects-list-item-#{project.id}") do - expect(page).not_to have_selector('[data-testid="project-topics"]') - end - end - end - - context 'last push widget', :use_clean_rails_memory_store_caching do - before do - event = create(:push_event, project: project, author: user) - - create(:push_event_payload, event: event, ref: 'feature', action: :created) - - Users::LastPushEventService.new(user).cache_last_push_event(event) - - visit dashboard_projects_path - end - - it 'shows "Create merge request" button' do - expect(page).to have_content 'You pushed to feature' - - within('#content-body') do - find_link('Create merge request', visible: false).click - end - - expect(page).to have_selector('.merge-request-form') - expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true - expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s - expect(page).to have_content "From feature into master" - end - end - - it 'avoids an N+1 query in dashboard index' do + context 'when user has access to the project' do + it 'shows role badge' do visit member_dashboard_projects_path wait_for_requests - control = ActiveRecord::QueryRecorder.new do - visit member_dashboard_projects_path - wait_for_requests + within_testid("projects-list-item-#{project.id}") do + expect(find_by_testid('user-access-role')).to have_content('Developer') end - - new_project = create(:project, :repository, name: 'new project') - create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha) - new_project.add_developer(user) - - # There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037 - # - User#max_member_access_for_project_ids - # - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline - # - Ci::Pipeline#detailed_status - - expect do - visit member_dashboard_projects_path - wait_for_requests - end.not_to exceed_query_limit(control).with_threshold(4) end end - context 'when feature :your_work_projects_vue is disabled' do + context 'when last_activity_at and update_at are present', time_travel_to: '2025-01-27T09:44:07Z' do + let_it_be(:project_with_last_activity) do + create( + :project, + namespace: user.namespace, + last_repository_updated_at: 1.hour.ago, + last_activity_at: Time.current + ) + end + + it 'shows the last_activity_at attribute as the update date', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/514342' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project_with_last_activity.id}") do + expect(page).to have_xpath("//time[@datetime='#{project_with_last_activity.last_activity_at.iso8601}']") + end + end + end + + context 'when last_activity_at is missing', time_travel_to: '2025-01-27T09:44:07Z' do + it 'shows the updated_at attribute as the update date' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project.id}") do + expect(page).to have_xpath("//time[@datetime='#{project.updated_at.iso8601}']") + end + end + end + + it 'shows personal projects on personal projects tab' do + visit personal_dashboard_projects_path + wait_for_requests + + expect(page).not_to have_content(project.name) + expect(page).to have_content(personal_project.name) + end + + it 'sorts projects by most stars when sorting by most stars' do + visit personal_dashboard_projects_path(sort: :stars_desc) + wait_for_requests + + expect(first('[data-testid*="projects-list-item"]')).to have_content(personal_project_with_stars.title) + end + + context 'when on Member projects tab' do + it 'shows all projects you are a member of' do + visit member_dashboard_projects_path + wait_for_requests + + expect(page).to have_content(project.name) + expect(page).to have_content(personal_project.name) + expect(page).to have_content(personal_project_with_stars.name) + expect(find('a[aria-selected="true"]')).to have_content('3') + end + end + + context 'when on Starred projects tab' do + it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects + + it 'shows the empty state when there are no starred projects' do + visit(starred_dashboard_projects_path) + wait_for_requests + + expect(page).to have_text("You haven't starred any projects yet.") + end + + it 'shows only starred projects' do + user.toggle_star(project2) + + visit(starred_dashboard_projects_path) + wait_for_requests + + expect(page).not_to have_content(project.name) + expect(page).to have_content(project2.name) + expect(find('a[aria-selected="true"]')).to have_content('1') + end + end + + describe 'with a pipeline' do + it 'shows that the last pipeline passed' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project.id}") do + expect(page).to have_css("[data-testid='ci-icon']") + expect(page).to have_css('[data-testid="status_success_borderless-icon"]') + expect(page).to have_link('Status: Passed') + end + end + + context 'guest user of project and project has private pipelines' do + let_it_be(:guest_user) { create(:user) } + let_it_be(:project_with_private_pipelines) { create(:project, namespace: user.namespace, public_builds: false) } + + before_all do + project_with_private_pipelines.add_guest(guest_user) + end + + before do + sign_in(guest_user) + end + + it 'does not show the pipeline status' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project_with_private_pipelines.id}") do + expect(page).not_to have_css("[data-testid='ci-icon']") + end + end + end + + context "when last_pipeline is missing" do + it 'does not show the pipeline status' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{personal_project.id}") do + expect(page).not_to have_css("[data-testid='ci-icon']") + end + end + end + end + + context 'when project has topics' do + let_it_be(:project_with_topics) { create(:project, namespace: user.namespace, topic_list: 'topic1') } + + it 'shows project topics' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project_with_topics.id}") do + expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) + end + end + end + + context 'when project does not have topics' do + it 'does not show project topics' do + visit member_dashboard_projects_path + wait_for_requests + + within_testid("projects-list-item-#{project.id}") do + expect(page).not_to have_selector('[data-testid="project-topics"]') + end + end + end + + context 'last push widget', :use_clean_rails_memory_store_caching do before do - stub_feature_flags(your_work_projects_vue: false) - end + event = create(:push_event, project: project, author: user) + + create(:push_event_payload, event: event, ref: 'feature', action: :created) + + Users::LastPushEventService.new(user).cache_last_push_event(event) - it 'does not mount JS app' do visit dashboard_projects_path - - expect(page).to have_content('Projects') - expect(page).not_to have_content('Active tab') end - it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" do - before do - visit dashboard_projects_path + it 'shows "Create merge request" button' do + expect(page).to have_content 'You pushed to feature' + + within('#content-body') do + find_link('Create merge request', visible: false).click end + + expect(page).to have_selector('.merge-request-form') + expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true + expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s + expect(page).to have_content "From feature into master" + end + end + + it 'avoids an N+1 query in dashboard index' do + visit member_dashboard_projects_path + wait_for_requests + + control = ActiveRecord::QueryRecorder.new do + visit member_dashboard_projects_path + wait_for_requests end - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :dashboard_projects_path, :projects + new_project = create(:project, :repository, name: 'new project') + create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha) + new_project.add_developer(user) - it 'links to the "Explore projects" page' do - visit dashboard_projects_path + # There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037 + # - User#max_member_access_for_project_ids + # - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline + # - Ci::Pipeline#detailed_status - expect(page).to have_link("Explore projects", href: starred_explore_projects_path) - end - - context 'when user has access to the project' do - it 'shows role badge' do - visit dashboard_projects_path - - within_testid('user-access-role') do - expect(page).to have_content('Developer') - end - end - - context 'when role changes', :use_clean_rails_memory_store_fragment_caching do - it 'displays the right role' do - visit dashboard_projects_path - - within_testid('user-access-role') do - expect(page).to have_content('Developer') - end - - project.members.last.update!(access_level: 40) - - visit dashboard_projects_path - - within_testid('user-access-role') do - expect(page).to have_content('Maintainer') - end - end - end - end - - context 'when last_activity_at and update_at are present' do - it 'shows the last_activity_at attribute as the update date' do - project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.zone.now) - - visit dashboard_projects_path - - expect(page).to have_xpath("//time[@datetime='#{project.last_activity_at.getutc.iso8601}']") - end - end - - context 'when last_activity_at is missing' do - it 'shows the updated_at attribute as the update date' do - project.update!(last_activity_at: nil) - project.touch - - visit dashboard_projects_path - - expect(page).to have_xpath("//time[@datetime='#{project.updated_at.getutc.iso8601}']") - end - end - - context 'when on Your projects tab' do - it 'shows all projects by default' do - visit dashboard_projects_path - - expect(page).to have_content(project.name) - expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1) - end - - it 'shows personal projects on personal projects tab' do - project3 = create(:project, namespace: user.namespace) - - visit dashboard_projects_path - - click_link 'Personal' - - expect(page).not_to have_content(project.name) - expect(page).to have_content(project3.name) - end - - it 'sorts projects by most stars when sorting by most stars' do - project_with_most_stars = create(:project, namespace: user.namespace, star_count: 10) - - visit dashboard_projects_path(sort: :stars_desc) - - expect(first('.project-row')).to have_content(project_with_most_stars.title) - end - end - - context 'when on Starred projects tab' do - it_behaves_like 'a "Your work" page with sidebar and breadcrumbs', :starred_dashboard_projects_path, :projects - - it 'shows the empty state when there are no starred projects' do - visit(starred_dashboard_projects_path) - - expect(page).to have_text(s_("StarredProjectsEmptyState|You don't have starred projects yet.")) - end - - it 'shows only starred projects' do - user.toggle_star(project2) - - visit(starred_dashboard_projects_path) - - expect(page).not_to have_content(project.name) - expect(page).to have_content(project2.name) - expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1) - expect(find('.gl-tabs-nav li:nth-child(2) .badge-pill')).to have_content(1) - end - end - - describe 'with a pipeline', :clean_gitlab_redis_shared_state do - let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) } - - before do - # Since the cache isn't updated when a new pipeline is created - # we need the pipeline to advance in the pipeline since the cache was created - # by visiting the login page. - pipeline.succeed - end - - it 'shows that the last pipeline passed' do - visit dashboard_projects_path - - within_testid('project_controls') do - expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") - expect(page).to have_css("[data-testid='ci-icon']") - expect(page).to have_css('[data-testid="status_success_borderless-icon"]') - expect(page).to have_link('Pipeline: passed') - end - end - - shared_examples 'hidden pipeline status' do - it 'does not show the pipeline status' do - visit dashboard_projects_path - - within_testid('project_controls') do - expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") - expect(page).not_to have_css("[data-testid='ci-icon']") - expect(page).not_to have_css('[data-testid="status_success_borderless-icon"]') - expect(page).not_to have_link('Pipeline: passed') - end - end - end - - context 'guest user of project and project has private pipelines' do - let(:guest_user) { create(:user) } - - before do - project.update!(public_builds: false) - project.add_guest(guest_user) - sign_in(guest_user) - end - - it_behaves_like 'hidden pipeline status' - end - - context "when last_pipeline is missing" do - before do - project.last_pipeline.delete - end - - it_behaves_like 'hidden pipeline status' - end - end - - describe 'with topics' do - context 'when project has topics' do - before do - project.update_attribute(:topic_list, 'topic1') - end - - it 'shows project topics if exist' do - visit dashboard_projects_path - - expect(page).to have_selector('[data-testid="project_topic_list"]') - expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) - end - end - - context 'when project does not have topics' do - it 'does not show project topics' do - visit dashboard_projects_path - - expect(page).not_to have_selector('[data-testid="project_topic_list"]') - end - end - end - - context 'last push widget', :use_clean_rails_memory_store_caching do - before do - event = create(:push_event, project: project, author: user) - - create(:push_event_payload, event: event, ref: 'feature', action: :created) - - Users::LastPushEventService.new(user).cache_last_push_event(event) - - visit dashboard_projects_path - end - - it 'shows "Create merge request" button' do - expect(page).to have_content 'You pushed to feature' - - within('#content-body') do - find_link('Create merge request', visible: false).click - end - - expect(page).to have_selector('.merge-request-form') - expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true - expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s - expect(page).to have_content "From feature into master" - end - end - - it 'avoids an N+1 query in dashboard index' do - create(:ci_pipeline, :with_job, status: :success, project: project, ref: project.default_branch, sha: project.commit.sha) - visit dashboard_projects_path - - control = ActiveRecord::QueryRecorder.new { visit dashboard_projects_path } - - new_project = create(:project, :repository, name: 'new project') - create(:ci_pipeline, :with_job, status: :success, project: new_project, ref: new_project.commit.sha) - new_project.add_developer(user) - - ActiveRecord::QueryRecorder.new { visit dashboard_projects_path }.count - - # There are a few known N+1 queries: https://gitlab.com/gitlab-org/gitlab/-/issues/214037 - # - User#max_member_access_for_project_ids - # - ProjectsHelper#load_pipeline_status / Ci::CommitWithPipeline#last_pipeline - # - Ci::Pipeline#detailed_status - - expect { visit dashboard_projects_path }.not_to exceed_query_limit(control).with_threshold(4) - end + expect do + visit member_dashboard_projects_path + wait_for_requests + end.not_to exceed_query_limit(control).with_threshold(4) end end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index f23fcbc119a..384140392dc 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -48,39 +48,18 @@ RSpec.describe 'Dashboard > User filters projects', :js, feature_category: :grou end end - context 'when feature flag your_work_projects_vue is enabled' do - it 'searches for projects' do - project2.add_developer(user) - visit member_dashboard_projects_path - wait_for_requests + it 'searches for projects' do + project2.add_developer(user) + visit member_dashboard_projects_path + wait_for_requests - expect(page).to have_content(project.name) - expect(page).to have_content(project2.name) + expect(page).to have_content(project.name) + expect(page).to have_content(project2.name) - search(project.name) + search(project.name) - expect(page).to have_content(project.name) - expect(page).not_to have_content(project2.name) - end - end - - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end - - it 'searches for projects' do - project2.add_developer(user) - visit dashboard_projects_path - - expect(page).to have_content(project.name) - expect(page).to have_content(project2.name) - - search(project.name) - - expect(page).to have_content(project.name) - expect(page).not_to have_content(project2.name) - end + expect(page).to have_content(project.name) + expect(page).not_to have_content(project2.name) end def search(term) diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb index 1e8e3113938..8c6f6920dd1 100644 --- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb +++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb @@ -26,88 +26,37 @@ RSpec.describe 'User visits the profile preferences page', :js, feature_category end end - context 'when your_work_projects_vue feature flag is enabled' do - before do - stub_feature_flags(your_work_projects_vue: true) - visit(profile_preferences_path) - end + it 'sets default dashboard preference to Your Contributed Projects (default)' do + visit(profile_preferences_path) - it 'sets default dashboard preference to Your Contributed Projects (default)' do - expect(page).to have_button('Your Contributed Projects (default)') - end - end - - context 'when your_work_projects_vue feature flag is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - visit(profile_preferences_path) - end - - it 'sets default dashboard preference to Your Projects (default)' do - expect(page).to have_button('Your Projects (default)') - end + expect(page).to have_button('Your Contributed Projects (default)') end describe 'User changes their default dashboard', :js do - context 'when feature flag your_work_projects_vue is enabled' do - before do - visit(profile_preferences_path) - end - - it 'creates a flash message' do - select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true - click_button 'Save changes' - - wait_for_requests - - expect_preferences_saved_message - end - - it 'updates their preference' do - select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true - click_button 'Save changes' - - wait_for_requests - - find('[data-track-label="gitlab_logo_link"]').click - wait_for_requests - - expect(page).to have_content("You haven't starred any projects yet.") - expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true - end + before do + visit(profile_preferences_path) end - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - visit(profile_preferences_path) - end + it 'creates a flash message' do + select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true + click_button 'Save changes' - it 'creates a flash message' do - select_from_listbox 'Starred Projects', from: 'Your Projects', exact_item_text: true - click_button 'Save changes' + wait_for_requests - wait_for_requests + expect_preferences_saved_message + end - expect_preferences_saved_message - end + it 'updates their preference' do + select_from_listbox 'Starred Projects', from: 'Your Contributed Projects (default)', exact_item_text: true + click_button 'Save changes' - it 'updates their preference' do - select_from_listbox 'Starred Projects', from: 'Your Projects', exact_item_text: true - click_button 'Save changes' + wait_for_requests - wait_for_requests + find('[data-track-label="gitlab_logo_link"]').click + wait_for_requests - find('[data-track-label="gitlab_logo_link"]').click - - expect(page).to have_content("You don't have starred projects yet") - expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true - - find('.shortcuts-activity').click - - expect(page).not_to have_content("You don't have starred projects yet") - expect(page).to have_current_path dashboard_projects_path, ignore_query: true - end + expect(page).to have_content("You haven't starred any projects yet.") + expect(page).to have_current_path starred_dashboard_projects_path, ignore_query: true end end diff --git a/spec/features/projects/show/user_interacts_with_stars_spec.rb b/spec/features/projects/show/user_interacts_with_stars_spec.rb index 0c6eaf03d66..e9d05100b82 100644 --- a/spec/features/projects/show/user_interacts_with_stars_spec.rb +++ b/spec/features/projects/show/user_interacts_with_stars_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Projects > Show > User interacts with project stars', feature_category: :groups_and_projects do +RSpec.describe 'Projects > Show > User interacts with project stars', :js, feature_category: :groups_and_projects do let(:project) { create(:project, :public, :repository) } context 'when user is signed in', :js do @@ -32,62 +32,32 @@ RSpec.describe 'Projects > Show > User interacts with project stars', feature_ca expect(page).to have_css('.star-count', text: 0) end - context 'when feature flag your_work_projects_vue is enabled', :js do - it 'validates starring a project' do - project.add_owner(user) + it 'validates starring a project' do + project.add_owner(user) - star_project + star_project - visit(member_dashboard_projects_path) - wait_for_requests + visit(member_dashboard_projects_path) + wait_for_requests - expect(find_link('Stars')).to have_content('1') - end - - it 'validates un-starring a project' do - project.add_owner(user) - - star_project - - unstar_project - - visit(member_dashboard_projects_path) - wait_for_requests - - expect(find_link('Stars')).to have_content('0') - end + expect(find_link('Stars')).to have_content('1') end - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end + it 'validates un-starring a project' do + project.add_owner(user) - it 'validates starring a project' do - project.add_owner(user) + star_project - star_project + unstar_project - visit(dashboard_projects_path) + visit(member_dashboard_projects_path) + wait_for_requests - expect(page).to have_css('.stars', text: 1) - end - - it 'validates un-starring a project' do - project.add_owner(user) - - star_project - - unstar_project - - visit(dashboard_projects_path) - - expect(page).to have_css('.stars', text: 0) - end + expect(find_link('Stars')).to have_content('0') end end - context 'when user is not signed in', :js do + context 'when user is not signed in' do before do visit(project_path(project)) end diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 63a15c0c1a0..7c9e62c8e66 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -10,34 +10,10 @@ RSpec.describe PreferencesHelper, feature_category: :shared do end describe '#dashboard_value' do - context 'when feature flag your_work_projects_vue is enabled' do - it 'returns dashboard of current user' do - allow(user).to receive(:dashboard).and_return('your_activity') + it 'returns dashboard of current user' do + allow(user).to receive(:dashboard).and_return('your_activity') - expect(helper.dashboard_value).to eq('your_activity') - end - end - - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end - - context 'when dashboard of current user is member_projects' do - it 'returns projects' do - allow(user).to receive(:dashboard).and_return('member_projects') - - expect(helper.dashboard_value).to eq('projects') - end - end - - context 'when dashboard of current user is not member_projects' do - it 'returns projects' do - allow(user).to receive(:dashboard).and_return('your_activity') - - expect(helper.dashboard_value).to eq('your_activity') - end - end + expect(helper.dashboard_value).to eq('your_activity') end end @@ -58,43 +34,20 @@ RSpec.describe PreferencesHelper, feature_category: :shared do expect { helper.dashboard_choices }.to raise_error(KeyError) end - context 'when feature flag your_work_projects_vue is enabled' do - it 'returns expected options' do - expect(helper.dashboard_choices).to match_array [ - { text: "Your Contributed Projects (default)", value: 'projects' }, - { text: "Starred Projects", value: 'stars' }, - { text: "Member Projects", value: 'member_projects' }, - { text: "Your Activity", value: 'your_activity' }, - { text: "Your Projects' Activity", value: 'project_activity' }, - { text: "Starred Projects' Activity", value: 'starred_project_activity' }, - { text: "Followed Users' Activity", value: 'followed_user_activity' }, - { text: "Your Groups", value: 'groups' }, - { text: "Your To-Do List", value: 'todos' }, - { text: "Assigned issues", value: 'issues' }, - { text: "Assigned merge requests", value: 'merge_requests' } - ] - end - end - - context 'when feature flag your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end - - it 'returns expected options' do - expect(helper.dashboard_choices).to match_array [ - { text: "Your Projects (default)", value: 'projects' }, - { text: "Starred Projects", value: 'stars' }, - { text: "Your Activity", value: 'your_activity' }, - { text: "Your Projects' Activity", value: 'project_activity' }, - { text: "Starred Projects' Activity", value: 'starred_project_activity' }, - { text: "Followed Users' Activity", value: 'followed_user_activity' }, - { text: "Your Groups", value: 'groups' }, - { text: "Your To-Do List", value: 'todos' }, - { text: "Assigned issues", value: 'issues' }, - { text: "Assigned merge requests", value: 'merge_requests' } - ] - end + it 'returns expected options' do + expect(helper.dashboard_choices).to match_array [ + { text: "Your Contributed Projects (default)", value: 'projects' }, + { text: "Starred Projects", value: 'stars' }, + { text: "Member Projects", value: 'member_projects' }, + { text: "Your Activity", value: 'your_activity' }, + { text: "Your Projects' Activity", value: 'project_activity' }, + { text: "Starred Projects' Activity", value: 'starred_project_activity' }, + { text: "Followed Users' Activity", value: 'followed_user_activity' }, + { text: "Your Groups", value: 'groups' }, + { text: "Your To-Do List", value: 'todos' }, + { text: "Assigned issues", value: 'issues' }, + { text: "Assigned merge requests", value: 'merge_requests' } + ] end end diff --git a/spec/lib/gitlab/github_import/markdown_text_spec.rb b/spec/lib/gitlab/github_import/markdown_text_spec.rb index cd7fbe240ad..4143875fee1 100644 --- a/spec/lib/gitlab/github_import/markdown_text_spec.rb +++ b/spec/lib/gitlab/github_import/markdown_text_spec.rb @@ -109,41 +109,41 @@ RSpec.describe Gitlab::GithubImport::MarkdownText, feature_category: :importers end end - describe '#to_s' do + describe '#perform' do it 'returns the text when the author was found' do author = double(:author, login: 'Alice') text = described_class.new('Hello', author, true) - expect(text.to_s).to eq('Hello') + expect(text.perform).to eq('Hello') end it 'returns the text when the author has no login' do author = double(:author, login: nil) text = described_class.new('Hello', author, true) - expect(text.to_s).to eq('Hello') + expect(text.perform).to eq('Hello') end it 'returns the text with an extra header when the author was not found' do author = double(:author, login: 'Alice') text = described_class.new('Hello', author) - expect(text.to_s).to eq("*Created by: Alice*\n\nHello") + expect(text.perform).to eq("*Created by: Alice*\n\nHello") end it 'cleans invalid chars' do author = double(:author, login: 'Alice') text = described_class.format("\u0000Hello", author) - expect(text.to_s).to eq("*Created by: Alice*\n\nHello") + expect(text).to eq("*Created by: Alice*\n\nHello") end - context "when the to_s is called" do + context "when the perform is called" do let_it_be(:project) { create(:project) } let(:text) { "I said to @sam_allen\0 the code" } let(:instance) { described_class.new(text, project:) } - subject(:format) { instance.to_s } + subject(:format) { instance.perform } it 'calls wrap_mentions_in_backticks and convert_ref_links method as a cleaning step' do expect(instance).to receive(:wrap_mentions_in_backticks) diff --git a/spec/views/dashboard/projects/index.html.haml_spec.rb b/spec/views/dashboard/projects/index.html.haml_spec.rb index 39293f719ff..5fb2349a9f7 100644 --- a/spec/views/dashboard/projects/index.html.haml_spec.rb +++ b/spec/views/dashboard/projects/index.html.haml_spec.rb @@ -11,98 +11,43 @@ RSpec.describe 'dashboard/projects/index.html.haml', feature_category: :groups_a allow(view).to receive(:time_ago_with_tooltip) end - context 'when feature :your_work_projects_vue is enabled' do + context 'when show_dashboard_projects_welcome_page? is true' do before do - stub_feature_flags(your_work_projects_vue: true) + allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(true) + render end - context 'when show_dashboard_projects_welcome_page? is true' do - before do - allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(true) - render - end - - it 'renders the zero_authorized_projects partial and not the projects Vue app' do - expect(rendered).not_to have_selector('#js-your-work-projects-app') - expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects') - end - - it 'does not render the "New project" button' do - expect(rendered).not_to have_link('New project') - end - - it 'does not render the "Explore projects" button' do - expect(rendered).not_to have_link('Explore projects') - end + it 'renders the zero_authorized_projects partial and not the projects Vue app' do + expect(rendered).not_to have_selector('#js-your-work-projects-app') + expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects') end - context 'when show_dashboard_projects_welcome_page? is false' do - before do - allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(false) - render - end + it 'does not render the "New project" button' do + expect(rendered).not_to have_link('New project') + end - it 'renders the projects Vue app and not the zero_authorized_projects partial' do - expect(rendered).to have_selector('#js-your-work-projects-app') - expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects') - end - - it 'does render the "New project" button' do - expect(rendered).to have_link('New project') - end - - it 'does render the "Explore projects" button' do - expect(rendered).to have_link('Explore projects') - end + it 'does not render the "Explore projects" button' do + expect(rendered).not_to have_link('Explore projects') end end - context 'when feature :your_work_projects_vue is disabled' do + context 'when show_dashboard_projects_welcome_page? is false' do before do - stub_feature_flags(your_work_projects_vue: false) + allow(view).to receive(:show_dashboard_projects_welcome_page?).and_return(false) + render end - context 'when projects exist' do - before do - assign(:projects, [build(:project, name: 'awesome stuff')]) - allow(view).to receive(:show_projects?).and_return(true) - render - end - - it 'shows the project the user is a member of in the list' do - expect(rendered).to have_content('awesome stuff') - end - - it 'shows the "New project" button' do - expect(rendered).to have_link('New project') - end - - it 'does not render zero_authorized_projects partial' do - expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects') - end - - it 'does not render #js-your-work-projects-app' do - expect(rendered).not_to have_selector('#js-your-work-projects-app') - end + it 'renders the projects Vue app and not the zero_authorized_projects partial' do + expect(rendered).to have_selector('#js-your-work-projects-app') + expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects') end - context 'when projects do not exist' do - before do - allow(view).to receive(:show_projects?).and_return(false) - render - end + it 'does render the "New project" button' do + expect(rendered).to have_link('New project') + end - it 'does not show the "New project" button' do - expect(rendered).not_to have_link('New project') - end - - it 'does render zero_authorized_projects partial' do - expect(rendered).to render_template('dashboard/projects/_zero_authorized_projects') - end - - it 'does not render #js-your-work-projects-app' do - expect(rendered).not_to have_selector('#js-your-work-projects-app') - end + it 'does render the "Explore projects" button' do + expect(rendered).to have_link('Explore projects') end end end diff --git a/spec/views/dashboard/projects/shared/_common.html.haml_spec.rb b/spec/views/dashboard/projects/shared/_common.html.haml_spec.rb index 24d12621479..e9c92e338e4 100644 --- a/spec/views/dashboard/projects/shared/_common.html.haml_spec.rb +++ b/spec/views/dashboard/projects/shared/_common.html.haml_spec.rb @@ -6,95 +6,13 @@ RSpec.describe 'dashboard/projects/shared/_common.html.haml', feature_category: let_it_be(:user) { build(:user) } before do - view.lookup_context.prefixes = ['dashboard/projects'] - - allow(view).to receive(:limited_counter_with_delimiter) allow(view).to receive(:current_user).and_return(user) - allow(view).to receive(:time_ago_with_tooltip) - allow(view).to receive(:empty_page).and_return('starred_empty_state') end - context 'when feature :your_work_projects_vue is enabled' do - before do - stub_feature_flags(your_work_projects_vue: true) - end + it 'renders #js-your-work-projects-app and not legacy project list' do + render - context 'when projects exist' do - before do - assign(:projects, [build(:project, name: 'awesome stuff')]) - allow(view).to receive(:any_projects?).and_return(true) - render - end - - it 'renders #js-your-work-projects-app and not legacy project list' do - render - - expect(rendered).to have_selector('#js-your-work-projects-app') - expect(rendered).not_to render_template('dashboard/projects/_projects') - end - end - - context 'when projects do not exist' do - before do - allow(view).to receive(:any_projects?).and_return(false) - render - end - - it 'renders #js-your-work-projects-app and does not render HAML empty state' do - render - - expect(rendered).to have_selector('#js-your-work-projects-app') - expect(rendered).not_to render_template('dashboard/projects/_zero_authorized_projects') - end - end - end - - context 'when feature :your_work_projects_vue is disabled' do - before do - stub_feature_flags(your_work_projects_vue: false) - end - - context 'when projects exist' do - before do - assign(:projects, [build(:project, name: 'awesome stuff')]) - allow(view).to receive(:any_projects?).and_return(true) - render - end - - it 'shows the project the user is a member of in the list' do - expect(rendered).to have_content('awesome stuff') - end - - it 'shows the "New project" button' do - expect(rendered).to have_link('New project') - end - - it 'does not render starred_empty_state partial' do - expect(rendered).not_to render_template('dashboard/projects/_starred_empty_state') - end - - it 'does not render #js-your-work-projects-app' do - expect(rendered).not_to have_selector('#js-your-work-projects-app') - end - end - - context 'when projects do not exist' do - before do - allow(view).to receive(:any_projects?).and_return(false) - render - end - - it 'does show the "New project" button' do - expect(rendered).to have_link('New project') - end - - it 'does render starred_empty_state partial' do - expect(rendered).to render_template('dashboard/projects/_starred_empty_state') - end - - it 'does not render #js-your-work-projects-app' do - expect(rendered).not_to have_selector('#js-your-work-projects-app') - end - end + expect(rendered).to have_selector('#js-your-work-projects-app') + expect(rendered).not_to render_template('dashboard/projects/_projects') end end