Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-11 21:08:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-11 21:08:31 +0300
commit1a2f754734eb189e371e25e685413808f69a7f2c (patch)
tree2c97884971f36d9026600897b74364d2e212a109 /spec
parentf1ce71c88c407709987dd4a7b40bdb7596b6baa2 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/ide/user_opens_merge_request_spec.rb4
-rw-r--r--spec/features/incidents/incident_details_spec.rb2
-rw-r--r--spec/features/issues/discussion_lock_spec.rb1
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb3
-rw-r--r--spec/features/issues/issue_detail_spec.rb5
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb4
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb3
-rw-r--r--spec/features/issues/user_toggles_subscription_spec.rb4
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb4
-rw-r--r--spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb3
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb2
-rw-r--r--spec/features/projects/issuable_templates_spec.rb2
-rw-r--r--spec/features/reportable_note/issue_spec.rb4
-rw-r--r--spec/finders/notes_finder_spec.rb45
-rw-r--r--spec/frontend/boards/components/board_app_spec.js38
-rw-r--r--spec/frontend/boards/components/board_card_spec.js39
-rw-r--r--spec/frontend/boards/components/board_content_sidebar_spec.js87
-rw-r--r--spec/frontend/boards/components/board_content_spec.js6
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js10
-rw-r--r--spec/frontend/boards/mock_data.js36
-rw-r--r--spec/frontend/ci/artifacts/components/app_spec.js (renamed from spec/frontend/artifacts/components/app_spec.js)8
-rw-r--r--spec/frontend/ci/artifacts/components/artifact_row_spec.js (renamed from spec/frontend/artifacts/components/artifact_row_spec.js)6
-rw-r--r--spec/frontend/ci/artifacts/components/artifacts_bulk_delete_spec.js (renamed from spec/frontend/artifacts/components/artifacts_bulk_delete_spec.js)6
-rw-r--r--spec/frontend/ci/artifacts/components/artifacts_table_row_details_spec.js (renamed from spec/frontend/artifacts/components/artifacts_table_row_details_spec.js)12
-rw-r--r--spec/frontend/ci/artifacts/components/feedback_banner_spec.js (renamed from spec/frontend/artifacts/components/feedback_banner_spec.js)4
-rw-r--r--spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js (renamed from spec/frontend/artifacts/components/job_artifacts_table_spec.js)18
-rw-r--r--spec/frontend/ci/artifacts/components/job_checkbox_spec.js (renamed from spec/frontend/artifacts/components/job_checkbox_spec.js)4
-rw-r--r--spec/frontend/ci/artifacts/graphql/cache_update_spec.js (renamed from spec/frontend/artifacts/graphql/cache_update_spec.js)4
-rw-r--r--spec/frontend/fixtures/job_artifacts.rb2
-rw-r--r--spec/frontend/ide/components/commit_sidebar/form_spec.js19
-rw-r--r--spec/frontend/invite_members/components/invite_group_notification_spec.js14
-rw-r--r--spec/frontend/invite_members/components/invite_groups_modal_spec.js26
-rw-r--r--spec/frontend/issues/show/components/header_actions_spec.js317
-rw-r--r--spec/frontend/issues/show/components/new_header_actions_popover_spec.js67
-rw-r--r--spec/frontend/notes/components/note_form_spec.js6
-rw-r--r--spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap17
-rw-r--r--spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js21
-rw-r--r--spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js56
-rw-r--r--spec/graphql/types/work_items/widget_interface_spec.rb13
-rw-r--r--spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb12
-rw-r--r--spec/helpers/issues_helper_spec.rb10
-rw-r--r--spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb21
-rw-r--r--spec/migrations/20230317162059_add_current_user_todos_work_item_widget_spec.rb21
-rw-r--r--spec/migrations/20230323101138_add_award_emoji_work_item_widget_spec.rb8
-rw-r--r--spec/migrations/20230328100534_truncate_error_tracking_tables_spec.rb56
-rw-r--r--spec/models/note_spec.rb26
-rw-r--r--spec/models/work_items/widget_definition_spec.rb3
-rw-r--r--spec/models/work_items/widgets/award_emoji_spec.rb30
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb45
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb42
-rw-r--r--spec/scripts/generate_rspec_pipeline_spec.rb69
-rw-r--r--spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb33
-rw-r--r--spec/tooling/lib/tooling/find_changes_spec.rb136
-rw-r--r--spec/tooling/lib/tooling/find_tests_spec.rb52
-rw-r--r--spec/tooling/lib/tooling/helpers/file_handler_spec.rb35
-rw-r--r--spec/tooling/lib/tooling/mappings/graphql_base_type_mappings_spec.rb48
-rw-r--r--spec/tooling/lib/tooling/mappings/js_to_system_specs_mappings_spec.rb48
-rw-r--r--spec/tooling/lib/tooling/mappings/partial_to_views_mappings_spec.rb59
-rw-r--r--spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb52
-rw-r--r--spec/tooling/lib/tooling/mappings/view_to_system_specs_mappings_spec.rb45
-rw-r--r--spec/tooling/lib/tooling/predictive_tests_spec.rb109
-rw-r--r--spec/tooling/quality/test_level_spec.rb11
63 files changed, 1421 insertions, 474 deletions
diff --git a/spec/features/ide/user_opens_merge_request_spec.rb b/spec/features/ide/user_opens_merge_request_spec.rb
index 0074b4b1eb0..dc280133a20 100644
--- a/spec/features/ide/user_opens_merge_request_spec.rb
+++ b/spec/features/ide/user_opens_merge_request_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'IDE merge request', :js, feature_category: :web_ide do
+ include CookieHelper
+
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:project) { create(:project, :public, :repository) }
let(:user) { project.first_owner }
@@ -12,6 +14,8 @@ RSpec.describe 'IDE merge request', :js, feature_category: :web_ide do
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
+
visit(merge_request_path(merge_request))
end
diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb
index 709919d0196..a166ff46177 100644
--- a/spec/features/incidents/incident_details_spec.rb
+++ b/spec/features/incidents/incident_details_spec.rb
@@ -94,6 +94,7 @@ RSpec.describe 'Incident details', :js, feature_category: :incident_management d
end
it 'routes the user to the incident details page when the `issue_type` is set to incident' do
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, issue)
wait_for_requests
@@ -113,6 +114,7 @@ RSpec.describe 'Incident details', :js, feature_category: :incident_management d
end
it 'routes the user to the issue details page when the `issue_type` is set to issue' do
+ set_cookie('new-actions-popover-viewed', 'true')
visit incident_project_issues_path(project, incident)
wait_for_requests
diff --git a/spec/features/issues/discussion_lock_spec.rb b/spec/features/issues/discussion_lock_spec.rb
index 47865d2b6ba..fb9addff1a2 100644
--- a/spec/features/issues/discussion_lock_spec.rb
+++ b/spec/features/issues/discussion_lock_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'Discussion Lock', :js, feature_category: :team_planning do
before do
sign_in(user)
+ stub_feature_flags(moved_mr_sidebar: false)
end
context 'when a user is a team member' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 2bd5373b715..665c7307231 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
+ include CookieHelper
+
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') }
@@ -45,6 +47,7 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do
before do
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, issue_to_edit)
wait_for_requests
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index d5f90bb9260..29a61d584ee 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -98,6 +98,7 @@ RSpec.describe 'Issue Detail', :js, feature_category: :team_planning do
project.add_developer(user_to_be_deleted)
sign_in(user_to_be_deleted)
+ stub_feature_flags(moved_mr_sidebar: false)
visit project_issue_path(project, issue)
wait_for_requests
@@ -129,7 +130,7 @@ RSpec.describe 'Issue Detail', :js, feature_category: :team_planning do
describe 'when an issue `issue_type` is edited' do
before do
sign_in(user)
-
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, issue)
wait_for_requests
end
@@ -163,7 +164,7 @@ RSpec.describe 'Issue Detail', :js, feature_category: :team_planning do
describe 'when an incident `issue_type` is edited' do
before do
sign_in(user)
-
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, incident)
wait_for_requests
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 2ae347d4f9e..ee71181fba2 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Issue Sidebar', feature_category: :team_planning do
include MobileHelpers
include Features::InviteMembersModalHelpers
+ include CookieHelper
let_it_be(:group) { create(:group, :nested) }
let_it_be(:project) { create(:project, :public, namespace: group) }
@@ -20,6 +21,7 @@ RSpec.describe 'Issue Sidebar', feature_category: :team_planning do
context 'when signed in' do
before do
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
end
context 'when concerning the assignee', :js do
@@ -205,6 +207,7 @@ RSpec.describe 'Issue Sidebar', feature_category: :team_planning do
context 'as an allowed user' do
before do
+ stub_feature_flags(moved_mr_sidebar: false)
project.add_developer(user)
visit_issue(project, issue)
end
@@ -293,6 +296,7 @@ RSpec.describe 'Issue Sidebar', feature_category: :team_planning do
context 'as a guest' do
before do
+ stub_feature_flags(moved_mr_sidebar: false)
project.add_guest(user)
visit_issue(project, issue)
end
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 3a927e76fd1..4730406c2b2 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe "Issues > User edits issue", :js, feature_category: :team_planning do
+ include CookieHelper
+
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:project_with_milestones) { create(:project_empty_repo, :public) }
let_it_be(:user) { create(:user) }
@@ -18,6 +20,7 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin
project.add_developer(user)
project_with_milestones.add_developer(user)
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
end
context "from edit page" do
diff --git a/spec/features/issues/user_toggles_subscription_spec.rb b/spec/features/issues/user_toggles_subscription_spec.rb
index 904fafdf56a..00b04c10d33 100644
--- a/spec/features/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/issues/user_toggles_subscription_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin
context 'user is not logged in' do
before do
+ stub_feature_flags(moved_mr_sidebar: false)
visit(project_issue_path(project, issue))
end
@@ -20,9 +21,9 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin
context 'user is logged in' do
before do
+ stub_feature_flags(moved_mr_sidebar: false)
project.add_developer(user)
sign_in(user)
-
visit(project_issue_path(project, issue))
end
@@ -52,6 +53,7 @@ RSpec.describe "User toggles subscription", :js, feature_category: :team_plannin
context 'user is logged in without edit permission' do
before do
+ stub_feature_flags(moved_mr_sidebar: false)
sign_in(user2)
visit(project_issue_path(project, issue))
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index d4ccc4a93b5..3bcc8255ab7 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User manages subscription', :js, feature_category: :code_review_workflow do
+ include CookieHelper
+
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
@@ -10,7 +12,7 @@ RSpec.describe 'User manages subscription', :js, feature_category: :code_review_
before do
stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
-
+ set_cookie('new-actions-popover-viewed', 'true')
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
index 7cb1c95f6dc..601310cbacf 100644
--- a/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Merge request > User opens checkout branch modal', :js, feature_category: :code_review_workflow do
include ProjectForksHelper
+ include CookieHelper
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
@@ -11,6 +12,7 @@ RSpec.describe 'Merge request > User opens checkout branch modal', :js, feature_
before do
project.add_maintainer(user)
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
end
describe 'for fork' do
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index ad2ceeb23e2..21c62b0d0d8 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Merge request > User sees check out branch modal', :js, feature_category: :code_review_workflow do
+ include CookieHelper
+
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -10,6 +12,7 @@ RSpec.describe 'Merge request > User sees check out branch modal', :js, feature_
before do
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_merge_request_path(project, merge_request)
wait_for_requests
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index 0de59ea21c5..dae28cbb05c 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Merge request > User selects branches for new MR', :js, feature_category: :code_review_workflow do
include ListboxHelpers
+ include CookieHelper
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
@@ -17,6 +18,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js, feature_
before do
project.add_maintainer(user)
sign_in(user)
+ set_cookie('new-actions-popover-viewed', 'true')
end
it 'selects the source branch sha when a tag with the same name exists' do
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index adf410ce6e8..77f88994bfb 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'issuable templates', :js, feature_category: :projects do
include ProjectForksHelper
+ include CookieHelper
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -12,6 +13,7 @@ RSpec.describe 'issuable templates', :js, feature_category: :projects do
before do
project.add_maintainer(user)
sign_in user
+ set_cookie('new-actions-popover-viewed', 'true')
end
context 'user creates an issue using templates' do
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index 55e7f5897bc..a18cdf27294 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Reportable note on issue', :js, feature_category: :team_planning do
+ include CookieHelper
+
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
@@ -11,7 +13,7 @@ RSpec.describe 'Reportable note on issue', :js, feature_category: :team_planning
before do
project.add_maintainer(user)
sign_in(user)
-
+ set_cookie('new-actions-popover-viewed', 'true')
visit project_issue_path(project, issue)
end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 1df0aa411d6..e93c0c790c2 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -128,6 +128,51 @@ RSpec.describe NotesFinder do
end
end
+ context 'for notes from users who have been banned', :enable_admin_mode, feature_category: :instance_resiliency do
+ subject(:finder) { described_class.new(user, project: project).execute }
+
+ let_it_be(:banned_user) { create(:banned_user).user }
+ let!(:banned_note) { create(:note_on_issue, project: project, author: banned_user) }
+
+ context 'when :hidden_notes feature is not enabled' do
+ before do
+ stub_feature_flags(hidden_notes: false)
+ end
+
+ context 'when user is not an admin' do
+ it { is_expected.to include(banned_note) }
+ end
+
+ context 'when @current_user is nil' do
+ let(:user) { nil }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ context 'when :hidden_notes feature is enabled' do
+ before do
+ stub_feature_flags(hidden_notes: true)
+ end
+
+ context 'when user is an admin' do
+ let(:user) { create(:admin) }
+
+ it { is_expected.to include(banned_note) }
+ end
+
+ context 'when user is not an admin' do
+ it { is_expected.not_to include(banned_note) }
+ end
+
+ context 'when @current_user is nil' do
+ let(:user) { nil }
+
+ it { is_expected.to be_empty }
+ end
+ end
+ end
+
context 'for target type' do
let(:project) { create(:project, :repository) }
let!(:note1) { create :note_on_issue, project: project }
diff --git a/spec/frontend/boards/components/board_app_spec.js b/spec/frontend/boards/components/board_app_spec.js
index 148e696b57b..77ba6cdc9c0 100644
--- a/spec/frontend/boards/components/board_app_spec.js
+++ b/spec/frontend/boards/components/board_app_spec.js
@@ -1,14 +1,20 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
+import createMockApollo from 'helpers/mock_apollo_helper';
import BoardApp from '~/boards/components/board_app.vue';
+import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
+import { rawIssue } from '../mock_data';
describe('BoardApp', () => {
let wrapper;
let store;
+ const mockApollo = createMockApollo();
Vue.use(Vuex);
+ Vue.use(VueApollo);
const createStore = ({ mockGetters = {} } = {}) => {
store = new Vuex.Store({
@@ -23,12 +29,22 @@ describe('BoardApp', () => {
});
};
- const createComponent = () => {
+ const createComponent = ({ isApolloBoard = false, issue = rawIssue } = {}) => {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: activeBoardItemQuery,
+ data: {
+ activeBoardItem: issue,
+ },
+ });
+
wrapper = shallowMount(BoardApp, {
+ apolloProvider: mockApollo,
store,
provide: {
initialBoardId: 'gid://gitlab/Board/1',
initialFilterParams: {},
+ isIssueBoard: true,
+ isApolloBoard,
},
});
};
@@ -50,4 +66,22 @@ describe('BoardApp', () => {
expect(wrapper.classes()).not.toContain('is-compact');
});
+
+ describe('Apollo boards', () => {
+ beforeEach(async () => {
+ createComponent({ isApolloBoard: true });
+ await nextTick();
+ });
+
+ it('should have is-compact class when a card is selected', () => {
+ expect(wrapper.classes()).toContain('is-compact');
+ });
+
+ it('should not have is-compact class when no card is selected', async () => {
+ createComponent({ isApolloBoard: true, issue: {} });
+ await nextTick();
+
+ expect(wrapper.classes()).not.toContain('is-compact');
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js
index 46116bed4cf..91e9b6f8cfa 100644
--- a/spec/frontend/boards/components/board_card_spec.js
+++ b/spec/frontend/boards/components/board_card_spec.js
@@ -1,8 +1,10 @@
import { GlLabel } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BoardCard from '~/boards/components/board_card.vue';
import BoardCardInner from '~/boards/components/board_card_inner.vue';
import { inactiveId } from '~/boards/constants';
@@ -14,6 +16,14 @@ describe('Board card', () => {
let mockActions;
Vue.use(Vuex);
+ Vue.use(VueApollo);
+
+ const mockSetActiveBoardItemResolver = jest.fn();
+ const mockApollo = createMockApollo([], {
+ Mutation: {
+ setActiveBoardItem: mockSetActiveBoardItemResolver,
+ },
+ });
const createStore = ({ initialState = {} } = {}) => {
mockActions = {
@@ -36,11 +46,11 @@ describe('Board card', () => {
const mountComponent = ({
propsData = {},
provide = {},
- mountFn = shallowMount,
stubs = { BoardCardInner },
item = mockIssue,
} = {}) => {
- wrapper = mountFn(BoardCard, {
+ wrapper = shallowMountExtended(BoardCard, {
+ apolloProvider: mockApollo,
stubs: {
...stubs,
BoardCardInner,
@@ -56,9 +66,9 @@ describe('Board card', () => {
groupId: null,
rootPath: '/',
scopedLabelsAvailable: false,
+ isIssueBoard: true,
isEpicBoard: false,
issuableType: 'issue',
- isProjectBoard: false,
isGroupBoard: true,
disabled: false,
isApolloBoard: false,
@@ -218,4 +228,25 @@ describe('Board card', () => {
expect(wrapper.attributes('style')).toBeUndefined();
});
});
+
+ describe('Apollo boards', () => {
+ beforeEach(async () => {
+ createStore();
+ mountComponent({ provide: { isApolloBoard: true } });
+ await nextTick();
+ });
+
+ it('set active board item on client when clicking on card', async () => {
+ await selectCard();
+
+ expect(mockSetActiveBoardItemResolver).toHaveBeenCalledWith(
+ {},
+ {
+ boardItem: mockIssue,
+ },
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js
index 90376a4a553..558a0a3b933 100644
--- a/spec/frontend/boards/components/board_content_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_content_sidebar_spec.js
@@ -1,10 +1,15 @@
import { GlDrawer } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import { MountingPortal } from 'portal-vue';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
import { stubComponent } from 'helpers/stub_component';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { ISSUABLE } from '~/boards/constants';
@@ -14,13 +19,21 @@ import SidebarSeverityWidget from '~/sidebar/components/severity/sidebar_severit
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import SidebarLabelsWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
-import { mockActiveIssue, mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
+import { mockActiveIssue, mockIssue, rawIssue } from '../mock_data';
Vue.use(Vuex);
+Vue.use(VueApollo);
describe('BoardContentSidebar', () => {
let wrapper;
let store;
+ const mockSetActiveBoardItemResolver = jest.fn();
+ const mockApollo = createMockApollo([], {
+ Mutation: {
+ setActiveBoardItem: mockSetActiveBoardItemResolver,
+ },
+ });
+
const createStore = ({ mockGetters = {}, mockActions = {} } = {}) => {
store = new Vuex.Store({
state: {
@@ -32,30 +45,29 @@ describe('BoardContentSidebar', () => {
activeBoardItem: () => {
return { ...mockActiveIssue, epic: null };
},
- groupPathForActiveIssue: () => mockIssueGroupPath,
- projectPathForActiveIssue: () => mockIssueProjectPath,
- isSidebarOpen: () => true,
...mockGetters,
},
actions: mockActions,
});
};
- const createComponent = () => {
- /*
- Dynamically imported components (in our case ee imports)
- aren't stubbed automatically in VTU v1:
- https://github.com/vuejs/vue-test-utils/issues/1279.
+ const createComponent = ({ isApolloBoard = false } = {}) => {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: activeBoardItemQuery,
+ data: {
+ activeBoardItem: rawIssue,
+ },
+ });
- This requires us to additionally mock apollo or vuex stores.
- */
- wrapper = shallowMount(BoardContentSidebar, {
+ wrapper = shallowMountExtended(BoardContentSidebar, {
+ apolloProvider: mockApollo,
provide: {
canUpdate: true,
rootPath: '/',
groupId: 1,
issuableType: TYPE_ISSUE,
isGroupBoard: false,
+ isApolloBoard,
},
store,
stubs: {
@@ -63,24 +75,6 @@ describe('BoardContentSidebar', () => {
template: '<div><slot name="header"></slot><slot></slot></div>',
}),
},
- mocks: {
- $apollo: {
- queries: {
- participants: {
- loading: false,
- },
- currentIteration: {
- loading: false,
- },
- iterations: {
- loading: false,
- },
- attributesList: {
- loading: false,
- },
- },
- },
- },
});
};
@@ -101,10 +95,12 @@ describe('BoardContentSidebar', () => {
});
});
- it('does not render GlDrawer when isSidebarOpen is false', () => {
- createStore({ mockGetters: { isSidebarOpen: () => false } });
+ it('does not render GlDrawer when no active item is set', async () => {
+ createStore({ mockGetters: { activeBoardItem: () => ({ id: '', iid: '' }) } });
createComponent();
+ await nextTick();
+
expect(wrapper.findComponent(GlDrawer).props('open')).toBe(false);
});
@@ -189,4 +185,27 @@ describe('BoardContentSidebar', () => {
expect(wrapper.findComponent(SidebarSeverityWidget).exists()).toBe(true);
});
});
+
+ describe('Apollo boards', () => {
+ beforeEach(async () => {
+ createStore();
+ createComponent({ isApolloBoard: true });
+ await nextTick();
+ });
+
+ it('calls setActiveBoardItemMutation on close', async () => {
+ wrapper.findComponent(GlDrawer).vm.$emit('close');
+
+ await waitForPromises();
+
+ expect(mockSetActiveBoardItemResolver).toHaveBeenCalledWith(
+ {},
+ {
+ boardItem: null,
+ },
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
index 33351bf8efd..ab51f477966 100644
--- a/spec/frontend/boards/components/board_content_spec.js
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -6,6 +6,7 @@ import Draggable from 'vuedraggable';
import Vuex from 'vuex';
import eventHub from '~/boards/eventhub';
+import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import createMockApollo from 'helpers/mock_apollo_helper';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
@@ -78,6 +79,11 @@ describe('BoardContent', () => {
isApolloBoard,
},
store,
+ stubs: {
+ BoardContentSidebar: stubComponent(BoardContentSidebar, {
+ template: '<div></div>',
+ }),
+ },
});
};
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
index a20884baf3b..296f25d96c4 100644
--- a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
+++ b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js
@@ -1,6 +1,7 @@
import { GlAlert, GlFormInput, GlForm, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { createStore } from '~/boards/stores';
@@ -21,7 +22,7 @@ const TEST_ISSUE_B = {
webUrl: 'webUrl',
};
-describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
+describe('BoardSidebarTitle', () => {
let wrapper;
let store;
@@ -39,6 +40,10 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
store,
provide: {
canUpdate: true,
+ isApolloBoard: false,
+ },
+ propsData: {
+ activeItem: item,
},
stubs: {
'board-editable-item': BoardEditableItem,
@@ -86,7 +91,8 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => {
await nextTick();
});
- it('collapses sidebar and renders new title', () => {
+ it('collapses sidebar and renders new title', async () => {
+ await waitForPromises();
expect(findCollapsed().isVisible()).toBe(true);
expect(findTitle().text()).toContain(TEST_TITLE);
});
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index d5c6871d9c4..cc0945da9f4 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -277,6 +277,9 @@ export const labels = [
},
];
+export const mockIssueFullPath = 'gitlab-org/test-subgroup/gitlab-test';
+export const mockEpicFullPath = 'gitlab-org/test-subgroup';
+
export const rawIssue = {
title: 'Issue 1',
id: 'gid://gitlab/Issue/436',
@@ -302,12 +305,24 @@ export const rawIssue = {
epic: {
id: 'gid://gitlab/Epic/41',
},
+ totalTimeSpent: 0,
+ humanTimeEstimate: null,
+ humanTotalTimeSpent: null,
+ emailsDisabled: false,
+ hidden: false,
+ webUrl: `${mockIssueFullPath}/-/issue/27`,
+ relativePosition: null,
+ severity: null,
+ milestone: null,
+ weight: null,
+ blocked: false,
+ blockedByCount: 0,
+ iteration: null,
+ healthStatus: null,
type: 'ISSUE',
+ __typename: 'Issue',
};
-export const mockIssueFullPath = 'gitlab-org/test-subgroup/gitlab-test';
-export const mockEpicFullPath = 'gitlab-org/test-subgroup';
-
export const mockIssue = {
id: 'gid://gitlab/Issue/436',
iid: '27',
@@ -329,7 +344,22 @@ export const mockIssue = {
epic: {
id: 'gid://gitlab/Epic/41',
},
+ totalTimeSpent: 0,
+ humanTimeEstimate: null,
+ humanTotalTimeSpent: null,
+ emailsDisabled: false,
+ hidden: false,
+ webUrl: `${mockIssueFullPath}/-/issue/27`,
+ relativePosition: null,
+ severity: null,
+ milestone: null,
+ weight: null,
+ blocked: false,
+ blockedByCount: 0,
+ iteration: null,
+ healthStatus: null,
type: 'ISSUE',
+ __typename: 'Issue',
};
export const mockEpic = {
diff --git a/spec/frontend/artifacts/components/app_spec.js b/spec/frontend/ci/artifacts/components/app_spec.js
index 931c4703e95..435b03e82ab 100644
--- a/spec/frontend/artifacts/components/app_spec.js
+++ b/spec/frontend/ci/artifacts/components/app_spec.js
@@ -2,13 +2,13 @@ import { GlSkeletonLoader } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
-import ArtifactsApp from '~/artifacts/components/app.vue';
-import JobArtifactsTable from '~/artifacts/components/job_artifacts_table.vue';
-import getBuildArtifactsSizeQuery from '~/artifacts/graphql/queries/get_build_artifacts_size.query.graphql';
+import ArtifactsApp from '~/ci/artifacts/components/app.vue';
+import JobArtifactsTable from '~/ci/artifacts/components/job_artifacts_table.vue';
+import getBuildArtifactsSizeQuery from '~/ci/artifacts/graphql/queries/get_build_artifacts_size.query.graphql';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { PAGE_TITLE, TOTAL_ARTIFACTS_SIZE, SIZE_UNKNOWN } from '~/artifacts/constants';
+import { PAGE_TITLE, TOTAL_ARTIFACTS_SIZE, SIZE_UNKNOWN } from '~/ci/artifacts/constants';
const TEST_BUILD_ARTIFACTS_SIZE = 1024;
const TEST_PROJECT_PATH = 'project/path';
diff --git a/spec/frontend/artifacts/components/artifact_row_spec.js b/spec/frontend/ci/artifacts/components/artifact_row_spec.js
index 268772ed4c0..86875cd8566 100644
--- a/spec/frontend/artifacts/components/artifact_row_spec.js
+++ b/spec/frontend/ci/artifacts/components/artifact_row_spec.js
@@ -1,10 +1,10 @@
import { GlBadge, GlButton, GlFriendlyWrap, GlFormCheckbox } from '@gitlab/ui';
-import mockGetJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
+import mockGetJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import ArtifactRow from '~/artifacts/components/artifact_row.vue';
-import { BULK_DELETE_FEATURE_FLAG } from '~/artifacts/constants';
+import ArtifactRow from '~/ci/artifacts/components/artifact_row.vue';
+import { BULK_DELETE_FEATURE_FLAG } from '~/ci/artifacts/constants';
describe('ArtifactRow component', () => {
let wrapper;
diff --git a/spec/frontend/artifacts/components/artifacts_bulk_delete_spec.js b/spec/frontend/ci/artifacts/components/artifacts_bulk_delete_spec.js
index 876906b2c3c..dd077dcac9f 100644
--- a/spec/frontend/artifacts/components/artifacts_bulk_delete_spec.js
+++ b/spec/frontend/ci/artifacts/components/artifacts_bulk_delete_spec.js
@@ -1,12 +1,12 @@
import { GlSprintf, GlModal } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import mockGetJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
+import mockGetJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import ArtifactsBulkDelete from '~/artifacts/components/artifacts_bulk_delete.vue';
-import bulkDestroyArtifactsMutation from '~/artifacts/graphql/mutations/bulk_destroy_job_artifacts.mutation.graphql';
+import ArtifactsBulkDelete from '~/ci/artifacts/components/artifacts_bulk_delete.vue';
+import bulkDestroyArtifactsMutation from '~/ci/artifacts/graphql/mutations/bulk_destroy_job_artifacts.mutation.graphql';
Vue.use(VueApollo);
diff --git a/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js b/spec/frontend/ci/artifacts/components/artifacts_table_row_details_spec.js
index 6bf3498f9b0..ebdb7e25c45 100644
--- a/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js
+++ b/spec/frontend/ci/artifacts/components/artifacts_table_row_details_spec.js
@@ -1,15 +1,15 @@
import { GlModal } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import getJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
+import getJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
import waitForPromises from 'helpers/wait_for_promises';
-import ArtifactsTableRowDetails from '~/artifacts/components/artifacts_table_row_details.vue';
-import ArtifactRow from '~/artifacts/components/artifact_row.vue';
-import ArtifactDeleteModal from '~/artifacts/components/artifact_delete_modal.vue';
+import ArtifactsTableRowDetails from '~/ci/artifacts/components/artifacts_table_row_details.vue';
+import ArtifactRow from '~/ci/artifacts/components/artifact_row.vue';
+import ArtifactDeleteModal from '~/ci/artifacts/components/artifact_delete_modal.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
-import destroyArtifactMutation from '~/artifacts/graphql/mutations/destroy_artifact.mutation.graphql';
-import { I18N_DESTROY_ERROR, I18N_MODAL_TITLE } from '~/artifacts/constants';
+import destroyArtifactMutation from '~/ci/artifacts/graphql/mutations/destroy_artifact.mutation.graphql';
+import { I18N_DESTROY_ERROR, I18N_MODAL_TITLE } from '~/ci/artifacts/constants';
import { createAlert } from '~/alert';
jest.mock('~/alert');
diff --git a/spec/frontend/artifacts/components/feedback_banner_spec.js b/spec/frontend/ci/artifacts/components/feedback_banner_spec.js
index af9599daefa..53e0fdac6f6 100644
--- a/spec/frontend/artifacts/components/feedback_banner_spec.js
+++ b/spec/frontend/ci/artifacts/components/feedback_banner_spec.js
@@ -1,12 +1,12 @@
import { GlBanner } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import FeedbackBanner from '~/artifacts/components/feedback_banner.vue';
+import FeedbackBanner from '~/ci/artifacts/components/feedback_banner.vue';
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
import {
I18N_FEEDBACK_BANNER_TITLE,
I18N_FEEDBACK_BANNER_BUTTON,
FEEDBACK_URL,
-} from '~/artifacts/constants';
+} from '~/ci/artifacts/constants';
const mockBannerImagePath = 'banner/image/path';
diff --git a/spec/frontend/artifacts/components/job_artifacts_table_spec.js b/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js
index 40f3c9633ab..2855b0ecb37 100644
--- a/spec/frontend/artifacts/components/job_artifacts_table_spec.js
+++ b/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js
@@ -9,17 +9,17 @@ import {
} from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import getJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
+import getJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import waitForPromises from 'helpers/wait_for_promises';
-import JobArtifactsTable from '~/artifacts/components/job_artifacts_table.vue';
-import FeedbackBanner from '~/artifacts/components/feedback_banner.vue';
-import ArtifactsTableRowDetails from '~/artifacts/components/artifacts_table_row_details.vue';
-import ArtifactDeleteModal from '~/artifacts/components/artifact_delete_modal.vue';
-import ArtifactsBulkDelete from '~/artifacts/components/artifacts_bulk_delete.vue';
+import JobArtifactsTable from '~/ci/artifacts/components/job_artifacts_table.vue';
+import FeedbackBanner from '~/ci/artifacts/components/feedback_banner.vue';
+import ArtifactsTableRowDetails from '~/ci/artifacts/components/artifacts_table_row_details.vue';
+import ArtifactDeleteModal from '~/ci/artifacts/components/artifact_delete_modal.vue';
+import ArtifactsBulkDelete from '~/ci/artifacts/components/artifacts_bulk_delete.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
-import getJobArtifactsQuery from '~/artifacts/graphql/queries/get_job_artifacts.query.graphql';
+import getJobArtifactsQuery from '~/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
ARCHIVE_FILE_TYPE,
@@ -27,8 +27,8 @@ import {
I18N_FETCH_ERROR,
INITIAL_CURRENT_PAGE,
BULK_DELETE_FEATURE_FLAG,
-} from '~/artifacts/constants';
-import { totalArtifactsSizeForJob } from '~/artifacts/utils';
+} from '~/ci/artifacts/constants';
+import { totalArtifactsSizeForJob } from '~/ci/artifacts/utils';
import { createAlert } from '~/alert';
jest.mock('~/alert');
diff --git a/spec/frontend/artifacts/components/job_checkbox_spec.js b/spec/frontend/ci/artifacts/components/job_checkbox_spec.js
index 95cc548b8c8..ae70bb4b17b 100644
--- a/spec/frontend/artifacts/components/job_checkbox_spec.js
+++ b/spec/frontend/ci/artifacts/components/job_checkbox_spec.js
@@ -1,7 +1,7 @@
import { GlFormCheckbox } from '@gitlab/ui';
-import mockGetJobArtifactsResponse from 'test_fixtures/graphql/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
+import mockGetJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import JobCheckbox from '~/artifacts/components/job_checkbox.vue';
+import JobCheckbox from '~/ci/artifacts/components/job_checkbox.vue';
describe('JobCheckbox component', () => {
let wrapper;
diff --git a/spec/frontend/artifacts/graphql/cache_update_spec.js b/spec/frontend/ci/artifacts/graphql/cache_update_spec.js
index 4d610328298..3c415534c7c 100644
--- a/spec/frontend/artifacts/graphql/cache_update_spec.js
+++ b/spec/frontend/ci/artifacts/graphql/cache_update_spec.js
@@ -1,5 +1,5 @@
-import getJobArtifactsQuery from '~/artifacts/graphql/queries/get_job_artifacts.query.graphql';
-import { removeArtifactFromStore } from '~/artifacts/graphql/cache_update';
+import getJobArtifactsQuery from '~/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql';
+import { removeArtifactFromStore } from '~/ci/artifacts/graphql/cache_update';
describe('Artifact table cache updates', () => {
let store;
diff --git a/spec/frontend/fixtures/job_artifacts.rb b/spec/frontend/fixtures/job_artifacts.rb
index e53cdbbaaa5..6dadd6750f1 100644
--- a/spec/frontend/fixtures/job_artifacts.rb
+++ b/spec/frontend/fixtures/job_artifacts.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Job Artifacts (GraphQL fixtures)' do
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:user) { create(:user) }
- job_artifacts_query_path = 'artifacts/graphql/queries/get_job_artifacts.query.graphql'
+ job_artifacts_query_path = 'ci/artifacts/graphql/queries/get_job_artifacts.query.graphql'
it "graphql/#{job_artifacts_query_path}.json" do
create(:ci_build, :failed, :artifacts, :trace_artifact, pipeline: pipeline)
diff --git a/spec/frontend/ide/components/commit_sidebar/form_spec.js b/spec/frontend/ide/components/commit_sidebar/form_spec.js
index 0c0998c037a..ba30073dff2 100644
--- a/spec/frontend/ide/components/commit_sidebar/form_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/form_spec.js
@@ -21,6 +21,7 @@ import { COMMIT_TO_NEW_BRANCH } from '~/ide/stores/modules/commit/constants';
describe('IDE commit form', () => {
let wrapper;
let store;
+ const showModalSpy = jest.fn();
const createComponent = () => {
wrapper = shallowMount(CommitForm, {
@@ -29,7 +30,11 @@ describe('IDE commit form', () => {
GlTooltip: createMockDirective('gl-tooltip'),
},
stubs: {
- GlModal: stubComponent(GlModal),
+ GlModal: stubComponent(GlModal, {
+ methods: {
+ show: showModalSpy,
+ },
+ }),
},
});
};
@@ -57,6 +62,7 @@ describe('IDE commit form', () => {
tooltip: getBinding(findCommitButtonTooltip().element, 'gl-tooltip').value.title,
});
const findForm = () => wrapper.find('form');
+ const findModal = () => wrapper.findComponent(GlModal);
const submitForm = () => findForm().trigger('submit');
const findCommitMessageInput = () => wrapper.findComponent(CommitMessageField);
const setCommitMessageInput = (val) => findCommitMessageInput().vm.$emit('input', val);
@@ -298,22 +304,19 @@ describe('IDE commit form', () => {
${() => createCodeownersCommitError('test message')} | ${{ actionPrimary: { text: 'Create new branch' } }}
${createUnexpectedCommitError} | ${{ actionPrimary: null }}
`('opens error modal if commitError with $error', async ({ createError, props }) => {
- const modal = wrapper.findComponent(GlModal);
- modal.vm.show = jest.fn();
-
const error = createError();
store.state.commit.commitError = error;
await nextTick();
- expect(modal.vm.show).toHaveBeenCalled();
- expect(modal.props()).toMatchObject({
+ expect(showModalSpy).toHaveBeenCalled();
+ expect(findModal().props()).toMatchObject({
actionCancel: { text: 'Cancel' },
...props,
});
// Because of the legacy 'mountComponent' approach here, the only way to
// test the text of the modal is by viewing the content of the modal added to the document.
- expect(modal.html()).toContain(error.messageHTML);
+ expect(findModal().html()).toContain(error.messageHTML);
});
});
@@ -339,7 +342,7 @@ describe('IDE commit form', () => {
await nextTick();
- wrapper.findComponent(GlModal).vm.$emit('ok');
+ findModal().vm.$emit('ok');
await waitForPromises();
diff --git a/spec/frontend/invite_members/components/invite_group_notification_spec.js b/spec/frontend/invite_members/components/invite_group_notification_spec.js
index 3e6ba6da9f4..1da2e7b705d 100644
--- a/spec/frontend/invite_members/components/invite_group_notification_spec.js
+++ b/spec/frontend/invite_members/components/invite_group_notification_spec.js
@@ -2,7 +2,7 @@ import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { sprintf } from '~/locale';
import InviteGroupNotification from '~/invite_members/components/invite_group_notification.vue';
-import { GROUP_MODAL_ALERT_BODY } from '~/invite_members/constants';
+import { GROUP_MODAL_TO_GROUP_ALERT_BODY } from '~/invite_members/constants';
describe('InviteGroupNotification', () => {
let wrapper;
@@ -13,7 +13,11 @@ describe('InviteGroupNotification', () => {
const createComponent = () => {
wrapper = shallowMountExtended(InviteGroupNotification, {
provide: { freeUsersLimit: 5 },
- propsData: { name: 'name' },
+ propsData: {
+ name: 'name',
+ notificationLink: '_notification_link_',
+ notificationText: GROUP_MODAL_TO_GROUP_ALERT_BODY,
+ },
stubs: { GlSprintf },
});
};
@@ -28,15 +32,13 @@ describe('InviteGroupNotification', () => {
});
it('shows the correct message', () => {
- const message = sprintf(GROUP_MODAL_ALERT_BODY, { count: 5 });
+ const message = sprintf(GROUP_MODAL_TO_GROUP_ALERT_BODY, { count: 5 });
expect(findAlert().text()).toMatchInterpolatedText(message);
});
it('has a help link', () => {
- expect(findLink().attributes('href')).toEqual(
- 'https://docs.gitlab.com/ee/user/group/manage.html#share-a-group-with-another-group',
- );
+ expect(findLink().attributes('href')).toEqual('_notification_link_');
});
});
});
diff --git a/spec/frontend/invite_members/components/invite_groups_modal_spec.js b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
index 82b4717fbf1..4f082145562 100644
--- a/spec/frontend/invite_members/components/invite_groups_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_groups_modal_spec.js
@@ -12,6 +12,12 @@ import {
displaySuccessfulInvitationAlert,
reloadOnInvitationSuccess,
} from '~/invite_members/utils/trigger_successful_invite_alert';
+import {
+ GROUP_MODAL_TO_GROUP_ALERT_BODY,
+ GROUP_MODAL_TO_GROUP_ALERT_LINK,
+ GROUP_MODAL_TO_PROJECT_ALERT_BODY,
+ GROUP_MODAL_TO_PROJECT_ALERT_LINK,
+} from '~/invite_members/constants';
import { propsData, sharedGroup } from '../mock_data/group_modal';
jest.mock('~/invite_members/utils/trigger_successful_invite_alert');
@@ -91,6 +97,26 @@ describe('InviteGroupsModal', () => {
expect(findInviteGroupAlert().exists()).toBe(false);
});
+
+ it('shows the user limit notification alert with correct link and text for group', () => {
+ createComponent({ freeUserCapEnabled: true });
+
+ expect(findInviteGroupAlert().props()).toMatchObject({
+ name: propsData.name,
+ notificationText: GROUP_MODAL_TO_GROUP_ALERT_BODY,
+ notificationLink: GROUP_MODAL_TO_GROUP_ALERT_LINK,
+ });
+ });
+
+ it('shows the user limit notification alert with correct link and text for project', () => {
+ createComponent({ freeUserCapEnabled: true, isProject: true });
+
+ expect(findInviteGroupAlert().props()).toMatchObject({
+ name: propsData.name,
+ notificationText: GROUP_MODAL_TO_PROJECT_ALERT_BODY,
+ notificationLink: GROUP_MODAL_TO_PROJECT_ALERT_LINK,
+ });
+ });
});
describe('submitting the invite form', () => {
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js
index 58ec7387851..bd8e79a90ec 100644
--- a/spec/frontend/issues/show/components/header_actions_spec.js
+++ b/spec/frontend/issues/show/components/header_actions_spec.js
@@ -2,6 +2,8 @@ import Vue, { nextTick } from 'vue';
import { GlDropdownItem, GlLink, GlModal, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
+import VueApollo from 'vue-apollo';
+import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking } from 'helpers/tracking_helper';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { STATUS_CLOSED, STATUS_OPEN, TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants';
@@ -14,17 +16,22 @@ import promoteToEpicMutation from '~/issues/show/queries/promote_to_epic.mutatio
import * as urlUtility from '~/lib/utils/url_utility';
import eventHub from '~/notes/event_hub';
import createStore from '~/notes/stores';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
+import updateIssueMutation from '~/issues/show/queries/update_issue.mutation.graphql';
+import toast from '~/vue_shared/plugins/global_toast';
jest.mock('~/alert');
jest.mock('~/issues/show/event_hub', () => ({ $emit: jest.fn() }));
+jest.mock('~/vue_shared/plugins/global_toast');
describe('HeaderActions component', () => {
let dispatchEventSpy;
- let mutateMock;
let wrapper;
let visitUrlSpy;
Vue.use(Vuex);
+ Vue.use(VueApollo);
const store = createStore();
@@ -45,15 +52,28 @@ describe('HeaderActions component', () => {
reportedUserId: 1,
reportedFromUrl: 'http://localhost:/gitlab-org/-/issues/32',
submitAsSpamPath: 'gitlab-org/gitlab-test/-/issues/32/submit_as_spam',
+ issuableEmailAddress: null,
+ fullPath: 'full-path',
};
- const updateIssueMutationResponse = { data: { updateIssue: { errors: [] } } };
+ const updateIssueMutationResponse = {
+ data: {
+ updateIssue: {
+ errors: [],
+ issuable: {
+ id: 'gid://gitlab/Issue/511',
+ state: STATUS_OPEN,
+ },
+ },
+ },
+ };
const promoteToEpicMutationResponse = {
data: {
promoteToEpic: {
errors: [],
epic: {
+ id: 'gid://gitlab/Epic/1',
webPath: '/groups/gitlab-org/-/epics/1',
},
},
@@ -69,6 +89,20 @@ describe('HeaderActions component', () => {
},
};
+ const mockIssueReferenceData = {
+ data: {
+ workspace: {
+ id: 'gid://gitlab/Project/7',
+ issuable: {
+ id: 'gid://gitlab/Issue/511',
+ reference: 'flightjs/Flight#33',
+ __typename: 'Issue',
+ },
+ __typename: 'Project',
+ },
+ },
+ };
+
const findToggleIssueStateButton = () => wrapper.find(`[data-testid="toggle-button"]`);
const findEditButton = () => wrapper.find(`[data-testid="edit-button"]`);
@@ -77,33 +111,54 @@ describe('HeaderActions component', () => {
const findDesktopDropdown = () => findDropdownBy('desktop-dropdown');
const findMobileDropdownItems = () => findMobileDropdown().findAllComponents(GlDropdownItem);
const findDesktopDropdownItems = () => findDesktopDropdown().findAllComponents(GlDropdownItem);
+ const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector);
+ const findReportAbuseSelectorItem = () => wrapper.find(`[data-testid="report-abuse-item"]`);
+ const findNotificationWidget = () => wrapper.find(`[data-testid="notification-toggle"]`);
+ const findLockIssueWidget = () => wrapper.find(`[data-testid="lock-issue-toggle"]`);
+ const findCopyRefenceDropdownItem = () => wrapper.find(`[data-testid="copy-reference"]`);
+ const findCopyEmailItem = () => wrapper.find(`[data-testid="copy-email"]`);
const findModal = () => wrapper.findComponent(GlModal);
const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index);
+ const issueReferenceSuccessHandler = jest.fn().mockResolvedValue(mockIssueReferenceData);
+ const updateIssueMutationResponseHandler = jest
+ .fn()
+ .mockResolvedValue(updateIssueMutationResponse);
+ const promoteToEpicMutationSuccessResponseHandler = jest
+ .fn()
+ .mockResolvedValue(promoteToEpicMutationResponse);
+ const promoteToEpicMutationErrorHandler = jest
+ .fn()
+ .mockResolvedValue(promoteToEpicMutationErrorResponse);
+
const mountComponent = ({
props = {},
issueState = STATUS_OPEN,
blockedByIssues = [],
- mutateResponse = {},
+ movedMrSidebarEnabled = false,
+ promoteToEpicHandler = promoteToEpicMutationSuccessResponseHandler,
} = {}) => {
- mutateMock = jest.fn().mockResolvedValue(mutateResponse);
-
store.dispatch('setNoteableData', {
blocked_by_issues: blockedByIssues,
state: issueState,
});
+ const handlers = [
+ [issueReferenceQuery, issueReferenceSuccessHandler],
+ [updateIssueMutation, updateIssueMutationResponseHandler],
+ [promoteToEpicMutation, promoteToEpicHandler],
+ ];
+
return shallowMount(HeaderActions, {
+ apolloProvider: createMockApollo(handlers),
store,
provide: {
...defaultProps,
...props,
- },
- mocks: {
- $apollo: {
- mutate: mutateMock,
+ glFeatures: {
+ movedMrSidebar: movedMrSidebarEnabled,
},
},
stubs: {
@@ -138,7 +193,6 @@ describe('HeaderActions component', () => {
wrapper = mountComponent({
props: { issueType },
issueState,
- mutateResponse: updateIssueMutationResponse,
});
});
@@ -149,23 +203,19 @@ describe('HeaderActions component', () => {
it('calls apollo mutation', () => {
findToggleIssueStateButton().vm.$emit('click');
- expect(mutateMock).toHaveBeenCalledWith(
- expect.objectContaining({
- variables: {
- input: {
- iid: defaultProps.iid,
- projectPath: defaultProps.projectPath,
- stateEvent: newIssueState,
- },
- },
- }),
- );
+ expect(updateIssueMutationResponseHandler).toHaveBeenCalledWith({
+ input: {
+ iid: defaultProps.iid,
+ projectPath: defaultProps.projectPath,
+ stateEvent: newIssueState,
+ },
+ });
});
it('dispatches a custom event to update the issue page', async () => {
findToggleIssueStateButton().vm.$emit('click');
- await nextTick();
+ await waitForPromises();
expect(dispatchEventSpy).toHaveBeenCalledTimes(1);
});
@@ -290,28 +340,25 @@ describe('HeaderActions component', () => {
describe('when "Promote to epic" button is clicked', () => {
describe('when response is successful', () => {
- beforeEach(() => {
+ beforeEach(async () => {
visitUrlSpy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({});
wrapper = mountComponent({
- mutateResponse: promoteToEpicMutationResponse,
+ promoteToEpicHandler: promoteToEpicMutationSuccessResponseHandler,
});
wrapper.find('[data-testid="promote-button"]').vm.$emit('click');
+
+ await waitForPromises();
});
it('invokes GraphQL mutation when clicked', () => {
- expect(mutateMock).toHaveBeenCalledWith(
- expect.objectContaining({
- mutation: promoteToEpicMutation,
- variables: {
- input: {
- iid: defaultProps.iid,
- projectPath: defaultProps.projectPath,
- },
- },
- }),
- );
+ expect(promoteToEpicMutationSuccessResponseHandler).toHaveBeenCalledWith({
+ input: {
+ iid: defaultProps.iid,
+ projectPath: defaultProps.projectPath,
+ },
+ });
});
it('shows a success message and tells the user they are being redirected', () => {
@@ -329,14 +376,16 @@ describe('HeaderActions component', () => {
});
describe('when response contains errors', () => {
- beforeEach(() => {
+ beforeEach(async () => {
visitUrlSpy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({});
wrapper = mountComponent({
- mutateResponse: promoteToEpicMutationErrorResponse,
+ promoteToEpicHandler: promoteToEpicMutationErrorHandler,
});
wrapper.find('[data-testid="promote-button"]').vm.$emit('click');
+
+ await waitForPromises();
});
it('shows an error message', () => {
@@ -349,21 +398,17 @@ describe('HeaderActions component', () => {
describe('when `toggle.issuable.state` event is emitted', () => {
it('invokes a method to toggle the issue state', () => {
- wrapper = mountComponent({ mutateResponse: updateIssueMutationResponse });
+ wrapper = mountComponent();
eventHub.$emit('toggle.issuable.state');
- expect(mutateMock).toHaveBeenCalledWith(
- expect.objectContaining({
- variables: {
- input: {
- iid: defaultProps.iid,
- projectPath: defaultProps.projectPath,
- stateEvent: ISSUE_STATE_EVENT_CLOSE,
- },
- },
- }),
- );
+ expect(updateIssueMutationResponseHandler).toHaveBeenCalledWith({
+ input: {
+ iid: defaultProps.iid,
+ projectPath: defaultProps.projectPath,
+ stateEvent: ISSUE_STATE_EVENT_CLOSE,
+ },
+ });
});
});
@@ -392,17 +437,13 @@ describe('HeaderActions component', () => {
it('calls apollo mutation when primary button is clicked', () => {
findModal().vm.$emit('primary');
- expect(mutateMock).toHaveBeenCalledWith(
- expect.objectContaining({
- variables: {
- input: {
- iid: defaultProps.iid.toString(),
- projectPath: defaultProps.projectPath,
- stateEvent: ISSUE_STATE_EVENT_CLOSE,
- },
- },
- }),
- );
+ expect(updateIssueMutationResponseHandler).toHaveBeenCalledWith({
+ input: {
+ iid: defaultProps.iid.toString(),
+ projectPath: defaultProps.projectPath,
+ stateEvent: ISSUE_STATE_EVENT_CLOSE,
+ },
+ });
});
describe.each`
@@ -434,8 +475,6 @@ describe('HeaderActions component', () => {
});
describe('abuse category selector', () => {
- const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector);
-
beforeEach(() => {
wrapper = mountComponent({ props: { isIssueAuthor: false } });
});
@@ -445,7 +484,7 @@ describe('HeaderActions component', () => {
});
it('opens the drawer', async () => {
- findDesktopDropdownItems().at(2).vm.$emit('click');
+ findReportAbuseSelectorItem().vm.$emit('click');
await nextTick();
@@ -453,10 +492,160 @@ describe('HeaderActions component', () => {
});
it('closes the drawer', async () => {
- await findDesktopDropdownItems().at(2).vm.$emit('click');
+ await findReportAbuseSelectorItem().vm.$emit('click');
await findAbuseCategorySelector().vm.$emit('close-drawer');
expect(findAbuseCategorySelector().exists()).toEqual(false);
});
});
+
+ describe('notification toggle', () => {
+ describe('visibility', () => {
+ describe.each`
+ movedMrSidebarEnabled | issueType | visible
+ ${true} | ${TYPE_ISSUE} | ${true}
+ ${true} | ${TYPE_INCIDENT} | ${true}
+ ${false} | ${TYPE_ISSUE} | ${false}
+ ${false} | ${TYPE_INCIDENT} | ${false}
+ `(
+ `when movedMrSidebarEnabled flag is "$movedMrSidebarEnabled" with issue type "$issueType"`,
+ ({ movedMrSidebarEnabled, issueType, visible }) => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType,
+ },
+ movedMrSidebarEnabled,
+ });
+ });
+
+ it(`${visible ? 'shows' : 'hides'} Notification toggle`, () => {
+ expect(findNotificationWidget().exists()).toBe(visible);
+ });
+ },
+ );
+ });
+ });
+
+ describe('lock issue option', () => {
+ describe('visibility', () => {
+ describe.each`
+ movedMrSidebarEnabled | issueType | visible
+ ${true} | ${TYPE_ISSUE} | ${true}
+ ${true} | ${TYPE_INCIDENT} | ${false}
+ ${false} | ${TYPE_ISSUE} | ${false}
+ ${false} | ${TYPE_INCIDENT} | ${false}
+ `(
+ `when movedMrSidebarEnabled flag is "$movedMrSidebarEnabled" with issue type "$issueType"`,
+ ({ movedMrSidebarEnabled, issueType, visible }) => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType,
+ },
+ movedMrSidebarEnabled,
+ });
+ });
+
+ it(`${visible ? 'shows' : 'hides'} Lock issue option`, () => {
+ expect(findLockIssueWidget().exists()).toBe(visible);
+ });
+ },
+ );
+ });
+ });
+
+ describe('copy reference option', () => {
+ describe('visibility', () => {
+ describe.each`
+ movedMrSidebarEnabled | issueType | visible
+ ${true} | ${TYPE_ISSUE} | ${true}
+ ${true} | ${TYPE_INCIDENT} | ${true}
+ ${false} | ${TYPE_ISSUE} | ${false}
+ ${false} | ${TYPE_INCIDENT} | ${false}
+ `(
+ 'when movedMrSidebarFlagEnabled is "$movedMrSidebarEnabled" with issue type "$issueType"',
+ ({ movedMrSidebarEnabled, issueType, visible }) => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType,
+ },
+ movedMrSidebarEnabled,
+ });
+ });
+
+ it(`${visible ? 'shows' : 'hides'} Copy reference option`, () => {
+ expect(findCopyRefenceDropdownItem().exists()).toBe(visible);
+ });
+ },
+ );
+ });
+
+ describe('clicking when visible', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType: TYPE_ISSUE,
+ },
+ movedMrSidebarEnabled: true,
+ });
+ });
+
+ it('shows toast message', () => {
+ findCopyRefenceDropdownItem().vm.$emit('click');
+
+ expect(toast).toHaveBeenCalledWith('Reference copied');
+ });
+ });
+ });
+
+ describe('copy email option', () => {
+ describe('visibility', () => {
+ describe.each`
+ movedMrSidebarEnabled | issueType | issuableEmailAddress | visible
+ ${true} | ${TYPE_ISSUE} | ${'mock-email-address'} | ${true}
+ ${true} | ${TYPE_ISSUE} | ${''} | ${false}
+ ${true} | ${TYPE_INCIDENT} | ${'mock-email-address'} | ${true}
+ ${true} | ${TYPE_INCIDENT} | ${''} | ${false}
+ ${false} | ${TYPE_ISSUE} | ${'mock-email-address'} | ${false}
+ ${false} | ${TYPE_INCIDENT} | ${'mock-email-address'} | ${false}
+ `(
+ 'when movedMrSidebarEnabled flag is "$movedMrSidebarEnabled" issue type is "$issueType" and issuableEmailAddress="$issuableEmailAddress"',
+ ({ movedMrSidebarEnabled, issueType, issuableEmailAddress, visible }) => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType,
+ issuableEmailAddress,
+ },
+ movedMrSidebarEnabled,
+ });
+ });
+
+ it(`${visible ? 'shows' : 'hides'} Copy email option`, () => {
+ expect(findCopyEmailItem().exists()).toBe(visible);
+ });
+ },
+ );
+ });
+
+ describe('clicking when visible', () => {
+ beforeEach(() => {
+ wrapper = mountComponent({
+ props: {
+ issueType: TYPE_ISSUE,
+ issuableEmailAddress: 'mock-email-address',
+ },
+ movedMrSidebarEnabled: true,
+ });
+ });
+
+ it('shows toast message', () => {
+ findCopyEmailItem().vm.$emit('click');
+
+ expect(toast).toHaveBeenCalledWith('Email address copied');
+ });
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/new_header_actions_popover_spec.js b/spec/frontend/issues/show/components/new_header_actions_popover_spec.js
new file mode 100644
index 00000000000..71b7a3da1c3
--- /dev/null
+++ b/spec/frontend/issues/show/components/new_header_actions_popover_spec.js
@@ -0,0 +1,67 @@
+import { GlPopover } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import NewHeaderActionsPopover from '~/issues/show/components/new_header_actions_popover.vue';
+import { NEW_ACTIONS_POPOVER_KEY } from '~/issues/show/constants';
+import { TYPE_ISSUE } from '~/issues/constants';
+import * as utils from '~/lib/utils/common_utils';
+
+describe('NewHeaderActionsPopover', () => {
+ let wrapper;
+
+ const createComponent = ({ issueType = TYPE_ISSUE }) => {
+ wrapper = shallowMountExtended(NewHeaderActionsPopover, {
+ propsData: {
+ issueType,
+ },
+ stubs: {
+ GlPopover,
+ },
+ });
+ };
+
+ const findPopover = () => wrapper.findComponent(GlPopover);
+ const findConfirmButton = () => wrapper.findByTestId('confirm-button');
+
+ describe('without the popover cookie', () => {
+ beforeEach(() => {
+ utils.setCookie = jest.fn();
+
+ createComponent({});
+ });
+
+ it('renders the popover with correct text', () => {
+ expect(findPopover().exists()).toBe(true);
+ expect(findPopover().text()).toContain('issue actions');
+ });
+
+ it('does not call setCookie', () => {
+ expect(utils.setCookie).not.toHaveBeenCalled();
+ });
+
+ describe('when the confirm button is clicked', () => {
+ beforeEach(() => {
+ findConfirmButton().vm.$emit('click');
+ });
+
+ it('sets the popover cookie', () => {
+ expect(utils.setCookie).toHaveBeenCalledWith(NEW_ACTIONS_POPOVER_KEY, true);
+ });
+
+ it('hides the popover', () => {
+ expect(findPopover().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('with the popover cookie', () => {
+ beforeEach(() => {
+ jest.spyOn(utils, 'getCookie').mockReturnValue('true');
+
+ createComponent({});
+ });
+
+ it('does not render the popover', () => {
+ expect(findPopover().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/notes/components/note_form_spec.js b/spec/frontend/notes/components/note_form_spec.js
index 00f75b14e6b..e385c478dd6 100644
--- a/spec/frontend/notes/components/note_form_spec.js
+++ b/spec/frontend/notes/components/note_form_spec.js
@@ -1,4 +1,4 @@
-import { GlLink } from '@gitlab/ui';
+import { GlLink, GlFormCheckbox } from '@gitlab/ui';
import { nextTick } from 'vue';
import batchComments from '~/batch_comments/stores/modules/batch_comments';
import NoteForm from '~/notes/components/note_form.vue';
@@ -234,7 +234,7 @@ describe('issue_note_form component', () => {
});
it('shows resolve checkbox', () => {
- expect(wrapper.find('.js-resolve-checkbox').exists()).toBe(true);
+ expect(wrapper.findComponent(GlFormCheckbox).exists()).toBe(true);
});
it('hides resolve checkbox', async () => {
@@ -253,7 +253,7 @@ describe('issue_note_form component', () => {
},
});
- expect(wrapper.find('.js-resolve-checkbox').exists()).toBe(false);
+ expect(wrapper.findComponent(GlFormCheckbox).exists()).toBe(false);
});
it('hides actions for commits', async () => {
diff --git a/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap b/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap
index 213a732d59b..e9ee6ebdb5c 100644
--- a/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap
+++ b/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap
@@ -5,7 +5,6 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
aria-label="Breadcrumb"
class="gl-breadcrumbs"
>
-
<ol
class="breadcrumb gl-breadcrumb-list"
>
@@ -16,7 +15,10 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
class=""
target="_self"
>
-
+ <!---->
+ <span>
+
+ </span>
</a>
</li>
@@ -30,7 +32,10 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
href="#"
target="_self"
>
-
+ <!---->
+ <span>
+
+ </span>
</a>
</li>
@@ -44,7 +49,6 @@ exports[`Registry Breadcrumb when is rootRoute renders 1`] = `
aria-label="Breadcrumb"
class="gl-breadcrumbs"
>
-
<ol
class="breadcrumb gl-breadcrumb-list"
>
@@ -56,7 +60,10 @@ exports[`Registry Breadcrumb when is rootRoute renders 1`] = `
class=""
target="_self"
>
-
+ <!---->
+ <span>
+
+ </span>
</a>
</li>
diff --git a/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
index d26ef7298ce..5e766e9a41c 100644
--- a/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
+++ b/spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
@@ -29,6 +29,7 @@ describe('IssuableLockForm', () => {
const findEditForm = () => wrapper.findComponent(EditForm);
const findSidebarLockStatusTooltip = () =>
getBinding(findSidebarCollapseIcon().element, 'gl-tooltip');
+ const findIssuableLockClickable = () => wrapper.find('[data-testid="issuable-lock"]');
const initStore = (isLocked) => {
if (issuableType === ISSUABLE_TYPE_ISSUE) {
@@ -48,7 +49,7 @@ describe('IssuableLockForm', () => {
store.getters.getNoteableData.discussion_locked = isLocked;
};
- const createComponent = ({ props = {} }, movedMrSidebar = false) => {
+ const createComponent = ({ props = {}, movedMrSidebar = false }) => {
wrapper = shallowMount(IssuableLockForm, {
store,
provide: {
@@ -169,11 +170,27 @@ describe('IssuableLockForm', () => {
`('displays $message when merge request is $locked', async ({ locked, message }) => {
initStore(locked);
- createComponent({}, true);
+ createComponent({ movedMrSidebar: true });
await wrapper.find('.dropdown-item').trigger('click');
expect(toast).toHaveBeenCalledWith(message);
});
});
+
+ describe('moved_mr_sidebar flag', () => {
+ describe('when the flag is off', () => {
+ it('does not show the non editable lock status', () => {
+ createComponent({ movedMrSidebar: false });
+ expect(findIssuableLockClickable().exists()).toBe(false);
+ });
+ });
+
+ describe('when the flag is on', () => {
+ it('does not show the non editable lock status', () => {
+ createComponent({ movedMrSidebar: true });
+ expect(findIssuableLockClickable().exists()).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
index 128ef69e5f1..037b56d9904 100644
--- a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
+++ b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
@@ -1,4 +1,3 @@
-import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
import ReviewerAvatarLink from '~/sidebar/components/reviewers/reviewer_avatar_link.vue';
@@ -23,8 +22,9 @@ describe('UncollapsedReviewerList component', () => {
let wrapper;
const findAllRerequestButtons = () => wrapper.findAll('[data-testid="re-request-button"]');
- const findAllRerequestSuccessIcons = () => wrapper.findAll('[data-testid="re-request-success"]');
- const findAllReviewerApprovalIcons = () => wrapper.findAll('[data-testid="re-approved"]');
+ const findAllReviewerApprovalIcons = () => wrapper.findAll('[data-testid="approved"]');
+ const findAllReviewedNotApprovedIcons = () =>
+ wrapper.findAll('[data-testid="reviewed-not-approved"]');
const findAllReviewerAvatarLinks = () => wrapper.findAllComponents(ReviewerAvatarLink);
function createComponent(props = {}, glFeatures = {}) {
@@ -42,13 +42,6 @@ describe('UncollapsedReviewerList component', () => {
});
}
- const callRerequestCallback = async () => {
- const payload = wrapper.emitted('request-review')[0][0];
- // Call payload which is normally called by a parent component
- payload.callback(payload.userId, true);
- await nextTick();
- };
-
describe('single reviewer', () => {
const user = userDataMock();
@@ -71,13 +64,6 @@ describe('UncollapsedReviewerList component', () => {
expect(findAllRerequestButtons().at(0).props('loading')).toBe(true);
});
-
- it('renders re-request success icon', async () => {
- await findAllRerequestButtons().at(0).vm.$emit('click');
- await callRerequestCallback();
-
- expect(findAllRerequestSuccessIcons().at(0).exists()).toBe(true);
- });
});
describe('multiple reviewers', () => {
@@ -92,20 +78,32 @@ describe('UncollapsedReviewerList component', () => {
approved: true,
},
};
+ const user3 = {
+ ...user,
+ id: 3,
+ name: 'lizabeth-wilderman',
+ username: 'lizabeth-wilderman',
+ mergeRequestInteraction: {
+ ...user.mergeRequestInteraction,
+ approved: false,
+ reviewed: true,
+ },
+ };
beforeEach(() => {
createComponent({
- users: [user, user2],
+ users: [user, user2, user3],
});
});
- it('has both users', () => {
- expect(findAllReviewerAvatarLinks()).toHaveLength(2);
+ it('has three users', () => {
+ expect(findAllReviewerAvatarLinks()).toHaveLength(3);
});
- it('shows both users with avatar, and author name', () => {
+ it('shows all users with avatar, and author name', () => {
expect(wrapper.text()).toContain(user.name);
expect(wrapper.text()).toContain(user2.name);
+ expect(wrapper.text()).toContain(user3.name);
});
it('renders approval icon', () => {
@@ -118,21 +116,19 @@ describe('UncollapsedReviewerList component', () => {
expect(icon.attributes('title')).toBe('Approved by @hello-world');
});
+ it('shows that lizabeth-wilderman reviewed but did not approve', () => {
+ const icon = findAllReviewedNotApprovedIcons().at(1);
+
+ expect(icon.attributes('title')).toBe('Reviewed by @lizabeth-wilderman but not yet approved');
+ });
+
it('renders re-request loading icon', async () => {
await findAllRerequestButtons().at(1).vm.$emit('click');
const allRerequestButtons = findAllRerequestButtons();
- expect(allRerequestButtons).toHaveLength(2);
+ expect(allRerequestButtons).toHaveLength(3);
expect(allRerequestButtons.at(1).props('loading')).toBe(true);
});
-
- it('renders re-request success icon', async () => {
- await findAllRerequestButtons().at(1).vm.$emit('click');
- await callRerequestCallback();
-
- expect(findAllRerequestButtons()).toHaveLength(1);
- expect(findAllRerequestSuccessIcons()).toHaveLength(1);
- });
});
});
diff --git a/spec/graphql/types/work_items/widget_interface_spec.rb b/spec/graphql/types/work_items/widget_interface_spec.rb
index 045c1620815..d955ec5023e 100644
--- a/spec/graphql/types/work_items/widget_interface_spec.rb
+++ b/spec/graphql/types/work_items/widget_interface_spec.rb
@@ -15,13 +15,14 @@ RSpec.describe Types::WorkItems::WidgetInterface do
using RSpec::Parameterized::TableSyntax
where(:widget_class, :widget_type_name) do
- WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
- WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
- WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
- WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType
- WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType
- WorkItems::Widgets::Notifications | Types::WorkItems::Widgets::NotificationsType
+ WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
+ WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
+ WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
+ WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType
+ WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType
+ WorkItems::Widgets::Notifications | Types::WorkItems::Widgets::NotificationsType
WorkItems::Widgets::CurrentUserTodos | Types::WorkItems::Widgets::CurrentUserTodosType
+ WorkItems::Widgets::AwardEmoji | Types::WorkItems::Widgets::AwardEmojiType
end
with_them do
diff --git a/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb b/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb
new file mode 100644
index 00000000000..493e628ac83
--- /dev/null
+++ b/spec/graphql/types/work_items/widgets/award_emoji_type_spec.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::WorkItems::Widgets::AwardEmojiType, feature_category: :team_planning do
+ it 'exposes the expected fields' do
+ expected_fields = %i[award_emoji downvotes upvotes type]
+
+ expect(described_class.graphql_name).to eq('WorkItemWidgetAwardEmoji')
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index d940c696fb3..38cbb5a1d66 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe IssuesHelper do
+ include Features::MergeRequestHelpers
+
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:issue) { create(:issue, project: project) }
@@ -235,10 +237,13 @@ RSpec.describe IssuesHelper do
describe '#issue_header_actions_data' do
let(:current_user) { create(:user) }
+ let(:merge_request) { create(:merge_request, :opened, source_project: project, author: current_user) }
+ let(:issuable_sidebar_issue) { serialize_issuable_sidebar(current_user, project, merge_request) }
before do
allow(helper).to receive(:current_user).and_return(current_user)
allow(helper).to receive(:can?).and_return(true)
+ allow(helper).to receive(:issuable_sidebar).and_return(issuable_sidebar_issue)
end
it 'returns expected result' do
@@ -257,10 +262,11 @@ RSpec.describe IssuesHelper do
report_abuse_path: add_category_abuse_reports_path,
reported_user_id: issue.author.id,
reported_from_url: issue_url(issue),
- submit_as_spam_path: mark_as_spam_project_issue_path(project, issue)
+ submit_as_spam_path: mark_as_spam_project_issue_path(project, issue),
+ issuable_email_address: issuable_sidebar_issue[:create_note_email]
}
- expect(helper.issue_header_actions_data(project, issue, current_user)).to include(expected)
+ expect(helper.issue_header_actions_data(project, issue, current_user, issuable_sidebar_issue)).to include(expected)
end
end
diff --git a/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb b/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb
index 065b6d00ddb..7161ca35edd 100644
--- a/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb
+++ b/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb
@@ -4,24 +4,5 @@ require 'spec_helper'
require_migration!
RSpec.describe AddNotificationsWorkItemWidget, :migration, feature_category: :team_planning do
- let(:migration) { described_class.new }
- let(:work_item_definitions) { table(:work_item_widget_definitions) }
-
- describe '#up' do
- it 'creates notifications widget definition in all types' do
- work_item_definitions.where(name: 'Notifications').delete_all
-
- expect { migrate! }.to change { work_item_definitions.count }.by(7)
- expect(work_item_definitions.all.pluck(:name)).to include('Notifications')
- end
- end
-
- describe '#down' do
- it 'removes definitions for notifications widget' do
- migrate!
-
- expect { migration.down }.to change { work_item_definitions.count }.by(-7)
- expect(work_item_definitions.all.pluck(:name)).not_to include('Notifications')
- end
- end
+ it_behaves_like 'migration that adds widget to work items definitions', widget_name: 'Notifications'
end
diff --git a/spec/migrations/20230317162059_add_current_user_todos_work_item_widget_spec.rb b/spec/migrations/20230317162059_add_current_user_todos_work_item_widget_spec.rb
index f044b15fa6b..1df80a519f2 100644
--- a/spec/migrations/20230317162059_add_current_user_todos_work_item_widget_spec.rb
+++ b/spec/migrations/20230317162059_add_current_user_todos_work_item_widget_spec.rb
@@ -4,24 +4,5 @@ require 'spec_helper'
require_migration!
RSpec.describe AddCurrentUserTodosWorkItemWidget, :migration, feature_category: :team_planning do
- let(:migration) { described_class.new }
- let(:work_item_definitions) { table(:work_item_widget_definitions) }
-
- describe '#up' do
- it 'creates notifications widget definition in all types' do
- work_item_definitions.where(name: 'Current user todos').delete_all
-
- expect { migrate! }.to change { work_item_definitions.count }.by(7)
- expect(work_item_definitions.all.pluck(:name)).to include('Current user todos')
- end
- end
-
- describe '#down' do
- it 'removes definitions for notifications widget' do
- migrate!
-
- expect { migration.down }.to change { work_item_definitions.count }.by(-7)
- expect(work_item_definitions.all.pluck(:name)).not_to include('Current user todos')
- end
- end
+ it_behaves_like 'migration that adds widget to work items definitions', widget_name: 'Current user todos'
end
diff --git a/spec/migrations/20230323101138_add_award_emoji_work_item_widget_spec.rb b/spec/migrations/20230323101138_add_award_emoji_work_item_widget_spec.rb
new file mode 100644
index 00000000000..16a205c5da5
--- /dev/null
+++ b/spec/migrations/20230323101138_add_award_emoji_work_item_widget_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddAwardEmojiWorkItemWidget, :migration, feature_category: :team_planning do
+ it_behaves_like 'migration that adds widget to work items definitions', widget_name: 'Award emoji'
+end
diff --git a/spec/migrations/20230328100534_truncate_error_tracking_tables_spec.rb b/spec/migrations/20230328100534_truncate_error_tracking_tables_spec.rb
new file mode 100644
index 00000000000..efbbe22fd1b
--- /dev/null
+++ b/spec/migrations/20230328100534_truncate_error_tracking_tables_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe TruncateErrorTrackingTables, :migration, feature_category: :redis do
+ let(:migration) { described_class.new }
+
+ context 'when on GitLab.com' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ context 'when using Main db' do
+ it 'truncates the table' do
+ expect(described_class.connection).to receive(:execute).with('TRUNCATE table error_tracking_errors CASCADE')
+
+ migration.up
+ end
+ end
+
+ context 'when uses CI db', migration: :gitlab_ci do
+ before do
+ skip_if_multiple_databases_not_setup(:ci)
+ end
+
+ it 'does not truncate the table' do
+ expect(described_class.connection).not_to receive(:execute).with('TRUNCATE table error_tracking_errors CASCADE')
+
+ migration.up
+ end
+ end
+ end
+
+ context 'when on self-managed' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(false)
+ end
+
+ context 'when using Main db' do
+ it 'does not truncate the table' do
+ expect(described_class.connection).not_to receive(:execute).with('TRUNCATE table error_tracking_errors CASCADE')
+
+ migration.up
+ end
+ end
+
+ context 'when uses CI db', migration: :gitlab_ci do
+ it 'does not truncate the table' do
+ expect(described_class.connection).not_to receive(:execute).with('TRUNCATE table error_tracking_errors CASCADE')
+
+ migration.up
+ end
+ end
+ end
+end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index c1de8125c0d..ed2bf26e129 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -1666,6 +1666,32 @@ RSpec.describe Note do
end
end
end
+
+ describe '.without_hidden' do
+ subject { described_class.without_hidden }
+
+ context 'when a note with a banned author exists' do
+ let_it_be(:banned_user) { create(:banned_user).user }
+ let_it_be(:banned_note) { create(:note, author: banned_user) }
+
+ context 'when the :hidden_notes feature is disabled' do
+ before do
+ stub_feature_flags(hidden_notes: false)
+ end
+
+ it { is_expected.to include(banned_note, note1) }
+ end
+
+ context 'when the :hidden_notes feature is enabled' do
+ before do
+ stub_feature_flags(hidden_notes: true)
+ end
+
+ it { is_expected.not_to include(banned_note) }
+ it { is_expected.to include(note1) }
+ end
+ end
+ end
end
describe 'banzai_render_context' do
diff --git a/spec/models/work_items/widget_definition_spec.rb b/spec/models/work_items/widget_definition_spec.rb
index f4d132bec52..a33e08a1bf2 100644
--- a/spec/models/work_items/widget_definition_spec.rb
+++ b/spec/models/work_items/widget_definition_spec.rb
@@ -13,7 +13,8 @@ RSpec.describe WorkItems::WidgetDefinition, feature_category: :team_planning do
::WorkItems::Widgets::Milestone,
::WorkItems::Widgets::Notes,
::WorkItems::Widgets::Notifications,
- ::WorkItems::Widgets::CurrentUserTodos
+ ::WorkItems::Widgets::CurrentUserTodos,
+ ::WorkItems::Widgets::AwardEmoji
]
if Gitlab.ee?
diff --git a/spec/models/work_items/widgets/award_emoji_spec.rb b/spec/models/work_items/widgets/award_emoji_spec.rb
new file mode 100644
index 00000000000..bb61aa41669
--- /dev/null
+++ b/spec/models/work_items/widgets/award_emoji_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::AwardEmoji, feature_category: :team_planning do
+ let_it_be(:work_item) { create(:work_item) }
+ let_it_be(:emoji1) { create(:award_emoji, name: 'star', awardable: work_item) }
+ let_it_be(:emoji2) { create(:award_emoji, :upvote, awardable: work_item) }
+ let_it_be(:emoji3) { create(:award_emoji, :downvote, awardable: work_item) }
+
+ describe '.type' do
+ it { expect(described_class.type).to eq(:award_emoji) }
+ end
+
+ describe '#type' do
+ it { expect(described_class.new(work_item).type).to eq(:award_emoji) }
+ end
+
+ describe '#downvotes' do
+ it { expect(described_class.new(work_item).downvotes).to eq(1) }
+ end
+
+ describe '#upvotes' do
+ it { expect(described_class.new(work_item).upvotes).to eq(1) }
+ end
+
+ describe '#award_emoji' do
+ it { expect(described_class.new(work_item).award_emoji).to match_array([emoji1, emoji2, emoji3]) }
+ end
+end
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index d5dd12de63e..b792505374e 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -341,6 +341,51 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
end
end
+ context 'when fetching work item award emoji widget' do
+ let(:fields) do
+ <<~GRAPHQL
+ nodes {
+ widgets {
+ type
+ ... on WorkItemWidgetAwardEmoji {
+ awardEmoji {
+ nodes {
+ name
+ emoji
+ user { id }
+ }
+ }
+ upvotes
+ downvotes
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ before do
+ create(:award_emoji, name: 'star', user: current_user, awardable: item1)
+ create(:award_emoji, :upvote, awardable: item1)
+ create(:award_emoji, :downvote, awardable: item1)
+ end
+
+ it 'executes limited number of N+1 queries', :use_sql_query_cache do
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: current_user)
+ end
+
+ create_list(:work_item, 2, project: project) do |item|
+ create(:award_emoji, name: 'rocket', awardable: item)
+ create_list(:award_emoji, 2, :upvote, awardable: item)
+ create_list(:award_emoji, 2, :downvote, awardable: item)
+ end
+
+ expect { post_graphql(query, current_user: current_user) }
+ .not_to exceed_all_query_limit(control)
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
def item_ids
graphql_dig_at(items_data, :node, :id)
end
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index fe6f75548a5..a89c2268208 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -486,6 +486,48 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
end
end
end
+
+ describe 'award emoji widget' do
+ let_it_be(:emoji) { create(:award_emoji, name: 'star', awardable: work_item) }
+ let_it_be(:upvote) { create(:award_emoji, :upvote, awardable: work_item) }
+ let_it_be(:downvote) { create(:award_emoji, :downvote, awardable: work_item) }
+
+ let(:work_item_fields) do
+ <<~GRAPHQL
+ id
+ widgets {
+ type
+ ... on WorkItemWidgetAwardEmoji {
+ upvotes
+ downvotes
+ awardEmoji {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'returns widget information' do
+ expect(work_item_data).to include(
+ 'id' => work_item.to_gid.to_s,
+ 'widgets' => include(
+ hash_including(
+ 'type' => 'AWARD_EMOJI',
+ 'upvotes' => work_item.upvotes,
+ 'downvotes' => work_item.downvotes,
+ 'awardEmoji' => {
+ 'nodes' => match_array(
+ [emoji, upvote, downvote].map { |e| { 'name' => e.name } }
+ )
+ }
+ )
+ )
+ )
+ end
+ end
end
context 'when an Issue Global ID is provided' do
diff --git a/spec/scripts/generate_rspec_pipeline_spec.rb b/spec/scripts/generate_rspec_pipeline_spec.rb
index b3eaf9e9127..91b5739cf63 100644
--- a/spec/scripts/generate_rspec_pipeline_spec.rb
+++ b/spec/scripts/generate_rspec_pipeline_spec.rb
@@ -13,42 +13,49 @@ RSpec.describe GenerateRspecPipeline, :silence_stdout, feature_category: :toolin
"spec/lib/gitlab/background_migration/a_spec.rb spec/lib/gitlab/background_migration/b_spec.rb " \
"spec/models/a_spec.rb spec/models/b_spec.rb " \
"spec/controllers/a_spec.rb spec/controllers/b_spec.rb " \
- "spec/features/a_spec.rb spec/features/b_spec.rb"
+ "spec/features/a_spec.rb spec/features/b_spec.rb " \
+ "ee/spec/features/a_spec.rb"
end
let(:pipeline_template) { Tempfile.new(['pipeline_template', '.yml.erb']) }
let(:pipeline_template_content) do
<<~YAML
- <% if rspec_files_per_test_level[:migration][:files].size > 0 %>
+ <% if test_suite_prefix.nil? && rspec_files_per_test_level[:migration][:files].size > 0 %>
rspec migration:
<% if rspec_files_per_test_level[:migration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:migration][:parallelization] %>
<% end %>
<% end %>
- <% if rspec_files_per_test_level[:background_migration][:files].size > 0 %>
+ <% if test_suite_prefix.nil? && rspec_files_per_test_level[:background_migration][:files].size > 0 %>
rspec background_migration:
<% if rspec_files_per_test_level[:background_migration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:background_migration][:parallelization] %>
<% end %>
<% end %>
- <% if rspec_files_per_test_level[:unit][:files].size > 0 %>
+ <% if test_suite_prefix.nil? && rspec_files_per_test_level[:unit][:files].size > 0 %>
rspec unit:
<% if rspec_files_per_test_level[:unit][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:unit][:parallelization] %>
<% end %>
<% end %>
- <% if rspec_files_per_test_level[:integration][:files].size > 0 %>
+ <% if test_suite_prefix.nil? && rspec_files_per_test_level[:integration][:files].size > 0 %>
rspec integration:
<% if rspec_files_per_test_level[:integration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:integration][:parallelization] %>
<% end %>
<% end %>
- <% if rspec_files_per_test_level[:system][:files].size > 0 %>
+ <% if test_suite_prefix.nil? && rspec_files_per_test_level[:system][:files].size > 0 %>
rspec system:
<% if rspec_files_per_test_level[:system][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:system][:parallelization] %>
<% end %>
<% end %>
+ <% if test_suite_prefix == 'ee/' && rspec_files_per_test_level[:system][:files].size > 0 %>
+ rspec-ee system:
+ <% if rspec_files_per_test_level[:system][:parallelization] > 1 %>
+ parallel: <%= rspec_files_per_test_level[:system][:parallelization] %>
+ <% end %>
+ <% end %>
YAML
end
@@ -65,7 +72,8 @@ RSpec.describe GenerateRspecPipeline, :silence_stdout, feature_category: :toolin
"spec/controllers/a_spec.rb": 60.2,
"spec/controllers/ab_spec.rb": 180.4,
"spec/features/a_spec.rb": 360.1,
- "spec/features/b_spec.rb": 180.5
+ "spec/features/b_spec.rb": 180.5,
+ "ee/spec/features/a_spec.rb": 180.5
}
JSON
end
@@ -177,6 +185,53 @@ RSpec.describe GenerateRspecPipeline, :silence_stdout, feature_category: :toolin
end
end
+ context 'when test_suite_prefix is given' do
+ subject do
+ described_class.new(
+ rspec_files_path: rspec_files.path,
+ pipeline_template_path: pipeline_template.path,
+ knapsack_report_path: knapsack_report.path,
+ test_suite_prefix: 'ee/'
+ )
+ end
+
+ it 'generates the pipeline config based on the test_suite_prefix' do
+ subject.generate!
+
+ expect(File.read("#{pipeline_template.path}.yml"))
+ .to eq("rspec-ee system:")
+ end
+ end
+
+ context 'when generated_pipeline_path is given' do
+ let(:custom_pipeline_filename) { Tempfile.new(['custom_pipeline_filename', '.yml']) }
+
+ around do |example|
+ example.run
+ ensure
+ custom_pipeline_filename.close
+ custom_pipeline_filename.unlink
+ end
+
+ subject do
+ described_class.new(
+ rspec_files_path: rspec_files.path,
+ pipeline_template_path: pipeline_template.path,
+ generated_pipeline_path: custom_pipeline_filename.path
+ )
+ end
+
+ it 'writes the pipeline config in the given generated_pipeline_path' do
+ subject.generate!
+
+ expect(File.read(custom_pipeline_filename.path))
+ .to eq(
+ "rspec migration:\nrspec background_migration:\nrspec unit:\n" \
+ "rspec integration:\nrspec system:"
+ )
+ end
+ end
+
context 'when rspec_files does not exist' do
subject { described_class.new(rspec_files_path: nil, pipeline_template_path: pipeline_template.path) }
diff --git a/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb b/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb
new file mode 100644
index 00000000000..28eac52256f
--- /dev/null
+++ b/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'migration that adds widget to work items definitions' do |widget_name:|
+ let(:migration) { described_class.new }
+ let(:work_item_definitions) { table(:work_item_widget_definitions) }
+
+ describe '#up' do
+ it "creates widget definition in all types" do
+ work_item_definitions.where(name: widget_name).delete_all
+
+ expect { migrate! }.to change { work_item_definitions.count }.by(7)
+ expect(work_item_definitions.all.pluck(:name)).to include(widget_name)
+ end
+
+ it 'logs a warning if the type is missing' do
+ allow(described_class::WorkItemType).to receive(:find_by_name_and_namespace_id).and_call_original
+ allow(described_class::WorkItemType).to receive(:find_by_name_and_namespace_id)
+ .with('Issue', nil).and_return(nil)
+
+ expect(Gitlab::AppLogger).to receive(:warn).with('type Issue is missing, not adding widget')
+ migrate!
+ end
+ end
+
+ describe '#down' do
+ it "removes definitions for widget" do
+ migrate!
+
+ expect { migration.down }.to change { work_item_definitions.count }.by(-7)
+ expect(work_item_definitions.all.pluck(:name)).not_to include(widget_name)
+ end
+ end
+end
diff --git a/spec/tooling/lib/tooling/find_changes_spec.rb b/spec/tooling/lib/tooling/find_changes_spec.rb
index 3616732e328..5932eb5e919 100644
--- a/spec/tooling/lib/tooling/find_changes_spec.rb
+++ b/spec/tooling/lib/tooling/find_changes_spec.rb
@@ -3,57 +3,66 @@
require_relative '../../../../tooling/lib/tooling/find_changes'
require_relative '../../../support/helpers/stub_env'
require 'json'
+require 'tempfile'
RSpec.describe Tooling::FindChanges, feature_category: :tooling do
include StubENV
+ attr_accessor :changed_files_file, :predictive_tests_file, :frontend_fixtures_mapping_file
+
let(:instance) do
- described_class.new(
- output_file: output_file,
- matched_tests_file: matched_tests_file,
- frontend_fixtures_mapping_path: frontend_fixtures_mapping_path
- )
+ described_class.new(changed_files_pathname, predictive_tests_pathname, frontend_fixtures_mapping_pathname)
end
- let(:gitlab_client) { double('GitLab') } # rubocop:disable RSpec/VerifiedDoubles
- let(:output_file) { 'output.txt' }
- let(:output_file_content) { 'first_file second_file' }
- let(:matched_tests_file) { 'matched_tests.txt' }
- let(:frontend_fixtures_mapping_path) { 'frontend_fixtures_mapping.json' }
- let(:file_changes) { ['file1.rb', 'file2.rb'] }
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:frontend_fixtures_mapping_pathname) { frontend_fixtures_mapping_file.path }
+ let(:gitlab_client) { double('GitLab') } # rubocop:disable RSpec/VerifiedDoubles
+
+ around do |example|
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('predictive_tests_file')
+ self.frontend_fixtures_mapping_file = Tempfile.new('frontend_fixtures_mapping_file')
+
+ # See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
+ # Tempfile.html#class-Tempfile-label-Explicit+close
+ begin
+ example.run
+ ensure
+ frontend_fixtures_mapping_file.close
+ frontend_fixtures_mapping_file.unlink
+ predictive_tests_file.close
+ predictive_tests_file.unlink
+ changed_files_file.close
+ changed_files_file.unlink
+ end
+ end
before do
stub_env(
'CI_API_V4_URL' => 'gitlab_api_url',
'CI_MERGE_REQUEST_IID' => '1234',
'CI_MERGE_REQUEST_PROJECT_PATH' => 'dummy-project',
- 'PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE' => 'dummy-token',
- 'RSPEC_TESTS_MAPPING_PATH' => '/tmp/does-not-exist.out'
+ 'PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE' => 'dummy-token'
)
allow(instance).to receive(:gitlab).and_return(gitlab_client)
- allow(File).to receive(:exist?).and_call_original
- allow(File).to receive(:read).and_call_original
- allow(File).to receive(:write)
end
describe '#execute' do
subject { instance.execute }
- context 'when there is no output file' do
- let(:output_file) { nil }
+ context 'when there is no changed files file' do
+ let(:changed_files_pathname) { nil }
it 'raises an ArgumentError' do
- expect { subject }.to raise_error(ArgumentError, "An path to an output file must be given as first argument.")
+ expect { subject }.to raise_error(
+ ArgumentError, "A path to the changed files file must be given as first argument."
+ )
end
end
- context 'when an output file is provided' do
- before do
- allow(File).to receive(:exist?).with(output_file).and_return(true)
- allow(File).to receive(:read).with(output_file).and_return(output_file_content)
- end
-
+ context 'when an changed files file is provided' do
it 'does not call GitLab API to retrieve the MR diff' do
expect(gitlab_client).not_to receive(:merge_request_changes)
@@ -61,64 +70,57 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
end
context 'when there are no file changes' do
- let(:output_file_content) { '' }
-
- it 'writes an empty string to output file' do
- expect(File).to receive(:write).with(output_file, '')
-
- subject
+ it 'writes an empty string to changed files file' do
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
context 'when there are file changes' do
- let(:output_file_content) { 'first_file_changed second_file_changed' }
+ before do
+ File.write(changed_files_pathname, changed_files_file_content)
+ end
- it 'writes file changes to output file' do
- expect(File).to receive(:write).with(output_file, output_file_content)
+ let(:changed_files_file_content) { 'first_file_changed second_file_changed' }
- subject
+ # This is because we don't have frontend fixture mappings: we will just write the same data that we read.
+ it 'does not change the changed files file' do
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
context 'when there is no matched tests file' do
- let(:matched_tests_file) { '' }
-
- it 'does not add frontend fixtures mapping to the output file' do
- expect(File).to receive(:write).with(output_file, output_file_content)
+ let(:predictive_tests_pathname) { nil }
- subject
+ it 'does not add frontend fixtures mapping to the changed files file' do
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
context 'when there is no frontend fixture files' do
- let(:frontend_fixtures_mapping_path) { '' }
+ let(:frontend_fixtures_mapping_pathname) { nil }
- it 'does not add frontend fixtures mapping to the output file' do
- expect(File).to receive(:write).with(output_file, output_file_content)
-
- subject
+ it 'does not add frontend fixtures mapping to the changed files file' do
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
context 'when the matched tests file and frontend fixture files are provided' do
before do
- allow(File).to receive(:exist?).with(matched_tests_file).and_return(true)
- allow(File).to receive(:exist?).with(frontend_fixtures_mapping_path).and_return(true)
-
- allow(File).to receive(:read).with(matched_tests_file).and_return(matched_tests)
- allow(File).to receive(:read).with(frontend_fixtures_mapping_path).and_return(frontend_fixtures_mapping_json)
+ File.write(predictive_tests_pathname, matched_tests)
+ File.write(frontend_fixtures_mapping_pathname, frontend_fixtures_mapping_json)
+ File.write(changed_files_pathname, changed_files_file_content)
end
+ let(:changed_files_file_content) { '' }
+
context 'when there are no mappings for the matched tests' do
let(:matched_tests) { 'match_spec1 match_spec_2' }
let(:frontend_fixtures_mapping_json) do
{ other_spec: ['other_mapping'] }.to_json
end
- it 'does not add frontend fixtures mapping to the output file' do
- expect(File).to receive(:write).with(output_file, output_file_content)
-
- subject
+ it 'does not change the changed files file' do
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
@@ -129,10 +131,20 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
{ match_spec1: spec_mappings }.to_json
end
- it 'adds the frontend fixtures mappings to the output file' do
- expect(File).to receive(:write).with(output_file, "#{output_file_content} #{spec_mappings.join(' ')}")
+ context 'when the changed files file is initially empty' do
+ it 'adds the frontend fixtures mappings to the changed files file' do
+ expect { subject }.to change { File.read(changed_files_pathname) }.from('').to(spec_mappings.join(' '))
+ end
+ end
+
+ context 'when the changed files file is initially not empty' do
+ let(:changed_files_file_content) { 'initial_content1 initial_content2' }
- subject
+ it 'adds the frontend fixtures mappings to the changed files file' do
+ expect { subject }.to change { File.read(changed_files_pathname) }
+ .from(changed_files_file_content)
+ .to("#{changed_files_file_content} #{spec_mappings.join(' ')}")
+ end
end
end
end
@@ -155,15 +167,9 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
end
context 'when a file is passed as an argument' do
- let(:output_file) { 'output_file.out' }
-
- it 'does not read the output file' do
- expect(File).not_to receive(:read).with(output_file)
-
- subject
- end
+ let(:changed_files_pathname) { 'does-not-exist.out' }
- it 'calls GitLab API anyways' do
+ it 'calls GitLab API' do
expect(gitlab_client).to receive(:merge_request_changes)
.with('dummy-project', '1234')
diff --git a/spec/tooling/lib/tooling/find_tests_spec.rb b/spec/tooling/lib/tooling/find_tests_spec.rb
index e531fb9548e..905f81c4bbd 100644
--- a/spec/tooling/lib/tooling/find_tests_spec.rb
+++ b/spec/tooling/lib/tooling/find_tests_spec.rb
@@ -7,27 +7,29 @@ require_relative '../../../support/helpers/stub_env'
RSpec.describe Tooling::FindTests, feature_category: :tooling do
include StubENV
- attr_accessor :changes_file, :matching_tests_paths
+ attr_accessor :changed_files_file, :predictive_tests_file
- let(:instance) { described_class.new(changes_file, matching_tests_paths) }
+ let(:instance) { described_class.new(changed_files_pathname, predictive_tests_pathname) }
let(:mock_test_file_finder) { instance_double(TestFileFinder::FileFinder) }
let(:new_matching_tests) { ["new_matching_spec.rb"] }
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:matching_tests_paths_content) { "previously_matching_spec.rb" }
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:predictive_tests_content) { "previously_matching_spec.rb" }
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.matching_tests_paths = Tempfile.new('matching_tests')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('predictive_tests_file')
# See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
# Tempfile.html#class-Tempfile-label-Explicit+close
begin
example.run
ensure
- changes_file.close
- matching_tests_paths.close
- changes_file.unlink
- matching_tests_paths.unlink
+ changed_files_file.close
+ predictive_tests_file.close
+ changed_files_file.unlink
+ predictive_tests_file.unlink
end
end
@@ -42,44 +44,44 @@ RSpec.describe Tooling::FindTests, feature_category: :tooling do
)
# We write into the temp files initially, to later check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(matching_tests_paths, matching_tests_paths_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(predictive_tests_pathname, predictive_tests_content)
end
describe '#execute' do
subject { instance.execute }
- context 'when the matching_tests_paths file does not exist' do
- let(:instance) { described_class.new(non_existing_output_file, matching_tests_paths) }
- let(:non_existing_output_file) { 'tmp/another_file.out' }
+ context 'when the predictive_tests_pathname file does not exist' do
+ let(:instance) { described_class.new(non_existing_output_pathname, predictive_tests_pathname) }
+ let(:non_existing_output_pathname) { 'tmp/another_file.out' }
around do |example|
example.run
ensure
- FileUtils.rm_rf(non_existing_output_file)
+ FileUtils.rm_rf(non_existing_output_pathname)
end
it 'creates the file' do
- expect { subject }.to change { File.exist?(non_existing_output_file) }.from(false).to(true)
+ expect { subject }.to change { File.exist?(non_existing_output_pathname) }.from(false).to(true)
end
end
- context 'when the matching_tests_paths file already exists' do
+ context 'when the predictive_tests_pathname file already exists' do
it 'does not create an empty file' do
- expect(File).not_to receive(:write).with(matching_tests_paths, '')
+ expect(File).not_to receive(:write).with(predictive_tests_pathname, '')
subject
end
end
it 'does not modify the content of the input file' do
- expect { subject }.not_to change { File.read(changes_file) }
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
it 'does not overwrite the output file' do
- expect { subject }.to change { File.read(matching_tests_paths) }
- .from(matching_tests_paths_content)
- .to("#{matching_tests_paths_content} #{new_matching_tests.uniq.join(' ')}")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_content)
+ .to("#{predictive_tests_content} #{new_matching_tests.uniq.join(' ')}")
end
it 'loads the tests.yml file with a pattern matching mapping' do
@@ -148,8 +150,8 @@ RSpec.describe Tooling::FindTests, feature_category: :tooling do
it 'writes uniquely matching specs to the output' do
subject
- expect(File.read(matching_tests_paths).split(' ')).to match_array(
- matching_tests_paths_content.split(' ') + new_matching_tests.uniq
+ expect(File.read(predictive_tests_pathname).split(' ')).to match_array(
+ predictive_tests_content.split(' ') + new_matching_tests.uniq
)
end
end
diff --git a/spec/tooling/lib/tooling/helpers/file_handler_spec.rb b/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
index 7c8310c4bd9..d6f68baeb90 100644
--- a/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
+++ b/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
@@ -42,18 +42,18 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
subject { instance.read_array_from_file(input_file_path) }
context 'when the input file does not exist' do
- let(:non_existing_input_file) { 'tmp/another_file.out' }
+ let(:non_existing_input_pathname) { 'tmp/another_file.out' }
- subject { instance.read_array_from_file(non_existing_input_file) }
+ subject { instance.read_array_from_file(non_existing_input_pathname) }
around do |example|
example.run
ensure
- FileUtils.rm_rf(non_existing_input_file)
+ FileUtils.rm_rf(non_existing_input_pathname)
end
it 'creates the file' do
- expect { subject }.to change { File.exist?(non_existing_input_file) }.from(false).to(true)
+ expect { subject }.to change { File.exist?(non_existing_input_pathname) }.from(false).to(true)
end
end
@@ -67,9 +67,10 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
end
describe '#write_array_to_file' do
- let(:content_array) { %w[new_entry] }
+ let(:content_array) { %w[new_entry] }
+ let(:overwrite_flag) { false }
- subject { instance.write_array_to_file(output_file_path, content_array) }
+ subject { instance.write_array_to_file(output_file_path, content_array, overwrite: overwrite_flag) }
context 'when the output file does not exist' do
let(:non_existing_output_file) { 'tmp/another_file.out' }
@@ -93,6 +94,14 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
it 'writes the correct content to the file' do
expect { subject }.to change { File.read(output_file_path) }.from('').to(content_array.join(' '))
end
+
+ context 'when the content array is not sorted' do
+ let(:content_array) { %w[new_entry a_new_entry] }
+
+ it 'sorts the array before writing it to file' do
+ expect { subject }.to change { File.read(output_file_path) }.from('').to(content_array.sort.join(' '))
+ end
+ end
end
context 'when the output file is not empty' do
@@ -100,8 +109,18 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
it 'appends the correct content to the file' do
expect { subject }.to change { File.read(output_file_path) }
- .from(initial_content)
- .to((initial_content.split(' ') + content_array).join(' '))
+ .from(initial_content)
+ .to((initial_content.split(' ') + content_array).join(' '))
+ end
+
+ context 'when the overwrite flag is set to true' do
+ let(:overwrite_flag) { true }
+
+ it 'overwrites the previous content' do
+ expect { subject }.to change { File.read(output_file_path) }
+ .from(initial_content)
+ .to(content_array.join(' '))
+ end
end
end
end
diff --git a/spec/tooling/lib/tooling/mappings/graphql_base_type_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/graphql_base_type_mappings_spec.rb
index 521958573fd..b6459428214 100644
--- a/spec/tooling/lib/tooling/mappings/graphql_base_type_mappings_spec.rb
+++ b/spec/tooling/lib/tooling/mappings/graphql_base_type_mappings_spec.rb
@@ -6,11 +6,17 @@ require_relative '../../../../../tooling/lib/tooling/mappings/graphql_base_type_
RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :tooling do
# We set temporary folders, and those readers give access to those folder paths
attr_accessor :foss_folder, :ee_folder, :jh_folder
- attr_accessor :changes_file, :matching_tests_paths
+ attr_accessor :changed_files_file, :predictive_tests_file
+
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:instance) { described_class.new(changed_files_pathname, predictive_tests_pathname) }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:predictive_tests_initial_content) { "previously_matching_spec.rb" }
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.matching_tests_paths = Tempfile.new('matching_tests')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('predictive_tests_file')
Dir.mktmpdir('FOSS') do |foss_folder|
Dir.mktmpdir('EE') do |ee_folder|
@@ -24,20 +30,16 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
begin
example.run
ensure
- changes_file.close
- matching_tests_paths.close
- changes_file.unlink
- matching_tests_paths.unlink
+ changed_files_file.close
+ predictive_tests_file.close
+ changed_files_file.unlink
+ predictive_tests_file.unlink
end
end
end
end
end
- let(:instance) { described_class.new(changes_file, matching_tests_paths) }
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:matching_tests_paths_initial_content) { "previously_matching_spec.rb" }
-
before do
stub_const("Tooling::Mappings::GraphqlBaseTypeMappings::GRAPHQL_TYPES_FOLDERS", {
nil => [foss_folder],
@@ -46,23 +48,23 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
})
# We write into the temp files initially, to later check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(matching_tests_paths, matching_tests_paths_initial_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(predictive_tests_pathname, predictive_tests_initial_content)
end
describe '#execute' do
subject { instance.execute }
context 'when no GraphQL files were changed' do
- let(:changes_file_content) { '' }
+ let(:changed_files_content) { '' }
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
context 'when some GraphQL files were changed' do
- let(:changes_file_content) do
+ let(:changed_files_content) do
[
"#{foss_folder}/my_graphql_file.rb",
"#{foss_folder}/my_other_graphql_file.rb"
@@ -76,7 +78,7 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
end
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -92,9 +94,9 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
end
it 'writes the correct specs in the output' do
- expect { subject }.to change { File.read(matching_tests_paths) }
- .from(matching_tests_paths_initial_content)
- .to("#{matching_tests_paths_initial_content} spec/my_graphql_file_spec.rb")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_initial_content)
+ .to("#{predictive_tests_initial_content} spec/my_graphql_file_spec.rb")
end
end
end
@@ -110,7 +112,7 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
end
context 'when no files were changed' do
- let(:changes_file_content) { '' }
+ let(:changed_files_content) { '' }
it 'returns an empty array' do
expect(subject).to match_array([])
@@ -118,7 +120,7 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
end
context 'when GraphQL files were changed' do
- let(:changes_file_content) do
+ let(:changed_files_content) do
[
"#{foss_folder}/my_graphql_file.rb",
"#{foss_folder}/my_other_graphql_file.rb",
@@ -135,7 +137,7 @@ RSpec.describe Tooling::Mappings::GraphqlBaseTypeMappings, feature_category: :to
end
context 'when files are deleted' do
- let(:changes_file_content) { "#{foss_folder}/deleted.rb" }
+ let(:changed_files_content) { "#{foss_folder}/deleted.rb" }
it 'returns an empty array' do
expect(subject).to match_array([])
diff --git a/spec/tooling/lib/tooling/mappings/js_to_system_specs_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/js_to_system_specs_mappings_spec.rb
index c36d14cd267..e1f35bedebb 100644
--- a/spec/tooling/lib/tooling/mappings/js_to_system_specs_mappings_spec.rb
+++ b/spec/tooling/lib/tooling/mappings/js_to_system_specs_mappings_spec.rb
@@ -6,23 +6,25 @@ require_relative '../../../../../tooling/lib/tooling/mappings/js_to_system_specs
RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :tooling do
# We set temporary folders, and those readers give access to those folder paths
attr_accessor :js_base_folder, :system_specs_base_folder
- attr_accessor :changes_file, :matching_tests_paths
+ attr_accessor :changed_files_file, :predictive_tests_file
+
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:predictive_tests_content) { "previously_matching_spec.rb" }
let(:instance) do
described_class.new(
- changes_file,
- matching_tests_paths,
+ changed_files_pathname,
+ predictive_tests_pathname,
system_specs_base_folder: system_specs_base_folder,
js_base_folder: js_base_folder
)
end
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:matching_tests_paths_content) { "previously_matching_spec.rb" }
-
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.matching_tests_paths = Tempfile.new('matching_tests')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('predictive_tests_file')
Dir.mktmpdir do |tmp_js_base_folder|
Dir.mktmpdir do |tmp_system_specs_base_folder|
@@ -34,10 +36,10 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
begin
example.run
ensure
- changes_file.close
- matching_tests_paths.close
- changes_file.unlink
- matching_tests_paths.unlink
+ changed_files_file.close
+ predictive_tests_file.close
+ changed_files_file.unlink
+ predictive_tests_file.unlink
end
end
end
@@ -45,22 +47,22 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
before do
# We write into the temp files initially, to later check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(matching_tests_paths, matching_tests_paths_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(predictive_tests_pathname, predictive_tests_content)
end
describe '#execute' do
subject { instance.execute }
before do
- File.write(changes_file, changed_files.join(' '))
+ File.write(changed_files_pathname, changed_files.join(' '))
end
context 'when no JS files were changed' do
let(:changed_files) { [] }
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -69,7 +71,7 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
context 'when the JS files are not present on disk' do
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -81,7 +83,7 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
context 'when no system specs match the JS keyword' do
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -92,9 +94,9 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
end
it 'adds the new specs to the output file' do
- expect { subject }.to change { File.read(matching_tests_paths) }
- .from(matching_tests_paths_content)
- .to("#{matching_tests_paths_content} #{system_specs_base_folder}/confidential_issues/issues_spec.rb")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_content)
+ .to("#{predictive_tests_content} #{system_specs_base_folder}/confidential_issues/issues_spec.rb")
end
end
end
@@ -108,7 +110,7 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
File.write("#{js_base_folder}/index.js", "index.js")
File.write("#{js_base_folder}/index-with-ee-in-it.js", "index-with-ee-in-it.js")
File.write("#{js_base_folder}/index-with-jh-in-it.js", "index-with-jh-in-it.js")
- File.write(changes_file, changed_files.join(' '))
+ File.write(changed_files_pathname, changed_files.join(' '))
end
context 'when no files were changed' do
@@ -148,7 +150,7 @@ RSpec.describe Tooling::Mappings::JsToSystemSpecsMappings, feature_category: :to
end
describe '#construct_js_keywords' do
- subject { described_class.new(changes_file, matching_tests_paths).construct_js_keywords(js_files) }
+ subject { described_class.new(changed_files_file, predictive_tests_file).construct_js_keywords(js_files) }
let(:js_files) do
%w[
diff --git a/spec/tooling/lib/tooling/mappings/partial_to_views_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/partial_to_views_mappings_spec.rb
index 30965ed985d..75ddee18985 100644
--- a/spec/tooling/lib/tooling/mappings/partial_to_views_mappings_spec.rb
+++ b/spec/tooling/lib/tooling/mappings/partial_to_views_mappings_spec.rb
@@ -5,15 +5,20 @@ require 'fileutils'
require_relative '../../../../../tooling/lib/tooling/mappings/partial_to_views_mappings'
RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :tooling do
- attr_accessor :view_base_folder, :changes_file, :output_file
+ attr_accessor :view_base_folder, :changed_files_file, :views_with_partials_file
- let(:instance) { described_class.new(changes_file, output_file, view_base_folder: view_base_folder) }
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:output_file_content) { "previously_added_view.html.haml" }
+ let(:instance) do
+ described_class.new(changed_files_pathname, views_with_partials_pathname, view_base_folder: view_base_folder)
+ end
+
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:views_with_partials_pathname) { views_with_partials_file.path }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:views_with_partials_content) { "previously_added_view.html.haml" }
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.output_file = Tempfile.new('output_file')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.views_with_partials_file = Tempfile.new('views_with_partials_file')
# See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
# Tempfile.html#class-Tempfile-label-Explicit+close
@@ -23,24 +28,24 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
example.run
end
ensure
- changes_file.close
- output_file.close
- changes_file.unlink
- output_file.unlink
+ changed_files_file.close
+ views_with_partials_file.close
+ changed_files_file.unlink
+ views_with_partials_file.unlink
end
end
before do
# We write into the temp files initially, to check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(output_file, output_file_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(views_with_partials_pathname, views_with_partials_content)
end
describe '#execute' do
subject { instance.execute }
- let(:changed_files) { ["#{view_base_folder}/my_view.html.haml"] }
- let(:changes_file_content) { changed_files.join(" ") }
+ let(:changed_files) { ["#{view_base_folder}/my_view.html.haml"] }
+ let(:changed_files_content) { changed_files.join(" ") }
before do
# We create all of the changed_files, so that they are part of the filtered files
@@ -48,12 +53,12 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
end
it 'does not modify the content of the input file' do
- expect { subject }.not_to change { File.read(changes_file) }
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
context 'when no partials were modified' do
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(output_file) }
+ expect { subject }.not_to change { File.read(views_with_partials_pathname) }
end
end
@@ -77,7 +82,7 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
end
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(output_file) }
+ expect { subject }.not_to change { File.read(views_with_partials_pathname) }
end
end
@@ -87,9 +92,9 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
end
it 'writes the view including the partial to the output' do
- expect { subject }.to change { File.read(output_file) }
- .from(output_file_content)
- .to(output_file_content + " #{view_base_folder}/my_view.html.haml")
+ expect { subject }.to change { File.read(views_with_partials_pathname) }
+ .from(views_with_partials_content)
+ .to(views_with_partials_content + " #{view_base_folder}/my_view.html.haml")
end
end
end
@@ -98,7 +103,7 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
describe '#filter_files' do
subject { instance.filter_files }
- let(:changes_file_content) { file_path }
+ let(:changed_files_content) { file_path }
context 'when the file does not exist on disk' do
let(:file_path) { "#{view_base_folder}/_index.html.erb" }
@@ -164,11 +169,11 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
before do
FileUtils.mkdir_p("#{view_base_folder}/components/subfolder")
- File.write(changes_file_content, "I am a partial!")
+ File.write(changed_files_content, "I am a partial!")
end
context 'when the partial is not part of the changed files' do
- let(:changes_file_content) { "#{view_base_folder}/components/subfolder/_not_the_partial.html.haml" }
+ let(:changed_files_content) { "#{view_base_folder}/components/subfolder/_not_the_partial.html.haml" }
it 'returns false' do
expect(subject).to be_falsey
@@ -176,7 +181,7 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
end
context 'when the partial is part of the changed files' do
- let(:changes_file_content) { "#{view_base_folder}/components/subfolder/_relative_partial.html.haml" }
+ let(:changed_files_content) { "#{view_base_folder}/components/subfolder/_relative_partial.html.haml" }
it 'returns true' do
expect(subject).to be_truthy
@@ -191,11 +196,11 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
before do
FileUtils.mkdir_p("#{view_base_folder}/components")
FileUtils.mkdir_p("#{view_base_folder}/shared")
- File.write(changes_file_content, "I am a partial!")
+ File.write(changed_files_content, "I am a partial!")
end
context 'when the partial is not part of the changed files' do
- let(:changes_file_content) { "#{view_base_folder}/shared/not_the_partial" }
+ let(:changed_files_content) { "#{view_base_folder}/shared/not_the_partial" }
it 'returns false' do
expect(subject).to be_falsey
@@ -203,7 +208,7 @@ RSpec.describe Tooling::Mappings::PartialToViewsMappings, feature_category: :too
end
context 'when the partial is part of the changed files' do
- let(:changes_file_content) { "#{view_base_folder}/shared/_absolute_partial.html.haml" }
+ let(:changed_files_content) { "#{view_base_folder}/shared/_absolute_partial.html.haml" }
it 'returns true' do
expect(subject).to be_truthy
diff --git a/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb
index bf07ad1951b..6d007843716 100644
--- a/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb
+++ b/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb
@@ -6,23 +6,25 @@ require_relative '../../../../../tooling/lib/tooling/mappings/view_to_js_mapping
RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling do
# We set temporary folders, and those readers give access to those folder paths
attr_accessor :view_base_folder, :js_base_folder
- attr_accessor :changes_file, :matching_tests_paths
+ attr_accessor :changed_files_file, :predictive_tests_file
+
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:predictive_tests_content) { "previously_matching_spec.rb" }
let(:instance) do
described_class.new(
- changes_file,
- matching_tests_paths,
+ changed_files_pathname,
+ predictive_tests_pathname,
view_base_folder: view_base_folder,
js_base_folder: js_base_folder
)
end
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:matching_tests_paths_content) { "previously_matching_spec.rb" }
-
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.matching_tests_paths = Tempfile.new('matching_tests')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('matching_tests')
Dir.mktmpdir do |tmp_js_base_folder|
Dir.mktmpdir do |tmp_views_base_folder|
@@ -34,10 +36,10 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
begin
example.run
ensure
- changes_file.close
- matching_tests_paths.close
- changes_file.unlink
- matching_tests_paths.unlink
+ changed_files_file.close
+ predictive_tests_file.close
+ changed_files_file.unlink
+ predictive_tests_file.unlink
end
end
end
@@ -45,8 +47,8 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
before do
# We write into the temp files initially, to later check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(matching_tests_paths, matching_tests_paths_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(predictive_tests_pathname, predictive_tests_content)
end
describe '#execute' do
@@ -55,7 +57,7 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
subject { instance.execute }
before do
- File.write(changes_file, changed_files.join(' '))
+ File.write(changed_files_pathname, changed_files.join(' '))
end
context 'when no view files have been changed' do
@@ -64,7 +66,7 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
end
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -82,7 +84,7 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
end
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -99,7 +101,7 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
context 'when no matching JS files are found' do
it 'does not change the output file' do
- expect { subject }.not_to change { File.read(matching_tests_paths) }
+ expect { subject }.not_to change { File.read(predictive_tests_pathname) }
end
end
@@ -119,9 +121,9 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
end
it 'adds the matching JS files to the output' do
- expect { subject }.to change { File.read(matching_tests_paths) }
- .from(matching_tests_paths_content)
- .to("#{matching_tests_paths_content} #{js_base_folder}/index.js")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_content)
+ .to("#{predictive_tests_content} #{js_base_folder}/index.js")
end
end
end
@@ -165,9 +167,9 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
end
it 'scans those partials for the HTML attribute value' do
- expect { subject }.to change { File.read(matching_tests_paths) }
- .from(matching_tests_paths_content)
- .to("#{matching_tests_paths_content} #{js_base_folder}/index.js")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_content)
+ .to("#{predictive_tests_content} #{js_base_folder}/index.js")
end
end
end
@@ -178,7 +180,7 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d
before do
File.write("#{js_base_folder}/index.js", "index.js")
File.write("#{view_base_folder}/index.html", "index.html")
- File.write(changes_file, changed_files.join(' '))
+ File.write(changed_files_pathname, changed_files.join(' '))
end
context 'when no files were changed' do
diff --git a/spec/tooling/lib/tooling/mappings/view_to_system_specs_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/view_to_system_specs_mappings_spec.rb
index d4f4bbd7f06..b8a13c50c9b 100644
--- a/spec/tooling/lib/tooling/mappings/view_to_system_specs_mappings_spec.rb
+++ b/spec/tooling/lib/tooling/mappings/view_to_system_specs_mappings_spec.rb
@@ -5,15 +5,20 @@ require 'fileutils'
require_relative '../../../../../tooling/lib/tooling/mappings/view_to_system_specs_mappings'
RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :tooling do
- attr_accessor :view_base_folder, :changes_file, :output_file
+ attr_accessor :view_base_folder, :changed_files_file, :predictive_tests_file
- let(:instance) { described_class.new(changes_file, output_file, view_base_folder: view_base_folder) }
- let(:changes_file_content) { "changed_file1 changed_file2" }
- let(:output_file_initial_content) { "previously_added_spec.rb" }
+ let(:instance) do
+ described_class.new(changed_files_pathname, predictive_tests_pathname, view_base_folder: view_base_folder)
+ end
+
+ let(:changed_files_pathname) { changed_files_file.path }
+ let(:predictive_tests_pathname) { predictive_tests_file.path }
+ let(:changed_files_content) { "changed_file1 changed_file2" }
+ let(:predictive_tests_initial_content) { "previously_added_spec.rb" }
around do |example|
- self.changes_file = Tempfile.new('changes')
- self.output_file = Tempfile.new('output_file')
+ self.changed_files_file = Tempfile.new('changed_files_file')
+ self.predictive_tests_file = Tempfile.new('predictive_tests_file')
# See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
# Tempfile.html#class-Tempfile-label-Explicit+close
@@ -23,10 +28,10 @@ RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :
example.run
end
ensure
- changes_file.close
- output_file.close
- changes_file.unlink
- output_file.unlink
+ changed_files_file.close
+ predictive_tests_file.close
+ changed_files_file.unlink
+ predictive_tests_file.unlink
end
end
@@ -34,13 +39,13 @@ RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :
FileUtils.mkdir_p("#{view_base_folder}/app/views/dashboard")
# We write into the temp files initially, to check how the code modified those files
- File.write(changes_file, changes_file_content)
- File.write(output_file, output_file_initial_content)
+ File.write(changed_files_pathname, changed_files_content)
+ File.write(predictive_tests_pathname, predictive_tests_initial_content)
end
shared_examples 'writes nothing to the output file' do
it 'writes nothing to the output file' do
- expect { subject }.not_to change { File.read(changes_file) }
+ expect { subject }.not_to change { File.read(changed_files_pathname) }
end
end
@@ -48,7 +53,7 @@ RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :
subject { instance.execute }
let(:changed_files) { ["#{view_base_folder}/app/views/dashboard/my_view.html.haml"] }
- let(:changes_file_content) { changed_files.join(" ") }
+ let(:changed_files_content) { changed_files.join(" ") }
before do
# We create all of the changed_files, so that they are part of the filtered files
@@ -88,9 +93,9 @@ RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :
end
it 'writes that feature spec to the output file' do
- expect { subject }.to change { File.read(output_file) }
- .from(output_file_initial_content)
- .to("#{output_file_initial_content} #{expected_feature_spec}")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_initial_content)
+ .to("#{predictive_tests_initial_content} #{expected_feature_spec}")
end
end
@@ -111,9 +116,9 @@ RSpec.describe Tooling::Mappings::ViewToSystemSpecsMappings, feature_category: :
end
it 'writes all of the feature specs for the parent folder to the output file' do
- expect { subject }.to change { File.read(output_file) }
- .from(output_file_initial_content)
- .to("#{output_file_initial_content} #{expected_feature_specs.join(' ')}")
+ expect { subject }.to change { File.read(predictive_tests_pathname) }
+ .from(predictive_tests_initial_content)
+ .to("#{predictive_tests_initial_content} #{expected_feature_specs.join(' ')}")
end
end
end
diff --git a/spec/tooling/lib/tooling/predictive_tests_spec.rb b/spec/tooling/lib/tooling/predictive_tests_spec.rb
new file mode 100644
index 00000000000..79554037c48
--- /dev/null
+++ b/spec/tooling/lib/tooling/predictive_tests_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'tempfile'
+require_relative '../../../../tooling/lib/tooling/predictive_tests'
+require_relative '../../../support/helpers/stub_env'
+
+RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
+ include StubENV
+
+ let(:instance) { described_class.new }
+ let(:matching_tests_initial_content) { 'initial_matching_spec' }
+
+ attr_accessor :changed_files, :fixtures_mapping, :matching_js_files, :matching_tests, :views_with_partials
+
+ around do |example|
+ self.changed_files = Tempfile.new('test-folder/changed_files.txt')
+ self.fixtures_mapping = Tempfile.new('test-folder/fixtures_mapping.txt')
+ self.matching_js_files = Tempfile.new('test-folder/matching_js_files.txt')
+ self.matching_tests = Tempfile.new('test-folder/matching_tests.txt')
+ self.views_with_partials = Tempfile.new('test-folder/views_with_partials.txt')
+
+ # See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
+ # Tempfile.html#class-Tempfile-label-Explicit+close
+ begin
+ example.run
+ ensure
+ changed_files.close
+ changed_files.unlink
+ fixtures_mapping.close
+ fixtures_mapping.unlink
+ matching_js_files.close
+ matching_js_files.unlink
+ matching_tests.close
+ matching_tests.unlink
+ views_with_partials.close
+ views_with_partials.unlink
+ end
+ end
+
+ before do
+ stub_env(
+ 'RSPEC_CHANGED_FILES_PATH' => changed_files.path,
+ 'RSPEC_MATCHING_TESTS_PATH' => matching_tests.path,
+ 'RSPEC_VIEWS_INCLUDING_PARTIALS_PATH' => views_with_partials.path,
+ 'FRONTEND_FIXTURES_MAPPING_PATH' => fixtures_mapping.path,
+ 'RSPEC_MATCHING_JS_FILES_PATH' => matching_js_files.path,
+ 'RSPEC_TESTS_MAPPING_ENABLED' => "false",
+ 'RSPEC_TESTS_MAPPING_PATH' => '/tmp/does-not-exist.out'
+ )
+
+ # We write some data to later on verify that we only append to this file.
+ File.write(matching_tests.path, matching_tests_initial_content)
+ File.write(fixtures_mapping.path, '{}') # We write valid JSON, so that the file can be processed
+ end
+
+ describe '#execute' do
+ subject { instance.execute }
+
+ context 'when ENV variables are missing' do
+ before do
+ stub_env(
+ 'RSPEC_CHANGED_FILES_PATH' => '',
+ 'FRONTEND_FIXTURES_MAPPING_PATH' => ''
+ )
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ '[predictive tests] Missing ENV variable(s): RSPEC_CHANGED_FILES_PATH,FRONTEND_FIXTURES_MAPPING_PATH.'
+ )
+ end
+ end
+
+ context 'when all ENV variables are provided' do
+ before do
+ File.write(changed_files, changed_files_content)
+ end
+
+ context 'when no files were changed' do
+ let(:changed_files_content) { '' }
+
+ it 'does not change any files' do
+ expect { subject }.not_to change { File.read(changed_files.path) }
+ expect { subject }.not_to change { File.read(matching_tests.path) }
+ expect { subject }.not_to change { File.read(views_with_partials.path) }
+ expect { subject }.not_to change { File.read(fixtures_mapping.path) }
+ expect { subject }.not_to change { File.read(matching_js_files.path) }
+ end
+ end
+
+ context 'when some files were changed' do
+ let(:changed_files_content) { 'tooling/lib/tooling/predictive_tests.rb' }
+
+ it 'appends the spec file to RSPEC_MATCHING_TESTS_PATH' do
+ expect { subject }.to change { File.read(matching_tests.path) }
+ .from(matching_tests_initial_content)
+ .to("#{matching_tests_initial_content} spec/tooling/lib/tooling/predictive_tests_spec.rb")
+ end
+
+ it 'does not change files other than RSPEC_MATCHING_TESTS_PATH' do
+ expect { subject }.not_to change { File.read(changed_files.path) }
+ expect { subject }.not_to change { File.read(views_with_partials.path) }
+ expect { subject }.not_to change { File.read(fixtures_mapping.path) }
+ expect { subject }.not_to change { File.read(matching_js_files.path) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb
index aac7d19c079..a7e4e42206a 100644
--- a/spec/tooling/quality/test_level_spec.rb
+++ b/spec/tooling/quality/test_level_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Quality::TestLevel, feature_category: :tooling do
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
- .to eq("spec/{bin,channels,config,contracts,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,components}{,/**/}*_spec.rb")
+ .to eq("spec/{bin,channels,components,config,contracts,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling}{,/**/}*_spec.rb")
end
end
@@ -121,7 +121,7 @@ RSpec.describe Quality::TestLevel, feature_category: :tooling do
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
- .to eq(%r{spec/(bin|channels|config|contracts|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|components)/})
+ .to eq(%r{spec/(bin|channels|components|config|contracts|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling)/})
end
end
@@ -167,6 +167,13 @@ RSpec.describe Quality::TestLevel, feature_category: :tooling do
end
end
+ context 'when start_with == true' do
+ it 'returns a regexp' do
+ expect(described_class.new(['ee/']).regexp(:system, true))
+ .to eq(%r{^(ee/)spec/(features)/})
+ end
+ end
+
describe 'performance' do
it 'memoizes the regexp for a given level' do
expect(subject.regexp(:system).object_id).to eq(subject.regexp(:system).object_id)