diff options
Diffstat (limited to 'spec')
42 files changed, 552 insertions, 313 deletions
diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb index 542eddc2d16..d800ad7c187 100644 --- a/spec/controllers/health_controller_spec.rb +++ b/spec/controllers/health_controller_spec.rb @@ -69,8 +69,7 @@ describe HealthController do expect(json_response['cache_check']['status']).to eq('ok') expect(json_response['queues_check']['status']).to eq('ok') expect(json_response['shared_state_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['labels']['shard']).to eq('default') + expect(json_response['gitaly_check']['status']).to eq('ok') end end @@ -122,7 +121,6 @@ describe HealthController do expect(json_response['cache_check']['status']).to eq('ok') expect(json_response['queues_check']['status']).to eq('ok') expect(json_response['shared_state_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['status']).to eq('ok') end end diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 9e8a37171ec..7376841fac8 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -59,6 +59,13 @@ describe MetricsController do expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/) end + it 'returns Gitaly metrics' do + get :index + + expect(response.body).to match(/^gitaly_health_check_success{shard="default"} 1$/) + expect(response.body).to match(/^gitaly_health_check_latency_seconds{shard="default"} [0-9\.]+$/) + end + context 'prometheus metrics are disabled' do before do allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false) diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index e7aca94db66..f3ab4ff771a 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -124,6 +124,29 @@ feature 'Admin updates settings' do expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2') end + scenario 'Oauth providers do not raise validation errors when saving unrelated changes' do + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty + + page.within('.as-signin') do + uncheck 'Google' + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2') + + # Remove google_oauth2 from the Omniauth strategies + allow(Devise).to receive(:omniauth_providers).and_return([]) + + # Save an unrelated setting + page.within('.as-ci-cd') do + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2') + end + scenario 'Change Help page' do page.within('.as-help-page') do fill_in 'Help page text', with: 'Example text' diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 4c0f9971425..39bd4af6cd0 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -60,33 +60,6 @@ feature 'Protected Branches', :js do expect(page).to have_content('No branches to show') end end - - describe "Saved defaults" do - it "keeps the allowed to merge and push dropdowns defaults based on the previous selection" do - visit project_protected_branches_path(project) - form = '.js-new-protected-branch' - - within form do - find(".js-allowed-to-merge").click - wait_for_requests - click_link 'No one' - find(".js-allowed-to-push").click - wait_for_requests - click_link 'Developers + Maintainers' - end - - visit project_protected_branches_path(project) - - within form do - page.within(".js-allowed-to-merge") do - expect(page.find(".dropdown-toggle-text")).to have_content("No one") - end - page.within(".js-allowed-to-push") do - expect(page.find(".dropdown-toggle-text")).to have_content("Developers + Maintainers") - end - end - end - end end context 'logged in as admin' do @@ -97,6 +70,7 @@ feature 'Protected Branches', :js do describe "explicit protected branches" do it "allows creating explicit protected branches" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -110,6 +84,7 @@ feature 'Protected Branches', :js do project.repository.add_branch(admin, 'some-branch', commit.id) visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -118,6 +93,7 @@ feature 'Protected Branches', :js do it "displays an error message if the named branch does not exist" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -128,6 +104,7 @@ feature 'Protected Branches', :js do describe "wildcard protected branches" do it "allows creating protected branches with a wildcard" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -141,6 +118,7 @@ feature 'Protected Branches', :js do project.repository.add_branch(admin, 'staging-stable', 'master') visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -157,6 +135,7 @@ feature 'Protected Branches', :js do visit project_protected_branches_path(project) set_protected_branch_name('*-stable') + set_defaults click_on "Protect" visit project_protected_branches_path(project) @@ -180,4 +159,18 @@ feature 'Protected Branches', :js do find(".dropdown-input-field").set(branch_name) click_on("Create wildcard #{branch_name}") end + + def set_defaults + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 593b2ca1825..14297a1a544 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -157,7 +157,7 @@ describe ApplicationHelper do let(:noteable_type) { Issue } it 'returns paths for autocomplete_sources_controller' do sources = helper.autocomplete_data_sources(project, noteable_type) - expect(sources.keys).to match_array([:members, :issues, :merge_requests, :labels, :milestones, :commands]) + expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands]) sources.keys.each do |key| expect(sources[key]).not_to be_nil end diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js index efbe09a10a2..c2db81c6ce4 100644 --- a/spec/javascripts/behaviors/copy_as_gfm_spec.js +++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js @@ -44,4 +44,59 @@ describe('CopyAsGFM', () => { callPasteGFM(); }); }); + + describe('CopyAsGFM.copyGFM', () => { + // Stub getSelection to return a purpose-built object. + const stubSelection = (html, parentNode) => ({ + getRangeAt: () => ({ + commonAncestorContainer: { tagName: parentNode }, + cloneContents: () => { + const fragment = document.createDocumentFragment(); + const node = document.createElement('div'); + node.innerHTML = html; + Array.from(node.childNodes).forEach((item) => fragment.appendChild(item)); + return fragment; + }, + }), + rangeCount: 1, + }); + + const clipboardData = { + setData() {}, + }; + + const simulateCopy = () => { + const e = { + originalEvent: { + clipboardData, + }, + preventDefault() {}, + stopPropagation() {}, + }; + CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); + return clipboardData; + }; + + beforeEach(() => spyOn(clipboardData, 'setData')); + + describe('list handling', () => { + it('uses correct gfm for unordered lists', () => { + const selection = stubSelection('<li>List Item1</li><li>List Item2</li>\n', 'UL'); + spyOn(window, 'getSelection').and.returnValue(selection); + simulateCopy(); + + const expectedGFM = '- List Item1\n- List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); + }); + + it('uses correct gfm for ordered lists', () => { + const selection = stubSelection('<li>List Item1</li><li>List Item2</li>\n', 'OL'); + spyOn(window, 'getSelection').and.returnValue(selection); + simulateCopy(); + + const expectedGFM = '1. List Item1\n1. List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); + }); + }); + }); }); diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js index d1ebae33dab..7651792be2e 100644 --- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js +++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js @@ -26,7 +26,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(10, 10, 10), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).not.toBeGreaterThan(4); }); @@ -35,7 +35,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(1, 1, 1), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).toBeLessThan(1); }); diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index 819ed7896ca..a18e09da50a 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -16,7 +16,7 @@ describe('Pipelines table in Commits and Merge requests', function () { beforeEach(() => { mock = new MockAdapter(axios); - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTable = Vue.extend(pipelinesTable); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js index 4279add21d1..d1de9d132b8 100644 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ b/spec/javascripts/deploy_keys/components/key_spec.js @@ -88,7 +88,7 @@ describe('Deploy keys key', () => { }); it('expands all project labels after click', done => { - const length = vm.deployKey.deploy_keys_projects.length; + const { length } = vm.deployKey.deploy_keys_projects; vm.$el.querySelectorAll('.deploy-project-label')[1].click(); Vue.nextTick(() => { diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js index 7237274eb43..dea600a783a 100644 --- a/spec/javascripts/diffs/components/diff_content_spec.js +++ b/spec/javascripts/diffs/components/diff_content_spec.js @@ -1 +1,95 @@ -// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 +import Vue from 'vue'; +import DiffContentComponent from '~/diffs/components/diff_content.vue'; +import store from '~/mr_notes/stores'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; +import diffFileMockData from '../mock_data/diff_file'; + +describe('DiffContent', () => { + const Component = Vue.extend(DiffContentComponent); + let vm; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + + beforeEach(() => { + vm = mountComponentWithStore(Component, { + store, + props: { + diffFile: getDiffFileMock(), + }, + }); + }); + + describe('text based files', () => { + it('should render diff inline view', done => { + vm.$store.state.diffs.diffViewType = 'inline'; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(1); + + done(); + }); + }); + + it('should render diff parallel view', done => { + vm.$store.state.diffs.diffViewType = 'parallel'; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.parallel').length).toEqual(18); + + done(); + }); + }); + }); + + describe('Non-Text diffs', () => { + beforeEach(() => { + vm.diffFile.text = false; + }); + + describe('image diff', () => { + beforeEach(() => { + vm.diffFile.newPath = GREEN_BOX_IMAGE_URL; + vm.diffFile.newSha = 'DEF'; + vm.diffFile.oldPath = RED_BOX_IMAGE_URL; + vm.diffFile.oldSha = 'ABC'; + vm.diffFile.viewPath = ''; + }); + + it('should have image diff view in place', done => { + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0); + + expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1); + + done(); + }); + }); + }); + + describe('file diff', () => { + it('should have download buttons in place', done => { + const el = vm.$el; + vm.diffFile.newPath = 'test.abc'; + vm.diffFile.newSha = 'DEF'; + vm.diffFile.oldPath = 'test.abc'; + vm.diffFile.oldSha = 'ABC'; + + vm.$nextTick(() => { + expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0); + + expect(el.querySelector('.deleted .file-info').textContent.trim()).toContain('test.abc'); + expect(el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + expect(el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc'); + expect(el.querySelector('.added .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + done(); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index 312a684f4d2..cce10c4083c 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -92,7 +92,7 @@ describe('DiffLineGutterContent', () => { }); it('should return discussions for the given lineCode', () => { - const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode; + const { lineCode } = getDiffFileMock().highlightedDiffLines[1]; const component = createComponent({ lineCode, showCommentButton: true }); setDiscussions(component); diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js index 724d1948214..81cd4f9769a 100644 --- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js +++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js @@ -19,7 +19,15 @@ describe('DiffLineNoteForm', () => { diffLines, line: diffLines[0], noteTargetLine: diffLines[0], - }).$mount(); + }); + + Object.defineProperty(component, 'isLoggedIn', { + get() { + return true; + }, + }); + + component.$mount(); }); describe('methods', () => { @@ -56,6 +64,15 @@ describe('DiffLineNoteForm', () => { }); }); + describe('mounted', () => { + it('should init autosave', () => { + const key = 'autosave/Note/issue///DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1'; + + expect(component.autosave).toBeDefined(); + expect(component.autosave.key).toEqual(key); + }); + }); + describe('template', () => { it('should have note form', () => { const { $el } = component; diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index e61780c9928..f0bd098f698 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -12,15 +12,16 @@ import axios from '~/lib/utils/axios_utils'; import testAction from '../../helpers/vuex_action_helper'; describe('DiffsStoreActions', () => { - describe('setEndpoint', () => { - it('should set given endpoint', done => { + describe('setBaseConfig', () => { + it('should set given endpoint and project path', done => { const endpoint = '/diffs/set/endpoint'; + const projectPath = '/root/project'; testAction( - actions.setEndpoint, - endpoint, - { endpoint: '' }, - [{ type: types.SET_ENDPOINT, payload: endpoint }], + actions.setBaseConfig, + { endpoint, projectPath }, + { endpoint: '', projectPath: '' }, + [{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }], [], done, ); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index 5f1a6e9def7..02836fcaeea 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -3,13 +3,15 @@ import * as types from '~/diffs/store/mutation_types'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; describe('DiffsStoreMutations', () => { - describe('SET_ENDPOINT', () => { - it('should set endpoint', () => { + describe('SET_BASE_CONFIG', () => { + it('should set endpoint and project path', () => { const state = {}; const endpoint = '/diffs/endpoint'; + const projectPath = '/root/project'; - mutations[types.SET_ENDPOINT](state, endpoint); + mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath }); expect(state.endpoint).toEqual(endpoint); + expect(state.projectPath).toEqual(projectPath); }); }); diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 59bd2650081..d926663fac0 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -103,7 +103,7 @@ describe('RecentSearchesDropdownContent', () => { describe('processedItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(2); @@ -122,7 +122,7 @@ describe('RecentSearchesDropdownContent', () => { it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(0); }); @@ -131,13 +131,13 @@ describe('RecentSearchesDropdownContent', () => { describe('hasItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(true); }); it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(false); }); }); diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js index 1e6272bad0b..d063fcf4f2d 100644 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js @@ -15,8 +15,7 @@ describe('RecentSearchesRoot', () => { }; VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => { - data = options.data; - template = options.template; + ({ data, template } = options); }); RecentSearchesRoot.prototype.render.call(recentSearchesRoot); diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 2839020b2ca..21c462cd040 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -18,7 +18,7 @@ describe('GL Style Field Errors', function() { expect(this.$form).toBeDefined(); expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); - const inputs = this.fieldErrors.state.inputs; + const { inputs } = this.fieldErrors.state; expect(inputs.length).toBe(4); }); diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js index 2b92c485f41..03d4b472b87 100644 --- a/spec/javascripts/groups/components/app_spec.js +++ b/spec/javascripts/groups/components/app_spec.js @@ -67,7 +67,7 @@ describe('AppComponent', () => { it('should return list of groups from store', () => { spyOn(vm.store, 'getGroups'); - const groups = vm.groups; + const { groups } = vm; expect(vm.store.getGroups).toHaveBeenCalled(); expect(groups).not.toBeDefined(); }); @@ -77,7 +77,7 @@ describe('AppComponent', () => { it('should return pagination info from store', () => { spyOn(vm.store, 'getPaginationInfo'); - const pageInfo = vm.pageInfo; + const { pageInfo } = vm; expect(vm.store.getPaginationInfo).toHaveBeenCalled(); expect(pageInfo).not.toBeDefined(); }); @@ -293,7 +293,7 @@ describe('AppComponent', () => { beforeEach(() => { groupItem = Object.assign({}, mockParentGroupItem); groupItem.children = mockChildren; - childGroupItem = groupItem.children[0]; + [childGroupItem] = groupItem.children; groupItem.isChildrenLoading = false; vm.targetGroup = childGroupItem; vm.targetParentGroup = groupItem; diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js index 49a139855c8..d0cac5efc40 100644 --- a/spec/javascripts/groups/components/group_item_spec.js +++ b/spec/javascripts/groups/components/group_item_spec.js @@ -41,7 +41,7 @@ describe('GroupItemComponent', () => { describe('rowClass', () => { it('should return map of classes based on group details', () => { const classes = ['is-open', 'has-children', 'has-description', 'being-removed']; - const rowClass = vm.rowClass; + const { rowClass } = vm; expect(Object.keys(rowClass).length).toBe(classes.length); Object.keys(rowClass).forEach((className) => { diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js index 921d42a0871..05c6d587e9c 100644 --- a/spec/javascripts/helpers/init_vue_mr_page_helper.js +++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js @@ -6,6 +6,7 @@ import diffFileMockData from '../diffs/mock_data/diff_file'; export default function initVueMRPage() { const diffsAppEndpoint = '/diffs/app/endpoint'; + const diffsAppProjectPath = 'testproject'; const mrEl = document.createElement('div'); mrEl.className = 'merge-request fixture-mr'; mrEl.setAttribute('data-mr-action', 'diffs'); @@ -26,6 +27,7 @@ export default function initVueMRPage() { const diffsAppEl = document.createElement('div'); diffsAppEl.id = 'js-diffs-app'; diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); + diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath); diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); document.body.appendChild(diffsAppEl); diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index 9312e17704e..569fa5c7aae 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -1,3 +1,4 @@ +import * as pathUtils from 'path'; import { decorateData } from '~/ide/stores/utils'; import state from '~/ide/stores/state'; import commitState from '~/ide/stores/modules/commit/state'; @@ -14,13 +15,34 @@ export const resetStore = store => { store.replaceState(newState); }; -export const file = (name = 'name', id = name, type = '') => +export const file = (name = 'name', id = name, type = '', parent = null) => decorateData({ id, type, icon: 'icon', url: 'url', name, - path: name, + path: parent ? `${parent.path}/${name}` : name, + parentPath: parent ? parent.path : '', lastCommit: {}, }); + +export const createEntriesFromPaths = paths => + paths + .map(path => ({ + name: pathUtils.basename(path), + dir: pathUtils.dirname(path), + ext: pathUtils.extname(path), + })) + .reduce((entries, path, idx) => { + const { name } = path; + const parent = path.dir ? entries[path.dir] : null; + const type = path.ext ? 'blob' : 'tree'; + + const entry = file(name, (idx + 1).toString(), type, parent); + + return { + [entry.path]: entry, + ...entries, + }; + }, {}); diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index 96abd1dcd9e..90ebb95b687 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -63,7 +63,7 @@ describe('Multi-file editor library dirty diff controller', () => { [type]: true, }; - const range = getDecorator(change).range; + const { range } = getDecorator(change); expect(range.startLineNumber).toBe(1); expect(range.endLineNumber).toBe(2); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index e0ef57a3966..cefed9ddb43 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { showTreeEntry } from '~/ide/stores/actions/tree'; +import * as types from '~/ide/stores/mutation_types'; import store from '~/ide/stores'; import service from '~/ide/services'; import router from '~/ide/ide_router'; -import { file, resetStore } from '../../helpers'; +import { file, resetStore, createEntriesFromPaths } from '../../helpers'; describe('Multi-file store tree actions', () => { let projectTree; @@ -96,6 +99,37 @@ describe('Multi-file store tree actions', () => { }); }); + describe('showTreeEntry', () => { + beforeEach(() => { + const paths = [ + 'grandparent', + 'ancestor', + 'grandparent/parent', + 'grandparent/aunt', + 'grandparent/parent/child.txt', + 'grandparent/aunt/cousing.txt', + ]; + + Object.assign(store.state.entries, createEntriesFromPaths(paths)); + }); + + it('opens the parents', done => { + testAction( + showTreeEntry, + 'grandparent/parent/child.txt', + store.state, + [ + { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }, + { type: types.SET_TREE_OPEN, payload: 'grandparent' }, + ], + [ + { type: 'showTreeEntry' }, + ], + done, + ); + }); + }); + describe('getLastCommitData', () => { beforeEach(() => { spyOn(service, 'getTreeLastCommit').and.returnValue( diff --git a/spec/javascripts/namespace_select_spec.js b/spec/javascripts/namespace_select_spec.js index 3b2641f7646..07b82ce721e 100644 --- a/spec/javascripts/namespace_select_spec.js +++ b/spec/javascripts/namespace_select_spec.js @@ -22,7 +22,7 @@ describe('NamespaceSelect', () => { const dropdown = document.createElement('div'); // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('prevents click events', () => { @@ -43,7 +43,7 @@ describe('NamespaceSelect', () => { dropdown.dataset.isFilter = 'true'; // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('does not prevent click events', () => { diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js index 8f8ba231ae8..0b1b11de1fd 100644 --- a/spec/javascripts/notebook/cells/markdown_spec.js +++ b/spec/javascripts/notebook/cells/markdown_spec.js @@ -14,6 +14,7 @@ describe('Markdown component', () => { beforeEach((done) => { json = getJSONFixture('blob/notebook/basic.json'); + // eslint-disable-next-line prefer-destructuring cell = json.cells[1]; vm = new Component({ diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 78d8e9e572e..03ffc122795 100644 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -24,7 +24,7 @@ describe('Pipelines Table Row', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null); diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js index 4fc3c08145e..d21ba35e96d 100644 --- a/spec/javascripts/pipelines/pipelines_table_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_spec.js @@ -11,7 +11,7 @@ describe('Pipelines Table', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTableComponent = Vue.extend(pipelinesTableComp); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index a54219d58c2..60153672214 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -87,7 +87,7 @@ describe('SmartInterval', function () { setTimeout(() => { interval.cancel(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; const currentInterval = interval.getCurrentInterval(); const intervalLowerLimit = interval.cfg.startingInterval; @@ -106,7 +106,7 @@ describe('SmartInterval', function () { interval.resume(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; expect(intervalId).toBeTruthy(); diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index f7581251bf0..1c666fc6c55 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -74,7 +74,7 @@ describe('File Icon component', () => { size: 120, }); - const classList = vm.$el.firstChild.classList; + const { classList } = vm.$el.firstChild; const containsSizeClass = classList.contains('s120'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js index 68d57ebc8f0..cc030e29d61 100644 --- a/spec/javascripts/vue_shared/components/icon_spec.js +++ b/spec/javascripts/vue_shared/components/icon_spec.js @@ -44,7 +44,7 @@ describe('Sprite Icon Component', function () { }); it('should properly render img css', function () { - const classList = icon.$el.classList; + const { classList } = icon.$el; const containsSizeClass = classList.contains('s32'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js index 446f025c127..656b57d764e 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js @@ -51,7 +51,7 @@ describe('User Avatar Image Component', function () { }); it('should properly render img css', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const containsAvatar = classList.contains('avatar'); const containsSizeClass = classList.contains('s99'); const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses); @@ -73,7 +73,7 @@ describe('User Avatar Image Component', function () { }); it('should add lazy attributes', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const lazyClass = classList.contains('lazy'); expect(lazyClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js index adf80d0c2bb..4c5c242cbb3 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -21,7 +21,7 @@ describe('User Avatar Link Component', function () { propsData: this.propsData, }).$mount(); - this.userAvatarImage = this.userAvatarLink.$children[0]; + [this.userAvatarImage] = this.userAvatarLink.$children; }); it('should return a defined Vue component', function () { diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb index ed5d56e91d4..09bf21b5946 100644 --- a/spec/lib/gitaly/server_spec.rb +++ b/spec/lib/gitaly/server_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitaly::Server do + let(:server) { described_class.new('default') } + describe '.all' do let(:storages) { Gitlab.config.repositories.storages } @@ -17,6 +19,38 @@ describe Gitaly::Server do it { is_expected.to respond_to(:up_to_date?) } it { is_expected.to respond_to(:address) } + describe 'readable?' do + context 'when the storage is readable' do + it 'returns true' do + expect(server).to be_readable + end + end + + context 'when the storage is not readable' do + let(:server) { described_class.new('broken') } + + it 'returns false' do + expect(server).not_to be_readable + end + end + end + + describe 'writeable?' do + context 'when the storage is writeable' do + it 'returns true' do + expect(server).to be_writeable + end + end + + context 'when the storage is not writeable' do + let(:server) { described_class.new('broken') } + + it 'returns false' do + expect(server).not_to be_writeable + end + end + end + describe 'request memoization' do context 'when requesting multiple properties', :request_store do it 'uses memoization for the info request' do diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 280f799f2ab..eb7148ff108 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -1178,6 +1178,61 @@ describe Gitlab::Database::MigrationHelpers do end end + describe '#rename_column_using_background_migration' do + let!(:issue) { create(:issue, :closed, closed_at: Time.zone.now) } + + it 'renames a column using a background migration' do + expect(model) + .to receive(:add_column) + .with( + 'issues', + :closed_at_timestamp, + :datetime_with_timezone, + limit: anything, + precision: anything, + scale: anything + ) + + expect(model) + .to receive(:install_rename_triggers) + .with('issues', :closed_at, :closed_at_timestamp) + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 10.minutes, + 'CopyColumn', + ['issues', :closed_at, :closed_at_timestamp, issue.id, issue.id] + ) + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 1.hour + 10.minutes, + 'CleanupConcurrentRename', + ['issues', :closed_at, :closed_at_timestamp] + ) + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CopyColumn') + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CleanupConcurrentRename') + + model.rename_column_using_background_migration( + 'issues', + :closed_at, + :closed_at_timestamp + ) + end + end + describe '#perform_background_migration_inline?' do it 'returns true in a test environment' do allow(Rails.env) diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb deleted file mode 100644 index 9dcf272d25e..00000000000 --- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb +++ /dev/null @@ -1,200 +0,0 @@ -require 'spec_helper' - -describe Gitlab::HealthChecks::FsShardsCheck do - def command_exists?(command) - _, status = Gitlab::Popen.popen(%W{ #{command} 1 echo }) - status.zero? - rescue Errno::ENOENT - false - end - - def timeout_command - @timeout_command ||= - if command_exists?('timeout') - 'timeout' - elsif command_exists?('gtimeout') - 'gtimeout' - else - '' - end - end - - let(:metric_class) { Gitlab::HealthChecks::Metric } - let(:result_class) { Gitlab::HealthChecks::Result } - let(:repository_storages) { ['default'] } - let(:tmp_dir) { Dir.mktmpdir } - - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => tmp_dir) - }.with_indifferent_access - end - - before do - allow(described_class).to receive(:repository_storages) { repository_storages } - allow(described_class).to receive(:storages_paths) { storages_paths } - stub_const('Gitlab::HealthChecks::FsShardsCheck::TIMEOUT_EXECUTABLE', timeout_command) - end - - after do - FileUtils.remove_entry_secure(tmp_dir) if Dir.exist?(tmp_dir) - end - - shared_examples 'filesystem checks' do - describe '#readiness' do - subject { described_class.readiness } - - context 'storage has a tripped circuitbreaker', :broken_storage do - let(:repository_storages) { ['broken'] } - let(:storages_paths) do - Gitlab.config.repositories.storages - end - - it { is_expected.to include(result_class.new(false, 'circuitbreaker tripped', shard: 'broken')) } - end - - context 'storage points to not existing folder' do - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist') - }.with_indifferent_access - end - - before do - allow(described_class).to receive(:storage_circuitbreaker_test) { true } - end - - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } - end - - context 'storage points to directory that has both read and write rights' do - before do - FileUtils.chmod_R(0755, tmp_dir) - end - - it { is_expected.to include(result_class.new(true, nil, shard: 'default')) } - - it 'cleans up files used for testing' do - expect(described_class).to receive(:storage_write_test).with(any_args).and_call_original - - expect { subject }.not_to change(Dir.entries(tmp_dir), :count) - end - - context 'read test fails' do - before do - allow(described_class).to receive(:storage_read_test).with(any_args).and_return(false) - end - - it { is_expected.to include(result_class.new(false, 'cannot read from storage', shard: 'default')) } - end - - context 'write test fails' do - before do - allow(described_class).to receive(:storage_write_test).with(any_args).and_return(false) - end - - it { is_expected.to include(result_class.new(false, 'cannot write to storage', shard: 'default')) } - end - end - end - - describe '#metrics' do - context 'storage points to not existing folder' do - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist') - }.with_indifferent_access - end - - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0)) - end - end - - context 'storage points to directory that has both read and write rights' do - before do - FileUtils.chmod_R(0755, tmp_dir) - end - - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0)) - end - - it 'cleans up files used for metrics' do - expect { described_class.metrics }.not_to change(Dir.entries(tmp_dir), :count) - end - end - end - end - - context 'when timeout kills fs checks' do - before do - stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '1') - - allow(described_class).to receive(:exec_with_timeout).and_wrap_original { |m| m.call(%w(sleep 60)) } - FileUtils.chmod_R(0755, tmp_dir) - end - - describe '#readiness' do - subject { described_class.readiness } - - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } - end - - describe '#metrics' do - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - end - end - end - - context 'when popen always finds required binaries' do - before do - allow(described_class).to receive(:exec_with_timeout).and_wrap_original do |method, *args, &block| - begin - method.call(*args, &block) - rescue RuntimeError, Errno::ENOENT - raise 'expected not to happen' - end - end - - stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '10') - end - - it_behaves_like 'filesystem checks' - end - - context 'when popen never finds required binaries' do - before do - allow(Gitlab::Popen).to receive(:popen).and_raise(Errno::ENOENT) - end - - it_behaves_like 'filesystem checks' - end -end diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb index 724beefff69..4912cd48761 100644 --- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb +++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb @@ -30,13 +30,14 @@ describe Gitlab::HealthChecks::GitalyCheck do describe '#metrics' do subject { described_class.metrics } + let(:server) { double(storage: 'default', read_writeable?: up) } before do - expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check) + allow(Gitaly::Server).to receive(:new).and_return(server) end context 'Gitaly server is up' do - let(:gitaly_check) { double(check: { success: true }) } + let(:up) { true } it 'provides metrics' do expect(subject).to all(have_attributes(labels: { shard: 'default' })) @@ -46,7 +47,7 @@ describe Gitlab::HealthChecks::GitalyCheck do end context 'Gitaly server is down' do - let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) } + let(:up) { false } it 'provides metrics' do expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_success', value: 0)) diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 3e6656e0f12..02f74e2ea54 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -25,15 +25,6 @@ describe ApplicationSetting do it { is_expected.to allow_value(https).for(:after_sign_out_path) } it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) } - describe 'disabled_oauth_sign_in_sources validations' do - before do - allow(Devise).to receive(:omniauth_providers).and_return([:github]) - end - - it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) } - it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) } - end - describe 'default_artifacts_expire_in' do it 'sets an error if it cannot parse' do setting.update(default_artifacts_expire_in: 'a') @@ -314,6 +305,33 @@ describe ApplicationSetting do end end + describe '#disabled_oauth_sign_in_sources=' do + before do + allow(Devise).to receive(:omniauth_providers).and_return([:github]) + end + + it 'removes unknown sources (as strings) from the array' do + subject.disabled_oauth_sign_in_sources = %w[github test] + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to eq ['github'] + end + + it 'removes unknown sources (as symbols) from the array' do + subject.disabled_oauth_sign_in_sources = %i[github test] + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to eq ['github'] + end + + it 'ignores nil' do + subject.disabled_oauth_sign_in_sources = nil + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to be_empty + end + end + context 'restricted signup domains' do it 'sets single domain' do setting.domain_whitelist_raw = 'example.com' diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 090f91168ad..5157d8fc645 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -514,30 +514,21 @@ eos end describe '#uri_type' do - shared_examples 'URI type' do - it 'returns the URI type at the given path' do - expect(commit.uri_type('files/html')).to be(:tree) - expect(commit.uri_type('files/images/logo-black.png')).to be(:raw) - expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw) - expect(commit.uri_type('files/js/application.js')).to be(:blob) - end - - it "returns nil if the path doesn't exists" do - expect(commit.uri_type('this/path/doesnt/exist')).to be_nil - end - - it 'is nil if the path is nil or empty' do - expect(commit.uri_type(nil)).to be_nil - expect(commit.uri_type("")).to be_nil - end + it 'returns the URI type at the given path' do + expect(commit.uri_type('files/html')).to be(:tree) + expect(commit.uri_type('files/images/logo-black.png')).to be(:raw) + expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw) + expect(commit.uri_type('files/js/application.js')).to be(:blob) end - context 'when Gitaly commit_tree_entry feature is enabled' do - it_behaves_like 'URI type' + it "returns nil if the path doesn't exists" do + expect(commit.uri_type('this/path/doesnt/exist')).to be_nil + expect(commit.uri_type('../path/doesnt/exist')).to be_nil end - context 'when Gitaly commit_tree_entry feature is disabled', :disable_gitaly do - it_behaves_like 'URI type' + it 'is nil if the path is nil or empty' do + expect(commit.uri_type(nil)).to be_nil + expect(commit.uri_type("")).to be_nil end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d817a8376f4..c7e751130d8 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -46,7 +46,7 @@ describe Repository do it { is_expected.not_to include('feature') } it { is_expected.not_to include('fix') } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.branch_names_contains(sample_commit.id) @@ -192,7 +192,7 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore') @@ -226,7 +226,7 @@ describe Repository do is_expected.to eq('c1acaa5') end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id @@ -391,7 +391,7 @@ describe Repository do it_behaves_like 'finding commits by message' end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error { broken_repository.find_commits_by_message('s') } end @@ -695,7 +695,7 @@ describe Repository do expect(results).to match_array([]) end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.search_files_by_content('feature', 'master') @@ -744,7 +744,7 @@ describe Repository do expect(results).to match_array([]) end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error { broken_repository.search_files_by_name('files', 'master') } end @@ -796,7 +796,7 @@ describe Repository do describe '#fetch_ref' do let(:broken_repository) { create(:project, :broken_storage).repository } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.fetch_ref(broken_repository, source_ref: '1', target_ref: '2') @@ -2294,6 +2294,28 @@ describe Repository do end end + describe '#local_branches' do + it 'returns the local branches' do + masterrev = repository.find_branch('master').dereferenced_target + create_remote_branch('joe', 'remote_branch', masterrev) + repository.add_branch(user, 'local_branch', masterrev.id) + + expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) + expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) + end + end + + describe '#remote_branches' do + it 'returns the remote branches' do + masterrev = repository.find_branch('master').dereferenced_target + create_remote_branch('joe', 'remote_branch', masterrev) + repository.add_branch(user, 'local_branch', masterrev.id) + + expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false) + expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true) + end + end + describe '#commit_count' do context 'with a non-existing repository' do it 'returns 0' do diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 64f51d9843d..9bb6ed62393 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -155,6 +155,12 @@ describe API::Branches do end it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end end context 'when unauthenticated', 'and project is private' do @@ -169,6 +175,12 @@ describe API::Branches do it_behaves_like 'repository branch' + it 'returns that the current user can push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(true) + end + context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } @@ -202,6 +214,23 @@ describe API::Branches do end end + context 'when authenticated', 'as a developer and branch is protected' do + let(:current_user) { create(:user) } + let!(:protected_branch) { create(:protected_branch, project: project, name: branch_name) } + + before do + project.add_developer(current_user) + end + + it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end + end + context 'when authenticated', 'as a guest' do it_behaves_like '403 response' do let(:request) { get api(route, guest) } diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index bceaf8277ee..471b0a74a19 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -15,9 +15,14 @@ module StubObjectStorage return unless enabled + stub_object_storage(connection_params: uploader.object_store_credentials, + remote_directory: remote_directory) + end + + def stub_object_storage(connection_params:, remote_directory:) Fog.mock! - ::Fog::Storage.new(uploader.object_store_credentials).tap do |connection| + ::Fog::Storage.new(connection_params).tap do |connection| begin connection.directories.create(key: remote_directory) rescue Excon::Error::Conflict diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index 5241c0fa6f1..a8f2c2e7a5a 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -5,6 +5,12 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + within('.js-new-protected-branch') do allowed_to_push_button = find(".js-allowed-to-push") @@ -25,6 +31,18 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) @@ -59,6 +77,12 @@ shared_examples "protected branches > access control > CE" do end end + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) @@ -70,6 +94,18 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) |
