Promise.resolve({ data: mockMilestones }),
};
+export const mockReactionEmojiToken = {
+ type: 'my_reaction_emoji',
+ icon: 'thumb-up',
+ title: 'My-Reaction',
+ unique: true,
+ token: EmojiToken,
+ operators: [{ value: '=', description: 'is', default: 'true' }],
+ fetchEmojis: () => Promise.resolve(mockEmojis),
+};
+
export const mockMembershipToken = {
type: 'with_inherited_permissions',
icon: 'group',
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
new file mode 100644
index 00000000000..231f2f01428
--- /dev/null
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
@@ -0,0 +1,217 @@
+import {
+ GlFilteredSearchToken,
+ GlFilteredSearchSuggestion,
+ GlFilteredSearchTokenSegment,
+ GlDropdownDivider,
+} from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+
+import {
+ DEFAULT_LABEL_NONE,
+ DEFAULT_LABEL_ANY,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
+
+import { mockReactionEmojiToken, mockEmojis } from '../mock_data';
+
+jest.mock('~/flash');
+const GlEmoji = { template: '
![]()
' };
+const defaultStubs = {
+ Portal: true,
+ GlFilteredSearchSuggestionList: {
+ template: '
',
+ methods: {
+ getValue: () => '=',
+ },
+ },
+ GlEmoji,
+};
+
+function createComponent(options = {}) {
+ const {
+ config = mockReactionEmojiToken,
+ value = { data: '' },
+ active = false,
+ stubs = defaultStubs,
+ } = options;
+ return mount(EmojiToken, {
+ propsData: {
+ config,
+ value,
+ active,
+ },
+ provide: {
+ portalName: 'fake target',
+ alignSuggestions: function fakeAlignSuggestions() {},
+ suggestionsListClass: 'custom-class',
+ },
+ stubs,
+ });
+}
+
+describe('EmojiToken', () => {
+ let mock;
+ let wrapper;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ wrapper.destroy();
+ });
+
+ describe('computed', () => {
+ beforeEach(async () => {
+ wrapper = createComponent({ value: { data: mockEmojis[0].name } });
+
+ wrapper.setData({
+ emojis: mockEmojis,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ describe('currentValue', () => {
+ it('returns lowercase string for `value.data`', () => {
+ expect(wrapper.vm.currentValue).toBe(mockEmojis[0].name);
+ });
+ });
+
+ describe('activeEmoji', () => {
+ it('returns object for currently present `value.data`', () => {
+ expect(wrapper.vm.activeEmoji).toEqual(mockEmojis[0]);
+ });
+ });
+ });
+
+ describe('methods', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ describe('fetchEmojiBySearchTerm', () => {
+ it('calls `config.fetchEmojis` with provided searchTerm param', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchEmojis');
+
+ wrapper.vm.fetchEmojiBySearchTerm('foo');
+
+ expect(wrapper.vm.config.fetchEmojis).toHaveBeenCalledWith('foo');
+ });
+
+ it('sets response to `emojis` when request is successful', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockResolvedValue(mockEmojis);
+
+ wrapper.vm.fetchEmojiBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.emojis).toEqual(mockEmojis);
+ });
+ });
+
+ it('calls `createFlash` with flash error message when request fails', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
+
+ wrapper.vm.fetchEmojiBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(createFlash).toHaveBeenCalledWith('There was a problem fetching emojis.');
+ });
+ });
+
+ it('sets `loading` to false when request completes', () => {
+ jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
+
+ wrapper.vm.fetchEmojiBySearchTerm('foo');
+
+ return waitForPromises().then(() => {
+ expect(wrapper.vm.loading).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('template', () => {
+ const defaultEmojis = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
+
+ beforeEach(async () => {
+ wrapper = createComponent({
+ value: { data: `"${mockEmojis[0].name}"` },
+ });
+
+ wrapper.setData({
+ emojis: mockEmojis,
+ });
+
+ await wrapper.vm.$nextTick();
+ });
+
+ it('renders gl-filtered-search-token component', () => {
+ expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
+ });
+
+ it('renders token item when value is selected', () => {
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+
+ expect(tokenSegments).toHaveLength(3); // My Reaction, =, "thumbsup"
+ expect(tokenSegments.at(2).find(GlEmoji).attributes('data-name')).toEqual('thumbsup');
+ });
+
+ it('renders provided defaultEmojis as suggestions', async () => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockReactionEmojiToken, defaultEmojis },
+ stubs: { Portal: true, GlEmoji },
+ });
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+ const suggestionsSegment = tokenSegments.at(2);
+ suggestionsSegment.vm.$emit('activate');
+ await wrapper.vm.$nextTick();
+
+ const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
+
+ expect(suggestions).toHaveLength(defaultEmojis.length);
+ defaultEmojis.forEach((emoji, index) => {
+ expect(suggestions.at(index).text()).toBe(emoji.text);
+ });
+ });
+
+ it('does not render divider when no defaultEmojis', async () => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockReactionEmojiToken, defaultEmojis: [] },
+ stubs: { Portal: true, GlEmoji },
+ });
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+ const suggestionsSegment = tokenSegments.at(2);
+ suggestionsSegment.vm.$emit('activate');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false);
+ expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
+ });
+
+ it('renders `DEFAULT_LABEL_NONE` and `DEFAULT_LABEL_ANY` as default suggestions', async () => {
+ wrapper = createComponent({
+ active: true,
+ config: { ...mockReactionEmojiToken },
+ stubs: { Portal: true, GlEmoji },
+ });
+ const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
+ const suggestionsSegment = tokenSegments.at(2);
+ suggestionsSegment.vm.$emit('activate');
+ await wrapper.vm.$nextTick();
+
+ const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
+
+ expect(suggestions).toHaveLength(2);
+ expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_NONE.text);
+ expect(suggestions.at(1).text()).toBe(DEFAULT_LABEL_ANY.text);
+ });
+ });
+});
diff --git a/spec/graphql/types/milestone_type_spec.rb b/spec/graphql/types/milestone_type_spec.rb
index 806495250ac..5c2ae5cea3c 100644
--- a/spec/graphql/types/milestone_type_spec.rb
+++ b/spec/graphql/types/milestone_type_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do
it 'has the expected fields' do
expected_fields = %w[
- id title description state web_path
+ id iid title description state web_path
due_date start_date created_at updated_at
project_milestone group_milestone subgroup_milestone
stats
diff --git a/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb b/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
index 74cbeb85f16..04a43fb8b72 100644
--- a/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/names_suggestions/generator_spec.rb
@@ -44,7 +44,10 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
# ::Deployment.arel_table[:environment_id]
# )
let(:key_path) { 'counts.ingress_modsecurity_logging' }
- let(:name_suggestion) { /count_distinct_environment_id_from_
_deployments__clusters__clusters_applications_ingress/ }
+ let(:name_suggestion) do
+ constrains = /'\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'/
+ /count_distinct_environment_id_from__deployments___clusters___clusters_applications_ingress/
+ end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
index 664e7938a7e..a1dee442131 100644
--- a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
# If this spec fails, we need to add the new code review event to the correct aggregated metric
RSpec.describe 'Code review events' do
it 'the aggregated metrics contain all the code review metrics' do
- path = Rails.root.join('lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml')
+ path = Rails.root.join('config/metrics/aggregates/code_review.yml')
aggregated_events = YAML.safe_load(File.read(path), aliases: true)&.map(&:with_indifferent_access)
code_review_aggregated_events = aggregated_events
diff --git a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
index f8f6494b92e..1b73e5269d7 100644
--- a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
@@ -3,9 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_gitlab_redis_shared_state do
- let(:user1) { build(:user, id: 1) }
- let(:user2) { build(:user, id: 2) }
- let(:user3) { build(:user, id: 3) }
+ let_it_be(:user1) { build(:user, id: 1) }
+ let_it_be(:user2) { build(:user, id: 2) }
+ let_it_be(:user3) { build(:user, id: 3) }
+
let(:time) { Time.zone.now }
context 'for Issue title edit actions' do
@@ -272,10 +273,13 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
described_class.track_issue_title_changed_action(author: user1)
described_class.track_issue_description_changed_action(author: user1)
described_class.track_issue_assignee_changed_action(author: user1)
- described_class.track_issue_title_changed_action(author: user2, time: time - 2.days)
- described_class.track_issue_title_changed_action(author: user3, time: time - 3.days)
- described_class.track_issue_description_changed_action(author: user3, time: time - 3.days)
- described_class.track_issue_assignee_changed_action(author: user3, time: time - 3.days)
+
+ travel_to(2.days.ago) do
+ described_class.track_issue_title_changed_action(author: user2)
+ described_class.track_issue_title_changed_action(author: user3)
+ described_class.track_issue_description_changed_action(author: user3)
+ described_class.track_issue_assignee_changed_action(author: user3)
+ end
events = Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(described_class::ISSUE_CATEGORY)
today_count = Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: events, start_date: time, end_date: time)
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index c04f098d875..2ea4c5d8211 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -649,10 +649,12 @@ RSpec.describe Packages::Package, type: :model do
describe '.displayable' do
let_it_be(:hidden_package) { create(:maven_package, :hidden) }
let_it_be(:processing_package) { create(:maven_package, :processing) }
+ let_it_be(:error_package) { create(:maven_package, :error) }
subject { described_class.displayable }
- it 'does not include hidden packages', :aggregate_failures do
+ it 'does not include non-displayable packages', :aggregate_failures do
+ is_expected.to include(error_package)
is_expected.not_to include(hidden_package)
is_expected.not_to include(processing_package)
end
diff --git a/spec/services/packages/rubygems/create_gemspec_service_spec.rb b/spec/services/packages/rubygems/create_gemspec_service_spec.rb
index 4d5061933e0..198e978a47e 100644
--- a/spec/services/packages/rubygems/create_gemspec_service_spec.rb
+++ b/spec/services/packages/rubygems/create_gemspec_service_spec.rb
@@ -4,10 +4,10 @@ require 'spec_helper'
RSpec.describe Packages::Rubygems::CreateGemspecService do
include RubygemsHelpers
- let_it_be(:package) { create(:rubygems_package) }
let_it_be(:package_file) { create(:package_file, :gem) }
let_it_be(:gem) { gem_from_file(package_file.file) }
let_it_be(:gemspec) { gem.spec }
+ let_it_be(:package) { package_file.package }
let(:service) { described_class.new(package, gemspec) }
diff --git a/spec/services/submodules/update_service_spec.rb b/spec/services/submodules/update_service_spec.rb
index e7f92d5ba28..1a53da7b9fe 100644
--- a/spec/services/submodules/update_service_spec.rb
+++ b/spec/services/submodules/update_service_spec.rb
@@ -90,7 +90,7 @@ RSpec.describe Submodules::UpdateService do
let(:submodule) { '../six' }
it_behaves_like 'returns error result' do
- let(:error_message) { 'Invalid parameters' }
+ let(:error_message) { 'Invalid submodule path' }
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
index aa6e64a3820..4b956c2b566 100644
--- a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
@@ -14,10 +14,6 @@ RSpec.shared_examples 'a daily tracked issuable event' do
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user2)).to be_truthy
- expect(track_action(author: user3, time: time - 3.days)).to be_truthy
-
- expect(count_unique(date_from: time, date_to: time)).to eq(2)
- expect(count_unique(date_from: time - 5.days, date_to: 1.day.since(time))).to eq(3)
end
end