Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-24 00:10:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-24 00:10:44 +0300
commit5e450e9022861d03048cc733c20585ad0891f5aa (patch)
tree6a5eb2f639fe66b3fa52008e2f99c31e1ce2d60b /spec
parentc37dd28c4afd33fee46cff8ddfdada8a3f54564c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/groups.rb2
-rw-r--r--spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js28
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_form_spec.js96
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js75
-rw-r--r--spec/frontend/alerts_settings/components/mocks/apollo_mock.js17
-rw-r--r--spec/frontend/alerts_settings/mocks/alert_fields.json (renamed from spec/frontend/alerts_settings/mocks/alertFields.json)0
-rw-r--r--spec/frontend/alerts_settings/mocks/parsed_mapping.json122
-rw-r--r--spec/frontend/alerts_settings/utils/mapping_transformations_spec.js34
-rw-r--r--spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap2
-rw-r--r--spec/frontend/packages/shared/components/package_list_row_spec.js20
-rw-r--r--spec/frontend/pages/shared/wikis/wiki_alert_spec.js40
-rw-r--r--spec/frontend/pipelines/stage_spec.js34
-rw-r--r--spec/frontend/registry/explorer/components/delete_button_spec.js1
-rw-r--r--spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb5
-rw-r--r--spec/migrations/migrate_delayed_project_removal_from_namespaces_to_namespace_settings_spec.rb30
-rw-r--r--spec/migrations/reschedule_set_default_iteration_cadences_spec.rb14
-rw-r--r--spec/models/iteration_spec.rb54
-rw-r--r--spec/models/namespace_spec.rb24
-rw-r--r--spec/support/shared_examples/graphql/mutation_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/timebox_shared_examples.rb4
20 files changed, 460 insertions, 144 deletions
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 17db69e4699..065eb36375a 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -15,7 +15,7 @@ FactoryBot.define do
raise "Don't set owner for groups, use `group.add_owner(user)` instead"
end
- create(:namespace_settings, namespace: group)
+ create(:namespace_settings, namespace: group) unless group.namespace_settings
end
trait :public do
diff --git a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
index 7e1d1acb62c..dba9c8be669 100644
--- a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
+++ b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
@@ -1,10 +1,10 @@
import { GlIcon, GlFormInput, GlDropdown, GlSearchBoxByType, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AlertMappingBuilder, { i18n } from '~/alerts_settings/components/alert_mapping_builder.vue';
-import parsedMapping from '~/alerts_settings/components/mocks/parsedMapping.json';
import * as transformationUtils from '~/alerts_settings/utils/mapping_transformations';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
-import alertFields from '../mocks/alertFields.json';
+import alertFields from '../mocks/alert_fields.json';
+import parsedMapping from '../mocks/parsed_mapping.json';
describe('AlertMappingBuilder', () => {
let wrapper;
@@ -12,8 +12,8 @@ describe('AlertMappingBuilder', () => {
function mountComponent() {
wrapper = shallowMount(AlertMappingBuilder, {
propsData: {
- parsedPayload: parsedMapping.samplePayload.payloadAlerFields.nodes,
- savedMapping: parsedMapping.storedMapping.nodes,
+ parsedPayload: parsedMapping.payloadAlerFields,
+ savedMapping: parsedMapping.payloadAttributeMappings,
alertFields,
},
});
@@ -33,6 +33,15 @@ describe('AlertMappingBuilder', () => {
const findColumnInRow = (row, column) =>
wrapper.findAll('.gl-display-table-row').at(row).findAll('.gl-display-table-cell ').at(column);
+ const getDropdownContent = (dropdown, types) => {
+ const searchBox = dropdown.findComponent(GlSearchBoxByType);
+ const dropdownItems = dropdown.findAllComponents(GlDropdownItem);
+ const mappingOptions = parsedMapping.payloadAlerFields.filter(({ type }) =>
+ types.includes(type),
+ );
+ return { searchBox, dropdownItems, mappingOptions };
+ };
+
it('renders column captions', () => {
expect(findColumnInRow(0, 0).text()).toContain(i18n.columns.gitlabKeyTitle);
expect(findColumnInRow(0, 2).text()).toContain(i18n.columns.payloadKeyTitle);
@@ -63,10 +72,7 @@ describe('AlertMappingBuilder', () => {
it('renders mapping dropdown for each field', () => {
alertFields.forEach(({ types }, index) => {
const dropdown = findColumnInRow(index + 1, 2).find(GlDropdown);
- const searchBox = dropdown.findComponent(GlSearchBoxByType);
- const dropdownItems = dropdown.findAllComponents(GlDropdownItem);
- const { nodes } = parsedMapping.samplePayload.payloadAlerFields;
- const mappingOptions = nodes.filter(({ type }) => types.includes(type));
+ const { searchBox, dropdownItems, mappingOptions } = getDropdownContent(dropdown, types);
expect(dropdown.exists()).toBe(true);
expect(searchBox.exists()).toBe(true);
@@ -80,11 +86,7 @@ describe('AlertMappingBuilder', () => {
expect(dropdown.exists()).toBe(Boolean(numberOfFallbacks));
if (numberOfFallbacks) {
- const searchBox = dropdown.findComponent(GlSearchBoxByType);
- const dropdownItems = dropdown.findAllComponents(GlDropdownItem);
- const { nodes } = parsedMapping.samplePayload.payloadAlerFields;
- const mappingOptions = nodes.filter(({ type }) => types.includes(type));
-
+ const { searchBox, dropdownItems, mappingOptions } = getDropdownContent(dropdown, types);
expect(searchBox.exists()).toBe(Boolean(numberOfFallbacks));
expect(dropdownItems).toHaveLength(mappingOptions.length);
}
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
index 02229b3d3da..511b3d2a059 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
@@ -11,7 +11,8 @@ import waitForPromises from 'helpers/wait_for_promises';
import MappingBuilder from '~/alerts_settings/components/alert_mapping_builder.vue';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
import { typeSet } from '~/alerts_settings/constants';
-import alertFields from '../mocks/alertFields.json';
+import alertFields from '../mocks/alert_fields.json';
+import parsedMapping from '../mocks/parsed_mapping.json';
import { defaultAlertSettingsConfig } from './util';
describe('AlertsSettingsForm', () => {
@@ -39,6 +40,9 @@ describe('AlertsSettingsForm', () => {
multiIntegrations,
},
mocks: {
+ $apollo: {
+ query: jest.fn(),
+ },
$toast: {
show: mockToastShow,
},
@@ -146,7 +150,7 @@ describe('AlertsSettingsForm', () => {
enableIntegration(0, integrationName);
- const sampleMapping = { field: 'test' };
+ const sampleMapping = parsedMapping.payloadAttributeMappings;
findMappingBuilder().vm.$emit('onMappingUpdate', sampleMapping);
findForm().trigger('submit');
@@ -157,7 +161,7 @@ describe('AlertsSettingsForm', () => {
name: integrationName,
active: true,
payloadAttributeMappings: sampleMapping,
- payloadExample: null,
+ payloadExample: '{}',
},
},
]);
@@ -275,34 +279,47 @@ describe('AlertsSettingsForm', () => {
});
describe('Test payload section for HTTP integration', () => {
+ const validSamplePayload = JSON.stringify(alertFields);
+ const emptySamplePayload = '{}';
+
beforeEach(() => {
createComponent({
multipleHttpIntegrationsCustomMapping: true,
- props: {
+ data: {
currentIntegration: {
type: typeSet.http,
+ payloadExample: validSamplePayload,
+ payloadAttributeMappings: [],
},
- alertFields,
+ active: false,
+ resetPayloadAndMappingConfirmed: false,
},
+ props: { alertFields },
});
});
describe.each`
- active | resetSamplePayloadConfirmed | disabled
- ${true} | ${true} | ${undefined}
- ${false} | ${true} | ${'disabled'}
- ${true} | ${false} | ${'disabled'}
- ${false} | ${false} | ${'disabled'}
- `('', ({ active, resetSamplePayloadConfirmed, disabled }) => {
- const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
+ active | resetPayloadAndMappingConfirmed | disabled
+ ${true} | ${true} | ${undefined}
+ ${false} | ${true} | ${'disabled'}
+ ${true} | ${false} | ${'disabled'}
+ ${false} | ${false} | ${'disabled'}
+ `('', ({ active, resetPayloadAndMappingConfirmed, disabled }) => {
+ const payloadResetMsg = resetPayloadAndMappingConfirmed
+ ? 'was confirmed'
+ : 'was not confirmed';
const enabledState = disabled === 'disabled' ? 'disabled' : 'enabled';
const activeState = active ? 'active' : 'not active';
it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => {
wrapper.setData({
- customMapping: { samplePayload: true },
+ currentIntegration: {
+ type: typeSet.http,
+ payloadExample: validSamplePayload,
+ payloadAttributeMappings: [],
+ },
active,
- resetSamplePayloadConfirmed,
+ resetPayloadAndMappingConfirmed,
});
await wrapper.vm.$nextTick();
expect(findTestPayloadSection().find(GlFormTextarea).attributes('disabled')).toBe(disabled);
@@ -311,20 +328,27 @@ describe('AlertsSettingsForm', () => {
describe('action buttons for sample payload', () => {
describe.each`
- resetSamplePayloadConfirmed | samplePayload | caption
- ${false} | ${true} | ${'Edit payload'}
- ${true} | ${false} | ${'Submit payload'}
- ${true} | ${true} | ${'Submit payload'}
- ${false} | ${false} | ${'Submit payload'}
- `('', ({ resetSamplePayloadConfirmed, samplePayload, caption }) => {
- const samplePayloadMsg = samplePayload ? 'was provided' : 'was not provided';
- const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
+ resetPayloadAndMappingConfirmed | payloadExample | caption
+ ${false} | ${validSamplePayload} | ${'Edit payload'}
+ ${true} | ${emptySamplePayload} | ${'Submit payload'}
+ ${true} | ${validSamplePayload} | ${'Submit payload'}
+ ${false} | ${emptySamplePayload} | ${'Submit payload'}
+ `('', ({ resetPayloadAndMappingConfirmed, payloadExample, caption }) => {
+ const samplePayloadMsg = payloadExample ? 'was provided' : 'was not provided';
+ const payloadResetMsg = resetPayloadAndMappingConfirmed
+ ? 'was confirmed'
+ : 'was not confirmed';
it(`shows ${caption} button when sample payload ${samplePayloadMsg} and payload reset ${payloadResetMsg}`, async () => {
wrapper.setData({
selectedIntegration: typeSet.http,
- customMapping: { samplePayload },
- resetSamplePayloadConfirmed,
+ currentIntegration: {
+ payloadExample,
+ type: typeSet.http,
+ active: true,
+ payloadAttributeMappings: [],
+ },
+ resetPayloadAndMappingConfirmed,
});
await wrapper.vm.$nextTick();
expect(findActionBtn().text()).toBe(caption);
@@ -333,16 +357,20 @@ describe('AlertsSettingsForm', () => {
});
describe('Parsing payload', () => {
- it('displays a toast message on successful parse', async () => {
- jest.useFakeTimers();
+ beforeEach(() => {
wrapper.setData({
selectedIntegration: typeSet.http,
- customMapping: { samplePayload: false },
+ resetPayloadAndMappingConfirmed: true,
});
- await wrapper.vm.$nextTick();
+ });
+ it('displays a toast message on successful parse', async () => {
+ jest.spyOn(wrapper.vm.$apollo, 'query').mockResolvedValue({
+ data: {
+ project: { alertManagementPayloadFields: [] },
+ },
+ });
findActionBtn().vm.$emit('click');
- jest.advanceTimersByTime(1000);
await waitForPromises();
@@ -350,6 +378,16 @@ describe('AlertsSettingsForm', () => {
'Sample payload has been parsed. You can now map the fields.',
);
});
+
+ it('displays an error message under payload field on unsuccessful parse', async () => {
+ const errorMessage = 'Error parsing paylod';
+ jest.spyOn(wrapper.vm.$apollo, 'query').mockRejectedValue({ message: errorMessage });
+ findActionBtn().vm.$emit('click');
+
+ await waitForPromises();
+
+ expect(findTestPayloadSection().find('.invalid-feedback').text()).toBe(errorMessage);
+ });
});
});
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index 80293597ab6..409805fdbf7 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -14,6 +14,8 @@ import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutat
import destroyHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql';
+import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql';
+import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updateHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
@@ -31,7 +33,8 @@ import {
updateHttpVariables,
createPrometheusVariables,
updatePrometheusVariables,
- ID,
+ HTTP_ID,
+ PROMETHEUS_ID,
errorMsg,
getIntegrationsQueryResponse,
destroyIntegrationResponse,
@@ -50,8 +53,30 @@ describe('AlertsSettingsWrapper', () => {
let fakeApollo;
let destroyIntegrationHandler;
useMockIntersectionObserver();
+ const httpMappingData = {
+ payloadExample: '{"test: : "field"}',
+ payloadAttributeMappings: [],
+ payloadAlertFields: [],
+ };
+ const httpIntegrations = {
+ list: [
+ {
+ id: mockIntegrations[0].id,
+ ...httpMappingData,
+ },
+ {
+ id: mockIntegrations[1].id,
+ ...httpMappingData,
+ },
+ {
+ id: mockIntegrations[2].id,
+ httpMappingData,
+ },
+ ],
+ };
- const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon);
+ const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon);
+ const findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
async function destroyHttpIntegration(localWrapper) {
@@ -197,13 +222,13 @@ describe('AlertsSettingsWrapper', () => {
});
wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
type: typeSet.http,
- variables: { id: ID },
+ variables: { id: HTTP_ID },
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: resetHttpTokenMutation,
variables: {
- id: ID,
+ id: HTTP_ID,
},
});
});
@@ -232,7 +257,7 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
- data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
+ data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[3] },
loading: false,
});
@@ -261,13 +286,13 @@ describe('AlertsSettingsWrapper', () => {
});
wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
type: typeSet.prometheus,
- variables: { id: ID },
+ variables: { id: PROMETHEUS_ID },
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: resetPrometheusTokenMutation,
variables: {
- id: ID,
+ id: PROMETHEUS_ID,
},
});
});
@@ -328,6 +353,42 @@ describe('AlertsSettingsWrapper', () => {
mock.restore();
});
});
+
+ it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation` on HTTP integration edit', () => {
+ createComponent({
+ data: {
+ integrations: { list: mockIntegrations },
+ currentIntegration: mockIntegrations[0],
+ httpIntegrations,
+ },
+ loading: false,
+ });
+
+ jest.spyOn(wrapper.vm.$apollo, 'mutate');
+ findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateCurrentHttpIntegrationMutation,
+ variables: { ...mockIntegrations[0], ...httpMappingData },
+ });
+ });
+
+ it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation` on PROMETHEUS integration edit', () => {
+ createComponent({
+ data: {
+ integrations: { list: mockIntegrations },
+ currentIntegration: mockIntegrations[3],
+ httpIntegrations,
+ },
+ loading: false,
+ });
+
+ jest.spyOn(wrapper.vm.$apollo, 'mutate');
+ findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateCurrentPrometheusIntegrationMutation,
+ variables: mockIntegrations[3],
+ });
+ });
});
describe('with mocked Apollo client', () => {
diff --git a/spec/frontend/alerts_settings/components/mocks/apollo_mock.js b/spec/frontend/alerts_settings/components/mocks/apollo_mock.js
index e0eba1e8421..828580a436b 100644
--- a/spec/frontend/alerts_settings/components/mocks/apollo_mock.js
+++ b/spec/frontend/alerts_settings/components/mocks/apollo_mock.js
@@ -1,29 +1,34 @@
const projectPath = '';
-export const ID = 'gid://gitlab/AlertManagement::HttpIntegration/7';
+export const HTTP_ID = 'gid://gitlab/AlertManagement::HttpIntegration/7';
+export const PROMETHEUS_ID = 'gid://gitlab/PrometheusService/12';
export const errorMsg = 'Something went wrong';
export const createHttpVariables = {
name: 'Test Pre',
active: true,
projectPath,
+ type: 'HTTP',
};
export const updateHttpVariables = {
name: 'Test Pre',
active: true,
- id: ID,
+ id: HTTP_ID,
+ type: 'HTTP',
};
export const createPrometheusVariables = {
apiUrl: 'https://test-pre.com',
active: true,
projectPath,
+ type: 'PROMETHEUS',
};
export const updatePrometheusVariables = {
apiUrl: 'https://test-pre.com',
active: true,
- id: ID,
+ id: PROMETHEUS_ID,
+ type: 'PROMETHEUS',
};
export const getIntegrationsQueryResponse = {
@@ -99,6 +104,9 @@ export const destroyIntegrationResponse = {
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
+ payloadExample: '{"field": "value"}',
+ payloadAttributeMappings: [],
+ payloadAlertFields: [],
},
},
},
@@ -117,6 +125,9 @@ export const destroyIntegrationResponseWithErrors = {
'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json',
token: '89eb01df471d990ff5162a1c640408cf',
apiUrl: null,
+ payloadExample: '{"field": "value"}',
+ payloadAttributeMappings: [],
+ payloadAlertFields: [],
},
},
},
diff --git a/spec/frontend/alerts_settings/mocks/alertFields.json b/spec/frontend/alerts_settings/mocks/alert_fields.json
index ffe59dd0c05..ffe59dd0c05 100644
--- a/spec/frontend/alerts_settings/mocks/alertFields.json
+++ b/spec/frontend/alerts_settings/mocks/alert_fields.json
diff --git a/spec/frontend/alerts_settings/mocks/parsed_mapping.json b/spec/frontend/alerts_settings/mocks/parsed_mapping.json
new file mode 100644
index 00000000000..e985671a923
--- /dev/null
+++ b/spec/frontend/alerts_settings/mocks/parsed_mapping.json
@@ -0,0 +1,122 @@
+{
+ "payloadAlerFields": [
+ {
+ "path": [
+ "dashboardId"
+ ],
+ "label": "Dashboard Id",
+ "type": "string"
+ },
+ {
+ "path": [
+ "evalMatches"
+ ],
+ "label": "Eval Matches",
+ "type": "array"
+ },
+ {
+ "path": [
+ "createdAt"
+ ],
+ "label": "Created At",
+ "type": "datetime"
+ },
+ {
+ "path": [
+ "imageUrl"
+ ],
+ "label": "Image Url",
+ "type": "string"
+ },
+ {
+ "path": [
+ "message"
+ ],
+ "label": "Message",
+ "type": "string"
+ },
+ {
+ "path": [
+ "orgId"
+ ],
+ "label": "Org Id",
+ "type": "string"
+ },
+ {
+ "path": [
+ "panelId"
+ ],
+ "label": "Panel Id",
+ "type": "string"
+ },
+ {
+ "path": [
+ "ruleId"
+ ],
+ "label": "Rule Id",
+ "type": "string"
+ },
+ {
+ "path": [
+ "ruleName"
+ ],
+ "label": "Rule Name",
+ "type": "string"
+ },
+ {
+ "path": [
+ "ruleUrl"
+ ],
+ "label": "Rule Url",
+ "type": "string"
+ },
+ {
+ "path": [
+ "state"
+ ],
+ "label": "State",
+ "type": "string"
+ },
+ {
+ "path": [
+ "title"
+ ],
+ "label": "Title",
+ "type": "string"
+ },
+ {
+ "path": [
+ "tags",
+ "tag"
+ ],
+ "label": "Tags",
+ "type": "string"
+ }
+ ],
+ "payloadAttributeMappings": [
+ {
+ "fieldName": "title",
+ "label": "Title",
+ "type": "STRING",
+ "path": ["title"]
+ },
+ {
+ "fieldName": "description",
+ "label": "description",
+ "type": "STRING",
+ "path": ["description"]
+ },
+ {
+ "fieldName": "hosts",
+ "label": "Host",
+ "type": "ARRAY",
+ "path": ["hosts", "host"]
+ },
+ {
+ "fieldName": "startTime",
+ "label": "Created Atd",
+ "type": "STRING",
+ "path": ["time", "createdAt"]
+ }
+ ]
+}
diff --git a/spec/frontend/alerts_settings/utils/mapping_transformations_spec.js b/spec/frontend/alerts_settings/utils/mapping_transformations_spec.js
index 8c1977ffebe..62b95c6078b 100644
--- a/spec/frontend/alerts_settings/utils/mapping_transformations_spec.js
+++ b/spec/frontend/alerts_settings/utils/mapping_transformations_spec.js
@@ -1,29 +1,25 @@
-import parsedMapping from '~/alerts_settings/components/mocks/parsedMapping.json';
-import {
- getMappingData,
- getPayloadFields,
- transformForSave,
-} from '~/alerts_settings/utils/mapping_transformations';
-import alertFields from '../mocks/alertFields.json';
+import { getMappingData, transformForSave } from '~/alerts_settings/utils/mapping_transformations';
+import alertFields from '../mocks/alert_fields.json';
+import parsedMapping from '../mocks/parsed_mapping.json';
describe('Mapping Transformation Utilities', () => {
const nameField = {
label: 'Name',
path: ['alert', 'name'],
- type: 'string',
+ type: 'STRING',
};
const dashboardField = {
label: 'Dashboard Id',
path: ['alert', 'dashboardId'],
- type: 'string',
+ type: 'STRING',
};
describe('getMappingData', () => {
it('should return mapping data', () => {
const result = getMappingData(
alertFields,
- getPayloadFields(parsedMapping.samplePayload.payloadAlerFields.nodes.slice(0, 3)),
- parsedMapping.storedMapping.nodes.slice(0, 3),
+ parsedMapping.payloadAlerFields.slice(0, 3),
+ parsedMapping.payloadAttributeMappings.slice(0, 3),
);
result.forEach((data, index) => {
@@ -44,8 +40,8 @@ describe('Mapping Transformation Utilities', () => {
const mockMappingData = [
{
name: fieldName,
- mapping: 'alert_name',
- mappingFields: getPayloadFields([dashboardField, nameField]),
+ mapping: ['alert', 'name'],
+ mappingFields: [dashboardField, nameField],
},
];
const result = transformForSave(mockMappingData);
@@ -61,21 +57,11 @@ describe('Mapping Transformation Utilities', () => {
{
name: fieldName,
mapping: null,
- mappingFields: getPayloadFields([nameField, dashboardField]),
+ mappingFields: [nameField, dashboardField],
},
];
const result = transformForSave(mockMappingData);
expect(result).toEqual([]);
});
});
-
- describe('getPayloadFields', () => {
- it('should add name field to each payload field', () => {
- const result = getPayloadFields([nameField, dashboardField]);
- expect(result).toEqual([
- { ...nameField, name: 'alert_name' },
- { ...dashboardField, name: 'alert_dashboardId' },
- ]);
- });
- });
});
diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
index 73a64875026..77095f7c611 100644
--- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
@@ -102,7 +102,7 @@ exports[`packages_list_row renders 1`] = `
<gl-button-stub
aria-label="Remove package"
buttontextclasses=""
- category="primary"
+ category="secondary"
data-testid="action-delete"
icon="remove"
size="medium"
diff --git a/spec/frontend/packages/shared/components/package_list_row_spec.js b/spec/frontend/packages/shared/components/package_list_row_spec.js
index bd122167273..1c0ef7e3539 100644
--- a/spec/frontend/packages/shared/components/package_list_row_spec.js
+++ b/spec/frontend/packages/shared/components/package_list_row_spec.js
@@ -60,11 +60,9 @@ describe('packages_list_row', () => {
});
describe('when is is group', () => {
- beforeEach(() => {
+ it('has a package path component', () => {
mountComponent({ isGroup: true });
- });
- it('has a package path component', () => {
expect(findPackagePath().exists()).toBe(true);
expect(findPackagePath().props()).toMatchObject({ path: 'foo/bar/baz' });
});
@@ -92,10 +90,22 @@ describe('packages_list_row', () => {
});
});
- describe('delete event', () => {
- beforeEach(() => mountComponent({ packageEntity: packageWithoutTags }));
+ describe('delete button', () => {
+ it('exists and has the correct props', () => {
+ mountComponent({ packageEntity: packageWithoutTags });
+
+ expect(findDeleteButton().exists()).toBe(true);
+ expect(findDeleteButton().attributes()).toMatchObject({
+ icon: 'remove',
+ category: 'secondary',
+ variant: 'danger',
+ title: 'Remove package',
+ });
+ });
it('emits the packageToDelete event when the delete button is clicked', async () => {
+ mountComponent({ packageEntity: packageWithoutTags });
+
findDeleteButton().vm.$emit('click');
await wrapper.vm.$nextTick();
diff --git a/spec/frontend/pages/shared/wikis/wiki_alert_spec.js b/spec/frontend/pages/shared/wikis/wiki_alert_spec.js
new file mode 100644
index 00000000000..6a18473b1a7
--- /dev/null
+++ b/spec/frontend/pages/shared/wikis/wiki_alert_spec.js
@@ -0,0 +1,40 @@
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import WikiAlert from '~/pages/shared/wikis/components/wiki_alert.vue';
+
+describe('WikiAlert', () => {
+ let wrapper;
+ const ERROR = 'There is already a page with the same title in that path.';
+ const ERROR_WITH_LINK = 'Before text %{wikiLinkStart}the page%{wikiLinkEnd} after text.';
+ const PATH = '/test';
+
+ function createWrapper(propsData = {}, stubs = {}) {
+ wrapper = shallowMount(WikiAlert, {
+ propsData: { wikiPagePath: PATH, ...propsData },
+ stubs,
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findGlAlert = () => wrapper.findComponent(GlAlert);
+ const findGlLink = () => wrapper.findComponent(GlLink);
+ const findGlSprintf = () => wrapper.findComponent(GlSprintf);
+
+ describe('Wiki Alert', () => {
+ it('shows an alert when there is an error', () => {
+ createWrapper({ error: ERROR });
+ expect(findGlAlert().exists()).toBe(true);
+ expect(findGlSprintf().exists()).toBe(true);
+ expect(findGlSprintf().attributes('message')).toBe(ERROR);
+ });
+
+ it('shows a the link to the help path', () => {
+ createWrapper({ error: ERROR_WITH_LINK }, { GlAlert, GlSprintf });
+ expect(findGlLink().attributes('href')).toBe(PATH);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/stage_spec.js b/spec/frontend/pipelines/stage_spec.js
index bc4c5600d99..db15f6520ca 100644
--- a/spec/frontend/pipelines/stage_spec.js
+++ b/spec/frontend/pipelines/stage_spec.js
@@ -142,6 +142,8 @@ describe('Pipelines stage component', () => {
beforeEach(() => {
mock.onGet(dropdownPath).reply(200, stageReply);
mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200);
+
+ createComponent();
});
const clickCiAction = async () => {
@@ -152,34 +154,22 @@ describe('Pipelines stage component', () => {
await axios.waitForAll();
};
- describe('within pipeline table', () => {
- beforeEach(() => {
- createComponent({ type: 'PIPELINES_TABLE' });
- });
-
- it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', async () => {
- await clickCiAction();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable');
- });
- });
+ it('closes dropdown when job item action is clicked', async () => {
+ const hidden = jest.fn();
- describe('in MR widget', () => {
- beforeEach(() => {
- createComponent();
- });
+ wrapper.vm.$root.$on('bv::dropdown::hide', hidden);
- it('closes the dropdown when `pipelineActionRequestComplete` is triggered', async () => {
- const hidden = jest.fn();
+ expect(hidden).toHaveBeenCalledTimes(0);
- wrapper.vm.$root.$on('bv::dropdown::hide', hidden);
+ await clickCiAction();
- expect(hidden).toHaveBeenCalledTimes(0);
+ expect(hidden).toHaveBeenCalledTimes(1);
+ });
- await clickCiAction();
+ it('emits `pipelineActionRequestComplete` when job item action is clicked', async () => {
+ await clickCiAction();
- expect(hidden).toHaveBeenCalledTimes(1);
- });
+ expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/registry/explorer/components/delete_button_spec.js b/spec/frontend/registry/explorer/components/delete_button_spec.js
index a557d9afacc..4597c42add9 100644
--- a/spec/frontend/registry/explorer/components/delete_button_spec.js
+++ b/spec/frontend/registry/explorer/components/delete_button_spec.js
@@ -58,6 +58,7 @@ describe('delete_button', () => {
title: 'Foo title',
variant: 'danger',
disabled: 'true',
+ category: 'secondary',
});
});
diff --git a/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb b/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb
index 1f1877f5d2b..46c919f0854 100644
--- a/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb
+++ b/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb
@@ -58,12 +58,10 @@ RSpec.describe Gitlab::BackgroundMigration::SetDefaultIterationCadences, schema:
context 'when an iteration cadence exists for a group' do
let!(:group) { namespaces.create!(name: 'group', path: 'group') }
- let!(:iterations_cadence_1) { iterations_cadences.create!(group_id: group.id, start_date: 5.days.ago, title: 'Cadence 1') }
- let!(:iterations_cadence_2) { iterations_cadences.create!(group_id: group.id, start_date: 2.days.ago, title: 'Cadence 2') }
+ let!(:iterations_cadence_1) { iterations_cadences.create!(group_id: group.id, start_date: 2.days.ago, title: 'Cadence 1') }
let!(:iteration_1) { iterations.create!(group_id: group.id, iid: 1, title: 'Iteration 1', start_date: 10.days.ago, due_date: 8.days.ago) }
let!(:iteration_2) { iterations.create!(group_id: group.id, iterations_cadence_id: iterations_cadence_1.id, iid: 2, title: 'Iteration 2', start_date: 5.days.ago, due_date: 3.days.ago) }
- let!(:iteration_3) { iterations.create!(group_id: group.id, iterations_cadence_id: iterations_cadence_2.id, iid: 3, title: 'Iteration 3', start_date: 2.days.ago, due_date: 1.day.ago) }
subject { described_class.new.perform(group.id) }
@@ -76,7 +74,6 @@ RSpec.describe Gitlab::BackgroundMigration::SetDefaultIterationCadences, schema:
expect(iteration_1.reload.iterations_cadence_id).to eq(iterations_cadence_1.id)
expect(iteration_2.reload.iterations_cadence_id).to eq(iterations_cadence_1.id)
- expect(iteration_3.reload.iterations_cadence_id).to eq(iterations_cadence_2.id)
end
end
end
diff --git a/spec/migrations/migrate_delayed_project_removal_from_namespaces_to_namespace_settings_spec.rb b/spec/migrations/migrate_delayed_project_removal_from_namespaces_to_namespace_settings_spec.rb
new file mode 100644
index 00000000000..28a8dcf0d4c
--- /dev/null
+++ b/spec/migrations/migrate_delayed_project_removal_from_namespaces_to_namespace_settings_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require Rails.root.join('db', 'post_migrate', '20210215095328_migrate_delayed_project_removal_from_namespaces_to_namespace_settings.rb')
+
+RSpec.describe MigrateDelayedProjectRemovalFromNamespacesToNamespaceSettings, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:namespace_settings) { table(:namespace_settings) }
+
+ let!(:namespace_wo_settings) { namespaces.create!(name: generate(:name), path: generate(:name), delayed_project_removal: true) }
+ let!(:namespace_wo_settings_delay_false) { namespaces.create!(name: generate(:name), path: generate(:name), delayed_project_removal: false) }
+ let!(:namespace_w_settings_delay_true) { namespaces.create!(name: generate(:name), path: generate(:name), delayed_project_removal: true) }
+ let!(:namespace_w_settings_delay_false) { namespaces.create!(name: generate(:name), path: generate(:name), delayed_project_removal: false) }
+
+ let!(:namespace_settings_delay_true) { namespace_settings.create!(namespace_id: namespace_w_settings_delay_true.id, delayed_project_removal: false, created_at: DateTime.now, updated_at: DateTime.now) }
+ let!(:namespace_settings_delay_false) { namespace_settings.create!(namespace_id: namespace_w_settings_delay_false.id, delayed_project_removal: false, created_at: DateTime.now, updated_at: DateTime.now) }
+
+ it 'migrates delayed_project_removal to namespace_settings' do
+ disable_migrations_output { migrate! }
+
+ expect(namespace_settings.count).to eq(3)
+
+ expect(namespace_settings.find_by(namespace_id: namespace_wo_settings.id).delayed_project_removal).to eq(true)
+ expect(namespace_settings.find_by(namespace_id: namespace_wo_settings_delay_false.id)).to be_nil
+
+ expect(namespace_settings_delay_true.reload.delayed_project_removal).to eq(true)
+ expect(namespace_settings_delay_false.reload.delayed_project_removal).to eq(false)
+ end
+end
diff --git a/spec/migrations/reschedule_set_default_iteration_cadences_spec.rb b/spec/migrations/reschedule_set_default_iteration_cadences_spec.rb
index 25c2b4efe8b..fb629c90d9f 100644
--- a/spec/migrations/reschedule_set_default_iteration_cadences_spec.rb
+++ b/spec/migrations/reschedule_set_default_iteration_cadences_spec.rb
@@ -16,13 +16,13 @@ RSpec.describe RescheduleSetDefaultIterationCadences do
let(:group_7) { namespaces.create!(name: 'test_7', path: 'test_7') }
let(:group_8) { namespaces.create!(name: 'test_8', path: 'test_8') }
- let!(:iteration_1) { iterations.create!(iid: 1, title: 'iteration 1', group_id: group_1.id) }
- let!(:iteration_2) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_3.id) }
- let!(:iteration_3) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_4.id) }
- let!(:iteration_4) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_5.id) }
- let!(:iteration_5) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_6.id) }
- let!(:iteration_6) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_7.id) }
- let!(:iteration_7) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_8.id) }
+ let!(:iteration_1) { iterations.create!(iid: 1, title: 'iteration 1', group_id: group_1.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_2) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_3.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_3) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_4.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_4) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_5.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_5) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_6.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_6) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_7.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
+ let!(:iteration_7) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_8.id, start_date: 2.days.from_now, due_date: 3.days.from_now) }
around do |example|
freeze_time { Sidekiq::Testing.fake! { example.run } }
diff --git a/spec/models/iteration_spec.rb b/spec/models/iteration_spec.rb
index 7241a07a215..7c57f08b2bd 100644
--- a/spec/models/iteration_spec.rb
+++ b/spec/models/iteration_spec.rb
@@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe Iteration do
- let_it_be(:project) { create(:project) }
- let_it_be(:group) { create(:group) }
let(:set_cadence) { nil }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:group) }
@@ -67,7 +68,7 @@ RSpec.describe Iteration do
expect { iteration.save! }.to change { Iterations::Cadence.count }.by(1)
end
- it 'sets the newly created iterations_cadence to the reecord' do
+ it 'sets the newly created iterations_cadence to the record' do
iteration.save!
expect(iteration.iterations_cadence).to eq(Iterations::Cadence.last)
@@ -148,7 +149,7 @@ RSpec.describe Iteration do
context 'Validations' do
subject { build(:iteration, group: group, start_date: start_date, due_date: due_date) }
- describe '#not_belonging_to_project' do
+ describe 'when iteration belongs to project' do
subject { build(:iteration, project: project, start_date: Time.current, due_date: 1.day.from_now) }
it 'is invalid' do
@@ -180,13 +181,13 @@ RSpec.describe Iteration do
let(:due_date) { 6.days.from_now }
shared_examples_for 'overlapping dates' do |skip_constraint_test: false|
- context 'when start_date is in range' do
+ context 'when start_date overlaps' do
let(:start_date) { 5.days.from_now }
let(:due_date) { 3.weeks.from_now }
it 'is not valid' do
expect(subject).not_to be_valid
- expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
+ expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations within this group')
end
unless skip_constraint_test
@@ -197,13 +198,13 @@ RSpec.describe Iteration do
end
end
- context 'when end_date is in range' do
+ context 'when due_date overlaps' do
let(:start_date) { Time.current }
let(:due_date) { 6.days.from_now }
it 'is not valid' do
expect(subject).not_to be_valid
- expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
+ expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations within this group')
end
unless skip_constraint_test
@@ -217,7 +218,7 @@ RSpec.describe Iteration do
context 'when both overlap' do
it 'is not valid' do
expect(subject).not_to be_valid
- expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
+ expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations within this group')
end
unless skip_constraint_test
@@ -231,7 +232,7 @@ RSpec.describe Iteration do
context 'group' do
it_behaves_like 'overlapping dates' do
- let(:constraint_name) { 'iteration_start_and_due_daterange_group_id_constraint' }
+ let(:constraint_name) { 'iteration_start_and_due_date_iterations_cadence_id_constraint' }
end
context 'different group' do
@@ -249,11 +250,12 @@ RSpec.describe Iteration do
subject { build(:iteration, group: subgroup, start_date: start_date, due_date: due_date) }
- it_behaves_like 'overlapping dates', skip_constraint_test: true
+ it { is_expected.to be_valid }
end
end
- context 'project' do
+ # Skipped. Pending https://gitlab.com/gitlab-org/gitlab/-/issues/299864
+ xcontext 'project' do
let_it_be(:existing_iteration) { create(:iteration, :skip_project_validation, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) }
subject { build(:iteration, :skip_project_validation, project: project, start_date: start_date, due_date: due_date) }
@@ -283,16 +285,16 @@ RSpec.describe Iteration do
expect { subject.save! }.not_to raise_exception
end
end
- end
- context 'project in a group' do
- let_it_be(:project) { create(:project, group: create(:group)) }
- let_it_be(:existing_iteration) { create(:iteration, :skip_project_validation, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) }
+ context 'project in a group' do
+ let_it_be(:project) { create(:project, group: create(:group)) }
+ let_it_be(:existing_iteration) { create(:iteration, :skip_project_validation, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) }
- subject { build(:iteration, :skip_project_validation, project: project, start_date: start_date, due_date: due_date) }
+ subject { build(:iteration, :skip_project_validation, project: project, start_date: start_date, due_date: due_date) }
- it_behaves_like 'overlapping dates' do
- let(:constraint_name) { 'iteration_start_and_due_daterange_project_id_constraint' }
+ it_behaves_like 'overlapping dates' do
+ let(:constraint_name) { 'iteration_start_and_due_daterange_project_id_constraint' }
+ end
end
end
end
@@ -310,19 +312,23 @@ RSpec.describe Iteration do
let(:start_date) { 1.week.ago }
let(:due_date) { 1.week.from_now }
- it 'is not valid' do
- expect(subject).not_to be_valid
- expect(subject.errors[:start_date]).to include('cannot be in the past')
- end
+ it { is_expected.to be_valid }
end
context 'when due_date is in the past' do
+ let(:start_date) { 2.weeks.ago }
+ let(:due_date) { 1.week.ago }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when due_date is before start date' do
let(:start_date) { Time.current }
let(:due_date) { 1.week.ago }
it 'is not valid' do
expect(subject).not_to be_valid
- expect(subject.errors[:due_date]).to include('cannot be in the past')
+ expect(subject.errors[:due_date]).to include('must be greater than start date')
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index ed0b9063e32..b3c3c6aaa41 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Namespace do
include ProjectForksHelper
include GitHelpers
- let!(:namespace) { create(:namespace) }
+ let!(:namespace) { create(:namespace, :with_namespace_settings) }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:repository_storage) { 'default' }
@@ -116,6 +116,28 @@ RSpec.describe Namespace do
it { is_expected.to include_module(Namespaces::Traversal::Recursive) }
end
+ describe 'callbacks' do
+ describe 'before_save :ensure_delayed_project_removal_assigned_to_namespace_settings' do
+ it 'sets the matching value in namespace_settings' do
+ expect { namespace.update!(delayed_project_removal: true) }.to change {
+ namespace.namespace_settings.delayed_project_removal
+ }.from(false).to(true)
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(migrate_delayed_project_removal: false)
+ end
+
+ it 'does not set the matching value in namespace_settings' do
+ expect { namespace.update!(delayed_project_removal: true) }.not_to change {
+ namespace.namespace_settings.delayed_project_removal
+ }
+ end
+ end
+ end
+ end
+
describe '#visibility_level_field' do
it { expect(namespace.visibility_level_field).to eq(:visibility_level) }
end
diff --git a/spec/support/shared_examples/graphql/mutation_shared_examples.rb b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
index 84ebd4852b9..51d52cbb901 100644
--- a/spec/support/shared_examples/graphql/mutation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
@@ -48,6 +48,6 @@ RSpec.shared_examples 'a mutation that returns errors in the response' do |error
it do
post_graphql_mutation(mutation, current_user: current_user)
- expect(mutation_response['errors']).to eq(errors)
+ expect(mutation_response['errors']).to match_array(errors)
end
end
diff --git a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
index f91e4bd8cf7..68142e667a4 100644
--- a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
context 'with a project' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
- let(:instance) { build(timebox_type, *timebox_args, project: build(:project), group: nil) }
+ let(:instance) { build(timebox_type, *timebox_args, project: create(:project), group: nil) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } }
let(:usage) { timebox_table_name }
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
context 'with a group' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
- let(:instance) { build(timebox_type, *timebox_args, project: nil, group: build(:group)) }
+ let(:instance) { build(timebox_type, *timebox_args, project: nil, group: create(:group)) }
let(:scope) { :group }
let(:scope_attrs) { { namespace: instance.group } }
let(:usage) { timebox_table_name }