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>2021-12-16 00:11:32 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-16 00:11:32 +0300
commit1c898dc5c10bbedf94386d917259153d73608495 (patch)
treef939cf185da9e96f7aba2200fa5ac74deffd71f9 /spec
parent22baaecaa84003c554f35752a729331e956d7659 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/root_controller_spec.rb24
-rw-r--r--spec/features/dashboard/root_spec.rb19
-rw-r--r--spec/frontend/ide/components/ide_tree_list_spec.js13
-rw-r--r--spec/frontend/ide/ide_router_spec.js61
-rw-r--r--spec/frontend/ide/services/index_spec.js63
-rw-r--r--spec/frontend/ide/stores/actions/project_spec.js93
-rw-r--r--spec/frontend/ide/stores/mutations/project_spec.js37
-rw-r--r--spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js108
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js2
-rw-r--r--spec/frontend_integration/ide/helpers/ide_helper.js14
-rw-r--r--spec/frontend_integration/ide/helpers/start.js4
-rw-r--r--spec/frontend_integration/ide/user_opens_ide_spec.js6
-rw-r--r--spec/helpers/ide_helper_spec.rb2
-rw-r--r--spec/helpers/users/callouts_helper_spec.rb30
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/models/user_spec.rb14
-rw-r--r--spec/services/events/destroy_service_spec.rb50
-rw-r--r--spec/services/projects/destroy_service_spec.rb21
-rw-r--r--spec/views/projects/hooks/edit.html.haml_spec.rb33
-rw-r--r--spec/views/projects/hooks/index.html.haml_spec.rb36
20 files changed, 369 insertions, 262 deletions
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index 38f8d267a2c..c6a8cee2f70 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -131,28 +131,10 @@ RSpec.describe RootController do
context 'who uses the default dashboard setting', :aggregate_failures do
render_views
- context 'with customize homepage banner' do
- it 'renders the default dashboard' do
- get :index
-
- expect(response).to render_template 'root/index'
- expect(response.body).to have_css('.js-customize-homepage-banner')
- end
- end
-
- context 'without customize homepage banner' do
- before do
- Users::DismissCalloutService.new(
- container: nil, current_user: user, params: { feature_name: Users::CalloutsHelper::CUSTOMIZE_HOMEPAGE }
- ).execute
- end
-
- it 'renders the default dashboard' do
- get :index
+ it 'renders the default dashboard' do
+ get :index
- expect(response).to render_template 'root/index'
- expect(response.body).not_to have_css('.js-customize-homepage-banner')
- end
+ expect(response).to render_template 'dashboard/projects/index'
end
end
end
diff --git a/spec/features/dashboard/root_spec.rb b/spec/features/dashboard/root_spec.rb
deleted file mode 100644
index 55bb43c6fcf..00000000000
--- a/spec/features/dashboard/root_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Root path' do
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
-
- before do
- project.add_developer(user)
- sign_in(user)
- end
-
- it 'shows the customize banner', :js do
- visit root_path
-
- expect(page).to have_content('Do you want to customize this page?')
- end
-end
diff --git a/spec/frontend/ide/components/ide_tree_list_spec.js b/spec/frontend/ide/components/ide_tree_list_spec.js
index 85d9feb0c09..ace51204374 100644
--- a/spec/frontend/ide/components/ide_tree_list_spec.js
+++ b/spec/frontend/ide/components/ide_tree_list_spec.js
@@ -38,9 +38,16 @@ describe('IDE tree list', () => {
beforeEach(() => {
bootstrapWithTree();
+ jest.spyOn(vm, '$emit').mockImplementation(() => {});
+
vm.$mount();
});
+ it('emits tree-ready event', () => {
+ expect(vm.$emit).toHaveBeenCalledTimes(1);
+ expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ });
+
it('renders loading indicator', (done) => {
store.state.trees['abcproject/main'].loading = true;
@@ -61,9 +68,15 @@ describe('IDE tree list', () => {
beforeEach(() => {
bootstrapWithTree(emptyBranchTree);
+ jest.spyOn(vm, '$emit').mockImplementation(() => {});
+
vm.$mount();
});
+ it('still emits tree-ready event', () => {
+ expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ });
+
it('does not load files if the branch is empty', () => {
expect(vm.$el.textContent).not.toContain('fileName');
expect(vm.$el.textContent).toContain('No files');
diff --git a/spec/frontend/ide/ide_router_spec.js b/spec/frontend/ide/ide_router_spec.js
index 3fb7781b176..cd10812f8ea 100644
--- a/spec/frontend/ide/ide_router_spec.js
+++ b/spec/frontend/ide/ide_router_spec.js
@@ -6,6 +6,7 @@ describe('IDE router', () => {
const PROJECT_NAMESPACE = 'my-group/sub-group';
const PROJECT_NAME = 'my-project';
const TEST_PATH = `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/2`;
+ const DEFAULT_BRANCH = 'default-main';
let store;
let router;
@@ -13,34 +14,46 @@ describe('IDE router', () => {
beforeEach(() => {
window.history.replaceState({}, '', '/');
store = createStore();
- router = createRouter(store);
+ router = createRouter(store, DEFAULT_BRANCH);
jest.spyOn(store, 'dispatch').mockReturnValue(new Promise(() => {}));
});
- [
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/tree/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/weird:branch/name-123/-/src/tree/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/edit`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/merge_requests/2`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/2`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
- ].forEach((route) => {
- it(`finds project path when route is "${route}"`, () => {
- router.push(route);
-
- expect(store.dispatch).toHaveBeenCalledWith('getProjectData', {
- namespace: PROJECT_NAMESPACE,
- projectId: PROJECT_NAME,
- });
+ it.each`
+ route | expectedBranchId | expectedBasePath
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob/`} | ${'main'} | ${'src/blob/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob`} | ${'main'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/tree/`} | ${'main'} | ${'src/tree/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/weird:branch/name-123/-/src/tree/`} | ${'weird:branch/name-123'} | ${'src/tree/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/blob`} | ${'main'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/edit`} | ${'main'} | ${'src/edit'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/merge_requests/2`} | ${'main'} | ${'src/merge_requests/2'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob`} | ${'blob'} | ${''}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit`} | ${DEFAULT_BRANCH} | ${''}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}`} | ${DEFAULT_BRANCH} | ${''}
+ `('correctly opens Web IDE for $route', ({ route, expectedBranchId, expectedBasePath } = {}) => {
+ router.push(route);
+
+ expect(store.dispatch).toHaveBeenCalledWith('openBranch', {
+ projectId: `${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
+ branchId: expectedBranchId,
+ basePath: expectedBasePath,
+ });
+ });
+
+ it('correctly opens an MR', () => {
+ const expectedId = '2';
+
+ router.push(`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/${expectedId}`);
+
+ expect(store.dispatch).toHaveBeenCalledWith('openMergeRequest', {
+ projectId: `${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
+ mergeRequestId: expectedId,
+ targetProjectId: undefined,
});
+ expect(store.dispatch).not.toHaveBeenCalledWith('openBranch');
});
it('keeps router in sync when store changes', async () => {
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index 3cf6240c2c5..0fab828dfb3 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -216,35 +216,6 @@ describe('IDE services', () => {
);
});
- describe('getProjectData', () => {
- it('combines gql and API requests', () => {
- const gqlProjectData = {
- id: 'gid://gitlab/Project/1',
- userPermissions: {
- bogus: true,
- },
- };
- const expectedResponse = {
- ...projectData,
- ...gqlProjectData,
- id: 1,
- };
- Api.project.mockReturnValue(Promise.resolve({ data: { ...projectData } }));
- query.mockReturnValue(Promise.resolve({ data: { project: gqlProjectData } }));
-
- return services.getProjectData(TEST_NAMESPACE, TEST_PROJECT).then((response) => {
- expect(response).toEqual({ data: expectedResponse });
- expect(Api.project).toHaveBeenCalledWith(TEST_PROJECT_ID);
- expect(query).toHaveBeenCalledWith({
- query: getIdeProject,
- variables: {
- projectPath: TEST_PROJECT_ID,
- },
- });
- });
- });
- });
-
describe('getFiles', () => {
let mock;
let relativeUrlRoot;
@@ -336,4 +307,38 @@ describe('IDE services', () => {
});
});
});
+
+ describe('getProjectPermissionsData', () => {
+ const TEST_PROJECT_PATH = 'foo/bar';
+
+ it('queries for the project permissions', () => {
+ const result = { data: { project: projectData } };
+ query.mockResolvedValue(result);
+
+ return services.getProjectPermissionsData(TEST_PROJECT_PATH).then((data) => {
+ expect(data).toEqual(result.data.project);
+ expect(query).toHaveBeenCalledWith(
+ expect.objectContaining({
+ query: getIdeProject,
+ variables: { projectPath: TEST_PROJECT_PATH },
+ }),
+ );
+ });
+ });
+
+ it('converts the returned GraphQL id to the regular ID number', () => {
+ const projectId = 2;
+ const gqlProjectData = {
+ id: `gid://gitlab/Project/${projectId}`,
+ userPermissions: {
+ bogus: true,
+ },
+ };
+
+ query.mockResolvedValue({ data: { project: gqlProjectData } });
+ return services.getProjectPermissionsData(TEST_PROJECT_PATH).then((data) => {
+ expect(data.id).toBe(projectId);
+ });
+ });
+ });
});
diff --git a/spec/frontend/ide/stores/actions/project_spec.js b/spec/frontend/ide/stores/actions/project_spec.js
index ca6f7169059..e07dcf22860 100644
--- a/spec/frontend/ide/stores/actions/project_spec.js
+++ b/spec/frontend/ide/stores/actions/project_spec.js
@@ -2,9 +2,12 @@ import MockAdapter from 'axios-mock-adapter';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import testAction from 'helpers/vuex_action_helper';
import api from '~/api';
+import createFlash from '~/flash';
import service from '~/ide/services';
import { createStore } from '~/ide/stores';
import {
+ setProject,
+ fetchProjectPermissions,
refreshLastCommitData,
showBranchNotFoundError,
createNewBranchFromDefault,
@@ -13,8 +16,12 @@ import {
loadFile,
loadBranch,
} from '~/ide/stores/actions';
+import { logError } from '~/lib/logger';
import axios from '~/lib/utils/axios_utils';
+jest.mock('~/flash');
+jest.mock('~/lib/logger');
+
const TEST_PROJECT_ID = 'abc/def';
describe('IDE store project actions', () => {
@@ -34,6 +41,92 @@ describe('IDE store project actions', () => {
mock.restore();
});
+ describe('setProject', () => {
+ const project = { id: 'foo', path_with_namespace: TEST_PROJECT_ID };
+ const baseMutations = [
+ {
+ type: 'SET_PROJECT',
+ payload: {
+ projectPath: TEST_PROJECT_ID,
+ project,
+ },
+ },
+ {
+ type: 'SET_CURRENT_PROJECT',
+ payload: TEST_PROJECT_ID,
+ },
+ ];
+
+ it.each`
+ desc | payload | expectedMutations
+ ${'does not commit any action if project is not passed'} | ${undefined} | ${[]}
+ ${'commits correct actions in the correct order by default'} | ${{ project }} | ${[...baseMutations]}
+ `('$desc', async ({ payload, expectedMutations } = {}) => {
+ await testAction({
+ action: setProject,
+ payload,
+ state: store.state,
+ expectedMutations,
+ expectedActions: [],
+ });
+ });
+ });
+
+ describe('fetchProjectPermissions', () => {
+ const permissionsData = {
+ userPermissions: {
+ bogus: true,
+ },
+ };
+ const permissionsMutations = [
+ {
+ type: 'UPDATE_PROJECT',
+ payload: {
+ projectPath: TEST_PROJECT_ID,
+ props: {
+ ...permissionsData,
+ },
+ },
+ },
+ ];
+
+ let spy;
+
+ beforeEach(() => {
+ spy = jest.spyOn(service, 'getProjectPermissionsData');
+ });
+
+ afterEach(() => {
+ createFlash.mockRestore();
+ });
+
+ it.each`
+ desc | projectPath | responseSuccess | expectedMutations
+ ${'does not fetch permissions if project does not exist'} | ${undefined} | ${true} | ${[]}
+ ${'fetches permission when project is specified'} | ${TEST_PROJECT_ID} | ${true} | ${[...permissionsMutations]}
+ ${'flashes an error if the request fails'} | ${TEST_PROJECT_ID} | ${false} | ${[]}
+ `('$desc', async ({ projectPath, expectedMutations, responseSuccess } = {}) => {
+ store.state.currentProjectId = projectPath;
+ if (responseSuccess) {
+ spy.mockResolvedValue(permissionsData);
+ } else {
+ spy.mockRejectedValue();
+ }
+
+ await testAction({
+ action: fetchProjectPermissions,
+ state: store.state,
+ expectedMutations,
+ expectedActions: [],
+ });
+
+ if (!responseSuccess) {
+ expect(logError).toHaveBeenCalled();
+ expect(createFlash).toHaveBeenCalled();
+ }
+ });
+ });
+
describe('refreshLastCommitData', () => {
beforeEach(() => {
store.state.currentProjectId = 'abc/def';
diff --git a/spec/frontend/ide/stores/mutations/project_spec.js b/spec/frontend/ide/stores/mutations/project_spec.js
index b3ce39c33d2..0fdd7798f00 100644
--- a/spec/frontend/ide/stores/mutations/project_spec.js
+++ b/spec/frontend/ide/stores/mutations/project_spec.js
@@ -3,21 +3,48 @@ import state from '~/ide/stores/state';
describe('Multi-file store branch mutations', () => {
let localState;
+ const nonExistentProj = 'nonexistent';
+ const existingProj = 'abcproject';
beforeEach(() => {
localState = state();
- localState.projects = { abcproject: { empty_repo: true } };
+ localState.projects = { [existingProj]: { empty_repo: true } };
});
describe('TOGGLE_EMPTY_STATE', () => {
it('sets empty_repo for project to passed value', () => {
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: false });
+ mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: false });
- expect(localState.projects.abcproject.empty_repo).toBe(false);
+ expect(localState.projects[existingProj].empty_repo).toBe(false);
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: true });
+ mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: true });
- expect(localState.projects.abcproject.empty_repo).toBe(true);
+ expect(localState.projects[existingProj].empty_repo).toBe(true);
+ });
+ });
+
+ describe('UPDATE_PROJECT', () => {
+ it.each`
+ desc | projectPath | props | expectedProps
+ ${'extends existing project with the passed props'} | ${existingProj} | ${{ foo1: 'bar' }} | ${{ foo1: 'bar' }}
+ ${'overrides existing props on the exsiting project'} | ${existingProj} | ${{ empty_repo: false }} | ${{ empty_repo: false }}
+ ${'does nothing if the project does not exist'} | ${nonExistentProj} | ${{ foo2: 'bar' }} | ${undefined}
+ ${'does nothing if project is not passed'} | ${undefined} | ${{ foo3: 'bar' }} | ${undefined}
+ ${'does nothing if the props are not passed'} | ${existingProj} | ${undefined} | ${{}}
+ ${'does nothing if the props are empty'} | ${existingProj} | ${{}} | ${{}}
+ `('$desc', ({ projectPath, props, expectedProps } = {}) => {
+ const origProject = localState.projects[projectPath];
+
+ mutations.UPDATE_PROJECT(localState, { projectPath, props });
+
+ if (!expectedProps) {
+ expect(localState.projects[projectPath]).toBeUndefined();
+ } else {
+ expect(localState.projects[projectPath]).toEqual({
+ ...origProject,
+ ...expectedProps,
+ });
+ }
});
});
});
diff --git a/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js b/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js
deleted file mode 100644
index f84800d8266..00000000000
--- a/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import { GlBanner } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
-import axios from '~/lib/utils/axios_utils';
-import CustomizeHomepageBanner from '~/pages/dashboard/projects/index/components/customize_homepage_banner.vue';
-
-const svgPath = '/illustrations/background';
-const provide = {
- svgPath,
- preferencesBehaviorPath: 'some/behavior/path',
- calloutsPath: 'call/out/path',
- calloutsFeatureId: 'some-feature-id',
- trackLabel: 'home_page',
-};
-
-const createComponent = () => {
- return shallowMount(CustomizeHomepageBanner, { provide, stubs: { GlBanner } });
-};
-
-describe('CustomizeHomepageBanner', () => {
- let trackingSpy;
- let mockAxios;
- let wrapper;
-
- beforeEach(() => {
- mockAxios = new MockAdapter(axios);
- document.body.dataset.page = 'some:page';
- trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
- wrapper = createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- mockAxios.restore();
- unmockTracking();
- });
-
- it('should render the banner when not dismissed', () => {
- expect(wrapper.find(GlBanner).exists()).toBe(true);
- });
-
- it('should close the banner when dismiss is clicked', async () => {
- mockAxios.onPost(provide.calloutsPath).replyOnce(200);
- expect(wrapper.find(GlBanner).exists()).toBe(true);
- wrapper.find(GlBanner).vm.$emit('close');
-
- await wrapper.vm.$nextTick();
- expect(wrapper.find(GlBanner).exists()).toBe(false);
- });
-
- it('includes the body text from options', () => {
- expect(wrapper.html()).toContain(wrapper.vm.$options.i18n.body);
- });
-
- describe('tracking', () => {
- const preferencesTrackingEvent = 'click_go_to_preferences';
- const mockTrackingOnWrapper = () => {
- unmockTracking();
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
- };
-
- it('sets the needed data attributes for tracking button', async () => {
- await wrapper.vm.$nextTick();
- const button = wrapper.find(`[href='${wrapper.vm.preferencesBehaviorPath}']`);
-
- expect(button.attributes('data-track-action')).toEqual(preferencesTrackingEvent);
- expect(button.attributes('data-track-label')).toEqual(provide.trackLabel);
- });
-
- it('sends a tracking event when the banner is shown', () => {
- const trackCategory = undefined;
- const trackEvent = 'show_home_page_banner';
-
- expect(trackingSpy).toHaveBeenCalledWith(trackCategory, trackEvent, {
- label: provide.trackLabel,
- });
- });
-
- it('sends a tracking event when the banner is dismissed', async () => {
- mockTrackingOnWrapper();
- mockAxios.onPost(provide.calloutsPath).replyOnce(200);
- const trackCategory = undefined;
- const trackEvent = 'click_dismiss';
-
- wrapper.find(GlBanner).vm.$emit('close');
-
- await wrapper.vm.$nextTick();
- expect(trackingSpy).toHaveBeenCalledWith(trackCategory, trackEvent, {
- label: provide.trackLabel,
- });
- });
-
- it('sends a tracking event when the button is clicked', async () => {
- mockTrackingOnWrapper();
- mockAxios.onPost(provide.calloutsPath).replyOnce(200);
- const button = wrapper.find(`[href='${wrapper.vm.preferencesBehaviorPath}']`);
-
- triggerEvent(button.element);
-
- await wrapper.vm.$nextTick();
- expect(trackingSpy).toHaveBeenCalledWith('_category_', preferencesTrackingEvent, {
- label: provide.trackLabel,
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
index 1faa3b0af1d..884bc4684ba 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/mock_data.js
@@ -75,7 +75,7 @@ export const mockSuggestedColors = {
'#013220': 'Dark green',
'#6699cc': 'Blue-gray',
'#0000ff': 'Blue',
- '#e6e6fa': 'Lavendar',
+ '#e6e6fa': 'Lavender',
'#9400d3': 'Dark violet',
'#330066': 'Deep violet',
'#808080': 'Gray',
diff --git a/spec/frontend_integration/ide/helpers/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js
index 56b2e298aa3..54a522324f5 100644
--- a/spec/frontend_integration/ide/helpers/ide_helper.js
+++ b/spec/frontend_integration/ide/helpers/ide_helper.js
@@ -192,6 +192,13 @@ export const commit = async ({ newBranch = false, newMR = false, newBranchName =
switchLeftSidebarTab('Commit');
screen.getByTestId('begin-commit-button').click();
+ await waitForMonacoEditor();
+
+ const mrCheck = await screen.findByLabelText('Start a new merge request');
+ if (Boolean(mrCheck.checked) !== newMR) {
+ mrCheck.click();
+ }
+
if (!newBranch) {
const option = await screen.findByLabelText(/Commit to .+ branch/);
option.click();
@@ -201,12 +208,9 @@ export const commit = async ({ newBranch = false, newMR = false, newBranchName =
const branchNameInput = await screen.findByTestId('ide-new-branch-name');
fireEvent.input(branchNameInput, { target: { value: newBranchName } });
-
- const mrCheck = await screen.findByLabelText('Start a new merge request');
- if (Boolean(mrCheck.checked) !== newMR) {
- mrCheck.click();
- }
}
screen.getByText('Commit').click();
+
+ await waitForMonacoEditor();
};
diff --git a/spec/frontend_integration/ide/helpers/start.js b/spec/frontend_integration/ide/helpers/start.js
index 4451c1ee946..3c5ed9dfe20 100644
--- a/spec/frontend_integration/ide/helpers/start.js
+++ b/spec/frontend_integration/ide/helpers/start.js
@@ -4,16 +4,18 @@ import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { initIde } from '~/ide';
import extendStore from '~/ide/stores/extend';
+import { getProject, getEmptyProject } from 'jest/../frontend_integration/test_helpers/fixtures';
import { IDE_DATASET } from './mock_data';
export default (container, { isRepoEmpty = false, path = '', mrId = '' } = {}) => {
const projectName = isRepoEmpty ? 'lorem-ipsum-empty' : 'lorem-ipsum';
const pathSuffix = mrId ? `merge_requests/${mrId}` : `tree/master/-/${path}`;
+ const project = isRepoEmpty ? getEmptyProject() : getProject();
setWindowLocation(`${TEST_HOST}/-/ide/project/gitlab-test/${projectName}/${pathSuffix}`);
const el = document.createElement('div');
- Object.assign(el.dataset, IDE_DATASET);
+ Object.assign(el.dataset, IDE_DATASET, { project: JSON.stringify(project) });
container.appendChild(el);
const vm = initIde(el, { extendStore });
diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js
index f56cd008d1c..c9d78d1de8f 100644
--- a/spec/frontend_integration/ide/user_opens_ide_spec.js
+++ b/spec/frontend_integration/ide/user_opens_ide_spec.js
@@ -34,10 +34,10 @@ describe('IDE: User opens IDE', () => {
expect(await screen.findByText('No files')).toBeDefined();
});
- it('shows a "New file" button', async () => {
- const button = await screen.findByTitle('New file');
+ it('shows a "New file" button', () => {
+ const buttons = screen.queryAllByTitle('New file');
- expect(button.tagName).toEqual('BUTTON');
+ expect(buttons.map((x) => x.tagName)).toContain('BUTTON');
});
});
diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb
index a06c9ec6699..dc0a234f981 100644
--- a/spec/helpers/ide_helper_spec.rb
+++ b/spec/helpers/ide_helper_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe IdeHelper do
self.instance_variable_set(:@fork_info, fork_info)
self.instance_variable_set(:@project, project)
- serialized_project = API::Entities::Project.represent(project).to_json
+ serialized_project = API::Entities::Project.represent(project, current_user: project.creator).to_json
expect(helper.ide_data)
.to include(
diff --git a/spec/helpers/users/callouts_helper_spec.rb b/spec/helpers/users/callouts_helper_spec.rb
index ba4d8797a24..85e11c2ed3b 100644
--- a/spec/helpers/users/callouts_helper_spec.rb
+++ b/spec/helpers/users/callouts_helper_spec.rb
@@ -61,36 +61,6 @@ RSpec.describe Users::CalloutsHelper do
end
end
- describe '.show_customize_homepage_banner?' do
- subject { helper.show_customize_homepage_banner? }
-
- context 'when user has not dismissed' do
- before do
- allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { false }
- end
-
- context 'when user is on the default dashboard' do
- it { is_expected.to be true }
- end
-
- context 'when user is not on the default dashboard' do
- before do
- user.dashboard = 'stars'
- end
-
- it { is_expected.to be false }
- end
- end
-
- context 'when user dismissed' do
- before do
- allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { true }
- end
-
- it { is_expected.to be false }
- end
- end
-
describe '.render_flash_user_callout' do
it 'renders the flash_user_callout partial' do
expect(helper).to receive(:render)
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 0fa3756ff3a..6ffe2187466 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -253,7 +253,6 @@ MergeRequestDiffFile:
- b_mode
- too_large
- binary
-- diff
MergeRequestContextCommit:
- id
- authored_date
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3f9c3bc6858..19e09c99a15 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -6138,20 +6138,6 @@ RSpec.describe User do
end
end
- describe '#default_dashboard?' do
- it 'is the default dashboard' do
- user = build(:user)
-
- expect(user.default_dashboard?).to be true
- end
-
- it 'is not the default dashboard' do
- user = build(:user, dashboard: 'stars')
-
- expect(user.default_dashboard?).to be false
- end
- end
-
describe '.dormant' do
it 'returns dormant users' do
freeze_time do
diff --git a/spec/services/events/destroy_service_spec.rb b/spec/services/events/destroy_service_spec.rb
new file mode 100644
index 00000000000..8dcbb83eb1d
--- /dev/null
+++ b/spec/services/events/destroy_service_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Events::DestroyService do
+ subject(:service) { described_class.new(project) }
+
+ let_it_be(:project, reload: true) { create(:project, :repository) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:user) { create(:user) }
+
+ let!(:unrelated_event) { create(:event, :merged, project: another_project, target: another_project, author: user) }
+
+ before do
+ create(:event, :created, project: project, target: project, author: user)
+ create(:event, :created, project: project, target: merge_request, author: user)
+ create(:event, :merged, project: project, target: merge_request, author: user)
+ end
+
+ let(:events) { project.events }
+
+ describe '#execute', :aggregate_failures do
+ it 'deletes the events' do
+ response = nil
+
+ expect { response = subject.execute }.to change(Event, :count).by(-3)
+
+ expect(response).to be_success
+ expect(unrelated_event.reload).to be_present
+ end
+
+ context 'when an error is raised while deleting the records' do
+ before do
+ allow(project).to receive_message_chain(:events, :all, :delete_all).and_raise(ActiveRecord::ActiveRecordError)
+ end
+
+ it 'returns error' do
+ response = subject.execute
+
+ expect(response).to be_error
+ expect(response.message).to eq 'Failed to remove events.'
+ end
+
+ it 'does not delete events' do
+ expect { subject.execute }.not_to change(Event, :count)
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index ac84614121a..18bcfe3f3b4 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -545,6 +545,27 @@ RSpec.describe Projects::DestroyService, :aggregate_failures do
end
end
+ context 'when project has events' do
+ let!(:event) { create(:event, :created, project: project, target: project, author: user) }
+
+ it 'deletes events from the project' do
+ expect do
+ destroy_project(project, user)
+ end.to change(Event, :count).by(-1)
+ end
+
+ context 'when an error is returned while deleting events' do
+ it 'does not delete project' do
+ allow_next_instance_of(Events::DestroyService) do |instance|
+ allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: 'foo'))
+ end
+
+ expect(destroy_project(project, user)).to be_falsey
+ expect(project.delete_error).to include('Failed to remove events')
+ end
+ end
+ end
+
context 'error while destroying', :sidekiq_inline do
let!(:pipeline) { create(:ci_pipeline, project: project) }
let!(:builds) { create_list(:ci_build, 2, :artifacts, pipeline: pipeline) }
diff --git a/spec/views/projects/hooks/edit.html.haml_spec.rb b/spec/views/projects/hooks/edit.html.haml_spec.rb
new file mode 100644
index 00000000000..1265334a572
--- /dev/null
+++ b/spec/views/projects/hooks/edit.html.haml_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'projects/hooks/edit' do
+ let(:hook) { create(:project_hook, project: project) }
+
+ let_it_be_with_refind(:project) { create(:project) }
+
+ before do
+ assign :project, project
+ assign :hook, hook
+ end
+
+ it 'renders webhook page with "Recent events"' do
+ render
+
+ expect(rendered).to have_css('h4', text: _('Webhook'))
+ expect(rendered).to have_text(_('Recent events'))
+ end
+
+ context 'webhook is rate limited' do
+ before do
+ allow(hook).to receive(:rate_limited?).and_return(true)
+ end
+
+ it 'renders alert' do
+ render
+
+ expect(rendered).to have_text(s_('Webhooks|Webhook was automatically disabled'))
+ end
+ end
+end
diff --git a/spec/views/projects/hooks/index.html.haml_spec.rb b/spec/views/projects/hooks/index.html.haml_spec.rb
new file mode 100644
index 00000000000..eb2b7334b98
--- /dev/null
+++ b/spec/views/projects/hooks/index.html.haml_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'projects/hooks/index' do
+ let(:existing_hook) { create(:project_hook, project: project) }
+ let(:new_hook) { ProjectHook.new }
+
+ let_it_be_with_refind(:project) { create(:project) }
+
+ before do
+ assign :project, project
+ assign :hooks, [existing_hook]
+ assign :hook, new_hook
+ end
+
+ it 'renders webhooks page with "Project Hooks"' do
+ render
+
+ expect(rendered).to have_css('h4', text: _('Webhooks'))
+ expect(rendered).to have_text('Project Hooks')
+ expect(rendered).not_to have_css('.gl-badge', text: _('Disabled'))
+ end
+
+ context 'webhook is rate limited' do
+ before do
+ allow(existing_hook).to receive(:rate_limited?).and_return(true)
+ end
+
+ it 'renders "Disabled" badge' do
+ render
+
+ expect(rendered).to have_css('.gl-badge', text: _('Disabled'))
+ end
+ end
+end