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>2020-05-06 18:09:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-06 18:09:42 +0300
commita7beadc83470bd9ce23757a019795f49f95a6fff (patch)
treecb9ddaa8ea3eaf03b75184e682aef520ff46fc3f /spec
parent4279f24a19836d3e74e4aae8bea7acc2dd8222cc (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/alert_management_controller_spec.rb16
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/factories/ci/daily_build_group_report_results.rb (renamed from spec/factories/ci/daily_report_results.rb)9
-rw-r--r--spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb32
-rw-r--r--spec/frontend/__mocks__/@toast-ui/vue-editor/index.js3
-rw-r--r--spec/frontend/alert_management/components/alert_management_detail_spec.js56
-rw-r--r--spec/frontend/diffs/store/actions_spec.js41
-rw-r--r--spec/frontend/helpers/event_hub_factory_spec.js36
-rw-r--r--spec/frontend/issue_show/components/app_spec.js (renamed from spec/javascripts/issue_show/components/app_spec.js)398
-rw-r--r--spec/frontend/issue_show/components/description_spec.js (renamed from spec/javascripts/issue_show/components/description_spec.js)135
-rw-r--r--spec/frontend/issue_show/components/edited_spec.js (renamed from spec/javascripts/issue_show/components/edited_spec.js)0
-rw-r--r--spec/frontend/issue_show/components/fields/description_template_spec.js (renamed from spec/javascripts/issue_show/components/fields/description_template_spec.js)4
-rw-r--r--spec/frontend/issue_show/components/form_spec.js (renamed from spec/javascripts/issue_show/components/form_spec.js)19
-rw-r--r--spec/frontend/issue_show/components/title_spec.js (renamed from spec/javascripts/issue_show/components/title_spec.js)48
-rw-r--r--spec/frontend/static_site_editor/pages/home_spec.js23
-rw-r--r--spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js4
-rw-r--r--spec/helpers/projects/alert_management_helper_spec.rb15
-rw-r--r--spec/javascripts/issue_show/helpers.js1
-rw-r--r--spec/javascripts/issue_show/mock_data.js1
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml4
-rw-r--r--spec/models/ci/build_spec.rb55
-rw-r--r--spec/models/ci/daily_build_group_report_result_spec.rb (renamed from spec/models/ci/daily_report_result_spec.rb)25
-rw-r--r--spec/models/ci/pipeline_spec.rb4
-rw-r--r--spec/policies/group_policy_spec.rb22
-rw-r--r--spec/policies/project_policy_spec.rb1
-rw-r--r--spec/services/ci/daily_build_group_report_result_service_spec.rb (renamed from spec/services/ci/daily_report_result_service_spec.rb)41
-rw-r--r--spec/services/groups/import_export/export_service_spec.rb4
-rw-r--r--spec/services/groups/import_export/import_service_spec.rb8
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb7
-rw-r--r--spec/support/shared_examples/policies/wiki_policies_shared_examples.rb226
-rw-r--r--spec/workers/ci/daily_build_group_report_results_worker_spec.rb (renamed from spec/workers/ci/daily_report_results_worker_spec.rb)6
31 files changed, 648 insertions, 602 deletions
diff --git a/spec/controllers/projects/alert_management_controller_spec.rb b/spec/controllers/projects/alert_management_controller_spec.rb
index 4a6ebc46311..ccb9bfc3001 100644
--- a/spec/controllers/projects/alert_management_controller_spec.rb
+++ b/spec/controllers/projects/alert_management_controller_spec.rb
@@ -40,9 +40,9 @@ describe Projects::AlertManagementController do
end
describe 'GET #details' do
- context 'when alert_management_minimal is enabled' do
+ context 'when alert_management_detail is enabled' do
before do
- stub_feature_flags(alert_management_minimal: true)
+ stub_feature_flags(alert_management_detail: true)
end
it 'shows the page' do
@@ -52,9 +52,9 @@ describe Projects::AlertManagementController do
end
end
- context 'when alert_management_minimal is disabled' do
+ context 'when alert_management_detail is disabled' do
before do
- stub_feature_flags(alert_management_minimal: false)
+ stub_feature_flags(alert_management_detail: false)
end
it 'shows 404' do
@@ -64,4 +64,12 @@ describe Projects::AlertManagementController do
end
end
end
+
+ describe 'set_alert_id' do
+ it 'sets alert id from the route' do
+ get :details, params: { namespace_id: project.namespace, project_id: project, id: id }
+
+ expect(assigns(:alert_id)).to eq(id.to_s)
+ end
+ end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 875371d26c9..26786aab12c 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -320,6 +320,12 @@ FactoryBot.define do
end
end
+ trait :accessibility_reports do
+ after(:build) do |build|
+ build.job_artifacts << create(:ci_job_artifact, :accessibility, job: build)
+ end
+ end
+
trait :coverage_reports do
after(:build) do |build|
build.job_artifacts << create(:ci_job_artifact, :cobertura, job: build)
diff --git a/spec/factories/ci/daily_report_results.rb b/spec/factories/ci/daily_build_group_report_results.rb
index e2255e8a134..7f72991b3eb 100644
--- a/spec/factories/ci/daily_report_results.rb
+++ b/spec/factories/ci/daily_build_group_report_results.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :ci_daily_report_result, class: 'Ci::DailyReportResult' do
+ factory :ci_daily_build_group_report_result, class: 'Ci::DailyBuildGroupReportResult' do
ref_path { Gitlab::Git::BRANCH_REF_PREFIX + 'master' }
date { Time.zone.now.to_date }
project
last_pipeline factory: :ci_pipeline
- param_type { Ci::DailyReportResult.param_types[:coverage] }
- title { 'rspec' }
- value { 77.0 }
+ group_name { 'rspec' }
+ data do
+ { coverage: 77.0 }
+ end
end
end
diff --git a/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
new file mode 100644
index 00000000000..c3f17227701
--- /dev/null
+++ b/spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issues > Real-time sidebar', :js do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ project.add_developer(user)
+ end
+
+ it 'updates the assignee in real-time' do
+ Capybara::Session.new(:other_session)
+
+ using_session :other_session do
+ visit project_issue_path(project, issue)
+ expect(page.find('.assignee')).to have_content 'None'
+ end
+
+ gitlab_sign_in(user)
+ visit project_issue_path(project, issue)
+ expect(page.find('.assignee')).to have_content 'None'
+
+ click_button 'assign yourself'
+
+ using_session :other_session do
+ expect(page.find('.assignee')).to have_content user.name
+ end
+ end
+end
diff --git a/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js b/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
index 03279fc56a4..91bcef5cb62 100644
--- a/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
+++ b/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
@@ -10,6 +10,9 @@ export const Editor = {
initialEditType: {
type: String,
},
+ height: {
+ type: String,
+ },
},
render(h) {
return h('div');
diff --git a/spec/frontend/alert_management/components/alert_management_detail_spec.js b/spec/frontend/alert_management/components/alert_management_detail_spec.js
index 994de8d4d39..f5b5d669742 100644
--- a/spec/frontend/alert_management/components/alert_management_detail_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_detail_spec.js
@@ -4,14 +4,18 @@ import AlertDetails from '~/alert_management/components/alert_details.vue';
describe('AlertDetails', () => {
let wrapper;
- function mountComponent() {
- wrapper = shallowMount(AlertDetails);
+ function mountComponent(alert = {}) {
+ wrapper = shallowMount(AlertDetails, {
+ propsData: {
+ alertId: 'alertId',
+ projectPath: 'projectPath',
+ },
+ data() {
+ return { alert };
+ },
+ });
}
- beforeEach(() => {
- mountComponent();
- });
-
afterEach(() => {
if (wrapper) {
wrapper.destroy();
@@ -19,20 +23,42 @@ describe('AlertDetails', () => {
});
describe('Alert details', () => {
- it('renders a tab with overview information', () => {
- expect(wrapper.find('[data-testid="overviewTab"]').exists()).toBe(true);
- });
+ describe('when alert is null', () => {
+ beforeEach(() => {
+ mountComponent(null);
+ });
- it('renders a tab with full alert information', () => {
- expect(wrapper.find('[data-testid="fullDetailsTab"]').exists()).toBe(true);
+ describe('when alert is null', () => {
+ beforeEach(() => {
+ mountComponent(null);
+ });
+
+ it('shows an empty state', () => {
+ expect(wrapper.find('[data-testid="alertDetailsTabs"]').exists()).toBe(false);
+ });
+ });
});
- it('renders alert details', () => {
- expect(wrapper.find('[data-testid="startTimeItem"]').exists()).toBe(true);
+ describe('when alert is present', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('renders a tab with overview information', () => {
+ expect(wrapper.find('[data-testid="overviewTab"]').exists()).toBe(true);
+ });
+
+ it('renders a tab with full alert information', () => {
+ expect(wrapper.find('[data-testid="fullDetailsTab"]').exists()).toBe(true);
+ });
+
+ it('renders alert details', () => {
+ expect(wrapper.find('[data-testid="startTimeItem"]').exists()).toBe(true);
+ });
});
- it('renders a status dropdown', () => {
- expect(wrapper.find('[data-testid="statusDropdownItem"]').exists()).toBe(true);
+ it('renders a status dropdown containing three items', () => {
+ expect(wrapper.findAll('[data-testid="statusDropdownItem"]').length).toBe(3);
});
});
});
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 3b6021c8014..970bc99f8ff 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -44,6 +44,7 @@ import {
setExpandedDiffLines,
setSuggestPopoverDismissed,
changeCurrentCommit,
+ moveToNeighboringCommit,
} from '~/diffs/store/actions';
import eventHub from '~/notes/event_hub';
import * as types from '~/diffs/store/mutation_types';
@@ -1406,4 +1407,44 @@ describe('DiffsStoreActions', () => {
},
);
});
+
+ describe('moveToNeighboringCommit', () => {
+ it.each`
+ direction | expected | currentCommit
+ ${'next'} | ${'NEXTSHA'} | ${{ next_commit_id: 'NEXTSHA' }}
+ ${'previous'} | ${'PREVIOUSSHA'} | ${{ prev_commit_id: 'PREVIOUSSHA' }}
+ `(
+ 'for the direction "$direction", dispatches the action to move to the SHA "$expected"',
+ ({ direction, expected, currentCommit }) => {
+ return testAction(
+ moveToNeighboringCommit,
+ { direction },
+ { commit: currentCommit },
+ [],
+ [{ type: 'changeCurrentCommit', payload: { commitId: expected } }],
+ );
+ },
+ );
+
+ it.each`
+ direction | diffsAreLoading | currentCommit
+ ${'next'} | ${false} | ${{ prev_commit_id: 'PREVIOUSSHA' }}
+ ${'next'} | ${true} | ${{ prev_commit_id: 'PREVIOUSSHA' }}
+ ${'next'} | ${false} | ${undefined}
+ ${'previous'} | ${false} | ${{ next_commit_id: 'NEXTSHA' }}
+ ${'previous'} | ${true} | ${{ next_commit_id: 'NEXTSHA' }}
+ ${'previous'} | ${false} | ${undefined}
+ `(
+ 'given `{ "isloading": $diffsAreLoading, "commit": $currentCommit }` in state, no actions are dispatched',
+ ({ direction, diffsAreLoading, currentCommit }) => {
+ return testAction(
+ moveToNeighboringCommit,
+ { direction },
+ { commit: currentCommit, isLoading: diffsAreLoading },
+ [],
+ [],
+ );
+ },
+ );
+ });
});
diff --git a/spec/frontend/helpers/event_hub_factory_spec.js b/spec/frontend/helpers/event_hub_factory_spec.js
new file mode 100644
index 00000000000..ff00e29a40a
--- /dev/null
+++ b/spec/frontend/helpers/event_hub_factory_spec.js
@@ -0,0 +1,36 @@
+import createEventHub from '~/helpers/event_hub_factory';
+import mitt from 'mitt';
+
+jest.mock('mitt');
+
+mitt.mockReturnValue({
+ on: () => {},
+ off: () => {},
+ emit: () => {},
+});
+
+describe('event bus factory', () => {
+ let eventBus;
+
+ beforeEach(() => {
+ eventBus = createEventHub();
+ });
+
+ afterEach(() => {
+ eventBus = null;
+ });
+
+ it('creates an emitter', () => {
+ expect(mitt).toHaveBeenCalled();
+ });
+
+ it.each`
+ method
+ ${'on'}
+ ${'off'}
+ ${'emit'}
+ `('binds $$method to $method ', ({ method }) => {
+ expect(typeof eventBus[method]).toBe('function');
+ expect(eventBus[method]).toBe(eventBus[`$${method}`]);
+ });
+});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js
index 2d4d3ea28ff..42076e8da5c 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/frontend/issue_show/components/app_spec.js
@@ -1,9 +1,8 @@
-/* eslint-disable no-unused-vars */
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
-import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
-import GLDropdown from '~/gl_dropdown';
+import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
+import { visitUrl } from '~/lib/utils/url_utility';
import '~/behaviors/markdown/render_gfm';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
@@ -13,6 +12,9 @@ function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
}
+jest.mock('~/lib/utils/url_utility');
+jest.mock('~/issue_show/event_hub');
+
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
describe('Issuable output', () => {
@@ -20,9 +22,10 @@ describe('Issuable output', () => {
let realtimeRequestCount = 0;
let vm;
- beforeEach(done => {
+ beforeEach(() => {
setFixtures(`
<div>
+ <title>Title</title>
<div class="detail-page-description content-block">
<details open>
<summary>One</summary>
@@ -35,7 +38,6 @@ describe('Issuable output', () => {
<span id="task_status"></span>
</div>
`);
- spyOn(eventHub, '$emit');
const IssuableDescriptionComponent = Vue.extend(issuableApp);
@@ -53,7 +55,7 @@ describe('Issuable output', () => {
canUpdate: true,
canDestroy: true,
endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes',
- updateEndpoint: gl.TEST_HOST,
+ updateEndpoint: TEST_HOST,
issuableRef: '#1',
initialTitleHtml: '',
initialTitleText: '',
@@ -67,8 +69,6 @@ describe('Issuable output', () => {
issuableTemplateNamesPath: '/issuable-templates-path',
},
}).$mount();
-
- setTimeout(done);
});
afterEach(() => {
@@ -79,9 +79,10 @@ describe('Issuable output', () => {
vm.$destroy();
});
- it('should render a title/description/edited and update title/description/edited on update', done => {
+ it('should render a title/description/edited and update title/description/edited on update', () => {
let editedText;
- Vue.nextTick()
+ return axios
+ .waitForAll()
.then(() => {
editedText = vm.$el.querySelector('.edited-text');
})
@@ -100,8 +101,8 @@ describe('Issuable output', () => {
})
.then(() => {
vm.poll.makeRequest();
+ return axios.waitForAll();
})
- .then(() => new Promise(resolve => setTimeout(resolve)))
.then(() => {
expect(document.querySelector('title').innerText).toContain('2 (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
@@ -115,312 +116,239 @@ describe('Issuable output', () => {
expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
expect(editedText.querySelector('time')).toBeTruthy();
expect(vm.state.lock_version).toEqual(2);
- })
- .then(done)
- .catch(done.fail);
+ });
});
- it('shows actions if permissions are correct', done => {
+ it('shows actions if permissions are correct', () => {
vm.showForm = true;
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.btn')).not.toBeNull();
-
- done();
});
});
- it('does not show actions if permissions are incorrect', done => {
+ it('does not show actions if permissions are incorrect', () => {
vm.showForm = true;
vm.canUpdate = false;
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.btn')).toBeNull();
-
- done();
});
});
- it('does not update formState if form is already open', done => {
+ it('does not update formState if form is already open', () => {
vm.updateAndShowForm();
vm.state.titleText = 'testing 123';
vm.updateAndShowForm();
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(vm.store.formState.title).not.toBe('testing 123');
+ });
+ });
+
+ it('opens reCAPTCHA modal if update rejected as spam', () => {
+ let modal;
- done();
+ jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
+ data: {
+ recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ },
});
+
+ vm.canUpdate = true;
+ vm.showForm = true;
+
+ return vm
+ .$nextTick()
+ .then(() => {
+ vm.$refs.recaptchaModal.scriptSrc = '//scriptsrc';
+ return vm.updateIssuable();
+ })
+ .then(() => {
+ modal = vm.$el.querySelector('.js-recaptcha-modal');
+
+ expect(modal.style.display).not.toEqual('none');
+ expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
+ expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
+ })
+ .then(() => {
+ modal.querySelector('.close').click();
+ return vm.$nextTick();
+ })
+ .then(() => {
+ expect(modal.style.display).toEqual('none');
+ expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
+ });
});
describe('updateIssuable', () => {
- it('fetches new data after update', done => {
- spyOn(vm, 'updateStoreState').and.callThrough();
- spyOn(vm.service, 'getData').and.callThrough();
- spyOn(vm.service, 'updateIssuable').and.returnValue(
- Promise.resolve({
- data: { web_url: window.location.pathname },
- }),
- );
-
- vm.updateIssuable()
- .then(() => {
- expect(vm.updateStoreState).toHaveBeenCalled();
- expect(vm.service.getData).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ it('fetches new data after update', () => {
+ const updateStoreSpy = jest.spyOn(vm, 'updateStoreState');
+ const getDataSpy = jest.spyOn(vm.service, 'getData');
+ jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
+ data: { web_url: window.location.pathname },
+ });
+
+ return vm.updateIssuable().then(() => {
+ expect(updateStoreSpy).toHaveBeenCalled();
+ expect(getDataSpy).toHaveBeenCalled();
+ });
});
- it('correctly updates issuable data', done => {
- spyOn(vm.service, 'updateIssuable').and.returnValue(
- Promise.resolve({
- data: { web_url: window.location.pathname },
- }),
- );
+ it('correctly updates issuable data', () => {
+ const spy = jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
+ data: { web_url: window.location.pathname },
+ });
- vm.updateIssuable()
- .then(() => {
- expect(vm.service.updateIssuable).toHaveBeenCalledWith(vm.formState);
- expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
- })
- .then(done)
- .catch(done.fail);
+ return vm.updateIssuable().then(() => {
+ expect(spy).toHaveBeenCalledWith(vm.formState);
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
+ });
});
- it('does not redirect if issue has not moved', done => {
- const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'updateIssuable').and.returnValue(
- Promise.resolve({
- data: {
- web_url: window.location.pathname,
- confidential: vm.isConfidential,
- },
- }),
- );
-
- vm.updateIssuable();
+ it('does not redirect if issue has not moved', () => {
+ jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
+ data: {
+ web_url: window.location.pathname,
+ confidential: vm.isConfidential,
+ },
+ });
- setTimeout(() => {
+ return vm.updateIssuable().then(() => {
expect(visitUrl).not.toHaveBeenCalled();
- done();
});
});
- it('redirects if returned web_url has changed', done => {
- const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'updateIssuable').and.returnValue(
- Promise.resolve({
- data: {
- web_url: '/testing-issue-move',
- confidential: vm.isConfidential,
- },
- }),
- );
+ it('redirects if returned web_url has changed', () => {
+ jest.spyOn(vm.service, 'updateIssuable').mockResolvedValue({
+ data: {
+ web_url: '/testing-issue-move',
+ confidential: vm.isConfidential,
+ },
+ });
vm.updateIssuable();
- setTimeout(() => {
+ return vm.updateIssuable().then(() => {
expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move');
- done();
});
});
describe('shows dialog when issue has unsaved changed', () => {
- it('confirms on title change', done => {
+ it('confirms on title change', () => {
vm.showForm = true;
vm.state.titleText = 'title has changed';
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(e.returnValue).not.toBeNull();
-
- done();
});
});
- it('confirms on description change', done => {
+ it('confirms on description change', () => {
vm.showForm = true;
vm.state.descriptionText = 'description has changed';
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(e.returnValue).not.toBeNull();
-
- done();
});
});
- it('does nothing when nothing has changed', done => {
+ it('does nothing when nothing has changed', () => {
const e = { returnValue: null };
vm.handleBeforeUnloadEvent(e);
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(e.returnValue).toBeNull();
-
- done();
});
});
});
describe('error when updating', () => {
- it('closes form on error', done => {
- spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject());
- vm.updateIssuable();
-
- setTimeout(() => {
+ it('closes form on error', () => {
+ jest.spyOn(vm.service, 'updateIssuable').mockRejectedValue();
+ return vm.updateIssuable().then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating issue`,
);
-
- done();
});
});
- it('returns the correct error message for issuableType', done => {
- spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject());
+ it('returns the correct error message for issuableType', () => {
+ jest.spyOn(vm.service, 'updateIssuable').mockRejectedValue();
vm.issuableType = 'merge request';
- Vue.nextTick(() => {
- vm.updateIssuable();
-
- setTimeout(() => {
+ return vm
+ .$nextTick()
+ .then(vm.updateIssuable)
+ .then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`Error updating merge request`,
);
-
- done();
});
- });
});
- it('shows error message from backend if exists', done => {
+ it('shows error message from backend if exists', () => {
const msg = 'Custom error message from backend';
- spyOn(vm.service, 'updateIssuable').and.callFake(
- // eslint-disable-next-line prefer-promise-reject-errors
- () => Promise.reject({ response: { data: { errors: [msg] } } }),
- );
+ jest
+ .spyOn(vm.service, 'updateIssuable')
+ .mockRejectedValue({ response: { data: { errors: [msg] } } });
- vm.updateIssuable();
- setTimeout(() => {
+ return vm.updateIssuable().then(() => {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
`${vm.defaultErrorMessage}. ${msg}`,
);
-
- done();
});
});
});
});
- it('opens reCAPTCHA modal if update rejected as spam', done => {
- function mockScriptSrc() {
- const recaptchaChild = vm.$children.find(
- // eslint-disable-next-line no-underscore-dangle
- child => child.$options._componentTag === 'recaptcha-modal',
- );
-
- recaptchaChild.scriptSrc = '//scriptsrc';
- }
-
- let modal;
- const promise = new Promise(resolve => {
- resolve({
+ describe('deleteIssuable', () => {
+ it('changes URL when deleted', () => {
+ jest.spyOn(vm.service, 'deleteIssuable').mockResolvedValue({
data: {
- recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ web_url: '/test',
},
});
- });
-
- spyOn(vm.service, 'updateIssuable').and.returnValue(promise);
-
- vm.canUpdate = true;
- vm.showForm = true;
-
- vm.$nextTick()
- .then(() => mockScriptSrc())
- .then(() => vm.updateIssuable())
- .then(promise)
- .then(() => setTimeoutPromise())
- .then(() => {
- modal = vm.$el.querySelector('.js-recaptcha-modal');
- expect(modal.style.display).not.toEqual('none');
- expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
- expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
- })
- .then(() => modal.querySelector('.close').click())
- .then(() => vm.$nextTick())
- .then(() => {
- expect(modal.style.display).toEqual('none');
- expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
- })
- .then(done)
- .catch(done.fail);
- });
-
- describe('deleteIssuable', () => {
- it('changes URL when deleted', done => {
- const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.service, 'deleteIssuable').and.returnValue(
- Promise.resolve({
- data: {
- web_url: '/test',
- },
- }),
- );
-
- vm.deleteIssuable();
-
- setTimeout(() => {
+ return vm.deleteIssuable().then(() => {
expect(visitUrl).toHaveBeenCalledWith('/test');
-
- done();
});
});
- it('stops polling when deleting', done => {
- spyOnDependency(issuableApp, 'visitUrl');
- spyOn(vm.poll, 'stop').and.callThrough();
- spyOn(vm.service, 'deleteIssuable').and.returnValue(
- Promise.resolve({
- data: {
- web_url: '/test',
- },
- }),
- );
-
- vm.deleteIssuable();
-
- setTimeout(() => {
- expect(vm.poll.stop).toHaveBeenCalledWith();
+ it('stops polling when deleting', () => {
+ const spy = jest.spyOn(vm.poll, 'stop');
+ jest.spyOn(vm.service, 'deleteIssuable').mockResolvedValue({
+ data: {
+ web_url: '/test',
+ },
+ });
- done();
+ return vm.deleteIssuable().then(() => {
+ expect(spy).toHaveBeenCalledWith();
});
});
- it('closes form on error', done => {
- spyOn(vm.service, 'deleteIssuable').and.returnValue(Promise.reject());
+ it('closes form on error', () => {
+ jest.spyOn(vm.service, 'deleteIssuable').mockRejectedValue();
- vm.deleteIssuable();
-
- setTimeout(() => {
+ return vm.deleteIssuable().then(() => {
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
'Error deleting issue',
);
-
- done();
});
});
});
describe('updateAndShowForm', () => {
- it('shows locked warning if form is open & data is different', done => {
- vm.$nextTick()
+ it('shows locked warning if form is open & data is different', () => {
+ return vm
+ .$nextTick()
.then(() => {
vm.updateAndShowForm();
@@ -436,44 +364,38 @@ describe('Issuable output', () => {
expect(vm.formState.lockedWarningVisible).toEqual(true);
expect(vm.formState.lock_version).toEqual(1);
expect(vm.$el.querySelector('.alert')).not.toBeNull();
- })
- .then(done)
- .catch(done.fail);
+ });
});
});
describe('requestTemplatesAndShowForm', () => {
+ let formSpy;
+
beforeEach(() => {
- spyOn(vm, 'updateAndShowForm');
+ formSpy = jest.spyOn(vm, 'updateAndShowForm');
});
- it('shows the form if template names request is successful', done => {
+ it('shows the form if template names request is successful', () => {
const mockData = [{ name: 'Bug' }];
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
- vm.requestTemplatesAndShowForm()
- .then(() => {
- expect(vm.updateAndShowForm).toHaveBeenCalledWith(mockData);
- })
- .then(done)
- .catch(done.fail);
+ return vm.requestTemplatesAndShowForm().then(() => {
+ expect(formSpy).toHaveBeenCalledWith(mockData);
+ });
});
- it('shows the form if template names request failed', done => {
+ it('shows the form if template names request failed', () => {
mock
.onGet('/issuable-templates-path')
.reply(() => Promise.reject(new Error('something went wrong')));
- vm.requestTemplatesAndShowForm()
- .then(() => {
- expect(document.querySelector('.flash-container .flash-text').textContent).toContain(
- 'Error updating issue',
- );
+ return vm.requestTemplatesAndShowForm().then(() => {
+ expect(document.querySelector('.flash-container .flash-text').textContent).toContain(
+ 'Error updating issue',
+ );
- expect(vm.updateAndShowForm).toHaveBeenCalledWith();
- })
- .then(done)
- .catch(done.fail);
+ expect(formSpy).toHaveBeenCalledWith();
+ });
});
});
@@ -490,32 +412,26 @@ describe('Issuable output', () => {
});
describe('updateStoreState', () => {
- it('should make a request and update the state of the store', done => {
+ it('should make a request and update the state of the store', () => {
const data = { foo: 1 };
- spyOn(vm.store, 'updateState');
- spyOn(vm.service, 'getData').and.returnValue(Promise.resolve({ data }));
+ const getDataSpy = jest.spyOn(vm.service, 'getData').mockResolvedValue({ data });
+ const updateStateSpy = jest.spyOn(vm.store, 'updateState').mockImplementation(jest.fn);
- vm.updateStoreState()
- .then(() => {
- expect(vm.service.getData).toHaveBeenCalled();
- expect(vm.store.updateState).toHaveBeenCalledWith(data);
- })
- .then(done)
- .catch(done.fail);
+ return vm.updateStoreState().then(() => {
+ expect(getDataSpy).toHaveBeenCalled();
+ expect(updateStateSpy).toHaveBeenCalledWith(data);
+ });
});
- it('should show error message if store update fails', done => {
- spyOn(vm.service, 'getData').and.returnValue(Promise.reject());
+ it('should show error message if store update fails', () => {
+ jest.spyOn(vm.service, 'getData').mockRejectedValue();
vm.issuableType = 'merge request';
- vm.updateStoreState()
- .then(() => {
- expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
- `Error updating ${vm.issuableType}`,
- );
- })
- .then(done)
- .catch(done.fail);
+ return vm.updateStoreState().then(() => {
+ expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(
+ `Error updating ${vm.issuableType}`,
+ );
+ });
});
});
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/frontend/issue_show/components/description_spec.js
index 194f177d837..9c448c498e2 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/frontend/issue_show/components/description_spec.js
@@ -1,8 +1,12 @@
import $ from 'jquery';
import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent from 'helpers/vue_mount_component_helper';
+import { TEST_HOST } from 'helpers/test_constants';
import Description from '~/issue_show/components/description.vue';
+import TaskList from '~/task_list';
+
+jest.mock('~/task_list');
describe('Description component', () => {
let vm;
@@ -13,7 +17,7 @@ describe('Description component', () => {
descriptionText: 'test',
updatedAt: new Date().toString(),
taskStatus: '',
- updateUrl: gl.TEST_HOST,
+ updateUrl: TEST_HOST,
};
beforeEach(() => {
@@ -39,25 +43,26 @@ describe('Description component', () => {
$('.issuable-meta .flash-container').remove();
});
- it('animates description changes', done => {
+ it('animates description changes', () => {
vm.descriptionHtml = 'changed';
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'),
- ).toBeTruthy();
-
- setTimeout(() => {
+ return vm
+ .$nextTick()
+ .then(() => {
+ expect(
+ vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'),
+ ).toBeTruthy();
+ jest.runAllTimers();
+ return vm.$nextTick();
+ })
+ .then(() => {
expect(
vm.$el.querySelector('.md').classList.contains('issue-realtime-trigger-pulse'),
).toBeTruthy();
-
- done();
});
- });
});
- it('opens reCAPTCHA dialog if update rejected as spam', done => {
+ it('opens reCAPTCHA dialog if update rejected as spam', () => {
let modal;
const recaptchaChild = vm.$children.find(
// eslint-disable-next-line no-underscore-dangle
@@ -70,7 +75,8 @@ describe('Description component', () => {
recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
});
- vm.$nextTick()
+ return vm
+ .$nextTick()
.then(() => {
modal = vm.$el.querySelector('.js-recaptcha-modal');
@@ -83,128 +89,105 @@ describe('Description component', () => {
.then(() => {
expect(modal.style.display).toEqual('none');
expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
- })
- .then(done)
- .catch(done.fail);
+ });
});
- describe('TaskList', () => {
- let TaskList;
+ it('applies syntax highlighting and math when description changed', () => {
+ const vmSpy = jest.spyOn(vm, 'renderGFM');
+ const prototypeSpy = jest.spyOn($.prototype, 'renderGFM');
+ vm.descriptionHtml = 'changed';
+ return vm.$nextTick().then(() => {
+ expect(vm.$refs['gfm-content']).toBeDefined();
+ expect(vmSpy).toHaveBeenCalled();
+ expect(prototypeSpy).toHaveBeenCalled();
+ expect($.prototype.renderGFM).toHaveBeenCalled();
+ });
+ });
+
+ it('sets data-update-url', () => {
+ expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(TEST_HOST);
+ });
+
+ describe('TaskList', () => {
beforeEach(() => {
vm.$destroy();
+ TaskList.mockClear();
vm = mountComponent(
DescriptionComponent,
Object.assign({}, props, {
issuableType: 'issuableType',
}),
);
- TaskList = spyOnDependency(Description, 'TaskList');
});
- it('re-inits the TaskList when description changed', done => {
+ it('re-inits the TaskList when description changed', () => {
vm.descriptionHtml = 'changed';
- setTimeout(() => {
- expect(TaskList).toHaveBeenCalled();
- done();
- });
+ expect(TaskList).toHaveBeenCalled();
});
- it('does not re-init the TaskList when canUpdate is false', done => {
+ it('does not re-init the TaskList when canUpdate is false', () => {
vm.canUpdate = false;
vm.descriptionHtml = 'changed';
- setTimeout(() => {
- expect(TaskList).not.toHaveBeenCalled();
- done();
- });
+ expect(TaskList).toHaveBeenCalledTimes(1);
});
- it('calls with issuableType dataType', done => {
+ it('calls with issuableType dataType', () => {
vm.descriptionHtml = 'changed';
- setTimeout(() => {
- expect(TaskList).toHaveBeenCalledWith({
- dataType: 'issuableType',
- fieldName: 'description',
- selector: '.detail-page-description',
- onSuccess: jasmine.any(Function),
- onError: jasmine.any(Function),
- lockVersion: 0,
- });
-
- done();
+ expect(TaskList).toHaveBeenCalledWith({
+ dataType: 'issuableType',
+ fieldName: 'description',
+ selector: '.detail-page-description',
+ onSuccess: expect.any(Function),
+ onError: expect.any(Function),
+ lockVersion: 0,
});
});
});
describe('taskStatus', () => {
- it('adds full taskStatus', done => {
+ it('adds full taskStatus', () => {
vm.taskStatus = '1 of 1';
- setTimeout(() => {
+ return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(
'1 of 1',
);
-
- done();
});
});
- it('adds short taskStatus', done => {
+ it('adds short taskStatus', () => {
vm.taskStatus = '1 of 1';
- setTimeout(() => {
+ return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe(
'1/1 task',
);
-
- done();
});
});
- it('clears task status text when no tasks are present', done => {
+ it('clears task status text when no tasks are present', () => {
vm.taskStatus = '0 of 0';
- setTimeout(() => {
+ return vm.$nextTick().then(() => {
expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe('');
-
- done();
});
});
});
- it('applies syntax highlighting and math when description changed', done => {
- spyOn(vm, 'renderGFM').and.callThrough();
- spyOn($.prototype, 'renderGFM').and.callThrough();
- vm.descriptionHtml = 'changed';
-
- Vue.nextTick(() => {
- setTimeout(() => {
- expect(vm.$refs['gfm-content']).toBeDefined();
- expect(vm.renderGFM).toHaveBeenCalled();
- expect($.prototype.renderGFM).toHaveBeenCalled();
-
- done();
- });
- });
- });
-
- it('sets data-update-url', () => {
- expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST);
- });
-
describe('taskListUpdateError', () => {
it('should create flash notification and emit an event to parent', () => {
const msg =
'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.';
- spyOn(vm, '$emit');
+ const spy = jest.spyOn(vm, '$emit');
vm.taskListUpdateError();
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
- expect(vm.$emit).toHaveBeenCalledWith('taskListUpdateFailed');
+ expect(spy).toHaveBeenCalledWith('taskListUpdateFailed');
});
});
});
diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/frontend/issue_show/components/edited_spec.js
index a1683f060c0..a1683f060c0 100644
--- a/spec/javascripts/issue_show/components/edited_spec.js
+++ b/spec/frontend/issue_show/components/edited_spec.js
diff --git a/spec/javascripts/issue_show/components/fields/description_template_spec.js b/spec/frontend/issue_show/components/fields/description_template_spec.js
index 8d77a620d76..9ebab31f1ad 100644
--- a/spec/javascripts/issue_show/components/fields/description_template_spec.js
+++ b/spec/frontend/issue_show/components/fields/description_template_spec.js
@@ -5,7 +5,7 @@ describe('Issue description template component', () => {
let vm;
let formState;
- beforeEach(done => {
+ beforeEach(() => {
const Component = Vue.extend(descriptionTemplate);
formState = {
description: 'test',
@@ -19,8 +19,6 @@ describe('Issue description template component', () => {
projectNamespace: '/',
},
}).$mount();
-
- Vue.nextTick(done);
});
it('renders templates as JSON array in data attribute', () => {
diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/frontend/issue_show/components/form_spec.js
index a111333ac80..b06a3a89d3b 100644
--- a/spec/javascripts/issue_show/components/form_spec.js
+++ b/spec/frontend/issue_show/components/form_spec.js
@@ -1,8 +1,11 @@
import Vue from 'vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent from 'helpers/vue_mount_component_helper';
import formComponent from '~/issue_show/components/form.vue';
+import Autosave from '~/autosave';
import eventHub from '~/issue_show/event_hub';
+jest.mock('~/autosave');
+
describe('Inline edit form component', () => {
let vm;
const defaultProps = {
@@ -65,18 +68,16 @@ describe('Inline edit form component', () => {
});
describe('autosave', () => {
- let autosaveObj;
- let autosave;
+ let spy;
beforeEach(() => {
- autosaveObj = { reset: jasmine.createSpy() };
- autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
+ spy = jest.spyOn(Autosave.prototype, 'reset');
});
it('initialized Autosave on mount', () => {
createComponent();
- expect(autosave).toHaveBeenCalledTimes(2);
+ expect(Autosave).toHaveBeenCalledTimes(2);
});
it('calls reset on autosave when eventHub emits appropriate events', () => {
@@ -84,15 +85,15 @@ describe('Inline edit form component', () => {
eventHub.$emit('close.form');
- expect(autosaveObj.reset).toHaveBeenCalledTimes(2);
+ expect(spy).toHaveBeenCalledTimes(2);
eventHub.$emit('delete.issuable');
- expect(autosaveObj.reset).toHaveBeenCalledTimes(4);
+ expect(spy).toHaveBeenCalledTimes(4);
eventHub.$emit('update.issuable');
- expect(autosaveObj.reset).toHaveBeenCalledTimes(6);
+ expect(spy).toHaveBeenCalledTimes(6);
});
});
});
diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/frontend/issue_show/components/title_spec.js
index 9754c8a6755..c274048fdd5 100644
--- a/spec/javascripts/issue_show/components/title_spec.js
+++ b/spec/frontend/issue_show/components/title_spec.js
@@ -5,8 +5,9 @@ import eventHub from '~/issue_show/event_hub';
describe('Title component', () => {
let vm;
-
beforeEach(() => {
+ setFixtures(`<title />`);
+
const Component = Vue.extend(titleComponent);
const store = new Store({
titleHtml: '',
@@ -28,51 +29,39 @@ describe('Title component', () => {
expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>');
});
- it('updates page title when changing titleHtml', done => {
- spyOn(vm, 'setPageTitle');
+ it('updates page title when changing titleHtml', () => {
+ const spy = jest.spyOn(vm, 'setPageTitle');
vm.titleHtml = 'test';
- Vue.nextTick(() => {
- expect(vm.setPageTitle).toHaveBeenCalled();
-
- done();
+ return vm.$nextTick().then(() => {
+ expect(spy).toHaveBeenCalled();
});
});
- it('animates title changes', done => {
+ it('animates title changes', () => {
vm.titleHtml = 'test';
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.title').classList.contains('issue-realtime-pre-pulse'),
- ).toBeTruthy();
-
- setTimeout(() => {
- expect(
- vm.$el.querySelector('.title').classList.contains('issue-realtime-trigger-pulse'),
- ).toBeTruthy();
-
- done();
+ return vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-pre-pulse');
+ jest.runAllTimers();
+ return vm.$nextTick();
+ })
+ .then(() => {
+ expect(vm.$el.querySelector('.title').classList).toContain('issue-realtime-trigger-pulse');
});
- });
});
- it('updates page title after changing title', done => {
+ it('updates page title after changing title', () => {
vm.titleHtml = 'changed';
vm.titleText = 'changed';
- Vue.nextTick(() => {
+ return vm.$nextTick().then(() => {
expect(document.querySelector('title').textContent.trim()).toContain('changed');
-
- done();
});
});
describe('inline edit button', () => {
- beforeEach(() => {
- spyOn(eventHub, '$emit');
- });
-
it('should not show by default', () => {
expect(vm.$el.querySelector('.btn-edit')).toBeNull();
});
@@ -92,6 +81,7 @@ describe('Title component', () => {
});
it('should trigger open.form event when clicked', () => {
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm.showInlineEditButton = true;
vm.canUpdate = true;
diff --git a/spec/frontend/static_site_editor/pages/home_spec.js b/spec/frontend/static_site_editor/pages/home_spec.js
index 82e39447ae6..ca03abfe6bb 100644
--- a/spec/frontend/static_site_editor/pages/home_spec.js
+++ b/spec/frontend/static_site_editor/pages/home_spec.js
@@ -5,7 +5,7 @@ import { GlSkeletonLoader } from '@gitlab/ui';
import createState from '~/static_site_editor/store/state';
import Home from '~/static_site_editor/pages/home.vue';
-import EditArea from '~/static_site_editor/components/edit_area.vue';
+import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
import EditHeader from '~/static_site_editor/components/edit_header.vue';
import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue';
import PublishToolbar from '~/static_site_editor/components/publish_toolbar.vue';
@@ -71,10 +71,13 @@ describe('static_site_editor/pages/home', () => {
wrapper = shallowMount(Home, {
localVue,
store,
+ provide: {
+ glFeatures: { richContentEditor: true },
+ },
});
};
- const findEditArea = () => wrapper.find(EditArea);
+ const findRichContentEditor = () => wrapper.find(RichContentEditor);
const findEditHeader = () => wrapper.find(EditHeader);
const findInvalidContentMessage = () => wrapper.find(InvalidContentMessage);
const findPublishToolbar = () => wrapper.find(PublishToolbar);
@@ -103,8 +106,8 @@ describe('static_site_editor/pages/home', () => {
});
describe('when content is not loaded', () => {
- it('does not render edit area', () => {
- expect(findEditArea().exists()).toBe(false);
+ it('does not render rich content editor', () => {
+ expect(findRichContentEditor().exists()).toBe(false);
});
it('does not render edit header', () => {
@@ -129,8 +132,8 @@ describe('static_site_editor/pages/home', () => {
buildWrapper();
});
- it('renders the edit area', () => {
- expect(findEditArea().exists()).toBe(true);
+ it('renders the rich content editor', () => {
+ expect(findRichContentEditor().exists()).toBe(true);
});
it('renders the edit header', () => {
@@ -141,8 +144,8 @@ describe('static_site_editor/pages/home', () => {
expect(findSkeletonLoader().exists()).toBe(false);
});
- it('passes page content to edit area', () => {
- expect(findEditArea().props('value')).toBe(content);
+ it('passes page content to the rich content editor', () => {
+ expect(findRichContentEditor().props('value')).toBe(content);
});
it('passes page title to edit header', () => {
@@ -228,11 +231,11 @@ describe('static_site_editor/pages/home', () => {
expect(loadContentActionMock).toHaveBeenCalled();
});
- it('dispatches setContent action when edit area emits input event', () => {
+ it('dispatches setContent action when rich content editor emits input event', () => {
buildContentLoadedStore();
buildWrapper();
- findEditArea().vm.$emit('input', sourceContent);
+ findRichContentEditor().vm.$emit('input', sourceContent);
expect(setContentActionMock).toHaveBeenCalledWith(expect.anything(), sourceContent, undefined);
});
diff --git a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
index 933609c3072..774fe25387a 100644
--- a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
+++ b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
@@ -50,6 +50,10 @@ describe('Rich Content Editor', () => {
it('has the correct initial edit type', () => {
expect(findEditor().props().initialEditType).toBe('wysiwyg');
});
+
+ it('has the correct height', () => {
+ expect(findEditor().props().height).toBe('100%');
+ });
});
describe('when content is changed', () => {
diff --git a/spec/helpers/projects/alert_management_helper_spec.rb b/spec/helpers/projects/alert_management_helper_spec.rb
index 177dcb4ec2e..41f0f54b5ac 100644
--- a/spec/helpers/projects/alert_management_helper_spec.rb
+++ b/spec/helpers/projects/alert_management_helper_spec.rb
@@ -7,11 +7,11 @@ describe Projects::AlertManagementHelper do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:current_user) { create(:user) }
+ let_it_be(:project_path) { project.full_path }
describe '#alert_management_data' do
let(:user_can_enable_alert_management) { false }
let(:setting_path) { project_settings_operations_path(project) }
- let(:project_path) { project.full_path }
before do
allow(helper)
@@ -21,7 +21,7 @@ describe Projects::AlertManagementHelper do
end
context 'without alert_managements_setting' do
- it 'returns frontend configuration' do
+ it 'returns index page configuration' do
expect(alert_management_data(current_user, project)).to eq(
'project-path' => project_path,
'enable-alert-management-path' => setting_path,
@@ -32,4 +32,15 @@ describe Projects::AlertManagementHelper do
end
end
end
+
+ describe '#alert_management_detail_data' do
+ let(:alert_id) { 1 }
+
+ it 'returns detail page configuration' do
+ expect(alert_management_detail_data(project_path, alert_id)).to eq(
+ 'alert-id' => alert_id,
+ 'project-path' => project_path
+ )
+ end
+ end
end
diff --git a/spec/javascripts/issue_show/helpers.js b/spec/javascripts/issue_show/helpers.js
deleted file mode 100644
index 951acfd4e10..00000000000
--- a/spec/javascripts/issue_show/helpers.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from '../../frontend/issue_show/helpers.js';
diff --git a/spec/javascripts/issue_show/mock_data.js b/spec/javascripts/issue_show/mock_data.js
deleted file mode 100644
index 1b391bd1588..00000000000
--- a/spec/javascripts/issue_show/mock_data.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from '../../frontend/issue_show/mock_data';
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index d2790a6b858..03930c6c1a7 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -217,7 +217,7 @@ ci_pipelines:
- vulnerability_findings
- pipeline_config
- security_scans
-- daily_report_results
+- daily_build_group_report_results
pipeline_variables:
- pipeline
stages:
@@ -484,7 +484,7 @@ project:
- status_page_setting
- requirements
- export_jobs
-- daily_report_results
+- daily_build_group_report_results
- jira_imports
- compliance_framework_setting
- metrics_users_starred_dashboards
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 266d39a0dfb..3a608391b2b 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -3834,6 +3834,61 @@ describe Ci::Build do
end
end
+ describe '#collect_accessibility_reports!' do
+ subject { build.collect_accessibility_reports!(accessibility_report) }
+
+ let(:accessibility_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
+
+ it { expect(accessibility_report.urls).to eq({}) }
+
+ context 'when build has an accessibility report' do
+ context 'when there is an accessibility report with errors' do
+ before do
+ create(:ci_job_artifact, :accessibility, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to match_array(['https://about.gitlab.com/'])
+ expect(accessibility_report.errors_count).to eq(10)
+ expect(accessibility_report.scans_count).to eq(1)
+ expect(accessibility_report.passes_count).to eq(0)
+ end
+ end
+
+ context 'when there is an accessibility report without errors' do
+ before do
+ create(:ci_job_artifact, :accessibility_without_errors, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls.keys).to match_array(['https://pa11y.org/'])
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(1)
+ expect(accessibility_report.passes_count).to eq(1)
+ end
+ end
+
+ context 'when there is an accessibility report with an invalid url' do
+ before do
+ create(:ci_job_artifact, :accessibility_with_invalid_url, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the accessibility report' do
+ expect { subject }.not_to raise_error
+
+ expect(accessibility_report.urls).to be_empty
+ expect(accessibility_report.errors_count).to eq(0)
+ expect(accessibility_report.scans_count).to eq(0)
+ expect(accessibility_report.passes_count).to eq(0)
+ end
+ end
+ end
+ end
+
describe '#collect_coverage_reports!' do
subject { build.collect_coverage_reports!(coverage_report) }
diff --git a/spec/models/ci/daily_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb
index 61aa58c6692..d4c305c649a 100644
--- a/spec/models/ci/daily_report_result_spec.rb
+++ b/spec/models/ci/daily_build_group_report_result_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-describe Ci::DailyReportResult do
+describe Ci::DailyBuildGroupReportResult do
describe '.upsert_reports' do
let!(:rspec_coverage) do
create(
- :ci_daily_report_result,
- title: 'rspec',
+ :ci_daily_build_group_report_result,
+ group_name: 'rspec',
date: '2020-03-09',
- value: 71.2
+ data: { coverage: 71.2 }
)
end
let!(:new_pipeline) { create(:ci_pipeline) }
@@ -19,20 +19,18 @@ describe Ci::DailyReportResult do
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: described_class.param_types[rspec_coverage.param_type],
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- title: 'rspec',
- value: 81.0
+ group_name: 'rspec',
+ data: { 'coverage' => 81.0 }
},
{
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: described_class.param_types[rspec_coverage.param_type],
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- title: 'karma',
- value: 87.0
+ group_name: 'karma',
+ data: { 'coverage' => 87.0 }
}
])
@@ -40,16 +38,15 @@ describe Ci::DailyReportResult do
expect(rspec_coverage).to have_attributes(
last_pipeline_id: new_pipeline.id,
- value: 81.0
+ data: { 'coverage' => 81.0 }
)
- expect(described_class.find_by_title('karma')).to have_attributes(
+ expect(described_class.find_by_group_name('karma')).to have_attributes(
project_id: rspec_coverage.project_id,
ref_path: rspec_coverage.ref_path,
- param_type: rspec_coverage.param_type,
last_pipeline_id: new_pipeline.id,
date: rspec_coverage.date,
- value: 87.0
+ data: { 'coverage' => 87.0 }
)
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 3df87e5d2b4..b8e10f43ef4 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1163,8 +1163,8 @@ describe Ci::Pipeline, :mailer do
context "from #{status}" do
let(:from_status) { status }
- it 'schedules pipeline success worker' do
- expect(Ci::DailyReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id)
+ it 'schedules daily build group report results worker' do
+ expect(Ci::DailyBuildGroupReportResultsWorker).to receive(:perform_in).with(10.minutes, pipeline.id)
pipeline.succeed
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 13f1bcb389a..5a9ca9f7b7e 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -655,26 +655,4 @@ describe GroupPolicy do
end
end
end
-
- it_behaves_like 'model with wiki policies' do
- let(:container) { create(:group) }
-
- def set_access_level(access_level)
- allow(container).to receive(:wiki_access_level).and_return(access_level)
- end
-
- before do
- stub_feature_flags(group_wiki: true)
- end
-
- context 'when the feature flag is disabled' do
- before do
- stub_feature_flags(group_wiki: false)
- end
-
- it 'does not include the wiki permissions' do
- expect_disallowed(*permissions)
- end
- end
- end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index f214b1ccf17..4e15af7e0b5 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -124,6 +124,7 @@ describe ProjectPolicy do
it_behaves_like 'model with wiki policies' do
let(:container) { project }
+ let_it_be(:user) { owner }
def set_access_level(access_level)
project.project_feature.update_attribute(:wiki_access_level, access_level)
diff --git a/spec/services/ci/daily_report_result_service_spec.rb b/spec/services/ci/daily_build_group_report_result_service_spec.rb
index 240709bab0b..f0b72b8fd86 100644
--- a/spec/services/ci/daily_report_result_service_spec.rb
+++ b/spec/services/ci/daily_build_group_report_result_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::DailyReportResultService, '#execute' do
+describe Ci::DailyBuildGroupReportResultService, '#execute' do
let!(:pipeline) { create(:ci_pipeline, created_at: '2020-02-06 00:01:10') }
let!(:rspec_job) { create(:ci_build, pipeline: pipeline, name: '3/3 rspec', coverage: 80) }
let!(:karma_job) { create(:ci_build, pipeline: pipeline, name: '2/2 karma', coverage: 90) }
@@ -11,31 +11,29 @@ describe Ci::DailyReportResultService, '#execute' do
it 'creates daily code coverage record for each job in the pipeline that has coverage value' do
described_class.new.execute(pipeline)
- Ci::DailyReportResult.find_by(title: 'rspec').tap do |coverage|
+ Ci::DailyBuildGroupReportResult.find_by(group_name: 'rspec').tap do |coverage|
expect(coverage).to have_attributes(
project_id: pipeline.project.id,
last_pipeline_id: pipeline.id,
ref_path: pipeline.source_ref_path,
- param_type: 'coverage',
- title: rspec_job.group_name,
- value: rspec_job.coverage,
+ group_name: rspec_job.group_name,
+ data: { 'coverage' => rspec_job.coverage },
date: pipeline.created_at.to_date
)
end
- Ci::DailyReportResult.find_by(title: 'karma').tap do |coverage|
+ Ci::DailyBuildGroupReportResult.find_by(group_name: 'karma').tap do |coverage|
expect(coverage).to have_attributes(
project_id: pipeline.project.id,
last_pipeline_id: pipeline.id,
ref_path: pipeline.source_ref_path,
- param_type: 'coverage',
- title: karma_job.group_name,
- value: karma_job.coverage,
+ group_name: karma_job.group_name,
+ data: { 'coverage' => karma_job.coverage },
date: pipeline.created_at.to_date
)
end
- expect(Ci::DailyReportResult.find_by(title: 'extra')).to be_nil
+ expect(Ci::DailyBuildGroupReportResult.find_by(group_name: 'extra')).to be_nil
end
context 'when there are multiple builds with the same group name that report coverage' do
@@ -45,14 +43,13 @@ describe Ci::DailyReportResultService, '#execute' do
it 'creates daily code coverage record with the average as the value' do
described_class.new.execute(pipeline)
- Ci::DailyReportResult.find_by(title: 'test').tap do |coverage|
+ Ci::DailyBuildGroupReportResult.find_by(group_name: 'test').tap do |coverage|
expect(coverage).to have_attributes(
project_id: pipeline.project.id,
last_pipeline_id: pipeline.id,
ref_path: pipeline.source_ref_path,
- param_type: 'coverage',
- title: test_job_2.group_name,
- value: 75,
+ group_name: test_job_2.group_name,
+ data: { 'coverage' => 75.0 },
date: pipeline.created_at.to_date
)
end
@@ -77,8 +74,8 @@ describe Ci::DailyReportResultService, '#execute' do
end
it "updates the existing record's coverage value and last_pipeline_id" do
- rspec_coverage = Ci::DailyReportResult.find_by(title: 'rspec')
- karma_coverage = Ci::DailyReportResult.find_by(title: 'karma')
+ rspec_coverage = Ci::DailyBuildGroupReportResult.find_by(group_name: 'rspec')
+ karma_coverage = Ci::DailyBuildGroupReportResult.find_by(group_name: 'karma')
# Bump up the coverage values
described_class.new.execute(new_pipeline)
@@ -88,12 +85,12 @@ describe Ci::DailyReportResultService, '#execute' do
expect(rspec_coverage).to have_attributes(
last_pipeline_id: new_pipeline.id,
- value: new_rspec_job.coverage
+ data: { 'coverage' => new_rspec_job.coverage }
)
expect(karma_coverage).to have_attributes(
last_pipeline_id: new_pipeline.id,
- value: new_karma_job.coverage
+ data: { 'coverage' => new_karma_job.coverage }
)
end
end
@@ -117,8 +114,8 @@ describe Ci::DailyReportResultService, '#execute' do
end
it 'updates the existing daily code coverage records' do
- rspec_coverage = Ci::DailyReportResult.find_by(title: 'rspec')
- karma_coverage = Ci::DailyReportResult.find_by(title: 'karma')
+ rspec_coverage = Ci::DailyBuildGroupReportResult.find_by(group_name: 'rspec')
+ karma_coverage = Ci::DailyBuildGroupReportResult.find_by(group_name: 'karma')
# Run another one but for the older pipeline.
# This simulates the scenario wherein the success worker
@@ -135,12 +132,12 @@ describe Ci::DailyReportResultService, '#execute' do
expect(rspec_coverage).to have_attributes(
last_pipeline_id: pipeline.id,
- value: rspec_job.coverage
+ data: { 'coverage' => rspec_job.coverage }
)
expect(karma_coverage).to have_attributes(
last_pipeline_id: pipeline.id,
- value: karma_job.coverage
+ data: { 'coverage' => karma_job.coverage }
)
end
end
diff --git a/spec/services/groups/import_export/export_service_spec.rb b/spec/services/groups/import_export/export_service_spec.rb
index f77b5a2e5b9..e9e356ab4f6 100644
--- a/spec/services/groups/import_export/export_service_spec.rb
+++ b/spec/services/groups/import_export/export_service_spec.rb
@@ -50,7 +50,7 @@ describe Groups::ImportExport::ExportService do
end
it 'saves the models using ndjson tree saver' do
- stub_feature_flags(group_import_export_ndjson: true)
+ stub_feature_flags(group_export_ndjson: true)
expect(Gitlab::ImportExport::Group::TreeSaver).to receive(:new).and_call_original
@@ -58,7 +58,7 @@ describe Groups::ImportExport::ExportService do
end
it 'saves the models using legacy tree saver' do
- stub_feature_flags(group_import_export_ndjson: false)
+ stub_feature_flags(group_export_ndjson: false)
expect(Gitlab::ImportExport::Group::LegacyTreeSaver).to receive(:new).and_call_original
diff --git a/spec/services/groups/import_export/import_service_spec.rb b/spec/services/groups/import_export/import_service_spec.rb
index cd7ad1a1cfa..256e0a1b3c5 100644
--- a/spec/services/groups/import_export/import_service_spec.rb
+++ b/spec/services/groups/import_export/import_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe Groups::ImportExport::ImportService do
- context 'with group_import_export_ndjson feature flag disabled' do
+ context 'with group_import_ndjson feature flag disabled' do
let(:user) { create(:admin) }
let(:group) { create(:group) }
let(:import_logger) { instance_double(Gitlab::Import::Logger) }
@@ -11,7 +11,7 @@ describe Groups::ImportExport::ImportService do
subject(:service) { described_class.new(group: group, user: user) }
before do
- stub_feature_flags(group_import_export_ndjson: false)
+ stub_feature_flags(group_import_ndjson: false)
ImportExportUpload.create(group: group, import_file: import_file)
@@ -39,9 +39,9 @@ describe Groups::ImportExport::ImportService do
end
end
- context 'with group_import_export_ndjson feature flag enabled' do
+ context 'with group_import_ndjson feature flag enabled' do
before do
- stub_feature_flags(group_import_export_ndjson: true)
+ stub_feature_flags(group_import_ndjson: true)
end
context 'when importing a ndjson export' do
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index de64cea6474..a0d54666dff 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -14,17 +14,16 @@ RSpec.shared_context 'GroupPolicy context' do
%i[
read_label read_group upload_file read_namespace read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones
- read_group_merge_requests read_wiki
+ read_group_merge_requests
]
end
let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] }
- let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation download_wiki_code] }
- let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation create_wiki] }
+ let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation] }
+ let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation] }
let(:maintainer_permissions) do
%i[
create_projects
read_cluster create_cluster update_cluster admin_cluster add_cluster
- admin_wiki
]
end
let(:owner_permissions) do
diff --git a/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
index b91500ffd9c..bd9e3a26f1e 100644
--- a/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
+++ b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
@@ -1,152 +1,114 @@
# frozen_string_literal: true
RSpec.shared_examples 'model with wiki policies' do
- let(:container) { raise NotImplementedError }
- let(:permissions) { %i(read_wiki create_wiki update_wiki admin_wiki download_wiki_code) }
-
- # TODO: Remove this helper once we implement group features
- # https://gitlab.com/gitlab-org/gitlab/-/issues/208412
- def set_access_level(access_level)
- raise NotImplementedError
- end
-
- subject { described_class.new(owner, container) }
-
- context 'when the feature is disabled' do
- before do
- set_access_level(ProjectFeature::DISABLED)
- end
+ include ProjectHelpers
- it 'does not include the wiki permissions' do
- expect_disallowed(*permissions)
- end
+ let(:container) { raise NotImplementedError }
+ let(:user) { raise NotImplementedError }
- context 'when there is an external wiki' do
- it 'does not include the wiki permissions' do
- allow(container).to receive(:has_external_wiki?).and_return(true)
+ subject { described_class.new(user, container) }
- expect_disallowed(*permissions)
- end
+ let_it_be(:wiki_permissions) do
+ {}.tap do |permissions|
+ permissions[:guest] = %i[read_wiki]
+ permissions[:reporter] = permissions[:guest] + %i[download_wiki_code]
+ permissions[:developer] = permissions[:reporter] + %i[create_wiki]
+ permissions[:maintainer] = permissions[:developer] + %i[admin_wiki]
+ permissions[:all] = permissions[:maintainer]
end
end
- describe 'read_wiki' do
- subject { described_class.new(user, container) }
-
- member_roles = %i[guest developer]
- stranger_roles = %i[anonymous non_member]
-
- user_roles = stranger_roles + member_roles
+ using RSpec::Parameterized::TableSyntax
+
+ where(:container_level, :access_level, :membership, :access) do
+ :public | :enabled | :admin | :all
+ :public | :enabled | :maintainer | :maintainer
+ :public | :enabled | :developer | :developer
+ :public | :enabled | :reporter | :reporter
+ :public | :enabled | :guest | :guest
+ :public | :enabled | :non_member | :guest
+ :public | :enabled | :anonymous | :guest
+
+ :public | :private | :admin | :all
+ :public | :private | :maintainer | :maintainer
+ :public | :private | :developer | :developer
+ :public | :private | :reporter | :reporter
+ :public | :private | :guest | :guest
+ :public | :private | :non_member | nil
+ :public | :private | :anonymous | nil
+
+ :public | :disabled | :admin | nil
+ :public | :disabled | :maintainer | nil
+ :public | :disabled | :developer | nil
+ :public | :disabled | :reporter | nil
+ :public | :disabled | :guest | nil
+ :public | :disabled | :non_member | nil
+ :public | :disabled | :anonymous | nil
+
+ :internal | :enabled | :admin | :all
+ :internal | :enabled | :maintainer | :maintainer
+ :internal | :enabled | :developer | :developer
+ :internal | :enabled | :reporter | :reporter
+ :internal | :enabled | :guest | :guest
+ :internal | :enabled | :non_member | :guest
+ :internal | :enabled | :anonymous | nil
+
+ :internal | :private | :admin | :all
+ :internal | :private | :maintainer | :maintainer
+ :internal | :private | :developer | :developer
+ :internal | :private | :reporter | :reporter
+ :internal | :private | :guest | :guest
+ :internal | :private | :non_member | nil
+ :internal | :private | :anonymous | nil
+
+ :internal | :disabled | :admin | nil
+ :internal | :disabled | :maintainer | nil
+ :internal | :disabled | :developer | nil
+ :internal | :disabled | :reporter | nil
+ :internal | :disabled | :guest | nil
+ :internal | :disabled | :non_member | nil
+ :internal | :disabled | :anonymous | nil
+
+ :private | :private | :admin | :all
+ :private | :private | :maintainer | :maintainer
+ :private | :private | :developer | :developer
+ :private | :private | :reporter | :reporter
+ :private | :private | :guest | :guest
+ :private | :private | :non_member | nil
+ :private | :private | :anonymous | nil
+
+ :private | :disabled | :admin | nil
+ :private | :disabled | :maintainer | nil
+ :private | :disabled | :developer | nil
+ :private | :disabled | :reporter | nil
+ :private | :disabled | :guest | nil
+ :private | :disabled | :non_member | nil
+ :private | :disabled | :anonymous | nil
+ end
- # When a user is anonymous, their `current_user == nil`
- let(:user) { create(:user) unless user_role == :anonymous }
+ with_them do
+ let(:user) { create_user_from_membership(container, membership) }
+ let(:allowed_permissions) { wiki_permissions[access].dup || [] }
+ let(:disallowed_permissions) { wiki_permissions[:all] - allowed_permissions }
before do
- container.visibility = container_visibility
- set_access_level(wiki_access_level)
- container.add_user(user, user_role) if member_roles.include?(user_role)
- end
-
- title = ->(container_visibility, wiki_access_level, user_role) do
- [
- "container is #{Gitlab::VisibilityLevel.level_name container_visibility}",
- "wiki is #{ProjectFeature.str_from_access_level wiki_access_level}",
- "user is #{user_role}"
- ].join(', ')
- end
-
- describe 'Situations where :read_wiki is always false' do
- where(case_names: title,
- container_visibility: Gitlab::VisibilityLevel.options.values,
- wiki_access_level: [ProjectFeature::DISABLED],
- user_role: user_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
-
- describe 'Situations where :read_wiki is always true' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::PUBLIC],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: user_roles)
+ container.visibility = container_level.to_s
+ set_access_level(ProjectFeature.access_level_from_str(access_level.to_s))
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
+ if allowed_permissions.any? && [container_level, access_level, membership] != [:private, :private, :guest]
+ allowed_permissions << :download_wiki_code
end
end
- describe 'Situations where :read_wiki requires membership' do
- context 'the wiki is private, and the user is a member' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
- Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::PRIVATE],
- user_role: member_roles)
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the wiki is private, and the user is not member' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
- Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::PRIVATE],
- user_role: stranger_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
-
- context 'the wiki is enabled, and the user is a member' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: member_roles)
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the wiki is enabled, and the user is not a member' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
- wiki_access_level: [ProjectFeature::ENABLED],
- user_role: stranger_roles)
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
+ it 'allows actions based on membership' do
+ expect_allowed(*allowed_permissions)
+ expect_disallowed(*disallowed_permissions)
end
+ end
- describe 'Situations where :read_wiki prohibits anonymous access' do
- context 'the user is not anonymous' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
- user_role: user_roles.reject { |u| u == :anonymous })
-
- with_them do
- it { is_expected.to be_allowed(:read_wiki) }
- end
- end
-
- context 'the user is anonymous' do
- where(case_names: title,
- container_visibility: [Gitlab::VisibilityLevel::INTERNAL],
- wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
- user_role: %i[anonymous])
-
- with_them do
- it { is_expected.to be_disallowed(:read_wiki) }
- end
- end
- end
+ # TODO: Remove this helper once we implement group features
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/208412
+ def set_access_level(access_level)
+ raise NotImplementedError
end
end
diff --git a/spec/workers/ci/daily_report_results_worker_spec.rb b/spec/workers/ci/daily_build_group_report_results_worker_spec.rb
index b6543b32b09..d9706982a62 100644
--- a/spec/workers/ci/daily_report_results_worker_spec.rb
+++ b/spec/workers/ci/daily_build_group_report_results_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::DailyReportResultsWorker do
+describe Ci::DailyBuildGroupReportResultsWorker do
describe '#perform' do
let!(:pipeline) { create(:ci_pipeline) }
@@ -12,7 +12,7 @@ describe Ci::DailyReportResultsWorker do
let(:pipeline_id) { pipeline.id }
it 'executes service' do
- expect_any_instance_of(Ci::DailyReportResultService)
+ expect_any_instance_of(Ci::DailyBuildGroupReportResultService)
.to receive(:execute).with(pipeline)
subject
@@ -23,7 +23,7 @@ describe Ci::DailyReportResultsWorker do
let(:pipeline_id) { 123 }
it 'does not execute service' do
- expect_any_instance_of(Ci::DailyReportResultService)
+ expect_any_instance_of(Ci::DailyBuildGroupReportResultService)
.not_to receive(:execute)
expect { subject }