Commit a2f340d4 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 9cfcc6e6 666187aa
......@@ -127,6 +127,7 @@
- "{,ee/}{,spec/}{db,migrations}/**/*"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration/**/*"
- "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer
- "{,ee/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs
.backstage-patterns: &backstage-patterns
- "Dangerfile"
......
......@@ -1033,8 +1033,6 @@ Rails/SaveBang:
- 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/mentionable_shared_examples.rb'
- 'ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/build_execute_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/issue_epic_shared_examples.rb'
- 'ee/spec/workers/adjourned_project_deletion_worker_spec.rb'
- 'ee/spec/workers/clear_shared_runners_minutes_worker_spec.rb'
- 'ee/spec/workers/create_github_webhook_worker_spec.rb'
......@@ -1488,9 +1486,6 @@ Rails/SaveBang:
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb'
- 'spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb'
- 'spec/support/shared_examples/serializers/note_entity_shared_examples.rb'
- 'spec/support/shared_examples/services/common_system_notes_shared_examples.rb'
- 'spec/support/shared_examples/services/issuable_shared_examples.rb'
- 'spec/support/shared_examples/services/wiki_pages/destroy_service_shared_examples.rb'
- 'spec/tasks/gitlab/web_hook_rake_spec.rb'
- 'spec/uploaders/file_uploader_spec.rb'
- 'spec/uploaders/object_storage_spec.rb'
......@@ -64,4 +64,4 @@ export const trackAlertStatusUpdateOptions = {
label: 'Status',
};
export const DEFAULT_PAGE_SIZE = 10;
export const DEFAULT_PAGE_SIZE = 20;
......@@ -34,4 +34,4 @@ export const INCIDENT_STATUS_TABS = [
];
export const INCIDENT_SEARCH_DELAY = 300;
export const DEFAULT_PAGE_SIZE = 10;
export const DEFAULT_PAGE_SIZE = 20;
......@@ -4,6 +4,7 @@ import { mapActions, mapState } from 'vuex';
import { GlLink, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import DetailRow from './sidebar_detail_row.vue';
import ArtifactsBlock from './artifacts_block.vue';
......@@ -24,6 +25,7 @@ export default {
JobsContainer,
GlLink,
GlDeprecatedButton,
TooltipOnTruncate,
},
mixins: [timeagoMixin],
props: {
......@@ -111,7 +113,11 @@ export default {
<div class="sidebar-container">
<div class="blocks-container">
<div class="block d-flex flex-nowrap align-items-center">
<h4 class="my-0 mr-2 text-break-word">{{ job.name }}</h4>
<tooltip-on-truncate :title="job.name" truncate-target="child"
><h4 class="my-0 mr-2 gl-text-truncate">
{{ job.name }}
</h4>
</tooltip-on-truncate>
<div class="flex-grow-1 flex-shrink-0 text-right">
<gl-link
v-if="job.retry_path"
......
......@@ -275,6 +275,81 @@ export const convertToSentenceCase = string => {
*/
export const convertToTitleCase = string => string.replace(/\b[a-z]/g, s => s.toUpperCase());
const unicodeConversion = [
[/[ÀÁÂÃÅĀĂĄ]/g, 'A'],
[/[Æ]/g, 'AE'],
[/[ÇĆĈĊČ]/g, 'C'],
[/[ÈÉÊËĒĔĖĘĚ]/g, 'E'],
[/[ÌÍÎÏĨĪĬĮİ]/g, 'I'],
[/[Ððĥħ]/g, 'h'],
[/[ÑŃŅŇʼn]/g, 'N'],
[/[ÒÓÔÕÖØŌŎŐ]/g, 'O'],
[/[ÙÚÛŨŪŬŮŰŲ]/g, 'U'],
[/[ÝŶŸ]/g, 'Y'],
[/[Þñþńņň]/g, 'n'],
[/[ߌŜŞŠ]/g, 'S'],
[/[àáâãåāăąĸ]/g, 'a'],
[/[æ]/g, 'ae'],
[/[çćĉċč]/g, 'c'],
[/[èéêëēĕėęě]/g, 'e'],
[/[ìíîïĩīĭį]/g, 'i'],
[/[òóôõöøōŏő]/g, 'o'],
[/[ùúûũūŭůűų]/g, 'u'],
[/[ýÿŷ]/g, 'y'],
[/[ĎĐ]/g, 'D'],
[/[ďđ]/g, 'd'],
[/[ĜĞĠĢ]/g, 'G'],
[/[ĝğġģŊŋſ]/g, 'g'],
[/[ĤĦ]/g, 'H'],
[/[ıśŝşš]/g, 's'],
[/[IJ]/g, 'IJ'],
[/[ij]/g, 'ij'],
[/[Ĵ]/g, 'J'],
[/[ĵ]/g, 'j'],
[/[Ķ]/g, 'K'],
[/[ķ]/g, 'k'],
[/[ĹĻĽĿŁ]/g, 'L'],
[/[ĺļľŀł]/g, 'l'],
[/[Œ]/g, 'OE'],
[/[œ]/g, 'oe'],
[/[ŔŖŘ]/g, 'R'],
[/[ŕŗř]/g, 'r'],
[/[ŢŤŦ]/g, 'T'],
[/[ţťŧ]/g, 't'],
[/[Ŵ]/g, 'W'],
[/[ŵ]/g, 'w'],
[/[ŹŻŽ]/g, 'Z'],
[/[źżž]/g, 'z'],
[/ö/g, 'oe'],
[/ü/g, 'ue'],
[/ä/g, 'ae'],
// eslint-disable-next-line @gitlab/require-i18n-strings
[/Ö/g, 'Oe'],
// eslint-disable-next-line @gitlab/require-i18n-strings
[/Ü/g, 'Ue'],
// eslint-disable-next-line @gitlab/require-i18n-strings
[/Ä/g, 'Ae'],
];
/**
* Converts each non-ascii character in a string to
* it's ascii equivalent (if defined).
*
* e.g. "Dĭd söméònê äšk fœŕ Ůnĭċődę?" => "Did someone aesk foer Unicode?"
*
* @param {String} string
* @returns {String}
*/
export const convertUnicodeToAscii = string => {
let convertedString = string;
unicodeConversion.forEach(([regex, replacer]) => {
convertedString = convertedString.replace(regex, replacer);
});
return convertedString;
};
/**
* Splits camelCase or PascalCase words
* e.g. HelloWorld => Hello World
......
......@@ -76,6 +76,10 @@ export default class MilestoneSelect {
let getMilestones = Api.projectMilestones;
const reqParams = { state: 'active', include_parent_milestones: true };
if (term) {
reqParams.search = term.trim();
}
if (!contextId) {
contextId = $dropdown.get(0).dataset.groupId;
delete reqParams.include_parent_milestones;
......@@ -162,6 +166,7 @@ export default class MilestoneSelect {
`;
},
filterable: true,
filterRemote: true,
search: {
fields: ['title'],
},
......
import $ from 'jquery';
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import { convertToTitleCase, humanize, slugify } from '../lib/utils/text_utility';
import {
convertToTitleCase,
humanize,
slugify,
convertUnicodeToAscii,
} from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false;
let hasUserDefinedProjectName = false;
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugify($projectNameInput.val());
const slug = slugify(convertUnicodeToAscii($projectNameInput.val()));
$projectPathInput.val(slug);
};
......
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlButton,
},
props: {
isTodo: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
buttonLabel() {
return this.isTodo ? __('Mark as done') : __('Add a To-Do');
},
},
};
</script>
<template>
<gl-button v-bind="$attrs" :aria-label="buttonLabel" @click="$emit('click', $event)">
{{ buttonLabel }}
</gl-button>
</template>
......@@ -61,9 +61,9 @@ export default {
v-tooltip
:title="title"
:data-placement="placement"
class="js-show-tooltip"
class="js-show-tooltip gl-min-w-0"
>
<slot></slot>
</span>
<span v-else> <slot></slot> </span>
<span v-else class="gl-min-w-0"> <slot></slot> </span>
</template>
......@@ -151,3 +151,7 @@ ul.wiki-pages-list.content-list {
.empty-state-wiki .text-content {
max-width: 490px; // Widen to allow for the Confluence button
}
.wiki-form .markdown-area {
max-height: none;
}
= icon('spinner spin fw')
= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
Validating GitLab CI configuration…
= link_to 'Learn more', help_page_path('ci/yaml/README')
.text-center.gl-mt-3.gl-mb-3
= icon('spinner spin 2x', 'aria-hidden' => 'true', 'aria-label' => 'Loading content…', class: 'qa-spinner')
.text-center.gl-mt-4.gl-mb-3
= loading_icon(size: "md", css_class: "qa-spinner")
= icon('spinner spin fw')
= loading_icon(css_class: "gl-vertical-align-text-bottom")
Analyzing file…
= icon('spinner spin fw')
= loading_icon(css_class: "gl-vertical-align-text-bottom gl-mr-1")
Validating Route Map…
= link_to 'Learn more', help_page_path('ci/environments/index.md', anchor: 'going-from-source-files-to-public-pages')
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
.js-loading-icon.text-center.gl-mt-3.gl-mb-3.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
= icon('spinner spin 2x', 'aria-hidden' => 'true');
.text-center.gl-mt-4.gl-mb-3.js-loading-icon
= loading_icon(size: "md")
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
= icon('spinner spin 2x', class: 'gl-mt-3 gl-mb-3', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
= loading_icon(size: "md", css_class: "gl-mt-4 gl-mb-3")
.text-center.gl-mt-3.gl-mb-3.stl-controls
.btn-group
%button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } }
......
---
title: Truncate job title on log page
merge_request: 40107
author:
type: changed
---
title: Increase default page size for Alert and Incident management to 20 from 10
merge_request: 40139
author:
type: changed
---
title: Replace Unicode Characters with ASCII Equivalent in New Project Slug
merge_request: 39971
author: Kev @KevSlashNull
type: changed
---
title: Migrate .fa-spinner to .spinner for app/views/projects/blob/viewers
merge_request: 25046
author: nuwe1
type: other
---
title: Refactor spec/support/shared_examples/services/* and ee/spec/support/shared_examples/services/* to fix Rails/SaveBang Cop
merge_request: 39538
author: Rajendra Kadam
type: fixed
......@@ -38,6 +38,8 @@ For example, consider a migration that creates a table with two text columns,
```ruby
class CreateDbGuides < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
......@@ -179,6 +181,7 @@ in a post-deployment migration,
```ruby
class AddTextLimitMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
......
......@@ -321,80 +321,56 @@ it('tests a promise', async () => {
});
it('tests a promise rejection', async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: 'User with 1 not found.',
});
}
await expect(user.getUserName(1)).rejects.toThrow('User with 1 not found.');
});
```
You can also work with Promise chains. In this case, you can make use of the `done` callback and `done.fail` in case an error occurred. Following are some examples:
You can also simply return a promise from the test function.
NOTE: **Note:**
Using the `done` and `done.fail` callbacks is discouraged when working with
promises. They should only be used when testing callback-based code.
**Bad**:
```javascript
// missing done callback
// missing return
it('tests a promise', () => {
promise.then(data => {
expect(data).toBe(asExpected);
});
});
// missing catch
it('tests a promise', done => {
promise
.then(data => {
expect(data).toBe(asExpected);
})
.then(done);
});
// use done.fail in asynchronous tests
// uses done/done.fail
it('tests a promise', done => {
promise
.then(data => {
expect(data).toBe(asExpected);
})
.then(done)
.catch(fail);
});
// missing catch
it('tests a promise rejection', done => {
promise
.catch(error => {
expect(error).toBe(expectedError);
})
.then(done);
.catch(done.fail);
});
```
**Good**:
```javascript
// handling success
it('tests a promise', done => {
promise
// verifying a resolved promise
it('tests a promise', () => {
return promise
.then(data => {
expect(data).toBe(asExpected);
})
.then(done)
.catch(done.fail);
});
});
// failure case
it('tests a promise rejection', done => {
promise
.then(done.fail)
.catch(error => {
expect(error).toBe(expectedError);
})
.then(done)
.catch(done.fail);
// verifying a resolved promise using Jest's `resolves` matcher
it('tests a promise', () => {
return expect(promise).resolves.toBe(asExpected);
});
// verifying a rejected promise using Jest's `rejects` matcher
it('tests a promise rejection', () => {
return expect(promise).rejects.toThrow(expectedError);
});
```
......
......@@ -29,7 +29,7 @@ RSpec.shared_examples 'restricts access to protected environments' do |developer
context 'when user has access to the environment' do
before do
protected_environment.deploy_access_levels.create(user: user)
protected_environment.deploy_access_levels.create!(user: user)
end
it 'enqueues the build' do
......
......@@ -38,7 +38,7 @@ RSpec.shared_examples 'issue with epic_id parameter' do
context 'when user can add issues to the epic' do
before do
group.add_owner(user)
group.add_maintainer(user)
project.add_maintainer(user)
end
......@@ -57,7 +57,7 @@ RSpec.shared_examples 'issue with epic_id parameter' do
before do
subgroup = create(:group, parent: group)
create(:epic, group: subgroup)
project.update(group: subgroup)
project.update!(group: subgroup)
end
it 'creates epic issue link' do
......
......@@ -14,23 +14,23 @@ module Gitlab
# @param [ActiveSupport::TimeWithZone] end_date end of time frame
# @return [Integer] number of unique visitors
def unique_visits_for(targets:, start_date: 7.days.ago, end_date: start_date + 1.week)
target_ids = if targets == :analytics
self.class.analytics_ids
elsif targets == :compliance
self.class.compliance_ids
else
Array(targets)
end
events = if targets == :analytics
self.class.analytics_events
elsif targets == :compliance
self.class.compliance_events
else
Array(targets)
end
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: target_ids, start_date: start_date, end_date: end_date)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: events, start_date: start_date, end_date: end_date)
end
class << self
def analytics_ids
def analytics_events
Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category('analytics')
end
def compliance_ids
def compliance_events
Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category('compliance')
end
end
......
......@@ -589,8 +589,8 @@ module Gitlab
end
def analytics_unique_visits_data
results = ::Gitlab::Analytics::UniqueVisits.analytics_ids.each_with_object({}) do |target_id, hash|
hash[target_id] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target_id) }
results = ::Gitlab::Analytics::UniqueVisits.analytics_events.each_with_object({}) do |target, hash|
hash[target] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target) }
end
results['analytics_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :analytics) }
results['analytics_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :analytics, start_date: 4.weeks.ago.to_date, end_date: Date.current) }
......@@ -599,8 +599,8 @@ module Gitlab
end
def compliance_unique_visits_data
results = ::Gitlab::Analytics::UniqueVisits.compliance_ids.each_with_object({}) do |target_id, hash|
hash[target_id] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target_id) }
results = ::Gitlab::Analytics::UniqueVisits.compliance_events.each_with_object({}) do |target, hash|
hash[target] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target) }
end
results['compliance_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance) }
results['compliance_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance, start_date: 4.weeks.ago.to_date, end_date: Date.current) }
......
......@@ -35,13 +35,15 @@ module QA
private
def format_example(example)
file_path, line_number = location_including_shared_examples(example.metadata)
{
id: example.id,
description: example.description,
full_description: example.full_description,
status: example.execution_result.status.to_s,
file_path: example.metadata[:file_path],
line_number: example.metadata[:line_number],
file_path: file_path,
line_number: line_number.to_i,
run_time: example.execution_result.run_time,
pending_message: example.execution_result.pending_message,
status_issue: example.metadata[:status_issue],
......@@ -49,6 +51,15 @@ module QA
screenshot: example.metadata[:screenshot]
}
end
def location_including_shared_examples(metadata)
if metadata[:shared_group_inclusion_backtrace].empty?
[metadata[:file_path], metadata[:line_number]]
else
# If there are nested shared examples, the outermost location is last in the array
metadata[:shared_group_inclusion_backtrace].last.formatted_inclusion_location.split(':')
end
end
end
end
end
......@@ -4,13 +4,17 @@ require "spec_helper"
RSpec.describe "Issues > User edits issue", :js do
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) }
let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
let_it_be(:issue_with_milestones) { create(:issue, project: project_with_milestones, author: user, assignees: [user]) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:milestones) { create_list(:milestone, 25, project: project_with_milestones) }
before do
project.add_developer(user)
project_with_milestones.add_developer(user)
sign_in(user)
end
......@@ -218,6 +222,23 @@ RSpec.describe "Issues > User edits issue", :js do
end
end
end
it 'allows user to search milestone' do
visit project_issue_path(project_with_milestones, issue_with_milestones)
page.within('.milestone') do
click_link 'Edit'
wait_for_requests
# We need to enclose search string in quotes for exact match as all the milestone titles
# within tests are prefixed with `My title`.
find('.dropdown-input-field', visible: true).send_keys "\"#{milestones[0].title}\""
wait_for_requests
page.within '.dropdown-content' do
expect(page).to have_content milestones[0].title
end
end
end
end
context 'by unauthorized user' do
......
......@@ -49,11 +49,11 @@ describe('AlertManagementTable', () => {
const findIssueFields = () => wrapper.findAll('[data-testid="issueField"]');
const findAlertError = () => wrapper.find('[data-testid="alert-error"]');
const alertsCount = {
open: 14,
triggered: 10,
acknowledged: 6,
resolved: 1,
all: 16,
open: 24,
triggered: 20,
acknowledged: 16,
resolved: 11,
all: 26,
};
const selectFirstStatusOption = () => {
findFirstStatusOption().vm.$emit('click');
......
......@@ -30,9 +30,9 @@ describe('Incidents List', () => {
const incidentTemplateName = 'incident';
const incidentType = 'incident';
const incidentsCount = {
opened: 14,
closed: 1,
all: 16,
opened: 24,
closed: 10,
all: 26,
};
const findTable = () => wrapper.find(GlTable);
......
......@@ -205,6 +205,26 @@ describe('text_utility', () => {
});
});
describe('convertUnicodeToAscii', () => {
it('does nothing on an empty string', () => {
expect(textUtils.convertUnicodeToAscii('')).toBe('');
});
it('does nothing on an already ascii string', () => {
expect(textUtils.convertUnicodeToAscii('The quick brown fox jumps over the lazy dog.')).toBe(
'The quick brown fox jumps over the lazy dog.',
);
});
it('replaces Unicode characters', () => {
expect(textUtils.convertUnicodeToAscii('Dĭd söméònê äšk fœŕ Ůnĭċődę?')).toBe(
'Did someone aesk foer Unicode?',
);
expect(textUtils.convertUnicodeToAscii("Jürgen's Projekt")).toBe("Juergen's Projekt");
});
});
describe('splitCamelCase', () => {
it('separates a PascalCase word to two', () => {
expect(textUtils.splitCamelCase('HelloWorld')).toBe('Hello World');
......
import { shallowMount, mount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import TodoButton from '~/vue_shared/components/todo_button.vue';
describe('Todo Button', () => {
let wrapper;
const createComponent = (props = {}, mountFn = shallowMount) => {
wrapper = mountFn(TodoButton, {
propsData: {
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders GlButton', () => {
createComponent();
expect(wrapper.find(GlButton).exists()).toBe(true);
});
it('emits click event when clicked', () => {
createComponent({}, mount);
wrapper.find(GlButton).trigger('click');
expect(wrapper.emitted().click).toBeTruthy();
});
it.each`
label | isTodo
${'Mark as done'} | ${true}
${'Add a To-Do'} | ${false}
`('sets correct label when isTodo is $isTodo', ({ label, isTodo }) => {
createComponent({ isTodo });
expect(wrapper.find(GlButton).text()).toBe(label);
});
it('binds additional props to GlButton', () => {
createComponent({ loading: true });
expect(wrapper.find(GlButton).props('loading')).toBe(true);
});
});
......@@ -960,8 +960,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.analytics_unique_visits_data }
it 'returns the number of unique visits to pages with analytics features' do
::Gitlab::Analytics::UniqueVisits.analytics_ids.each do |target_id|
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: target_id).and_return(123)
::Gitlab::Analytics::UniqueVisits.analytics_events.each do |target|
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: target).and_return(123)
end
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :analytics).and_return(543)
......@@ -996,8 +996,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
described_class.clear_memoization(:unique_visit_service)
allow_next_instance_of(::Gitlab::Analytics::UniqueVisits) do |instance|
::Gitlab::Analytics::UniqueVisits.compliance_ids.each do |target_id|
allow(instance).to receive(:unique_visits_for).with(targets: target_id).and_return(123)
::Gitlab::Analytics::UniqueVisits.compliance_events.each do |target|
allow(instance).to receive(:unique_visits_for).with(targets: target).and_return(123)
end
allow(instance).to receive(:unique_visits_for).with(targets: :compliance).and_return(543)
......
......@@ -84,7 +84,7 @@ RSpec.describe Packages::Package, type: :model do
end
describe 'validations' do
subject { create(:package) }
subject { build(:package) }
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id, :version, :package_type) }
......@@ -95,7 +95,7 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.not_to allow_value("my(dom$$$ain)com.my-app").for(:name) }
context 'conan package' do
subject { create(:conan_package) }
subject { build_stubbed(:conan_package) }
let(:fifty_one_characters) {'f_b' * 17}
......@@ -112,7 +112,7 @@ RSpec.describe Packages::Package, type: :model do
describe '#version' do
RSpec.shared_examples 'validating version to be SemVer compliant for' do |factory_name|
context "for #{factory_name}" do
subject { create(factory_name) }
subject { build_stubbed(factory_name) }
it { is_expected.to allow_value('1.2.3').for(:version) }
it { is_expected.to allow_value('1.2.3-beta').for(:version) }
......@@ -126,7 +126,7 @@ RSpec.describe Packages::Package, type: :model do
end
context 'conan package' do
subject { create(:conan_package) }
subject { build_stubbed(:conan_package) }
let(:fifty_one_characters) {'1.2' * 17}
......@@ -142,7 +142,7 @@ RSpec.describe Packages::Package, type: :model do
end
context 'maven package' do
subject { create(:maven_package) }
subject { build_stubbed(:maven_package) }
it { is_expected.to allow_value('0').for(:version) }
it { is_expected.to allow_value('1').for(:version) }
......
......@@ -5,7 +5,7 @@ RSpec.shared_examples 'system note creation' do |update_params, note_text|
before do
issuable.assign_attributes(update_params)
issuable.save
issuable.save!
end
it 'creates 1 system note with the correct content' do
......
......@@ -19,7 +19,7 @@ RSpec.shared_examples 'system notes for milestones' do
let(:group_milestone) { create(:milestone, group: group) }
before do
project.update(namespace: group)
project.update!(namespace: group)
create(:group_member, group: group, user: user)
end
......
# frozen_string_literal: true
RSpec.shared_examples 'WikiPages::DestroyService#execute' do |container_type|
let(:container) { create(container_type) }
let(:container) { create(container_type) } # rubocop:disable Rails/SaveBang
let(:user) { create(:user) }
let(:page) { create(:wiki_page) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment