-
+
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
index e21371123e8..551c68a99ab 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
@@ -1,4 +1,4 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { stubExperiments } from 'helpers/experimentation_helper';
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
import eventHub from '~/invite_members/event_hub';
@@ -26,7 +26,7 @@ describe('Learn GitLab Section Link', () => {
});
const createWrapper = (action = defaultAction, props = {}) => {
- wrapper = shallowMount(LearnGitlabSectionLink, {
+ wrapper = mount(LearnGitlabSectionLink, {
propsData: { action, value: { ...defaultProps, ...props } },
});
};
@@ -36,6 +36,8 @@ describe('Learn GitLab Section Link', () => {
const findUncompletedLink = () => wrapper.find('[data-testid="uncompleted-learn-gitlab-link"]');
+ const videoTutorialLink = () => wrapper.find('[data-testid="video-tutorial-link"]');
+
it('renders no icon when not completed', () => {
createWrapper(undefined, { completed: false });
@@ -130,4 +132,44 @@ describe('Learn GitLab Section Link', () => {
unmockTracking();
});
});
+
+ describe('video_tutorials_continuous_onboarding experiment', () => {
+ describe('when control', () => {
+ beforeEach(() => {
+ stubExperiments({ video_tutorials_continuous_onboarding: 'control' });
+ createWrapper('codeOwnersEnabled');
+ });
+
+ it('renders no video link', () => {
+ expect(videoTutorialLink().exists()).toBe(false);
+ });
+ });
+
+ describe('when candidate', () => {
+ beforeEach(() => {
+ stubExperiments({ video_tutorials_continuous_onboarding: 'candidate' });
+ createWrapper('codeOwnersEnabled');
+ });
+
+ it('renders video link with blank target', () => {
+ const videoLinkElement = videoTutorialLink();
+
+ expect(videoLinkElement.exists()).toBe(true);
+ expect(videoLinkElement.attributes('target')).toEqual('_blank');
+ });
+
+ it('tracks the click', () => {
+ const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
+
+ videoTutorialLink().trigger('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_video_link', {
+ label: 'Add code owners',
+ property: 'Growth::Conversion::Experiment::LearnGitLab',
+ });
+
+ unmockTracking();
+ });
+ });
+ });
});
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 5e9a3d0a68b..81aeee0a3d2 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -522,11 +522,53 @@ RSpec.describe Resolvers::IssuesResolver do
end
end
+ context 'when sorting by escalation status' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:triggered_incident) { create(:incident, :with_escalation_status, project: project) }
+ let_it_be(:issue_no_status) { create(:issue, project: project) }
+ let_it_be(:resolved_incident) do
+ create(:incident, :with_escalation_status, project: project)
+ .tap { |issue| issue.escalation_status.resolve }
+ end
+
+ it 'sorts issues ascending' do
+ issues = resolve_issues(sort: :escalation_status_asc).to_a
+ expect(issues).to eq([triggered_incident, resolved_incident, issue_no_status])
+ end
+
+ it 'sorts issues descending' do
+ issues = resolve_issues(sort: :escalation_status_desc).to_a
+ expect(issues).to eq([resolved_incident, triggered_incident, issue_no_status])
+ end
+
+ it 'sorts issues created_at' do
+ issues = resolve_issues(sort: :created_desc).to_a
+ expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
+ end
+
+ context 'when incident_escalations feature flag is disabled' do
+ before do
+ stub_feature_flags(incident_escalations: false)
+ end
+
+ it 'defaults ascending status sort to created_desc' do
+ issues = resolve_issues(sort: :escalation_status_asc).to_a
+ expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
+ end
+
+ it 'defaults descending status sort to created_desc' do
+ issues = resolve_issues(sort: :escalation_status_desc).to_a
+ expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
+ end
+ end
+ end
+
context 'when sorting with non-stable cursors' do
%i[priority_asc priority_desc
popularity_asc popularity_desc
label_priority_asc label_priority_desc
- milestone_due_asc milestone_due_desc].each do |sort_by|
+ milestone_due_asc milestone_due_desc
+ escalation_status_asc escalation_status_desc].each do |sort_by|
it "uses offset-pagination when sorting by #{sort_by}" do
resolved = resolve_issues(sort: sort_by)
diff --git a/spec/graphql/types/issue_sort_enum_spec.rb b/spec/graphql/types/issue_sort_enum_spec.rb
index 4433709d193..95184477e75 100644
--- a/spec/graphql/types/issue_sort_enum_spec.rb
+++ b/spec/graphql/types/issue_sort_enum_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['IssueSort'] do
it 'exposes all the existing issue sort values' do
expect(described_class.values.keys).to include(
- *%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC]
+ *%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC ESCALATION_STATUS_ASC ESCALATION_STATUS_DESC]
)
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 29305ba435c..61ad9dc26be 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -238,6 +238,24 @@ RSpec.describe Issue do
end
end
+ context 'order by escalation status' do
+ let_it_be(:triggered_incident) { create(:incident_management_issuable_escalation_status, :triggered).issue }
+ let_it_be(:resolved_incident) { create(:incident_management_issuable_escalation_status, :resolved).issue }
+ let_it_be(:issue_no_status) { create(:issue) }
+
+ describe '.order_escalation_status_asc' do
+ subject { described_class.order_escalation_status_asc }
+
+ it { is_expected.to eq([triggered_incident, resolved_incident, issue_no_status]) }
+ end
+
+ describe '.order_escalation_status_desc' do
+ subject { described_class.order_escalation_status_desc }
+
+ it { is_expected.to eq([resolved_incident, triggered_incident, issue_no_status]) }
+ end
+ end
+
# TODO: Remove when NOT NULL constraint is added to the relationship
describe '#work_item_type' do
let(:issue) { create(:issue, :incident, project: reusable_project, work_item_type: nil) }
diff --git a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
index e1c7fd9d60d..85194e6eb20 100644
--- a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
@@ -28,6 +28,17 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.persisted?).to eq(true)
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
+
+ context 'when incident_escalations feature flag is disabled' do
+ let(:sort_value) { 'ESCALATION_STATUS_ASC' }
+
+ before do
+ stub_feature_flags(incident_escalations: false)
+ end
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
+ end
end
context 'when user has existing preference' do
@@ -45,5 +56,16 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
+
+ context 'when incident_escalations feature flag is disabled' do
+ let(:sort_value) { 'ESCALATION_STATUS_DESC' }
+
+ before do
+ stub_feature_flags(incident_escalations: false)
+ end
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
+ end
end
end