summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-29 15:09:39 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-29 15:09:39 +0000
commit58320d8e039f4edc8e8000d98f077017b6c78b61 (patch)
treece94f463703981b7103e762b60105aff59ee27e9 /spec
parent6f9f4f0580bf5a78af4c5da067d4acf43094dc98 (diff)
downloadgitlab-ce-58320d8e039f4edc8e8000d98f077017b6c78b61.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/import/github_controller_spec.rb65
-rw-r--r--spec/frontend/notes/stores/actions_spec.js61
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js16
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap12
-rw-r--r--spec/frontend/repository/components/last_commit_spec.js8
-rw-r--r--spec/frontend/repository/components/table/index_spec.js2
-rw-r--r--spec/frontend/sidebar/lock/constants.js2
-rw-r--r--spec/frontend/sidebar/lock/edit_form_buttons_spec.js171
-rw-r--r--spec/frontend/sidebar/lock/edit_form_spec.js69
-rw-r--r--spec/frontend/sidebar/lock/lock_issue_sidebar_spec.js206
-rw-r--r--spec/lib/gitlab/usage_data/topology_spec.rb39
-rw-r--r--spec/policies/personal_access_token_policy_spec.rb31
-rw-r--r--spec/requests/api/import_github_spec.rb2
-rw-r--r--spec/services/import/github_service_spec.rb6
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb9
15 files changed, 548 insertions, 151 deletions
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index a5a3dc463d3..0775903cff6 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -15,10 +15,7 @@ RSpec.describe Import::GithubController do
it "redirects to GitHub for an access token if logged in with GitHub" do
allow(controller).to receive(:logged_in_with_provider?).and_return(true)
expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
- allow_any_instance_of(Gitlab::LegacyGithubImport::Client)
- .to receive(:authorize_url)
- .with(users_import_github_callback_url)
- .and_call_original
+ allow(controller).to receive(:authorize_url).with(users_import_github_callback_url).and_call_original
get :new
@@ -46,13 +43,15 @@ RSpec.describe Import::GithubController do
end
describe "GET callback" do
+ before do
+ allow(controller).to receive(:get_token).and_return(token)
+ allow(controller).to receive(:oauth_options).and_return({})
+
+ stub_omniauth_provider('github')
+ end
+
it "updates access token" do
token = "asdasd12345"
- allow_any_instance_of(Gitlab::LegacyGithubImport::Client)
- .to receive(:get_token).and_return(token)
- allow_any_instance_of(Gitlab::LegacyGithubImport::Client)
- .to receive(:github_options).and_return({})
- stub_omniauth_provider('github')
get :callback
@@ -67,6 +66,54 @@ RSpec.describe Import::GithubController do
describe "GET status" do
it_behaves_like 'a GitHub-ish import controller: GET status'
+
+ context 'when using OAuth' do
+ before do
+ allow(controller).to receive(:logged_in_with_provider?).and_return(true)
+ end
+
+ context 'when OAuth config is missing' do
+ let(:new_import_url) { public_send("new_import_#{provider}_url") }
+
+ before do
+ allow(controller).to receive(:oauth_config).and_return(nil)
+ end
+
+ it 'returns missing config error' do
+ expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
+
+ get :status
+
+ expect(session[:"#{provider}_access_token"]).to be_nil
+ expect(controller).to redirect_to(new_import_url)
+ expect(flash[:alert]).to eq('OAuth configuration for GitHub missing.')
+ end
+ end
+ end
+
+ context 'when feature remove_legacy_github_client is disabled' do
+ before do
+ stub_feature_flags(remove_legacy_github_client: false)
+ end
+
+ it 'uses Gitlab::LegacyGitHubImport::Client' do
+ expect(controller.send(:client)).to be_instance_of(Gitlab::LegacyGithubImport::Client)
+
+ get :status
+ end
+ end
+
+ context 'when feature remove_legacy_github_client is enabled' do
+ before do
+ stub_feature_flags(remove_legacy_github_client: true)
+ end
+
+ it 'uses Gitlab::GithubImport::Client' do
+ expect(controller.send(:client)).to be_instance_of(Gitlab::GithubImport::Client)
+
+ get :status
+ end
+ end
end
describe "POST create" do
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 909a4a797ae..10d6202cae0 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -19,7 +19,9 @@ import {
} from '../mock_data';
import axios from '~/lib/utils/axios_utils';
import * as utils from '~/notes/stores/utils';
-import updateIssueConfidentialMutation from '~/sidebar/components/confidential/queries/update_issue_confidential.mutation.graphql';
+import updateIssueConfidentialMutation from '~/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql';
+import updateMergeRequestLockMutation from '~/sidebar/components/lock/mutations/update_merge_request_lock.mutation.graphql';
+import updateIssueLockMutation from '~/sidebar/components/lock/mutations/update_issue_lock.mutation.graphql';
const TEST_ERROR_MESSAGE = 'Test error message';
jest.mock('~/flash');
@@ -1263,4 +1265,61 @@ describe('Actions Notes Store', () => {
});
});
});
+
+ describe.each`
+ issuableType
+ ${'issue'} | ${'merge_request'}
+ `('updateLockedAttribute for issuableType=$issuableType', ({ issuableType }) => {
+ // Payload for mutation query
+ state = { noteableData: { discussion_locked: false } };
+ const targetType = issuableType;
+ const getters = { getNoteableData: { iid: '1', targetType } };
+
+ // Target state after mutation
+ const locked = true;
+ const actionArgs = { fullPath: 'full/path', locked };
+ const input = { iid: '1', projectPath: 'full/path', locked: true };
+
+ // Helper functions
+ const targetMutation = () => {
+ return targetType === 'issue' ? updateIssueLockMutation : updateMergeRequestLockMutation;
+ };
+
+ const mockResolvedValue = () => {
+ return targetType === 'issue'
+ ? { data: { issueSetLocked: { issue: { discussionLocked: locked } } } }
+ : { data: { mergeRequestSetLocked: { mergeRequest: { discussionLocked: locked } } } };
+ };
+
+ beforeEach(() => {
+ jest.spyOn(utils.gqClient, 'mutate').mockResolvedValue(mockResolvedValue());
+ });
+
+ it('calls gqClient mutation one time', () => {
+ actions.updateLockedAttribute({ commit: () => {}, state, getters }, actionArgs);
+
+ expect(utils.gqClient.mutate).toHaveBeenCalledTimes(1);
+ });
+
+ it('calls gqClient mutation with the correct values', () => {
+ actions.updateLockedAttribute({ commit: () => {}, state, getters }, actionArgs);
+
+ expect(utils.gqClient.mutate).toHaveBeenCalledWith({
+ mutation: targetMutation(),
+ variables: { input },
+ });
+ });
+
+ describe('on success of mutation', () => {
+ it('calls commit with the correct values', () => {
+ const commitSpy = jest.fn();
+
+ return actions
+ .updateLockedAttribute({ commit: commitSpy, state, getters }, actionArgs)
+ .then(() => {
+ expect(commitSpy).toHaveBeenCalledWith(mutationTypes.SET_ISSUABLE_LOCK, locked);
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index 0ad18ba9b6a..344ebf9bbd9 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -833,13 +833,27 @@ describe('Notes Store mutations', () => {
state = { noteableData: { confidential: false } };
});
- it('sets sort order', () => {
+ it('should set issuable as confidential', () => {
mutations.SET_ISSUE_CONFIDENTIAL(state, true);
expect(state.noteableData.confidential).toBe(true);
});
});
+ describe('SET_ISSUABLE_LOCK', () => {
+ let state;
+
+ beforeEach(() => {
+ state = { noteableData: { discussion_locked: false } };
+ });
+
+ it('should set issuable as locked', () => {
+ mutations.SET_ISSUABLE_LOCK(state, true);
+
+ expect(state.noteableData.discussion_locked).toBe(true);
+ });
+ });
+
describe('UPDATE_ASSIGNEES', () => {
it('should update assignees', () => {
const state = {
diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index 1dca65dd862..cf2e6b00800 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -10,7 +10,7 @@ exports[`Repository last commit component renders commit widget 1`] = `
imgcssclasses=""
imgsize="40"
imgsrc="https://test.com"
- linkhref="https://test.com/test"
+ linkhref="/test"
tooltipplacement="top"
tooltiptext=""
username=""
@@ -24,7 +24,7 @@ exports[`Repository last commit component renders commit widget 1`] = `
>
<gl-link-stub
class="commit-row-message item-title"
- href="https://test.com/commit/123"
+ href="/commit/123"
>
Commit title
</gl-link-stub>
@@ -36,7 +36,7 @@ exports[`Repository last commit component renders commit widget 1`] = `
>
<gl-link-stub
class="commit-author-link js-user-link"
- href="https://test.com/test"
+ href="/test"
>
Test
@@ -110,7 +110,7 @@ exports[`Repository last commit component renders the signature HTML as returned
imgcssclasses=""
imgsize="40"
imgsrc="https://test.com"
- linkhref="https://test.com/test"
+ linkhref="/test"
tooltipplacement="top"
tooltiptext=""
username=""
@@ -124,7 +124,7 @@ exports[`Repository last commit component renders the signature HTML as returned
>
<gl-link-stub
class="commit-row-message item-title"
- href="https://test.com/commit/123"
+ href="/commit/123"
>
Commit title
</gl-link-stub>
@@ -136,7 +136,7 @@ exports[`Repository last commit component renders the signature HTML as returned
>
<gl-link-stub
class="commit-author-link js-user-link"
- href="https://test.com/test"
+ href="/test"
>
Test
diff --git a/spec/frontend/repository/components/last_commit_spec.js b/spec/frontend/repository/components/last_commit_spec.js
index a5bfeb08fe4..c14a7f0e061 100644
--- a/spec/frontend/repository/components/last_commit_spec.js
+++ b/spec/frontend/repository/components/last_commit_spec.js
@@ -11,12 +11,12 @@ function createCommitData(data = {}) {
title: 'Commit title',
titleHtml: 'Commit title',
message: 'Commit message',
- webUrl: 'https://test.com/commit/123',
+ webPath: '/commit/123',
authoredDate: '2019-01-01',
author: {
name: 'Test',
avatarUrl: 'https://test.com',
- webUrl: 'https://test.com/test',
+ webPath: '/test',
},
pipeline: {
detailedStatus: {
@@ -108,7 +108,7 @@ describe('Repository last commit component', () => {
});
it('does not render description expander when description is null', () => {
- factory(createCommitData({ description: null }));
+ factory(createCommitData({ descriptionHtml: null }));
return vm.vm.$nextTick(() => {
expect(vm.find('.text-expander').exists()).toBe(false);
@@ -117,7 +117,7 @@ describe('Repository last commit component', () => {
});
it('expands commit description when clicking expander', () => {
- factory(createCommitData({ description: 'Test description' }));
+ factory(createCommitData({ descriptionHtml: 'Test description' }));
return vm.vm
.$nextTick()
diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js
index ed50f292b8c..10669330b61 100644
--- a/spec/frontend/repository/components/table/index_spec.js
+++ b/spec/frontend/repository/components/table/index_spec.js
@@ -13,7 +13,7 @@ const MOCK_BLOBS = [
flatPath: 'blob',
name: 'blob.md',
type: 'blob',
- webUrl: 'http://test.com',
+ webPath: '/blob',
},
{
id: '124abc',
diff --git a/spec/frontend/sidebar/lock/constants.js b/spec/frontend/sidebar/lock/constants.js
new file mode 100644
index 00000000000..b9f08e9286d
--- /dev/null
+++ b/spec/frontend/sidebar/lock/constants.js
@@ -0,0 +1,2 @@
+export const ISSUABLE_TYPE_ISSUE = 'issue';
+export const ISSUABLE_TYPE_MR = 'merge request';
diff --git a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js b/spec/frontend/sidebar/lock/edit_form_buttons_spec.js
index 66f9237ce97..df3cd16d3f7 100644
--- a/spec/frontend/sidebar/lock/edit_form_buttons_spec.js
+++ b/spec/frontend/sidebar/lock/edit_form_buttons_spec.js
@@ -1,31 +1,178 @@
import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
import EditFormButtons from '~/sidebar/components/lock/edit_form_buttons.vue';
+import eventHub from '~/sidebar/event_hub';
+import flash from '~/flash';
+import createStore from '~/notes/stores';
+import { createStore as createMrStore } from '~/mr_notes/stores';
+import { ISSUABLE_TYPE_ISSUE, ISSUABLE_TYPE_MR } from './constants';
+
+jest.mock('~/sidebar/event_hub', () => ({ $emit: jest.fn() }));
+jest.mock('~/flash');
describe('EditFormButtons', () => {
let wrapper;
+ let store;
+ let issuableType;
+ let issuableDisplayName;
+
+ const setIssuableType = pageType => {
+ issuableType = pageType;
+ issuableDisplayName = issuableType.replace(/_/g, ' ');
+ };
+
+ const findLockToggle = () => wrapper.find('[data-testid="lock-toggle"]');
+ const findGlLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const mountComponent = propsData => shallowMount(EditFormButtons, { propsData });
+ const createComponent = ({ props = {}, data = {}, resolved = true }) => {
+ store = issuableType === ISSUABLE_TYPE_ISSUE ? createStore() : createMrStore();
+
+ if (resolved) {
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ } else {
+ jest.spyOn(store, 'dispatch').mockRejectedValue();
+ }
+
+ wrapper = shallowMount(EditFormButtons, {
+ store,
+ provide: {
+ fullPath: '',
+ },
+ propsData: {
+ isLocked: false,
+ issuableDisplayName,
+ ...props,
+ },
+ data() {
+ return {
+ isLoading: false,
+ ...data,
+ };
+ },
+ });
+ };
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
- it('displays "Unlock" when locked', () => {
- wrapper = mountComponent({
- isLocked: true,
- updateLockedAttribute: () => {},
+ describe.each`
+ pageType
+ ${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR}
+ `('In $pageType page', ({ pageType }) => {
+ beforeEach(() => {
+ setIssuableType(pageType);
});
- expect(wrapper.text()).toContain('Unlock');
- });
+ describe('when isLoading', () => {
+ beforeEach(() => {
+ createComponent({ data: { isLoading: true } });
+ });
+
+ it('renders "Applying" in the toggle button', () => {
+ expect(findLockToggle().text()).toBe('Applying');
+ });
+
+ it('disables the toggle button', () => {
+ expect(findLockToggle().attributes('disabled')).toBe('disabled');
+ });
- it('displays "Lock" when unlocked', () => {
- wrapper = mountComponent({
- isLocked: false,
- updateLockedAttribute: () => {},
+ it('displays the GlLoadingIcon', () => {
+ expect(findGlLoadingIcon().exists()).toBe(true);
+ });
});
- expect(wrapper.text()).toContain('Lock');
+ describe.each`
+ isLocked | toggleText | statusText
+ ${false} | ${'Lock'} | ${'unlocked'}
+ ${true} | ${'Unlock'} | ${'locked'}
+ `('when $statusText', ({ isLocked, toggleText }) => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ isLocked,
+ },
+ });
+ });
+
+ it(`toggle button displays "${toggleText}"`, () => {
+ expect(findLockToggle().text()).toContain(toggleText);
+ });
+
+ describe('when toggled', () => {
+ describe(`when resolved`, () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ isLocked,
+ },
+ resolved: true,
+ });
+ findLockToggle().trigger('click');
+ });
+
+ it('dispatches the correct action', () => {
+ expect(store.dispatch).toHaveBeenCalledWith('updateLockedAttribute', {
+ locked: !isLocked,
+ fullPath: '',
+ });
+ });
+
+ it('resets loading', async () => {
+ await wrapper.vm.$nextTick().then(() => {
+ expect(findGlLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ it('emits close form', () => {
+ return wrapper.vm.$nextTick().then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('closeLockForm');
+ });
+ });
+
+ it('does not flash an error message', () => {
+ expect(flash).not.toHaveBeenCalled();
+ });
+ });
+
+ describe(`when not resolved`, () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ isLocked,
+ },
+ resolved: false,
+ });
+ findLockToggle().trigger('click');
+ });
+
+ it('dispatches the correct action', () => {
+ expect(store.dispatch).toHaveBeenCalledWith('updateLockedAttribute', {
+ locked: !isLocked,
+ fullPath: '',
+ });
+ });
+
+ it('resets loading', async () => {
+ await wrapper.vm.$nextTick().then(() => {
+ expect(findGlLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ it('emits close form', () => {
+ return wrapper.vm.$nextTick().then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('closeLockForm');
+ });
+ });
+
+ it('calls flash with the correct message', () => {
+ expect(flash).toHaveBeenCalledWith(
+ `Something went wrong trying to change the locked state of this ${issuableDisplayName}`,
+ );
+ });
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/sidebar/lock/edit_form_spec.js b/spec/frontend/sidebar/lock/edit_form_spec.js
index ec10a999a40..6a86ab14d90 100644
--- a/spec/frontend/sidebar/lock/edit_form_spec.js
+++ b/spec/frontend/sidebar/lock/edit_form_spec.js
@@ -1,37 +1,56 @@
-import Vue from 'vue';
-import editForm from '~/sidebar/components/lock/edit_form.vue';
+import { shallowMount } from '@vue/test-utils';
+import EditForm from '~/sidebar/components/lock/edit_form.vue';
+import { ISSUABLE_TYPE_ISSUE, ISSUABLE_TYPE_MR } from './constants';
-describe('EditForm', () => {
- let vm1;
- let vm2;
+describe('Edit Form Dropdown', () => {
+ let wrapper;
+ let issuableType; // Either ISSUABLE_TYPE_ISSUE or ISSUABLE_TYPE_MR
+ let issuableDisplayName;
- beforeEach(() => {
- const Component = Vue.extend(editForm);
- const toggleForm = () => {};
- const updateLockedAttribute = () => {};
+ const setIssuableType = pageType => {
+ issuableType = pageType;
+ issuableDisplayName = issuableType.replace(/_/g, ' ');
+ };
- vm1 = new Component({
- propsData: {
- isLocked: true,
- toggleForm,
- updateLockedAttribute,
- issuableType: 'issue',
- },
- }).$mount();
+ const findWarningText = () => wrapper.find('[data-testid="warning-text"]');
- vm2 = new Component({
+ const createComponent = ({ props }) => {
+ wrapper = shallowMount(EditForm, {
propsData: {
isLocked: false,
- toggleForm,
- updateLockedAttribute,
- issuableType: 'merge_request',
+ issuableDisplayName,
+ ...props,
},
- }).$mount();
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
});
- it('renders on the appropriate warning text', () => {
- expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
+ describe.each`
+ pageType
+ ${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR}
+ `('In $pageType page', ({ pageType }) => {
+ beforeEach(() => {
+ setIssuableType(pageType);
+ });
+
+ describe.each`
+ isLocked | lockStatusText | lockAction | warningText
+ ${false} | ${'unlocked'} | ${'Lock'} | ${'Only project members will be able to comment.'}
+ ${true} | ${'locked'} | ${'Unlock'} | ${'Everyone will be able to comment.'}
+ `('when $lockStatusText', ({ isLocked, lockAction, warningText }) => {
+ beforeEach(() => {
+ createComponent({ props: { isLocked } });
+ });
- expect(vm2.$el.innerHTML.includes('Lock this merge request?')).toBe(true);
+ it(`the appropriate warning text is rendered`, () => {
+ expect(findWarningText().text()).toContain(
+ `${lockAction} this ${issuableDisplayName}? ${warningText}`,
+ );
+ });
+ });
});
});
diff --git a/spec/frontend/sidebar/lock/lock_issue_sidebar_spec.js b/spec/frontend/sidebar/lock/lock_issue_sidebar_spec.js
index 00997326d87..811e5207256 100644
--- a/spec/frontend/sidebar/lock/lock_issue_sidebar_spec.js
+++ b/spec/frontend/sidebar/lock/lock_issue_sidebar_spec.js
@@ -1,99 +1,145 @@
-import Vue from 'vue';
+import { shallowMount } from '@vue/test-utils';
import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
-import lockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue';
+import LockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue';
+import EditForm from '~/sidebar/components/lock/edit_form.vue';
+import createStore from '~/notes/stores';
+import { createStore as createMrStore } from '~/mr_notes/stores';
+import { ISSUABLE_TYPE_ISSUE, ISSUABLE_TYPE_MR } from './constants';
describe('LockIssueSidebar', () => {
- let vm1;
- let vm2;
-
- beforeEach(() => {
- const Component = Vue.extend(lockIssueSidebar);
-
- const mediator = {
+ let wrapper;
+ let store;
+ let mediator;
+ let issuableType; // Either ISSUABLE_TYPE_ISSUE or ISSUABLE_TYPE_MR
+
+ const setIssuableType = pageType => {
+ issuableType = pageType;
+ };
+
+ const findSidebarCollapseIcon = () => wrapper.find('[data-testid="sidebar-collapse-icon"]');
+ const findLockStatus = () => wrapper.find('[data-testid="lock-status"]');
+ const findEditLink = () => wrapper.find('[data-testid="edit-link"]');
+ const findEditForm = () => wrapper.find(EditForm);
+
+ const initMediator = () => {
+ mediator = {
service: {
update: Promise.resolve(true),
},
-
- store: {
- isLockDialogOpen: false,
- },
+ store: {},
};
-
- vm1 = new Component({
+ };
+
+ const initStore = isLocked => {
+ if (issuableType === ISSUABLE_TYPE_ISSUE) {
+ store = createStore();
+ store.getters.getNoteableData.targetType = 'issue';
+ } else {
+ store = createMrStore();
+ }
+ store.getters.getNoteableData.discussion_locked = isLocked;
+ };
+
+ const createComponent = ({ props = {} }) => {
+ wrapper = shallowMount(LockIssueSidebar, {
+ store,
propsData: {
- isLocked: true,
isEditable: true,
mediator,
- issuableType: 'issue',
+ ...props,
},
- }).$mount();
-
- vm2 = new Component({
- propsData: {
- isLocked: false,
- isEditable: false,
- mediator,
- issuableType: 'merge_request',
- },
- }).$mount();
- });
-
- it('shows if locked and/or editable', () => {
- expect(vm1.$el.innerHTML.includes('Edit')).toBe(true);
-
- expect(vm1.$el.innerHTML.includes('Locked')).toBe(true);
-
- expect(vm2.$el.innerHTML.includes('Unlocked')).toBe(true);
- });
-
- it('displays the edit form when editable', done => {
- expect(vm1.isLockDialogOpen).toBe(false);
-
- vm1.$el.querySelector('.lock-edit').click();
-
- expect(vm1.isLockDialogOpen).toBe(true);
-
- vm1.$nextTick(() => {
- expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
-
- done();
});
- });
-
- it('tracks an event when "Edit" is clicked', () => {
- const spy = mockTracking('_category_', vm1.$el, jest.spyOn);
- triggerEvent('.lock-edit');
+ };
- expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', {
- label: 'right_sidebar',
- property: 'lock_issue',
- });
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
});
- it('displays the edit form when opened from collapsed state', done => {
- expect(vm1.isLockDialogOpen).toBe(false);
-
- vm1.$el.querySelector('.sidebar-collapsed-icon').click();
-
- expect(vm1.isLockDialogOpen).toBe(true);
-
- setImmediate(() => {
- expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true);
-
- done();
+ describe.each`
+ pageType
+ ${ISSUABLE_TYPE_ISSUE} | ${ISSUABLE_TYPE_MR}
+ `('In $pageType page', ({ pageType }) => {
+ beforeEach(() => {
+ setIssuableType(pageType);
+ initMediator();
});
- });
- it('does not display the edit form when opened from collapsed state if not editable', done => {
- expect(vm2.isLockDialogOpen).toBe(false);
-
- vm2.$el.querySelector('.sidebar-collapsed-icon').click();
-
- Vue.nextTick()
- .then(() => {
- expect(vm2.isLockDialogOpen).toBe(false);
- })
- .then(done)
- .catch(done.fail);
+ describe.each`
+ isLocked
+ ${false} | ${true}
+ `(`renders for isLocked = $isLocked`, ({ isLocked }) => {
+ beforeEach(() => {
+ initStore(isLocked);
+ createComponent({});
+ });
+
+ it('shows the lock status', () => {
+ expect(findLockStatus().text()).toBe(isLocked ? 'Locked' : 'Unlocked');
+ });
+
+ describe('edit form', () => {
+ let isEditable;
+ beforeEach(() => {
+ isEditable = false;
+ createComponent({ props: { isEditable } });
+ });
+
+ describe('when not editable', () => {
+ it('does not display the edit form when opened if not editable', () => {
+ expect(findEditForm().exists()).toBe(false);
+ findSidebarCollapseIcon().trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findEditForm().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('when editable', () => {
+ beforeEach(() => {
+ isEditable = true;
+ createComponent({ props: { isEditable } });
+ });
+
+ it('shows the editable status', () => {
+ expect(findEditLink().exists()).toBe(isEditable);
+ expect(findEditLink().text()).toBe('Edit');
+ });
+
+ describe("when 'Edit' is clicked", () => {
+ it('displays the edit form when editable', () => {
+ expect(findEditForm().exists()).toBe(false);
+ findEditLink().trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findEditForm().exists()).toBe(true);
+ });
+ });
+
+ it('tracks the event ', () => {
+ const spy = mockTracking('_category_', wrapper.element, jest.spyOn);
+ triggerEvent(findEditLink().element);
+
+ expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', {
+ label: 'right_sidebar',
+ property: 'lock_issue',
+ });
+ });
+ });
+
+ describe('When sidebar is collapsed', () => {
+ it('displays the edit form when opened', () => {
+ expect(findEditForm().exists()).toBe(false);
+ findSidebarCollapseIcon().trigger('click');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findEditForm().exists()).toBe(true);
+ });
+ });
+ });
+ });
+ });
+ });
});
});
diff --git a/spec/lib/gitlab/usage_data/topology_spec.rb b/spec/lib/gitlab/usage_data/topology_spec.rb
index 2a7adea261d..bf61ddd9d83 100644
--- a/spec/lib/gitlab/usage_data/topology_spec.rb
+++ b/spec/lib/gitlab/usage_data/topology_spec.rb
@@ -335,6 +335,40 @@ RSpec.describe Gitlab::UsageData::Topology do
end
end
+ context 'and unknown services are encountered' do
+ let(:unknown_service_process_count_response) do
+ [
+ {
+ 'metric' => { 'instance' => 'instance2:9000', 'job' => 'unknown-service-A' },
+ 'value' => [1000, '42']
+ },
+ {
+ 'metric' => { 'instance' => 'instance2:9001', 'job' => 'unknown-service-B' },
+ 'value' => [1000, '42']
+ }
+ ]
+ end
+
+ it 'filters out unknown service data and reports the unknown services as a failure' do
+ expect_prometheus_api_to(
+ receive_app_request_volume_query(result: []),
+ receive_node_memory_query(result: []),
+ receive_node_cpu_count_query(result: []),
+ receive_node_uname_info_query(result: []),
+ receive_node_service_memory_rss_query(result: []),
+ receive_node_service_memory_uss_query(result: []),
+ receive_node_service_memory_pss_query(result: []),
+ receive_node_service_process_count_query(result: unknown_service_process_count_response),
+ receive_node_service_app_server_workers_query(result: [])
+ )
+
+ expect(subject.dig(:topology, :failures)).to include(
+ { 'service_unknown' => 'unknown-service-A' },
+ { 'service_unknown' => 'unknown-service-B' }
+ )
+ end
+ end
+
context 'and an error is raised when querying Prometheus' do
it 'returns empty result with failures' do
expect_prometheus_api_to receive(:query)
@@ -534,11 +568,6 @@ RSpec.describe Gitlab::UsageData::Topology do
{
'metric' => { 'instance' => 'instance2:8080', 'job' => 'registry' },
'value' => [1000, '1']
- },
- # unknown service => should be stripped out
- {
- 'metric' => { 'instance' => 'instance2:9000', 'job' => 'not-a-gitlab-service' },
- 'value' => [1000, '42']
}
])
end
diff --git a/spec/policies/personal_access_token_policy_spec.rb b/spec/policies/personal_access_token_policy_spec.rb
new file mode 100644
index 00000000000..2236af81763
--- /dev/null
+++ b/spec/policies/personal_access_token_policy_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PersonalAccessTokenPolicy do
+ include AdminModeHelper
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_type, :owned_by_same_user, :expected_permitted?) do
+ :user | true | true
+ :user | false | false
+ :admin | false | true
+ end
+
+ with_them do
+ context 'determine if a token is readable by a user' do
+ let(:user) { build_stubbed(user_type) }
+ let(:token_owner) { owned_by_same_user ? user : build(:user) }
+ let(:token) { build(:personal_access_token, user: token_owner) }
+
+ subject { described_class.new(user, token) }
+
+ before do
+ enable_admin_mode!(user) if user.admin?
+ end
+
+ it { is_expected.to(expected_permitted? ? be_allowed(:read_token) : be_disallowed(:read_token)) }
+ end
+ end
+end
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index f026314f7a8..bbfb17fe753 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe API::ImportGithub do
before do
Grape::Endpoint.before_each do |endpoint|
- allow(endpoint).to receive(:client).and_return(double('client', user: provider_user, repo: provider_repo).as_null_object)
+ allow(endpoint).to receive(:client).and_return(double('client', user: provider_user, repository: provider_repo).as_null_object)
end
end
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index 266ff309662..713f8546d99 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Import::GithubService do
let_it_be(:user) { create(:user) }
let_it_be(:token) { 'complex-token' }
let_it_be(:access_params) { { github_access_token: 'github-complex-token' } }
- let_it_be(:client) { Gitlab::LegacyGithubImport::Client.new(token) }
+ let_it_be(:client) { Gitlab::GithubImport::Client.new(token) }
let_it_be(:params) { { repo_id: 123, new_name: 'new_repo', target_namespace: 'root' } }
let(:subject) { described_class.new(client, user, params) }
@@ -19,7 +19,7 @@ RSpec.describe Import::GithubService do
let(:exception) { Octokit::ClientError.new(status: 404, body: 'Not Found') }
before do
- expect(client).to receive(:repo).and_raise(exception)
+ expect(client).to receive(:repository).and_raise(exception)
end
it 'logs the original error' do
@@ -46,7 +46,7 @@ RSpec.describe Import::GithubService do
it 'raises an exception for unknown error causes' do
exception = StandardError.new('Not Implemented')
- expect(client).to receive(:repo).and_raise(exception)
+ expect(client).to receive(:repository).and_raise(exception)
expect(Gitlab::Import::Logger).not_to receive(:error)
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index a01fa49d701..312dcc4b03c 100644
--- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
@@ -111,8 +111,11 @@ RSpec.shared_examples 'a GitHub-ish import controller: GET status' do
end
it "handles an invalid access token" do
- allow_any_instance_of(Gitlab::LegacyGithubImport::Client)
- .to receive(:repos).and_raise(Octokit::Unauthorized)
+ allow_any_instance_of(Gitlab::LegacyGithubImport::Client).to receive(:repos).and_raise(Octokit::Unauthorized)
+
+ allow_next_instance_of(Octokit::Client) do |client|
+ allow(client).to receive(:repos).and_raise(Octokit::Unauthorized)
+ end
get :status
@@ -187,7 +190,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
end
before do
- stub_client(user: provider_user, repo: provider_repo)
+ stub_client(user: provider_user, repo: provider_repo, repository: provider_repo)
assign_session_token(provider)
end