summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/ci_variable_list/components/ci_environments_dropdown_spec.js139
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js380
-rw-r--r--spec/frontend/ci_variable_list/mocks.js116
-rw-r--r--spec/frontend/ci_variable_list/utils_spec.js68
-rw-r--r--spec/frontend/content_editor/remark_markdown_processing_spec.js2
-rw-r--r--spec/frontend/labels/labels_select_spec.js6
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js14
-rw-r--r--spec/frontend/projects/project_new_spec.js67
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_memory_usage_spec.js12
9 files changed, 775 insertions, 29 deletions
diff --git a/spec/frontend/ci_variable_list/components/ci_environments_dropdown_spec.js b/spec/frontend/ci_variable_list/components/ci_environments_dropdown_spec.js
new file mode 100644
index 00000000000..e9966576cab
--- /dev/null
+++ b/spec/frontend/ci_variable_list/components/ci_environments_dropdown_spec.js
@@ -0,0 +1,139 @@
+import { GlDropdown, GlDropdownItem, GlIcon, GlSearchBoxByType } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { allEnvironments } from '~/ci_variable_list/constants';
+import CiEnvironmentsDropdown from '~/ci_variable_list/components/ci_environments_dropdown.vue';
+
+describe('Ci environments dropdown', () => {
+ let wrapper;
+
+ const envs = ['dev', 'prod', 'staging'];
+ const defaultProps = { environments: envs, selectedEnvironmentScope: '' };
+
+ const findDropdownText = () => wrapper.findComponent(GlDropdown).text();
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findDropdownItemByIndex = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
+ const findActiveIconByIndex = (index) => findDropdownItemByIndex(index).findComponent(GlIcon);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+
+ const createComponent = ({ props = {}, searchTerm = '' } = {}) => {
+ wrapper = mount(CiEnvironmentsDropdown, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+
+ findSearchBox().vm.$emit('input', searchTerm);
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('No environments found', () => {
+ beforeEach(() => {
+ createComponent({ searchTerm: 'stable' });
+ });
+
+ it('renders create button with search term if environments do not contain search term', () => {
+ expect(findAllDropdownItems()).toHaveLength(2);
+ expect(findDropdownItemByIndex(1).text()).toBe('Create wildcard: stable');
+ });
+
+ it('renders empty results message', () => {
+ expect(findDropdownItemByIndex(0).text()).toBe('No matching results');
+ });
+ });
+
+ describe('Search term is empty', () => {
+ beforeEach(() => {
+ createComponent({ props: { environments: envs } });
+ });
+
+ it('renders all environments when search term is empty', () => {
+ expect(findAllDropdownItems()).toHaveLength(3);
+ expect(findDropdownItemByIndex(0).text()).toBe(envs[0]);
+ expect(findDropdownItemByIndex(1).text()).toBe(envs[1]);
+ expect(findDropdownItemByIndex(2).text()).toBe(envs[2]);
+ });
+
+ it('should not display active checkmark on the inactive stage', () => {
+ expect(findActiveIconByIndex(0).classes('gl-visibility-hidden')).toBe(true);
+ });
+ });
+
+ describe('when `*` is the value of selectedEnvironmentScope props', () => {
+ const wildcardScope = '*';
+
+ beforeEach(() => {
+ createComponent({ props: { selectedEnvironmentScope: wildcardScope } });
+ });
+
+ it('shows the `All environments` text and not the wildcard', () => {
+ expect(findDropdownText()).toContain(allEnvironments.text);
+ expect(findDropdownText()).not.toContain(wildcardScope);
+ });
+ });
+
+ describe('Environments found', () => {
+ const currentEnv = envs[2];
+
+ beforeEach(async () => {
+ createComponent({ searchTerm: currentEnv });
+ await nextTick();
+ });
+
+ it('renders only the environment searched for', () => {
+ expect(findAllDropdownItems()).toHaveLength(1);
+ expect(findDropdownItemByIndex(0).text()).toBe(currentEnv);
+ });
+
+ it('should not display create button', () => {
+ const environments = findAllDropdownItems().filter((env) => env.text().startsWith('Create'));
+ expect(environments).toHaveLength(0);
+ expect(findAllDropdownItems()).toHaveLength(1);
+ });
+
+ it('should not display empty results message', () => {
+ expect(wrapper.findComponent({ ref: 'noMatchingResults' }).exists()).toBe(false);
+ });
+
+ it('should clear the search term when showing the dropdown', () => {
+ wrapper.findComponent(GlDropdown).trigger('click');
+
+ expect(findSearchBox().text()).toBe('');
+ });
+
+ describe('Custom events', () => {
+ describe('when clicking on an environment', () => {
+ const itemIndex = 0;
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('should emit `select-environment` if an environment is clicked', async () => {
+ await nextTick();
+
+ await findDropdownItemByIndex(itemIndex).vm.$emit('click');
+
+ expect(wrapper.emitted('select-environment')).toEqual([[envs[itemIndex]]]);
+ });
+ });
+
+ describe('when creating a new environment from a search term', () => {
+ const search = 'new-env';
+ beforeEach(() => {
+ createComponent({ searchTerm: search });
+ });
+
+ it('should emit createClicked if an environment is clicked', async () => {
+ await nextTick();
+ findDropdownItemByIndex(1).vm.$emit('click');
+ expect(wrapper.emitted('create-environment-scope')).toEqual([[search]]);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
new file mode 100644
index 00000000000..51b902d97dc
--- /dev/null
+++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
@@ -0,0 +1,380 @@
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import { mockTracking } from 'helpers/tracking_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import CiEnvironmentsDropdown from '~/ci_variable_list/components/ci_environments_dropdown.vue';
+import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue';
+import {
+ ADD_VARIABLE_ACTION,
+ AWS_ACCESS_KEY_ID,
+ EDIT_VARIABLE_ACTION,
+ EVENT_LABEL,
+ EVENT_ACTION,
+ ENVIRONMENT_SCOPE_LINK_TITLE,
+} from '~/ci_variable_list/constants';
+import { mockVariablesWithScopes } from '../mocks';
+import ModalStub from '../stubs';
+
+describe('Ci variable modal', () => {
+ let wrapper;
+ let trackingSpy;
+
+ const maskableRegex = '^[a-zA-Z0-9_+=/@:.~-]{8,}$';
+
+ const defaultProvide = {
+ awsLogoSvgPath: '/logo',
+ awsTipCommandsLink: '/tips',
+ awsTipDeployLink: '/deploy',
+ awsTipLearnLink: '/learn-link',
+ containsVariableReferenceLink: '/reference',
+ environmentScopeLink: '/help/environments',
+ isProtectedByDefault: false,
+ maskedEnvironmentVariablesLink: '/variables-link',
+ maskableRegex,
+ protectedEnvironmentVariablesLink: '/protected-link',
+ };
+
+ const defaultProps = {
+ areScopedVariablesAvailable: true,
+ environments: [],
+ mode: ADD_VARIABLE_ACTION,
+ selectedVariable: {},
+ };
+
+ const createComponent = ({ mountFn = shallowMountExtended, props = {}, provide = {} } = {}) => {
+ wrapper = mountFn(CiVariableModal, {
+ attachTo: document.body,
+ provide: { ...defaultProvide, ...provide },
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ stubs: {
+ GlModal: ModalStub,
+ },
+ });
+ };
+
+ const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown);
+ const findReferenceWarning = () => wrapper.findByTestId('contains-variable-reference');
+ const findModal = () => wrapper.find(ModalStub);
+ const findAWSTip = () => wrapper.findByTestId('aws-guidance-tip');
+ const findAddorUpdateButton = () => wrapper.findByTestId('ciUpdateOrAddVariableBtn');
+ const deleteVariableButton = () =>
+ findModal()
+ .findAll(GlButton)
+ .wrappers.find((button) => button.props('variant') === 'danger');
+ const findProtectedVariableCheckbox = () =>
+ wrapper.findByTestId('ci-variable-protected-checkbox');
+ const findMaskedVariableCheckbox = () => wrapper.findByTestId('ci-variable-masked-checkbox');
+ const findValueField = () => wrapper.find('#ci-variable-value');
+ const findEnvScopeLink = () => wrapper.findByTestId('environment-scope-link');
+ const findEnvScopeInput = () => wrapper.findByTestId('environment-scope').find(GlFormInput);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('Adding a variable', () => {
+ describe('when no key/value pair are present', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('shows the submit button as disabled ', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeTruthy();
+ });
+ });
+
+ describe('when a key/value pair is present', () => {
+ beforeEach(() => {
+ createComponent({ props: { selectedVariable: mockVariablesWithScopes[0] } });
+ });
+
+ it('shows the submit button as enabled ', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeFalsy();
+ });
+ });
+
+ describe('events', () => {
+ const [currentVariable] = mockVariablesWithScopes;
+
+ beforeEach(() => {
+ createComponent({ props: { selectedVariable: currentVariable } });
+ jest.spyOn(wrapper.vm, '$emit');
+ });
+
+ it('Dispatches `add-variable` action on submit', () => {
+ findAddorUpdateButton().vm.$emit('click');
+ expect(wrapper.emitted('add-variable')).toEqual([[currentVariable]]);
+ });
+
+ it('Dispatches the `hideModal` event when dismissing', () => {
+ findModal().vm.$emit('hidden');
+ expect(wrapper.emitted('hideModal')).toEqual([[]]);
+ });
+ });
+ });
+
+ describe('when protected by default', () => {
+ describe('when adding a new variable', () => {
+ beforeEach(() => {
+ createComponent({ provide: { isProtectedByDefault: true } });
+ findModal().vm.$emit('shown');
+ });
+
+ it('updates the protected value to true', () => {
+ expect(
+ findProtectedVariableCheckbox().attributes('data-is-protected-checked'),
+ ).toBeTruthy();
+ });
+ });
+
+ describe('when editing a variable', () => {
+ beforeEach(() => {
+ createComponent({
+ provide: { isProtectedByDefault: false },
+ props: {
+ selectedVariable: {},
+ mode: EDIT_VARIABLE_ACTION,
+ },
+ });
+ findModal().vm.$emit('shown');
+ });
+
+ it('keeps the value as false', async () => {
+ expect(
+ findProtectedVariableCheckbox().attributes('data-is-protected-checked'),
+ ).toBeUndefined();
+ });
+ });
+ });
+
+ describe('Adding a new non-AWS variable', () => {
+ beforeEach(() => {
+ const [variable] = mockVariablesWithScopes;
+ createComponent({ mountFn: mountExtended, props: { selectedVariable: variable } });
+ });
+
+ it('does not show AWS guidance tip', () => {
+ const tip = findAWSTip();
+ expect(tip.exists()).toBe(true);
+ expect(tip.isVisible()).toBe(false);
+ });
+ });
+
+ describe('Adding a new AWS variable', () => {
+ beforeEach(() => {
+ const [variable] = mockVariablesWithScopes;
+ const AWSKeyVariable = {
+ ...variable,
+ key: AWS_ACCESS_KEY_ID,
+ value: 'AKIAIOSFODNN7EXAMPLEjdhy',
+ };
+ createComponent({ mountFn: mountExtended, props: { selectedVariable: AWSKeyVariable } });
+ });
+
+ it('shows AWS guidance tip', () => {
+ const tip = findAWSTip();
+ expect(tip.exists()).toBe(true);
+ expect(tip.isVisible()).toBe(true);
+ });
+ });
+
+ describe('Reference warning when adding a variable', () => {
+ describe('with a $ character', () => {
+ beforeEach(() => {
+ const [variable] = mockVariablesWithScopes;
+ const variableWithDollarSign = {
+ ...variable,
+ value: 'valueWith$',
+ };
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable: variableWithDollarSign },
+ });
+ });
+
+ it(`renders the variable reference warning`, () => {
+ expect(findReferenceWarning().exists()).toBe(true);
+ });
+ });
+
+ describe('without a $ character', () => {
+ beforeEach(() => {
+ const [variable] = mockVariablesWithScopes;
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable: variable },
+ });
+ });
+
+ it(`does not render the variable reference warning`, () => {
+ expect(findReferenceWarning().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('Editing a variable', () => {
+ const [variable] = mockVariablesWithScopes;
+
+ beforeEach(() => {
+ createComponent({ props: { selectedVariable: variable, mode: EDIT_VARIABLE_ACTION } });
+ jest.spyOn(wrapper.vm, '$emit');
+ });
+
+ it('button text is Update variable when updating', () => {
+ expect(findAddorUpdateButton().text()).toBe('Update variable');
+ });
+
+ it('Update variable button dispatches updateVariable with correct variable', () => {
+ findAddorUpdateButton().vm.$emit('click');
+ expect(wrapper.emitted('update-variable')).toEqual([[variable]]);
+ });
+
+ it('Propagates the `hideModal` event', () => {
+ findModal().vm.$emit('hidden');
+ expect(wrapper.emitted('hideModal')).toEqual([[]]);
+ });
+
+ it('dispatches `delete-variable` with correct variable to delete', () => {
+ deleteVariableButton().vm.$emit('click');
+ expect(wrapper.emitted('delete-variable')).toEqual([[variable]]);
+ });
+ });
+
+ describe('Environment scope', () => {
+ describe('when feature is available', () => {
+ it('renders the environment dropdown', () => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: true,
+ },
+ });
+
+ expect(findCiEnvironmentsDropdown().exists()).toBe(true);
+ expect(findCiEnvironmentsDropdown().isVisible()).toBe(true);
+ });
+
+ it('renders a link to documentation on scopes', () => {
+ createComponent({ mountFn: mountExtended });
+
+ const link = findEnvScopeLink();
+
+ expect(link.attributes('title')).toBe(ENVIRONMENT_SCOPE_LINK_TITLE);
+ expect(link.attributes('href')).toBe(defaultProvide.environmentScopeLink);
+ });
+ });
+
+ describe('when feature is not available', () => {
+ it('disables the dropdown', () => {
+ createComponent({
+ mountFn: mountExtended,
+ props: {
+ areScopedVariablesAvailable: false,
+ },
+ });
+
+ expect(findCiEnvironmentsDropdown().exists()).toBe(false);
+ expect(findEnvScopeInput().attributes('readonly')).toBe('readonly');
+ });
+ });
+ });
+
+ describe('Validations', () => {
+ const maskError = 'This variable can not be masked.';
+
+ describe('when the mask state is invalid', () => {
+ beforeEach(async () => {
+ const [variable] = mockVariablesWithScopes;
+ const invalidMaskVariable = {
+ ...variable,
+ value: 'd:;',
+ masked: false,
+ };
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable: invalidMaskVariable },
+ });
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ await findMaskedVariableCheckbox().trigger('click');
+ });
+
+ it('disables the submit button', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeTruthy();
+ });
+
+ it('shows the correct error text', () => {
+ expect(findModal().text()).toContain(maskError);
+ });
+
+ it('sends the correct tracking event', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
+ label: EVENT_LABEL,
+ property: ';',
+ });
+ });
+ });
+
+ describe.each`
+ value | masked | eventSent | trackingErrorProperty
+ ${'secretValue'} | ${false} | ${0} | ${null}
+ ${'short'} | ${true} | ${0} | ${null}
+ ${'dollar$ign'} | ${false} | ${1} | ${'$'}
+ ${'dollar$ign'} | ${true} | ${1} | ${'$'}
+ ${'unsupported|char'} | ${true} | ${1} | ${'|'}
+ ${'unsupported|char'} | ${false} | ${0} | ${null}
+ `('Adding a new variable', ({ value, masked, eventSent, trackingErrorProperty }) => {
+ beforeEach(async () => {
+ const [variable] = mockVariablesWithScopes;
+ const invalidKeyVariable = {
+ ...variable,
+ value: '',
+ masked: false,
+ };
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable: invalidKeyVariable },
+ });
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ await findValueField().vm.$emit('input', value);
+ if (masked) {
+ await findMaskedVariableCheckbox().trigger('click');
+ }
+ });
+
+ it(`${
+ eventSent > 0 ? 'sends the correct' : 'does not send the'
+ } variable validation tracking event with ${value}`, () => {
+ expect(trackingSpy).toHaveBeenCalledTimes(eventSent);
+
+ if (eventSent > 0) {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
+ label: EVENT_LABEL,
+ property: trackingErrorProperty,
+ });
+ }
+ });
+ });
+
+ describe('when masked variable has acceptable value', () => {
+ beforeEach(() => {
+ const [variable] = mockVariablesWithScopes;
+ const validMaskandKeyVariable = {
+ ...variable,
+ key: AWS_ACCESS_KEY_ID,
+ value: '12345678',
+ masked: true,
+ };
+ createComponent({
+ mountFn: mountExtended,
+ props: { selectedVariable: validMaskandKeyVariable },
+ });
+ });
+
+ it('does not disable the submit button', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeFalsy();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci_variable_list/mocks.js b/spec/frontend/ci_variable_list/mocks.js
new file mode 100644
index 00000000000..1ba50c74152
--- /dev/null
+++ b/spec/frontend/ci_variable_list/mocks.js
@@ -0,0 +1,116 @@
+import { variableTypes } from '~/ci_variable_list/constants';
+
+export const devName = 'dev';
+export const prodName = 'prod';
+
+export const mockVariables = [
+ {
+ __typename: 'CiVariable',
+ id: 1,
+ key: 'my-var',
+ masked: false,
+ protected: true,
+ value: 'env_val',
+ variableType: variableTypes.variableType,
+ },
+ {
+ __typename: 'CiVariable',
+ id: 2,
+ key: 'secret',
+ masked: true,
+ protected: false,
+ value: 'the_secret_value',
+ variableType: variableTypes.fileType,
+ },
+];
+
+export const mockVariablesWithScopes = mockVariables.map((variable) => {
+ return { ...variable, environmentScope: '*' };
+});
+
+const createDefaultVars = ({ withScope = true } = {}) => {
+ let base = mockVariables;
+
+ if (withScope) {
+ base = mockVariablesWithScopes;
+ }
+
+ return {
+ __typename: 'CiVariableConnection',
+ nodes: base,
+ };
+};
+
+const defaultEnvs = {
+ __typename: 'EnvironmentConnection',
+ nodes: [
+ {
+ __typename: 'Environment',
+ id: 1,
+ name: prodName,
+ },
+ {
+ __typename: 'Environment',
+ id: 2,
+ name: devName,
+ },
+ ],
+};
+
+export const mockEnvs = defaultEnvs.nodes;
+
+export const mockProjectEnvironments = {
+ data: {
+ project: {
+ __typename: 'Project',
+ id: 1,
+ environments: defaultEnvs,
+ },
+ },
+};
+
+export const mockProjectVariables = {
+ data: {
+ project: {
+ __typename: 'Project',
+ id: 1,
+ ciVariables: createDefaultVars(),
+ },
+ },
+};
+
+export const mockGroupEnvironments = {
+ data: {
+ group: {
+ __typename: 'Group',
+ id: 1,
+ environments: defaultEnvs,
+ },
+ },
+};
+
+export const mockGroupVariables = {
+ data: {
+ group: {
+ __typename: 'Group',
+ id: 1,
+ ciVariables: createDefaultVars(),
+ },
+ },
+};
+
+export const mockAdminVariables = {
+ data: {
+ ciVariables: createDefaultVars({ withScope: false }),
+ },
+};
+
+export const newVariable = {
+ id: 3,
+ environmentScope: 'new',
+ key: 'AWS_RANDOM_THING',
+ masked: true,
+ protected: false,
+ value: 'devops',
+ variableType: variableTypes.variableType,
+};
diff --git a/spec/frontend/ci_variable_list/utils_spec.js b/spec/frontend/ci_variable_list/utils_spec.js
new file mode 100644
index 00000000000..1676e786515
--- /dev/null
+++ b/spec/frontend/ci_variable_list/utils_spec.js
@@ -0,0 +1,68 @@
+import {
+ createJoinedEnvironments,
+ convertEnvironmentScope,
+ mapEnvironmentNames,
+} from '~/ci_variable_list/utils';
+import { allEnvironments } from '~/ci_variable_list/constants';
+
+describe('utils', () => {
+ const environments = ['dev', 'prod'];
+
+ describe('createJoinedEnvironments', () => {
+ it('returns only `environments` if `variables` argument is undefined', () => {
+ const variables = undefined;
+
+ expect(createJoinedEnvironments(variables, environments)).toEqual(environments);
+ });
+
+ it('returns a list of environments and environment scopes taken from variables in alphabetical order', () => {
+ const envScope1 = 'new1';
+ const envScope2 = 'new2';
+
+ const variables = [{ environmentScope: envScope1 }, { environmentScope: envScope2 }];
+
+ expect(createJoinedEnvironments(variables, environments)).toEqual([
+ environments[0],
+ envScope1,
+ envScope2,
+ environments[1],
+ ]);
+ });
+
+ it('removes duplicate environments', () => {
+ const envScope1 = environments[0];
+ const envScope2 = 'new2';
+
+ const variables = [{ environmentScope: envScope1 }, { environmentScope: envScope2 }];
+
+ expect(createJoinedEnvironments(variables, environments)).toEqual([
+ environments[0],
+ envScope2,
+ environments[1],
+ ]);
+ });
+ });
+
+ describe('convertEnvironmentScope', () => {
+ it('converts the * to the `All environments` text', () => {
+ expect(convertEnvironmentScope('*')).toBe(allEnvironments.text);
+ });
+
+ it('returns the environment as is if not the *', () => {
+ expect(convertEnvironmentScope('prod')).toBe('prod');
+ });
+ });
+
+ describe('mapEnvironmentNames', () => {
+ const envName = 'dev';
+ const envName2 = 'prod';
+
+ const nodes = [
+ { name: envName, otherProp: {} },
+ { name: envName2, otherProp: {} },
+ ];
+ it('flatten a nodes array with only their names', () => {
+ expect(mapEnvironmentNames(nodes)).toEqual([envName, envName2]);
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js
index e1de2823726..ca552644258 100644
--- a/spec/frontend/content_editor/remark_markdown_processing_spec.js
+++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js
@@ -1170,7 +1170,7 @@ _world_.
const trimmed = markdown.trim();
const document = await deserialize(trimmed);
- expect(expectedDoc).not.toBeFalsy();
+ expect(expectedDoc).not.toBe(false);
expect(document.toJSON()).toEqual(expectedDoc.toJSON());
expect(serialize(document)).toEqual(expectedMarkdown ?? trimmed);
},
diff --git a/spec/frontend/labels/labels_select_spec.js b/spec/frontend/labels/labels_select_spec.js
index f6e280564cc..63f7c725bc7 100644
--- a/spec/frontend/labels/labels_select_spec.js
+++ b/spec/frontend/labels/labels_select_spec.js
@@ -101,6 +101,12 @@ describe('LabelsSelect', () => {
expect($labelEl.find('a').attr('data-html')).toBe('true');
});
+ it('generated label item template has correct title for tooltip', () => {
+ expect($labelEl.find('a').attr('title')).toBe(
+ "<span class='font-weight-bold scoped-label-tooltip-title'>Scoped label</span><br>Foobar",
+ );
+ });
+
it('generated label item template has correct label styles and classes', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color};`,
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 312e4f636c3..2c6b603197d 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -348,15 +348,13 @@ describe('URL utility', () => {
describe('urlContainsSha', () => {
it('returns true when there is a valid 40-character SHA1 hash in the URL', () => {
shas.valid.forEach((sha) => {
- expect(
- urlUtils.urlContainsSha({ url: `http://urlstuff/${sha}/moreurlstuff` }),
- ).toBeTruthy();
+ expect(urlUtils.urlContainsSha({ url: `http://urlstuff/${sha}/moreurlstuff` })).toBe(true);
});
});
it('returns false when there is not a valid 40-character SHA1 hash in the URL', () => {
shas.invalid.forEach((str) => {
- expect(urlUtils.urlContainsSha({ url: `http://urlstuff/${str}/moreurlstuff` })).toBeFalsy();
+ expect(urlUtils.urlContainsSha({ url: `http://urlstuff/${str}/moreurlstuff` })).toBe(false);
});
});
});
@@ -813,13 +811,13 @@ describe('URL utility', () => {
});
it('should compare against the window location if no compare value is provided', () => {
- expect(urlUtils.urlIsDifferent('different')).toBeTruthy();
- expect(urlUtils.urlIsDifferent(current)).toBeFalsy();
+ expect(urlUtils.urlIsDifferent('different')).toBe(true);
+ expect(urlUtils.urlIsDifferent(current)).toBe(false);
});
it('should use the provided compare value', () => {
- expect(urlUtils.urlIsDifferent('different', current)).toBeTruthy();
- expect(urlUtils.urlIsDifferent(current, current)).toBeFalsy();
+ expect(urlUtils.urlIsDifferent('different', current)).toBe(true);
+ expect(urlUtils.urlIsDifferent(current, current)).toBe(false);
});
});
diff --git a/spec/frontend/projects/project_new_spec.js b/spec/frontend/projects/project_new_spec.js
index 3034037fb1d..4fcecc3a307 100644
--- a/spec/frontend/projects/project_new_spec.js
+++ b/spec/frontend/projects/project_new_spec.js
@@ -1,6 +1,7 @@
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import projectNew from '~/projects/project_new';
+import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
describe('New Project', () => {
let $projectImportUrl;
@@ -12,21 +13,27 @@ describe('New Project', () => {
beforeEach(() => {
setHTMLFixture(`
- <div class='toggle-import-form'>
- <div class='import-url-data'>
- <div class="form-group">
- <input id="project_import_url" />
- </div>
- <div id="import-url-auth-method">
- <div class="form-group">
- <input id="project-import-url-user" />
+ <div class="tab-pane active">
+ <div class='toggle-import-form'>
+ <form id="new_project">
+ <div class='import-url-data'>
+ <div class="form-group">
+ <input id="project_import_url" />
+ </div>
+ <div id="import-url-auth-method">
+ <div class="form-group">
+ <input id="project-import-url-user" />
+ </div>
+ <div class="form-group">
+ <input id="project_import_url_password" />
+ </div>
+ </div>
+ <input id="project_name" />
+ <input id="project_path" />
</div>
- <div class="form-group">
- <input id="project_import_url_password" />
- </div>
- </div>
- <input id="project_name" />
- <input id="project_path" />
+ <div class="js-user-readme-repo"></div>
+ <button class="js-create-project-button"/>
+ </form>
</div>
</div>
`);
@@ -45,6 +52,38 @@ describe('New Project', () => {
el.value = value;
};
+ describe('tracks manual path input', () => {
+ let trackingSpy;
+
+ beforeEach(() => {
+ trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
+ projectNew.bindEvents();
+ $projectPath.oldInputValue = '_old_value_';
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks the event', () => {
+ $projectPath.value = '_new_value_';
+
+ triggerEvent($projectPath, 'blur');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'user_input_path_slug', {
+ label: 'new_project_form',
+ });
+ });
+
+ it('does not track the event when there has been no change', () => {
+ $projectPath.value = '_old_value_';
+
+ triggerEvent($projectPath, 'blur');
+
+ expect(trackingSpy).not.toHaveBeenCalled();
+ });
+ });
+
describe('deriveProjectPathFromUrl', () => {
const dummyImportUrl = `${TEST_HOST}/dummy/import/url.git`;
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_memory_usage_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_memory_usage_spec.js
index f0106914674..193a16bae8d 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_memory_usage_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_memory_usage_spec.js
@@ -80,20 +80,20 @@ describe('MemoryUsage', () => {
it('should have default data', () => {
const data = MemoryUsage.data();
- expect(Array.isArray(data.memoryMetrics)).toBeTruthy();
+ expect(Array.isArray(data.memoryMetrics)).toBe(true);
expect(data.memoryMetrics.length).toBe(0);
expect(typeof data.deploymentTime).toBe('number');
expect(data.deploymentTime).toBe(0);
expect(typeof data.hasMetrics).toBe('boolean');
- expect(data.hasMetrics).toBeFalsy();
+ expect(data.hasMetrics).toBe(false);
expect(typeof data.loadFailed).toBe('boolean');
- expect(data.loadFailed).toBeFalsy();
+ expect(data.loadFailed).toBe(false);
expect(typeof data.loadingMetrics).toBe('boolean');
- expect(data.loadingMetrics).toBeTruthy();
+ expect(data.loadingMetrics).toBe(true);
expect(typeof data.backOffRequestCounter).toBe('number');
expect(data.backOffRequestCounter).toBe(0);
@@ -144,7 +144,7 @@ describe('MemoryUsage', () => {
vm.computeGraphData(metrics, deployment_time);
const { hasMetrics, memoryMetrics, deploymentTime, memoryFrom, memoryTo } = vm;
- expect(hasMetrics).toBeTruthy();
+ expect(hasMetrics).toBe(true);
expect(memoryMetrics.length).toBeGreaterThan(0);
expect(deploymentTime).toEqual(deployment_time);
expect(memoryFrom).toEqual('9.13');
@@ -171,7 +171,7 @@ describe('MemoryUsage', () => {
describe('template', () => {
it('should render template elements correctly', () => {
- expect(el.classList.contains('mr-memory-usage')).toBeTruthy();
+ expect(el.classList.contains('mr-memory-usage')).toBe(true);
expect(el.querySelector('.js-usage-info')).toBeDefined();
});