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>2022-08-31 12:13:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-31 12:13:12 +0300
commit6170bdc060501ecf6f817a530b3dc9f2e39ad4c3 (patch)
treec0e92981040c3aa0b2b78de816ee51cfb53ce661 /spec
parent136651d7cb69357d2823adefac430df389e81e17 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/concerns/product_analytics_tracking_spec.rb2
-rw-r--r--spec/controllers/concerns/redis_tracking_spec.rb3
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb8
-rw-r--r--spec/controllers/oauth/token_info_controller_spec.rb12
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/artifacts_controller_spec.rb6
-rw-r--r--spec/controllers/projects/feature_flags_controller_spec.rb4
-rw-r--r--spec/controllers/projects/grafana_api_controller_spec.rb4
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb2
-rw-r--r--spec/controllers/projects/registry/tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects/service_desk_controller_spec.rb2
-rw-r--r--spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb70
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js164
-rw-r--r--spec/frontend/releases/stores/modules/detail/getters_spec.js19
-rw-r--r--spec/frontend/releases/stores/modules/detail/mutations_spec.js7
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js (renamed from spec/frontend/work_items/pages/work_item_detail_spec.js)50
-rw-r--r--spec/frontend/work_items/components/work_item_weight_spec.js218
-rw-r--r--spec/frontend/work_items/mock_data.js14
-rw-r--r--spec/frontend/work_items/router_spec.js35
-rw-r--r--spec/support/shared_examples/models/synthetic_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/common_system_notes_shared_examples.rb2
21 files changed, 342 insertions, 286 deletions
diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb
index 250cc3cf2cf..2e734d81ea0 100644
--- a/spec/controllers/concerns/product_analytics_tracking_spec.rb
+++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do
skip_before_action :authenticate_user!, only: :show
track_event(:index, :show, name: 'g_analytics_valuestream', destinations: [:redis_hll, :snowplow],
- conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
+ conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
def index
render html: 'index'
diff --git a/spec/controllers/concerns/redis_tracking_spec.rb b/spec/controllers/concerns/redis_tracking_spec.rb
index 178684ae2d0..0ad8fa79e5e 100644
--- a/spec/controllers/concerns/redis_tracking_spec.rb
+++ b/spec/controllers/concerns/redis_tracking_spec.rb
@@ -11,7 +11,8 @@ RSpec.describe RedisTracking do
include RedisTracking
skip_before_action :authenticate_user!, only: :show
- track_redis_hll_event(:index, :show, name: 'g_compliance_approval_rules',
+ track_redis_hll_event(:index, :show,
+ name: 'g_compliance_approval_rules',
if: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
def index
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index af220e2d515..e73e61b6ec5 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -49,10 +49,10 @@ RSpec.describe Import::BitbucketController do
let(:expires_in) { 1.day }
let(:access_token) do
double(token: token,
- secret: secret,
- expires_at: expires_at,
- expires_in: expires_in,
- refresh_token: refresh_token)
+ secret: secret,
+ expires_at: expires_at,
+ expires_in: expires_in,
+ refresh_token: refresh_token)
end
before do
diff --git a/spec/controllers/oauth/token_info_controller_spec.rb b/spec/controllers/oauth/token_info_controller_spec.rb
index b66fff4d4e9..3cd952d4935 100644
--- a/spec/controllers/oauth/token_info_controller_spec.rb
+++ b/spec/controllers/oauth/token_info_controller_spec.rb
@@ -24,12 +24,12 @@ RSpec.describe Oauth::TokenInfoController do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to eq(
- 'scope' => %w[api],
- 'scopes' => %w[api],
- 'created_at' => access_token.created_at.to_i,
- 'expires_in' => access_token.expires_in,
- 'application' => { 'uid' => application.uid },
- 'resource_owner_id' => access_token.resource_owner_id,
+ 'scope' => %w[api],
+ 'scopes' => %w[api],
+ 'created_at' => access_token.created_at.to_i,
+ 'expires_in' => access_token.expires_in,
+ 'application' => { 'uid' => application.uid },
+ 'resource_owner_id' => access_token.resource_owner_id,
'expires_in_seconds' => access_token.expires_in
)
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 9ecef8b7450..df5da29495e 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -406,7 +406,7 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
before do
stub_last_request_id(last_request_id)
stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
- providers: [saml_config])
+ providers: [saml_config])
mock_auth_hash_with_saml_xml('saml', +'my-uid', user.email, mock_saml_response)
request.env['devise.mapping'] = Devise.mappings[:user]
request.env['omniauth.auth'] = Rails.application.env_config['omniauth.auth']
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index 958fcd4360c..263f488ddbf 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -488,7 +488,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with regular branch' do
before do
pipeline.update!(ref: 'master',
- sha: project.commit('master').sha)
+ sha: project.commit('master').sha)
get :latest_succeeded, params: params_from_ref('master')
end
@@ -499,7 +499,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name containing slash' do
before do
pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
+ sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome')
end
@@ -510,7 +510,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name and path containing slashes' do
before do
pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
+ sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome', job.name, 'file/README.md')
end
diff --git a/spec/controllers/projects/feature_flags_controller_spec.rb b/spec/controllers/projects/feature_flags_controller_spec.rb
index d7850cc5f33..29ad51d590f 100644
--- a/spec/controllers/projects/feature_flags_controller_spec.rb
+++ b/spec/controllers/projects/feature_flags_controller_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe Projects::FeatureFlagsController do
other_project = create(:project)
other_project.add_developer(user)
other_feature_flag = create(:operations_feature_flag, project: other_project,
- name: 'other_flag')
+ name: 'other_flag')
params = {
namespace_id: other_project.namespace,
project_id: other_project,
@@ -486,7 +486,7 @@ RSpec.describe Projects::FeatureFlagsController do
context 'when creating a version 2 feature flag with a gitlabUserList strategy' do
let!(:user_list) do
create(:operations_feature_flag_user_list, project: project,
- name: 'My List', user_xids: 'user1,user2')
+ name: 'My List', user_xids: 'user1,user2')
end
let(:params) do
diff --git a/spec/controllers/projects/grafana_api_controller_spec.rb b/spec/controllers/projects/grafana_api_controller_spec.rb
index baee9705127..2e25b0271ce 100644
--- a/spec/controllers/projects/grafana_api_controller_spec.rb
+++ b/spec/controllers/projects/grafana_api_controller_spec.rb
@@ -52,8 +52,8 @@ RSpec.describe Projects::GrafanaApiController do
.with(project, '1', 'api/v1/query_range',
{ 'query' => params[:query],
'start' => params[:start_time],
- 'end' => params[:end_time],
- 'step' => params[:step] })
+ 'end' => params[:end_time],
+ 'step' => params[:step] })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({})
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 77acd5fe13c..fa90a0d9abc 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -360,7 +360,7 @@ RSpec.describe Projects::PipelineSchedulesController do
id: pipeline_schedule,
schedule: schedule
},
- as: :html
+ as: :html
end
end
diff --git a/spec/controllers/projects/registry/tags_controller_spec.rb b/spec/controllers/projects/registry/tags_controller_spec.rb
index c03a280d2cd..7b786f4a8af 100644
--- a/spec/controllers/projects/registry/tags_controller_spec.rb
+++ b/spec/controllers/projects/registry/tags_controller_spec.rb
@@ -167,7 +167,7 @@ RSpec.describe Projects::Registry::TagsController do
repository_id: repository,
ids: names
},
- format: :json
+ format: :json
end
end
diff --git a/spec/controllers/projects/service_desk_controller_spec.rb b/spec/controllers/projects/service_desk_controller_spec.rb
index 1c4d6665414..bc507a033dc 100644
--- a/spec/controllers/projects/service_desk_controller_spec.rb
+++ b/spec/controllers/projects/service_desk_controller_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::ServiceDeskController do
let_it_be(:project) do
create(:project, :private, :custom_repo, service_desk_enabled: true,
- files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
+ files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
end
let_it_be(:user) { create(:user) }
diff --git a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
index 5a50b3de772..0fea0c1f37f 100644
--- a/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
+++ b/spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb
@@ -24,6 +24,76 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry
subject
expect(find('.breadcrumbs')).to have_content('Clean up image tags')
+
+ section = find('[data-testid="container-expiration-policy-project-settings"]')
+ expect(section).to have_text 'Clean up image tags'
+ end
+
+ it 'saves cleanup policy submit the form' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ select('Every day', from: 'Run cleanup')
+ select('50 tags per image name', from: 'Keep the most recent:')
+ fill_in('Keep tags matching:', with: 'stable')
+ select('7 days', from: 'Remove tags older than:')
+ fill_in('Remove tags matching:', with: '.*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+
+ expect(find('.gl-toast')).to have_content('Cleanup policy successfully saved.')
+ end
+
+ it 'does not save cleanup policy submit form with invalid regex' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ fill_in('Remove tags matching:', with: '*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+
+ expect(find('.gl-toast')).to have_content('Something went wrong while updating the cleanup policy.')
+ end
+ end
+
+ context 'with a project without expiration policy', :js do
+ before do
+ project.container_expiration_policy.destroy!
+ end
+
+ context 'with container_expiration_policies_enable_historic_entries enabled' do
+ before do
+ stub_application_setting(container_expiration_policies_enable_historic_entries: true)
+ end
+
+ it 'displays the related section' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ expect(find('[data-testid="enable-toggle"]'))
+ .to have_content('Disabled - Tags will not be automatically deleted.')
+ end
+ end
+ end
+
+ context 'with container_expiration_policies_enable_historic_entries disabled' do
+ before do
+ stub_application_setting(container_expiration_policies_enable_historic_entries: false)
+ end
+
+ it 'does not display the related section' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
+ end
+ end
end
end
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js
new file mode 100644
index 00000000000..51fcb1d7300
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js
@@ -0,0 +1,164 @@
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import component from '~/packages_and_registries/settings/project/components/cleanup_image_tags.vue';
+import ContainerExpirationPolicyForm from '~/packages_and_registries/settings/project/components/container_expiration_policy_form.vue';
+import {
+ CONTAINER_CLEANUP_POLICY_TITLE,
+ CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ FETCH_SETTINGS_ERROR_MESSAGE,
+ UNAVAILABLE_FEATURE_INTRO_TEXT,
+ UNAVAILABLE_USER_FEATURE_TEXT,
+} from '~/packages_and_registries/settings/project/constants';
+import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
+
+import {
+ expirationPolicyPayload,
+ emptyExpirationPolicyPayload,
+ containerExpirationPolicyData,
+} from '../mock_data';
+
+describe('Cleanup image tags project settings', () => {
+ let wrapper;
+ let fakeApollo;
+
+ const defaultProvidedValues = {
+ projectPath: 'path',
+ isAdmin: false,
+ adminSettingsPath: 'settingsPath',
+ enableHistoricEntries: false,
+ helpPagePath: 'helpPagePath',
+ showCleanupPolicyLink: false,
+ };
+
+ const findFormComponent = () => wrapper.findComponent(ContainerExpirationPolicyForm);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findTitle = () => wrapper.findByTestId('title');
+ const findDescription = () => wrapper.findByTestId('description');
+
+ const mountComponent = (provide = defaultProvidedValues, config) => {
+ wrapper = shallowMountExtended(component, {
+ stubs: {
+ GlSprintf,
+ },
+ provide,
+ ...config,
+ });
+ };
+
+ const mountComponentWithApollo = ({ provide = defaultProvidedValues, resolver } = {}) => {
+ Vue.use(VueApollo);
+
+ const requestHandlers = [[expirationPolicyQuery, resolver]];
+
+ fakeApollo = createMockApollo(requestHandlers);
+ mountComponent(provide, {
+ apolloProvider: fakeApollo,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('isEdited status', () => {
+ it.each`
+ description | apiResponse | workingCopy | result
+ ${'empty response and no changes from user'} | ${emptyExpirationPolicyPayload()} | ${{}} | ${false}
+ ${'empty response and changes from user'} | ${emptyExpirationPolicyPayload()} | ${{ enabled: true }} | ${true}
+ ${'response and no changes'} | ${expirationPolicyPayload()} | ${containerExpirationPolicyData()} | ${false}
+ ${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true}
+ ${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true}
+ `('$description', async ({ apiResponse, workingCopy, result }) => {
+ mountComponentWithApollo({
+ provide: { ...defaultProvidedValues, enableHistoricEntries: true },
+ resolver: jest.fn().mockResolvedValue(apiResponse),
+ });
+ await waitForPromises();
+
+ findFormComponent().vm.$emit('input', workingCopy);
+
+ await waitForPromises();
+
+ expect(findFormComponent().props('isEdited')).toBe(result);
+ });
+ });
+
+ it('renders the setting form', async () => {
+ mountComponentWithApollo({
+ resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
+ });
+ await waitForPromises();
+
+ expect(findFormComponent().exists()).toBe(true);
+ expect(findTitle().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_TITLE);
+ expect(findDescription().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_DESCRIPTION);
+ });
+
+ describe('the form is disabled', () => {
+ it('hides the form', () => {
+ mountComponent();
+
+ expect(findFormComponent().exists()).toBe(false);
+ });
+
+ it('shows an alert', () => {
+ mountComponent();
+
+ const text = findAlert().text();
+ expect(text).toContain(UNAVAILABLE_FEATURE_INTRO_TEXT);
+ expect(text).toContain(UNAVAILABLE_USER_FEATURE_TEXT);
+ });
+
+ describe('an admin is visiting the page', () => {
+ it('shows the admin part of the alert message', () => {
+ mountComponent({ ...defaultProvidedValues, isAdmin: true });
+
+ const sprintf = findAlert().find(GlSprintf);
+ expect(sprintf.text()).toBe('administration settings');
+ expect(sprintf.find(GlLink).attributes('href')).toBe(
+ defaultProvidedValues.adminSettingsPath,
+ );
+ });
+ });
+ });
+
+ describe('fetchSettingsError', () => {
+ beforeEach(async () => {
+ mountComponentWithApollo({
+ resolver: jest.fn().mockRejectedValue(new Error('GraphQL error')),
+ });
+ await waitForPromises();
+ });
+
+ it('hides the form', () => {
+ expect(findFormComponent().exists()).toBe(false);
+ });
+
+ it('shows an alert', () => {
+ expect(findAlert().html()).toContain(FETCH_SETTINGS_ERROR_MESSAGE);
+ });
+ });
+
+ describe('empty API response', () => {
+ it.each`
+ enableHistoricEntries | isShown
+ ${true} | ${true}
+ ${false} | ${false}
+ `('is $isShown that the form is shown', async ({ enableHistoricEntries, isShown }) => {
+ mountComponentWithApollo({
+ provide: {
+ ...defaultProvidedValues,
+ enableHistoricEntries,
+ },
+ resolver: jest.fn().mockResolvedValue(emptyExpirationPolicyPayload()),
+ });
+ await waitForPromises();
+
+ expect(findFormComponent().exists()).toBe(isShown);
+ });
+ });
+});
diff --git a/spec/frontend/releases/stores/modules/detail/getters_spec.js b/spec/frontend/releases/stores/modules/detail/getters_spec.js
index 4ac6eaebaa2..2982dc5c46c 100644
--- a/spec/frontend/releases/stores/modules/detail/getters_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/getters_spec.js
@@ -320,7 +320,9 @@ describe('Release edit/new getters', () => {
it(description, () => {
const expectedVariablesObject = { input: expect.objectContaining(expectedVariables) };
- const actualVariables = getters.releaseUpdateMutatationVariables(state);
+ const actualVariables = getters.releaseUpdateMutatationVariables(state, {
+ releasedAtChanged: Object.hasOwn(state.release, 'releasedAt'),
+ });
expect(actualVariables).toEqual(expectedVariablesObject);
});
@@ -409,4 +411,19 @@ describe('Release edit/new getters', () => {
},
);
});
+
+ describe('releasedAtChange', () => {
+ it('is false if the released at date has not changed', () => {
+ const date = new Date();
+ expect(
+ getters.releasedAtChanged({ originalReleasedAt: date, release: { releasedAt: date } }),
+ ).toBe(false);
+ });
+
+ it('is true if the date changed', () => {
+ const originalReleasedAt = new Date();
+ const releasedAt = new Date(2022, 5, 30);
+ expect(getters.releasedAtChanged({ originalReleasedAt, release: { releasedAt } })).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/releases/stores/modules/detail/mutations_spec.js b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
index 60b57c7a7ff..8bbf550b77d 100644
--- a/spec/frontend/releases/stores/modules/detail/mutations_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
@@ -36,6 +36,12 @@ describe('Release edit/new mutations', () => {
},
});
});
+
+ it('saves the original released at date as well', () => {
+ mutations[types.INITIALIZE_EMPTY_RELEASE](state);
+
+ expect(state.originalReleasedAt).toEqual(new Date());
+ });
});
describe(`${types.REQUEST_RELEASE}`, () => {
@@ -57,6 +63,7 @@ describe('Release edit/new mutations', () => {
expect(state.release).toEqual(release);
expect(state.originalRelease).toEqual(release);
+ expect(state.originalReleasedAt).toEqual(release.releasedAt);
});
});
diff --git a/spec/frontend/work_items/pages/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 95982db3620..51467656908 100644
--- a/spec/frontend/work_items/pages/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -2,6 +2,7 @@ import { GlAlert, GlBadge, GlLoadingIcon, GlSkeletonLoader, GlButton } from '@gi
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
+import workItemWeightSubscription from 'ee_component/work_items/graphql/work_item_weight.subscription.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
@@ -12,7 +13,6 @@ import WorkItemState from '~/work_items/components/work_item_state.vue';
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
import WorkItemLabels from '~/work_items/components/work_item_labels.vue';
-import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
import WorkItemInformation from '~/work_items/components/work_item_information.vue';
import { i18n } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
@@ -25,6 +25,7 @@ import {
workItemTitleSubscriptionResponse,
workItemResponseFactory,
mockParent,
+ workItemWeightSubscriptionResponse,
} from '../mock_data';
describe('WorkItemDetail component', () => {
@@ -41,6 +42,7 @@ describe('WorkItemDetail component', () => {
});
const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
const initialSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
+ const weightSubscriptionHandler = jest.fn().mockResolvedValue(workItemWeightSubscriptionResponse);
const findAlert = () => wrapper.findComponent(GlAlert);
const findSkeleton = () => wrapper.findComponent(GlSkeletonLoader);
@@ -51,7 +53,6 @@ describe('WorkItemDetail component', () => {
const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription);
const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels);
- const findWorkItemWeight = () => wrapper.findComponent(WorkItemWeight);
const findParent = () => wrapper.find('[data-testid="work-item-parent"]');
const findParentButton = () => findParent().findComponent(GlButton);
const findCloseButton = () => wrapper.find('[data-testid="work-item-close"]');
@@ -70,13 +71,19 @@ describe('WorkItemDetail component', () => {
includeWidgets = false,
error = undefined,
} = {}) => {
+ const handlers = [
+ [workItemQuery, handler],
+ [workItemTitleSubscription, subscriptionHandler],
+ confidentialityMock,
+ ];
+
+ if (IS_EE) {
+ handlers.push([workItemWeightSubscription, weightSubscriptionHandler]);
+ }
+
wrapper = shallowMount(WorkItemDetail, {
apolloProvider: createMockApollo(
- [
- [workItemQuery, handler],
- [workItemTitleSubscription, subscriptionHandler],
- confidentialityMock,
- ],
+ handlers,
{},
{
typePolicies: includeWidgets ? temporaryConfig.cacheConfig.typePolicies : {},
@@ -93,6 +100,7 @@ describe('WorkItemDetail component', () => {
glFeatures: {
workItemsMvc2: workItemsMvc2Enabled,
},
+ hasIssueWeightsFeature: true,
},
});
};
@@ -438,34 +446,6 @@ describe('WorkItemDetail component', () => {
});
});
- describe('weight widget', () => {
- describe.each`
- description | weightWidgetPresent | exists
- ${'when widget is returned from API'} | ${true} | ${true}
- ${'when widget is not returned from API'} | ${false} | ${false}
- `('$description', ({ weightWidgetPresent, exists }) => {
- it(`${weightWidgetPresent ? 'renders' : 'does not render'} weight component`, async () => {
- const response = workItemResponseFactory({ weightWidgetPresent });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(findWorkItemWeight().exists()).toBe(exists);
- });
- });
-
- it('shows an error message when it emits an `error` event', async () => {
- createComponent({ workItemsMvc2Enabled: true });
- await waitForPromises();
- const updateError = 'Failed to update';
-
- findWorkItemWeight().vm.$emit('error', updateError);
- await waitForPromises();
-
- expect(findAlert().text()).toBe(updateError);
- });
- });
-
describe('work item information', () => {
beforeEach(() => {
createComponent();
diff --git a/spec/frontend/work_items/components/work_item_weight_spec.js b/spec/frontend/work_items/components/work_item_weight_spec.js
deleted file mode 100644
index 38dc1af0bac..00000000000
--- a/spec/frontend/work_items/components/work_item_weight_spec.js
+++ /dev/null
@@ -1,218 +0,0 @@
-import { GlForm, GlFormInput } from '@gitlab/ui';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mockTracking } from 'helpers/tracking_helper';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { __ } from '~/locale';
-import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
-import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
-import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
-import { updateWorkItemMutationResponse } from 'jest/work_items/mock_data';
-
-describe('WorkItemWeight component', () => {
- Vue.use(VueApollo);
-
- let wrapper;
-
- const workItemId = 'gid://gitlab/WorkItem/1';
- const workItemType = 'Task';
-
- const findForm = () => wrapper.findComponent(GlForm);
- const findInput = () => wrapper.findComponent(GlFormInput);
-
- const createComponent = ({
- canUpdate = false,
- hasIssueWeightsFeature = true,
- isEditing = false,
- weight,
- mutationHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse),
- } = {}) => {
- wrapper = mountExtended(WorkItemWeight, {
- apolloProvider: createMockApollo([[updateWorkItemMutation, mutationHandler]]),
- propsData: {
- canUpdate,
- weight,
- workItemId,
- workItemType,
- },
- provide: {
- hasIssueWeightsFeature,
- },
- });
-
- if (isEditing) {
- findInput().vm.$emit('focus');
- }
- };
-
- describe('`issue_weights` licensed feature', () => {
- describe.each`
- description | hasIssueWeightsFeature | exists
- ${'when available'} | ${true} | ${true}
- ${'when not available'} | ${false} | ${false}
- `('$description', ({ hasIssueWeightsFeature, exists }) => {
- it(hasIssueWeightsFeature ? 'renders component' : 'does not render component', () => {
- createComponent({ hasIssueWeightsFeature });
-
- expect(findForm().exists()).toBe(exists);
- });
- });
- });
-
- describe('weight input', () => {
- it('has "Weight" label', () => {
- createComponent();
-
- expect(wrapper.findByLabelText(__('Weight')).exists()).toBe(true);
- });
-
- describe('placeholder attribute', () => {
- describe.each`
- description | isEditing | canUpdate | value
- ${'when not editing and cannot update'} | ${false} | ${false} | ${__('None')}
- ${'when editing and cannot update'} | ${true} | ${false} | ${__('None')}
- ${'when not editing and can update'} | ${false} | ${true} | ${__('None')}
- ${'when editing and can update'} | ${true} | ${true} | ${__('Enter a number')}
- `('$description', ({ isEditing, canUpdate, value }) => {
- it(`has a value of "${value}"`, async () => {
- createComponent({ canUpdate, isEditing });
- await nextTick();
-
- expect(findInput().attributes('placeholder')).toBe(value);
- });
- });
- });
-
- describe('readonly attribute', () => {
- describe.each`
- description | canUpdate | value
- ${'when cannot update'} | ${false} | ${'readonly'}
- ${'when can update'} | ${true} | ${undefined}
- `('$description', ({ canUpdate, value }) => {
- it(`renders readonly=${value}`, () => {
- createComponent({ canUpdate });
-
- expect(findInput().attributes('readonly')).toBe(value);
- });
- });
- });
-
- describe('type attribute', () => {
- describe.each`
- description | isEditing | canUpdate | type
- ${'when not editing and cannot update'} | ${false} | ${false} | ${'text'}
- ${'when editing and cannot update'} | ${true} | ${false} | ${'text'}
- ${'when not editing and can update'} | ${false} | ${true} | ${'text'}
- ${'when editing and can update'} | ${true} | ${true} | ${'number'}
- `('$description', ({ isEditing, canUpdate, type }) => {
- it(`has a value of "${type}"`, async () => {
- createComponent({ canUpdate, isEditing });
- await nextTick();
-
- expect(findInput().attributes('type')).toBe(type);
- });
- });
- });
-
- describe('value attribute', () => {
- describe.each`
- weight | value
- ${1} | ${'1'}
- ${0} | ${'0'}
- ${null} | ${''}
- ${undefined} | ${''}
- `('when `weight` prop is "$weight"', ({ weight, value }) => {
- it(`value is "${value}"`, () => {
- createComponent({ weight });
-
- expect(findInput().element.value).toBe(value);
- });
- });
- });
-
- describe('when blurred', () => {
- it('calls a mutation to update the weight when the input value is different', () => {
- const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
- createComponent({
- isEditing: true,
- weight: 0,
- mutationHandler: mutationSpy,
- canUpdate: true,
- });
-
- findInput().vm.$emit('blur', { target: { value: 1 } });
-
- expect(mutationSpy).toHaveBeenCalledWith({
- input: {
- id: workItemId,
- weightWidget: {
- weight: 1,
- },
- },
- });
- });
-
- it('does not call a mutation to update the weight when the input value is the same', () => {
- const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
- createComponent({ isEditing: true, mutationHandler: mutationSpy, canUpdate: true });
-
- findInput().trigger('blur');
-
- expect(mutationSpy).not.toHaveBeenCalledWith();
- });
-
- it('emits an error when there is a GraphQL error', async () => {
- const response = {
- data: {
- workItemUpdate: {
- errors: ['Error!'],
- workItem: {},
- },
- },
- };
- createComponent({
- isEditing: true,
- mutationHandler: jest.fn().mockResolvedValue(response),
- canUpdate: true,
- });
-
- findInput().trigger('blur');
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toEqual([
- ['Something went wrong while updating the task. Please try again.'],
- ]);
- });
-
- it('emits an error when there is a network error', async () => {
- createComponent({
- isEditing: true,
- mutationHandler: jest.fn().mockRejectedValue(new Error()),
- canUpdate: true,
- });
-
- findInput().trigger('blur');
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toEqual([
- ['Something went wrong while updating the task. Please try again.'],
- ]);
- });
-
- it('tracks updating the weight', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- createComponent({ canUpdate: true });
-
- findInput().trigger('blur');
-
- expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_weight', {
- category: TRACKING_CATEGORY_SHOW,
- label: 'item_weight',
- property: 'type_Task',
- });
- });
- });
- });
-});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index 8deca5c50b7..6a5aa48b610 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -407,6 +407,20 @@ export const workItemTitleSubscriptionResponse = {
},
};
+export const workItemWeightSubscriptionResponse = {
+ data: {
+ issuableWeightUpdated: {
+ id: 'gid://gitlab/WorkItem/1',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetWeight',
+ weight: 1,
+ },
+ ],
+ },
+ },
+};
+
export const workItemHierarchyEmptyResponse = {
data: {
workItem: {
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 99dcd886f7b..a86663cbdf9 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -1,5 +1,16 @@
import { mount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import workItemWeightSubscription from 'ee_component/work_items/graphql/work_item_weight.subscription.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import {
+ workItemResponseFactory,
+ workItemTitleSubscriptionResponse,
+ workItemWeightSubscriptionResponse,
+} from 'jest/work_items/mock_data';
import App from '~/work_items/components/app.vue';
+import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
+import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
import { createRouter } from '~/work_items/router';
@@ -7,26 +18,34 @@ import { createRouter } from '~/work_items/router';
describe('Work items router', () => {
let wrapper;
+ Vue.use(VueApollo);
+
+ const workItemQueryHandler = jest.fn().mockResolvedValue(workItemResponseFactory());
+ const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
+ const weightSubscriptionHandler = jest.fn().mockResolvedValue(workItemWeightSubscriptionResponse);
+
const createComponent = async (routeArg) => {
const router = createRouter('/work_item');
if (routeArg !== undefined) {
await router.push(routeArg);
}
+ const handlers = [
+ [workItemQuery, workItemQueryHandler],
+ [workItemTitleSubscription, titleSubscriptionHandler],
+ ];
+
+ if (IS_EE) {
+ handlers.push([workItemWeightSubscription, weightSubscriptionHandler]);
+ }
+
wrapper = mount(App, {
+ apolloProvider: createMockApollo(handlers),
router,
provide: {
fullPath: 'full-path',
issuesListPath: 'full-path/-/issues',
},
- mocks: {
- $apollo: {
- queries: {
- workItem: {},
- workItemTypes: {},
- },
- },
- },
});
};
diff --git a/spec/support/shared_examples/models/synthetic_note_shared_examples.rb b/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
index a41ade2950a..12e865b1312 100644
--- a/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
+++ b/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'a synthetic note' do |action|
- it_behaves_like 'a system note', exclude_project: true do
+ it_behaves_like 'a system note', exclude_project: true, skip_persistence_check: true do
let(:action) { action }
end
diff --git a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
index ce412ef55de..1887b38b50e 100644
--- a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
+++ b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
@@ -42,6 +42,7 @@ RSpec.shared_examples 'a system note' do |params|
it 'has the correct attributes', :aggregate_failures do
exclude_project = !params.nil? && params[:exclude_project]
+ skip_persistence_check = !params.nil? && params[:skip_persistence_check]
expect(subject).to be_valid
expect(subject).to be_system
@@ -50,6 +51,7 @@ RSpec.shared_examples 'a system note' do |params|
expect(subject.project).to eq project unless exclude_project
expect(subject.author).to eq author
+ expect(subject.system_note_metadata).to be_persisted unless skip_persistence_check
expect(subject.system_note_metadata.action).to eq(action)
expect(subject.system_note_metadata.commit_count).to eq(commit_count)
end