summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-15 15:08:30 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-15 15:08:30 +0000
commitce97c898865e06644ae9c04d5c3666775b9998cb (patch)
tree03e9e241b7d713a9fd137be4be841e38297e7acd /spec/frontend
parentb50c9d31e395afcab172d16760c5700c8cd5bcae (diff)
downloadgitlab-ce-ce97c898865e06644ae9c04d5c3666775b9998cb.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/__helpers__/gon_helper.js5
-rw-r--r--spec/frontend/__helpers__/shared_test_setup.js12
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_instructions_spec.js14
-rw-r--r--spec/frontend/ci/runner/components/registration/utils_spec.js14
-rw-r--r--spec/frontend/editor/source_editor_ci_schema_ext_spec.js11
-rw-r--r--spec/frontend/environment.js8
-rw-r--r--spec/frontend/header_search/components/app_spec.js6
-rw-r--r--spec/frontend/issues/show/components/app_spec.js551
-rw-r--r--spec/frontend/issues/show/mock_data/mock_data.js13
-rw-r--r--spec/frontend/lib/dompurify_spec.js14
-rw-r--r--spec/frontend/observability/observability_app_spec.js2
-rw-r--r--spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js7
-rw-r--r--spec/frontend/pages/import/history/components/import_error_details_spec.js10
-rw-r--r--spec/frontend/pages/import/history/components/import_history_app_spec.js10
-rw-r--r--spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js11
-rw-r--r--spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/entity_select/project_select_spec.js5
17 files changed, 289 insertions, 412 deletions
diff --git a/spec/frontend/__helpers__/gon_helper.js b/spec/frontend/__helpers__/gon_helper.js
new file mode 100644
index 00000000000..51d5ece5fc1
--- /dev/null
+++ b/spec/frontend/__helpers__/gon_helper.js
@@ -0,0 +1,5 @@
+export const createGon = (IS_EE) => {
+ return {
+ ee: IS_EE,
+ };
+};
diff --git a/spec/frontend/__helpers__/shared_test_setup.js b/spec/frontend/__helpers__/shared_test_setup.js
index 7fc81cf6548..0217835b2a3 100644
--- a/spec/frontend/__helpers__/shared_test_setup.js
+++ b/spec/frontend/__helpers__/shared_test_setup.js
@@ -6,6 +6,7 @@ import { enableAutoDestroy } from '@vue/test-utils';
import 'jquery';
import Translate from '~/vue_shared/translate';
import setWindowLocation from './set_window_location_helper';
+import { createGon } from './gon_helper';
import { setGlobalDateToFakeDate } from './fake_date';
import { TEST_HOST } from './test_constants';
import * as customMatchers from './matchers';
@@ -71,8 +72,13 @@ beforeEach(() => {
// eslint-disable-next-line jest/no-standalone-expect
expect.hasAssertions();
- // Reset the mocked window.location. This ensures tests don't interfere with
- // each other, and removes the need to tidy up if it was changed for a given
- // test.
+ // Reset globals: This ensures tests don't interfere with
+ // each other, and removes the need to tidy up if it was
+ // changed for a given test.
+
+ // Reset the mocked window.location
setWindowLocation(TEST_HOST);
+
+ // Reset window.gon object
+ window.gon = createGon(window.IS_EE);
});
diff --git a/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js b/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
index 72064a3895f..9b264e17c41 100644
--- a/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
@@ -16,10 +16,6 @@ import {
} from '~/ci/runner/constants';
import { runnerForRegistration } from '../../mock_data';
-const DUMMY_GON = {
- gitlab_url: TEST_HOST,
-};
-
const AUTH_TOKEN = 'AUTH_TOKEN';
const mockRunner = {
@@ -29,7 +25,6 @@ const mockRunner = {
describe('RegistrationInstructions', () => {
let wrapper;
- let originalGon;
const findHeading = () => wrapper.find('h1');
const findStepAt = (i) => extendedWrapper(wrapper.findAll('section').at(i));
@@ -48,13 +43,8 @@ describe('RegistrationInstructions', () => {
});
};
- beforeAll(() => {
- originalGon = window.gon;
- window.gon = { ...DUMMY_GON };
- });
-
- afterAll(() => {
- window.gon = originalGon;
+ beforeEach(() => {
+ window.gon.gitlab_url = TEST_HOST;
});
describe('renders heading', () => {
diff --git a/spec/frontend/ci/runner/components/registration/utils_spec.js b/spec/frontend/ci/runner/components/registration/utils_spec.js
index c2bf28b4a25..acf5993b15b 100644
--- a/spec/frontend/ci/runner/components/registration/utils_spec.js
+++ b/spec/frontend/ci/runner/components/registration/utils_spec.js
@@ -16,20 +16,10 @@ import {
const REGISTRATION_TOKEN = 'REGISTRATION_TOKEN';
const DESCRIPTION = 'RUNNER';
-const DUMMY_GON = {
- gitlab_url: TEST_HOST,
-};
describe('registration utils', () => {
- let originalGon;
-
- beforeAll(() => {
- originalGon = window.gon;
- window.gon = { ...DUMMY_GON };
- });
-
- afterAll(() => {
- window.gon = originalGon;
+ beforeEach(() => {
+ window.gon.gitlab_url = TEST_HOST;
});
describe.each([LINUX_PLATFORM, MACOS_PLATFORM, WINDOWS_PLATFORM])(
diff --git a/spec/frontend/editor/source_editor_ci_schema_ext_spec.js b/spec/frontend/editor/source_editor_ci_schema_ext_spec.js
index 21f8979f1a9..e515285601b 100644
--- a/spec/frontend/editor/source_editor_ci_schema_ext_spec.js
+++ b/spec/frontend/editor/source_editor_ci_schema_ext_spec.js
@@ -17,7 +17,6 @@ describe('~/editor/editor_ci_config_ext', () => {
let editor;
let instance;
let editorEl;
- let originalGitlabUrl;
const createMockEditor = ({ blobPath = defaultBlobPath } = {}) => {
setHTMLFixture('<div id="editor"></div>');
@@ -31,16 +30,8 @@ describe('~/editor/editor_ci_config_ext', () => {
instance.use({ definition: CiSchemaExtension });
};
- beforeAll(() => {
- originalGitlabUrl = gon.gitlab_url;
- gon.gitlab_url = TEST_HOST;
- });
-
- afterAll(() => {
- gon.gitlab_url = originalGitlabUrl;
- });
-
beforeEach(() => {
+ gon.gitlab_url = TEST_HOST;
createMockEditor();
});
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
index 82e3b50aeb8..7b160c48501 100644
--- a/spec/frontend/environment.js
+++ b/spec/frontend/environment.js
@@ -8,6 +8,7 @@ const {
setGlobalDateToRealDate,
} = require('./__helpers__/fake_date/fake_date');
const { TEST_HOST } = require('./__helpers__/test_constants');
+const { createGon } = require('./__helpers__/gon_helper');
const ROOT_PATH = path.resolve(__dirname, '../..');
@@ -40,11 +41,12 @@ class CustomEnvironment extends TestEnvironment {
});
const { IS_EE } = projectConfig.testEnvironmentOptions;
- this.global.gon = {
- ee: IS_EE,
- };
+
this.global.IS_EE = IS_EE;
+ // Set up global `gon` object
+ this.global.gon = createGon(IS_EE);
+
// Set up global `gl` object
this.global.gl = {};
diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js
index 8adc108d38a..35104b84a88 100644
--- a/spec/frontend/header_search/components/app_spec.js
+++ b/spec/frontend/header_search/components/app_spec.js
@@ -350,8 +350,8 @@ describe('HeaderSearchApp', () => {
describe('events', () => {
beforeEach(() => {
- createComponent();
window.gon.current_username = MOCK_USERNAME;
+ createComponent();
});
describe('Header Search Input', () => {
@@ -459,8 +459,8 @@ describe('HeaderSearchApp', () => {
${2} | ${'test1'}
`('currentFocusedOption', ({ MOCK_INDEX, search }) => {
beforeEach(() => {
- createComponent({ search });
window.gon.current_username = MOCK_USERNAME;
+ createComponent({ search });
findHeaderSearchInput().vm.$emit('click');
});
@@ -500,8 +500,8 @@ describe('HeaderSearchApp', () => {
const MOCK_INDEX = 1;
beforeEach(() => {
- createComponent();
window.gon.current_username = MOCK_USERNAME;
+ createComponent();
findHeaderSearchInput().vm.$emit('click');
});
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js
index abc44f19855..1006f54eeaf 100644
--- a/spec/frontend/issues/show/components/app_spec.js
+++ b/spec/frontend/issues/show/components/app_spec.js
@@ -1,7 +1,6 @@
import { GlIcon, GlIntersectionObserver } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { setHTMLFixture } from 'helpers/fixtures';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -21,29 +20,27 @@ import FormComponent from '~/issues/show/components/form.vue';
import TitleComponent from '~/issues/show/components/title.vue';
import IncidentTabs from '~/issues/show/components/incidents/incident_tabs.vue';
import PinnedLinks from '~/issues/show/components/pinned_links.vue';
-import { POLLING_DELAY } from '~/issues/show/constants';
import eventHub from '~/issues/show/event_hub';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import { HTTP_STATUS_OK, HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import {
appProps,
initialRequest,
publishedIncidentUrl,
+ putRequest,
secondRequest,
zoomMeetingUrl,
} from '../mock_data/mock_data';
jest.mock('~/alert');
-jest.mock('~/issues/show/event_hub');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/behaviors/markdown/render_gfm');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
describe('Issuable output', () => {
- let mock;
- let realtimeRequestCount = 0;
+ let axiosMock;
let wrapper;
const findStickyHeader = () => wrapper.findByTestId('issue-sticky-header');
@@ -57,7 +54,7 @@ describe('Issuable output', () => {
const findForm = () => wrapper.findComponent(FormComponent);
const findPinnedLinks = () => wrapper.findComponent(PinnedLinks);
- const mountComponent = (props = {}, options = {}, data = {}) => {
+ const createComponent = ({ props = {}, options = {}, data = {} } = {}) => {
wrapper = shallowMountExtended(IssuableApp, {
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
@@ -78,6 +75,28 @@ describe('Issuable output', () => {
},
...options,
});
+
+ jest.advanceTimersToNextTimer(2);
+ return waitForPromises();
+ };
+
+ const emitHubEvent = (event) => {
+ eventHub.$emit(event);
+ return waitForPromises();
+ };
+
+ const openForm = () => {
+ return emitHubEvent('open.form');
+ };
+
+ const updateIssuable = () => {
+ return emitHubEvent('update.issuable');
+ };
+
+ const advanceToNextPoll = () => {
+ // We get new data through the HTTP request.
+ jest.advanceTimersToNextTimer();
+ return waitForPromises();
};
beforeEach(() => {
@@ -97,78 +116,100 @@ describe('Issuable output', () => {
</div>
`);
- mock = new MockAdapter(axios);
- mock
- .onGet('/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes')
- .reply(() => {
- const res = Promise.resolve([HTTP_STATUS_OK, REALTIME_REQUEST_STACK[realtimeRequestCount]]);
- realtimeRequestCount += 1;
- return res;
- });
+ jest.spyOn(eventHub, '$emit');
- mountComponent();
+ axiosMock = new MockAdapter(axios);
+ const endpoint = '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes';
- jest.advanceTimersByTime(2);
+ axiosMock.onGet(endpoint).replyOnce(HTTP_STATUS_OK, REALTIME_REQUEST_STACK[0], {
+ 'POLL-INTERVAL': '1',
+ });
+ axiosMock.onGet(endpoint).reply(HTTP_STATUS_OK, REALTIME_REQUEST_STACK[1], {
+ 'POLL-INTERVAL': '-1',
+ });
+ axiosMock.onPut().reply(HTTP_STATUS_OK, putRequest);
});
- afterEach(() => {
- mock.restore();
- realtimeRequestCount = 0;
- wrapper.vm.poll.stop();
- resetHTMLFixture();
- });
+ describe('update', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
- it('should render a title/description/edited and update title/description/edited on update', () => {
- return axios
- .waitForAll()
- .then(() => {
- expect(findTitle().props('titleText')).toContain('this is a title');
- expect(findDescription().props('descriptionText')).toContain('this is a description');
-
- expect(findEdited().exists()).toBe(true);
- expect(findEdited().props('updatedByPath')).toMatch(/\/some_user$/);
- expect(findEdited().props('updatedAt')).toBe(initialRequest.updated_at);
- expect(wrapper.vm.state.lock_version).toBe(initialRequest.lock_version);
- })
- .then(() => {
- wrapper.vm.poll.makeRequest();
- return axios.waitForAll();
- })
- .then(() => {
- expect(findTitle().props('titleText')).toContain('2');
- expect(findDescription().props('descriptionText')).toContain('42');
-
- expect(findEdited().exists()).toBe(true);
- expect(findEdited().props('updatedByName')).toBe('Other User');
- expect(findEdited().props('updatedByPath')).toMatch(/\/other_user$/);
- expect(findEdited().props('updatedAt')).toBe(secondRequest.updated_at);
- });
- });
+ it('should render a title/description/edited and update title/description/edited on update', async () => {
+ expect(findTitle().props('titleText')).toContain(initialRequest.title_text);
+ expect(findDescription().props('descriptionText')).toContain('this is a description');
- it('shows actions if permissions are correct', async () => {
- wrapper.vm.showForm = true;
+ expect(findEdited().exists()).toBe(true);
+ expect(findEdited().props('updatedByPath')).toMatch(/\/some_user$/);
+ expect(findEdited().props('updatedAt')).toBe(initialRequest.updated_at);
+ expect(findDescription().props().lockVersion).toBe(initialRequest.lock_version);
- await nextTick();
- expect(findForm().exists()).toBe(true);
- });
+ await advanceToNextPoll();
- it('does not show actions if permissions are incorrect', async () => {
- wrapper.vm.showForm = true;
- wrapper.setProps({ canUpdate: false });
+ expect(findTitle().props('titleText')).toContain('2');
+ expect(findDescription().props('descriptionText')).toContain('42');
- await nextTick();
- expect(findForm().exists()).toBe(false);
+ expect(findEdited().exists()).toBe(true);
+ expect(findEdited().props('updatedByName')).toBe('Other User');
+ expect(findEdited().props('updatedByPath')).toMatch(/\/other_user$/);
+ expect(findEdited().props('updatedAt')).toBe(secondRequest.updated_at);
+ });
});
- it('does not update formState if form is already open', async () => {
- wrapper.vm.updateAndShowForm();
+ describe('with permissions', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
+ it('shows actions on `open.form` event', async () => {
+ expect(findForm().exists()).toBe(false);
+
+ await openForm();
+
+ expect(findForm().exists()).toBe(true);
+ });
+
+ it('update formState if form is not open', async () => {
+ const titleValue = initialRequest.title_text;
+
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(titleValue);
+
+ await advanceToNextPoll();
+
+ // The title component has the new data, so the state was updated
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(secondRequest.title_text);
+ });
+
+ it('does not update formState if form is already open', async () => {
+ const titleValue = initialRequest.title_text;
+
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().props('titleText')).toBe(titleValue);
+
+ await openForm();
+
+ // Opening the form, the data has not changed
+ expect(findForm().props().formState.title).toBe(titleValue);
+
+ await advanceToNextPoll();
+
+ // We expect the prop value not to have changed after another API call
+ expect(findForm().props().formState.title).toBe(titleValue);
+ });
+ });
- wrapper.vm.state.titleText = 'testing 123';
+ describe('without permissions', () => {
+ beforeEach(async () => {
+ await createComponent({ props: { canUpdate: false } });
+ });
- wrapper.vm.updateAndShowForm();
+ it('does not show actions if permissions are incorrect', async () => {
+ await openForm();
- await nextTick();
- expect(wrapper.vm.store.formState.title).not.toBe('testing 123');
+ expect(findForm().exists()).toBe(false);
+ });
});
describe('Pinned links propagated', () => {
@@ -176,268 +217,130 @@ describe('Issuable output', () => {
prop | value
${'zoomMeetingUrl'} | ${zoomMeetingUrl}
${'publishedIncidentUrl'} | ${publishedIncidentUrl}
- `('sets the $prop correctly on underlying pinned links', ({ prop, value }) => {
+ `('sets the $prop correctly on underlying pinned links', async ({ prop, value }) => {
+ await createComponent();
+
expect(findPinnedLinks().props(prop)).toBe(value);
});
});
- describe('updateIssuable', () => {
+ describe('updating an issue', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
it('fetches new data after update', async () => {
- const updateStoreSpy = jest.spyOn(wrapper.vm, 'updateStoreState');
- const getDataSpy = jest.spyOn(wrapper.vm.service, 'getData');
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: { web_url: window.location.pathname },
- });
+ await advanceToNextPoll();
+
+ await updateIssuable();
- await wrapper.vm.updateIssuable();
- expect(updateStoreSpy).toHaveBeenCalled();
- expect(getDataSpy).toHaveBeenCalled();
+ expect(axiosMock.history.put).toHaveLength(1);
+ // The call was made with the new data
+ expect(axiosMock.history.put[0].data.title).toEqual(findTitle().props().title);
});
- it('correctly updates issuable data', async () => {
- const spy = jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: { web_url: window.location.pathname },
- });
+ it('closes the form after fetching data', async () => {
+ await updateIssuable();
- await wrapper.vm.updateIssuable();
- expect(spy).toHaveBeenCalledWith(wrapper.vm.formState);
expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
});
it('does not redirect if issue has not moved', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: window.location.pathname,
- confidential: wrapper.vm.isConfidential,
- },
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ confidential: appProps.isConfidential,
});
- await wrapper.vm.updateIssuable();
+ await updateIssuable();
+
expect(visitUrl).not.toHaveBeenCalled();
});
it('does not redirect if issue has not moved and user has switched tabs', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: '',
- confidential: wrapper.vm.isConfidential,
- },
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ web_url: '',
+ confidential: appProps.isConfidential,
});
- await wrapper.vm.updateIssuable();
+ await updateIssuable();
+
expect(visitUrl).not.toHaveBeenCalled();
});
it('redirects if returned web_url has changed', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockResolvedValue({
- data: {
- web_url: '/testing-issue-move',
- confidential: wrapper.vm.isConfidential,
- },
- });
-
- wrapper.vm.updateIssuable();
+ const webUrl = '/testing-issue-move';
- await wrapper.vm.updateIssuable();
- expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move');
- });
-
- describe('shows dialog when issue has unsaved changed', () => {
- it('confirms on title change', async () => {
- wrapper.vm.showForm = true;
- wrapper.vm.state.titleText = 'title has changed';
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
-
- await nextTick();
- expect(e.returnValue).not.toBeNull();
+ axiosMock.onPut().reply(HTTP_STATUS_OK, {
+ ...putRequest,
+ web_url: webUrl,
+ confidential: appProps.isConfidential,
});
- it('confirms on description change', async () => {
- wrapper.vm.showForm = true;
- wrapper.vm.state.descriptionText = 'description has changed';
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
-
- await nextTick();
- expect(e.returnValue).not.toBeNull();
- });
+ await updateIssuable();
- it('does nothing when nothing has changed', async () => {
- const e = { returnValue: null };
- wrapper.vm.handleBeforeUnloadEvent(e);
-
- await nextTick();
- expect(e.returnValue).toBeNull();
- });
+ expect(visitUrl).toHaveBeenCalledWith(webUrl);
});
describe('error when updating', () => {
- it('closes form on error', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue();
+ it('closes form', async () => {
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED);
- await wrapper.vm.updateIssuable();
- expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(createAlert).toHaveBeenCalledWith({ message: `Error updating issue` });
- });
+ await updateIssuable();
- it('returns the correct error message for issuableType', async () => {
- jest.spyOn(wrapper.vm.service, 'updateIssuable').mockRejectedValue();
- wrapper.setProps({ issuableType: 'merge request' });
-
- await nextTick();
- await wrapper.vm.updateIssuable();
expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
- expect(createAlert).toHaveBeenCalledWith({ message: `Error updating merge request` });
- });
-
- it('shows error message from backend if exists', async () => {
- const msg = 'Custom error message from backend';
- jest
- .spyOn(wrapper.vm.service, 'updateIssuable')
- .mockRejectedValue({ response: { data: { errors: [msg] } } });
-
- await wrapper.vm.updateIssuable();
expect(createAlert).toHaveBeenCalledWith({
- message: `${wrapper.vm.defaultErrorMessage}. ${msg}`,
+ message: `Error updating issue. Request failed with status code 401`,
});
});
- });
- });
-
- describe('updateAndShowForm', () => {
- it('shows locked warning if form is open & data is different', async () => {
- await nextTick();
- wrapper.vm.updateAndShowForm();
-
- wrapper.vm.poll.makeRequest();
-
- await new Promise((resolve) => {
- wrapper.vm.$watch('formState.lockedWarningVisible', (value) => {
- if (value) {
- resolve();
- }
- });
- });
-
- expect(wrapper.vm.formState.lockedWarningVisible).toBe(true);
- expect(wrapper.vm.formState.lock_version).toBe(1);
- });
- });
-
- describe('requestTemplatesAndShowForm', () => {
- let formSpy;
-
- beforeEach(() => {
- formSpy = jest.spyOn(wrapper.vm, 'updateAndShowForm');
- });
-
- it('shows the form if template names as hash request is successful', () => {
- const mockData = {
- test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
- };
- mock
- .onGet('/issuable-templates-path')
- .reply(() => Promise.resolve([HTTP_STATUS_OK, mockData]));
-
- return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(formSpy).toHaveBeenCalledWith(mockData);
- });
- });
- it('shows the form if template names as array request is successful', () => {
- const mockData = [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }];
- mock
- .onGet('/issuable-templates-path')
- .reply(() => Promise.resolve([HTTP_STATUS_OK, mockData]));
+ it('returns the correct error message for issuableType', async () => {
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED);
- return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(formSpy).toHaveBeenCalledWith(mockData);
- });
- });
+ await updateIssuable();
- it('shows the form if template names request failed', () => {
- mock
- .onGet('/issuable-templates-path')
- .reply(() => Promise.reject(new Error('something went wrong')));
+ wrapper.setProps({ issuableType: 'merge request' });
- return wrapper.vm.requestTemplatesAndShowForm().then(() => {
- expect(createAlert).toHaveBeenCalledWith({ message: 'Error updating issue' });
+ await updateIssuable();
- expect(formSpy).toHaveBeenCalledWith();
+ expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form');
+ expect(createAlert).toHaveBeenCalledWith({
+ message: `Error updating merge request. Request failed with status code 401`,
+ });
});
- });
- });
- describe('updateStoreState', () => {
- it('should make a request and update the state of the store', () => {
- const data = { foo: 1 };
- const getDataSpy = jest.spyOn(wrapper.vm.service, 'getData').mockResolvedValue({ data });
- const updateStateSpy = jest
- .spyOn(wrapper.vm.store, 'updateState')
- .mockImplementation(jest.fn);
-
- return wrapper.vm.updateStoreState().then(() => {
- expect(getDataSpy).toHaveBeenCalled();
- expect(updateStateSpy).toHaveBeenCalledWith(data);
- });
- });
+ it('shows error message from backend if exists', async () => {
+ const msg = 'Custom error message from backend';
+ axiosMock.onPut().reply(HTTP_STATUS_UNAUTHORIZED, { errors: [msg] });
- it('should show error message if store update fails', () => {
- jest.spyOn(wrapper.vm.service, 'getData').mockRejectedValue();
- wrapper.setProps({ issuableType: 'merge request' });
+ await updateIssuable();
- return wrapper.vm.updateStoreState().then(() => {
expect(createAlert).toHaveBeenCalledWith({
- message: `Error updating ${wrapper.vm.issuableType}`,
+ message: `Error updating issue. ${msg}`,
});
});
});
});
- describe('issueChanged', () => {
- beforeEach(() => {
- wrapper.vm.store.formState.title = '';
- wrapper.vm.store.formState.description = '';
- wrapper.setProps({
- initialDescriptionText: '',
- initialTitleText: '',
- });
- });
-
- it('returns true when title is changed', () => {
- wrapper.vm.store.formState.title = 'RandomText';
-
- expect(wrapper.vm.issueChanged).toBe(true);
+ describe('Locked warning', () => {
+ beforeEach(async () => {
+ await createComponent();
});
- it('returns false when title is empty null', () => {
- wrapper.vm.store.formState.title = null;
-
- expect(wrapper.vm.issueChanged).toBe(false);
- });
-
- it('returns true when description is changed', () => {
- wrapper.vm.store.formState.description = 'RandomText';
-
- expect(wrapper.vm.issueChanged).toBe(true);
- });
-
- it('returns false when description is empty null', () => {
- wrapper.vm.store.formState.description = null;
-
- expect(wrapper.vm.issueChanged).toBe(false);
- });
-
- it('returns false when `initialDescriptionText` is null and `formState.description` is empty string', () => {
- wrapper.vm.store.formState.description = '';
- wrapper.setProps({ initialDescriptionText: null });
+ it('shows locked warning if form is open & data is different', async () => {
+ await openForm();
+ await advanceToNextPoll();
- expect(wrapper.vm.issueChanged).toBe(false);
+ expect(findForm().props().formState.lockedWarningVisible).toBe(true);
+ expect(findForm().props().formState.lock_version).toBe(1);
});
});
describe('sticky header', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
describe('when title is in view', () => {
it('is not shown', () => {
expect(findStickyHeader().exists()).toBe(false);
@@ -445,21 +348,18 @@ describe('Issuable output', () => {
});
describe('when title is not in view', () => {
- beforeEach(() => {
- wrapper.vm.state.titleText = 'Sticky header title';
+ beforeEach(async () => {
wrapper.findComponent(GlIntersectionObserver).vm.$emit('disappear');
});
it('shows with title', () => {
- expect(findStickyHeader().text()).toContain('Sticky header title');
+ expect(findStickyHeader().text()).toContain(initialRequest.title_text);
});
it('shows with title for an epic', async () => {
- wrapper.setProps({ issuableType: 'epic' });
+ await wrapper.setProps({ issuableType: 'epic' });
- await nextTick();
-
- expect(findStickyHeader().text()).toContain('Sticky header title');
+ expect(findStickyHeader().text()).toContain(' this is a title');
});
it.each`
@@ -471,9 +371,7 @@ describe('Issuable output', () => {
`(
'shows with state icon "$statusIcon" for $issuableType when status is $issuableStatus',
async ({ issuableType, issuableStatus, statusIcon }) => {
- wrapper.setProps({ issuableType, issuableStatus });
-
- await nextTick();
+ await wrapper.setProps({ issuableType, issuableStatus });
expect(findStickyHeader().findComponent(GlIcon).props('name')).toBe(statusIcon);
},
@@ -485,9 +383,7 @@ describe('Issuable output', () => {
${'shows with Closed when status is closed'} | ${STATUS_CLOSED}
${'shows with Open when status is reopened'} | ${STATUS_REOPENED}
`('$title', async ({ state }) => {
- wrapper.setProps({ issuableStatus: state });
-
- await nextTick();
+ await wrapper.setProps({ issuableStatus: state });
expect(findStickyHeader().text()).toContain(IssuableStatusText[state]);
});
@@ -497,9 +393,7 @@ describe('Issuable output', () => {
${'does not show confidential badge when issue is not confidential'} | ${false}
${'shows confidential badge when issue is confidential'} | ${true}
`('$title', async ({ isConfidential }) => {
- wrapper.setProps({ isConfidential });
-
- await nextTick();
+ await wrapper.setProps({ isConfidential });
const confidentialEl = findConfidentialBadge();
expect(confidentialEl.exists()).toBe(isConfidential);
@@ -516,9 +410,7 @@ describe('Issuable output', () => {
${'does not show locked badge when issue is not locked'} | ${false}
${'shows locked badge when issue is locked'} | ${true}
`('$title', async ({ isLocked }) => {
- wrapper.setProps({ isLocked });
-
- await nextTick();
+ await wrapper.setProps({ isLocked });
expect(findLockedBadge().exists()).toBe(isLocked);
});
@@ -528,9 +420,7 @@ describe('Issuable output', () => {
${'does not show hidden badge when issue is not hidden'} | ${false}
${'shows hidden badge when issue is hidden'} | ${true}
`('$title', async ({ isHidden }) => {
- wrapper.setProps({ isHidden });
-
- await nextTick();
+ await wrapper.setProps({ isHidden });
const hiddenBadge = findHiddenBadge();
@@ -547,6 +437,10 @@ describe('Issuable output', () => {
});
describe('Composable description component', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
const findIncidentTabs = () => wrapper.findComponent(IncidentTabs);
const borderClass = 'gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-mb-6';
@@ -565,13 +459,13 @@ describe('Issuable output', () => {
});
describe('when using incident tabs description wrapper', () => {
- beforeEach(() => {
- mountComponent(
- {
+ beforeEach(async () => {
+ await createComponent({
+ props: {
descriptionComponent: IncidentTabs,
showTitleBorder: false,
},
- {
+ options: {
mocks: {
$apollo: {
queries: {
@@ -582,7 +476,7 @@ describe('Issuable output', () => {
},
},
},
- );
+ });
});
it('does not the description component', () => {
@@ -600,48 +494,77 @@ describe('Issuable output', () => {
});
describe('taskListUpdateStarted', () => {
- it('stops polling', () => {
- jest.spyOn(wrapper.vm.poll, 'stop');
+ beforeEach(async () => {
+ await createComponent();
+ });
+
+ it('stops polling', async () => {
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
- wrapper.vm.taskListUpdateStarted();
+ findDescription().vm.$emit('taskListUpdateStarted');
- expect(wrapper.vm.poll.stop).toHaveBeenCalled();
+ await advanceToNextPoll();
+
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
});
});
describe('taskListUpdateSucceeded', () => {
- it('enables polling', () => {
- jest.spyOn(wrapper.vm.poll, 'enable');
- jest.spyOn(wrapper.vm.poll, 'makeDelayedRequest');
+ beforeEach(async () => {
+ await createComponent();
+ findDescription().vm.$emit('taskListUpdateStarted');
+ });
- wrapper.vm.taskListUpdateSucceeded();
+ it('enables polling', async () => {
+ // Ensure that polling is not working before
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+ await advanceToNextPoll();
- expect(wrapper.vm.poll.enable).toHaveBeenCalled();
- expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY);
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+
+ // Enable Polling an move forward
+ findDescription().vm.$emit('taskListUpdateSucceeded');
+ await advanceToNextPoll();
+
+ // Title has changed: polling works!
+ expect(findTitle().props().titleText).toBe(secondRequest.title_text);
});
});
describe('taskListUpdateFailed', () => {
- it('enables polling and calls updateStoreState', () => {
- jest.spyOn(wrapper.vm.poll, 'enable');
- jest.spyOn(wrapper.vm.poll, 'makeDelayedRequest');
- jest.spyOn(wrapper.vm, 'updateStoreState');
+ beforeEach(async () => {
+ await createComponent();
+ findDescription().vm.$emit('taskListUpdateStarted');
+ });
+
+ it('enables polling and calls updateStoreState', async () => {
+ // Ensure that polling is not working before
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
+ await advanceToNextPoll();
- wrapper.vm.taskListUpdateFailed();
+ expect(findTitle().props().titleText).toBe(initialRequest.title_text);
- expect(wrapper.vm.poll.enable).toHaveBeenCalled();
- expect(wrapper.vm.poll.makeDelayedRequest).toHaveBeenCalledWith(POLLING_DELAY);
- expect(wrapper.vm.updateStoreState).toHaveBeenCalled();
+ // Enable Polling an move forward
+ findDescription().vm.$emit('taskListUpdateFailed');
+ await advanceToNextPoll();
+
+ // Title has changed: polling works!
+ expect(findTitle().props().titleText).toBe(secondRequest.title_text);
});
});
describe('saveDescription event', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
it('makes request to update issue', async () => {
const description = 'I have been updated!';
findDescription().vm.$emit('saveDescription', description);
+
await waitForPromises();
- expect(mock.history.put[0].data).toContain(description);
+ expect(axiosMock.history.put[0].data).toContain(description);
});
});
});
diff --git a/spec/frontend/issues/show/mock_data/mock_data.js b/spec/frontend/issues/show/mock_data/mock_data.js
index d09bbf7c7f6..86d09665947 100644
--- a/spec/frontend/issues/show/mock_data/mock_data.js
+++ b/spec/frontend/issues/show/mock_data/mock_data.js
@@ -24,6 +24,19 @@ export const secondRequest = {
lock_version: 2,
};
+export const putRequest = {
+ web_url: window.location.pathname,
+ title: '<p>PUT</p>',
+ title_text: 'PUT',
+ description: '<p>PUT_DESC</p>',
+ description_text: 'PUT_DESC',
+ task_status: '0 of 0 completed',
+ updated_at: '2016-05-15T12:31:04.428Z',
+ updated_by_name: 'Other User',
+ updated_by_path: '/other_user',
+ lock_version: 2,
+};
+
export const descriptionProps = {
canUpdate: true,
descriptionHtml: 'test',
diff --git a/spec/frontend/lib/dompurify_spec.js b/spec/frontend/lib/dompurify_spec.js
index f767a673553..fdc8789c1a8 100644
--- a/spec/frontend/lib/dompurify_spec.js
+++ b/spec/frontend/lib/dompurify_spec.js
@@ -49,8 +49,6 @@ const forbiddenDataAttrs = defaultConfig.FORBID_ATTR;
const acceptedDataAttrs = ['data-random', 'data-custom'];
describe('~/lib/dompurify', () => {
- let originalGon;
-
it('uses local configuration when given', () => {
// As dompurify uses a "Persistent Configuration", it might
// ignore config, this check verifies we respect
@@ -104,15 +102,10 @@ describe('~/lib/dompurify', () => {
${'root'} | ${rootGon}
${'absolute'} | ${absoluteGon}
`('when gon contains $type icon urls', ({ type, gon }) => {
- beforeAll(() => {
- originalGon = window.gon;
+ beforeEach(() => {
window.gon = gon;
});
- afterAll(() => {
- window.gon = originalGon;
- });
-
it('allows no href attrs', () => {
const htmlHref = `<svg><use></use></svg>`;
expect(sanitize(htmlHref)).toBe(htmlHref);
@@ -137,14 +130,9 @@ describe('~/lib/dompurify', () => {
describe('when gon does not contain icon urls', () => {
beforeAll(() => {
- originalGon = window.gon;
window.gon = {};
});
- afterAll(() => {
- window.gon = originalGon;
- });
-
it.each([...safeUrls.root, ...safeUrls.absolute, ...unsafeUrls])('sanitizes URL %s', (url) => {
const htmlHref = `<svg><use href="${url}"></use></svg>`;
const htmlXlink = `<svg><use xlink:href="${url}"></use></svg>`;
diff --git a/spec/frontend/observability/observability_app_spec.js b/spec/frontend/observability/observability_app_spec.js
index 1f8386b97db..5f41e36595f 100644
--- a/spec/frontend/observability/observability_app_spec.js
+++ b/spec/frontend/observability/observability_app_spec.js
@@ -51,7 +51,7 @@ describe('Observability root app', () => {
describe('iframe src', () => {
const TEST_USERNAME = 'test-user';
- beforeAll(() => {
+ beforeEach(() => {
gon.current_username = TEST_USERNAME;
});
diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
index 79db288c19b..477511cde64 100644
--- a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
+++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
@@ -68,15 +68,10 @@ describe('BulkImportsHistoryApp', () => {
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
- const originalApiVersion = gon.api_version;
- beforeAll(() => {
+ beforeEach(() => {
gon.api_version = 'v4';
});
- afterAll(() => {
- gon.api_version = originalApiVersion;
- });
-
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(API_URL).reply(HTTP_STATUS_OK, DUMMY_RESPONSE, DEFAULT_HEADERS);
diff --git a/spec/frontend/pages/import/history/components/import_error_details_spec.js b/spec/frontend/pages/import/history/components/import_error_details_spec.js
index e51ace44fe6..239826c1458 100644
--- a/spec/frontend/pages/import/history/components/import_error_details_spec.js
+++ b/spec/frontend/pages/import/history/components/import_error_details_spec.js
@@ -21,16 +21,8 @@ describe('ImportErrorDetails', () => {
});
}
- const originalApiVersion = gon.api_version;
- beforeAll(() => {
- gon.api_version = 'v4';
- });
-
- afterAll(() => {
- gon.api_version = originalApiVersion;
- });
-
beforeEach(() => {
+ gon.api_version = 'v4';
mock = new MockAdapter(axios);
});
diff --git a/spec/frontend/pages/import/history/components/import_history_app_spec.js b/spec/frontend/pages/import/history/components/import_history_app_spec.js
index 07a031e5949..43cbac25fe8 100644
--- a/spec/frontend/pages/import/history/components/import_history_app_spec.js
+++ b/spec/frontend/pages/import/history/components/import_history_app_spec.js
@@ -59,17 +59,9 @@ describe('ImportHistoryApp', () => {
});
}
- const originalApiVersion = gon.api_version;
- beforeAll(() => {
+ beforeEach(() => {
gon.api_version = 'v4';
gon.features = { fullPathProjectSearch: true };
- });
-
- afterAll(() => {
- gon.api_version = originalApiVersion;
- });
-
- beforeEach(() => {
mock = new MockAdapter(axios);
});
diff --git a/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js
index 509ce3c3443..af578b69a81 100644
--- a/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js
@@ -11,7 +11,6 @@ jest.mock('~/alert');
describe('ProjectNamespace component', () => {
let wrapper;
- let originalGon;
const data = {
project: {
@@ -85,14 +84,8 @@ describe('ProjectNamespace component', () => {
findListBox().vm.$emit('shown');
};
- beforeAll(() => {
- originalGon = window.gon;
- window.gon = { gitlab_url: gitlabUrl };
- });
-
- afterAll(() => {
- window.gon = originalGon;
- wrapper.destroy();
+ beforeEach(() => {
+ gon.gitlab_url = gitlabUrl;
});
describe('Initial state', () => {
diff --git a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
index 4246cc15ee3..b9c8655d5d8 100644
--- a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
+++ b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
@@ -22,10 +22,6 @@ describe('Sidebar date Widget', () => {
let fakeApollo;
const date = '2021-04-15';
- window.gon = {
- first_day_of_week: 1,
- };
-
const findEditableItem = () => wrapper.findComponent(SidebarEditableItem);
const findPopoverIcon = () => wrapper.find('[data-testid="inherit-date-popover"]');
const findDatePicker = () => wrapper.findComponent(GlDatepicker);
@@ -61,6 +57,10 @@ describe('Sidebar date Widget', () => {
});
};
+ beforeEach(() => {
+ window.gon.first_day_of_week = 1;
+ });
+
afterEach(() => {
fakeApollo = null;
});
diff --git a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
index 57dce032d30..32ce2155494 100644
--- a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
+++ b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
@@ -63,11 +63,8 @@ describe('ProjectSelect', () => {
};
const openListbox = () => findListbox().vm.$emit('shown');
- beforeAll(() => {
- gon.api_version = apiVersion;
- });
-
beforeEach(() => {
+ gon.api_version = apiVersion;
mock = new MockAdapter(axios);
});