summaryrefslogtreecommitdiff
path: root/spec/frontend/pages/projects
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/pages/projects')
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js78
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js133
-rw-r--r--spec/frontend/pages/projects/graphs/code_coverage_spec.js6
-rw-r--r--spec/frontend/pages/projects/graphs/mock_data.js91
-rw-r--r--spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js121
5 files changed, 352 insertions, 77 deletions
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js
new file mode 100644
index 00000000000..73e3c385d33
--- /dev/null
+++ b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js
@@ -0,0 +1,78 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlBadge, GlButton, GlLink } from '@gitlab/ui';
+import ForkGroupsListItem from '~/pages/projects/forks/new/components/fork_groups_list_item.vue';
+
+describe('Fork groups list item component', () => {
+ let wrapper;
+
+ const DEFAULT_PROPS = {
+ hasReachedProjectLimit: false,
+ };
+
+ const DEFAULT_GROUP_DATA = {
+ id: 22,
+ name: 'Gitlab Org',
+ description: 'Ad et ipsam earum id aut nobis.',
+ visibility: 'public',
+ full_name: 'Gitlab Org',
+ created_at: '2020-06-22T03:32:05.664Z',
+ updated_at: '2020-06-22T03:32:05.664Z',
+ avatar_url: null,
+ fork_path: '/twitter/typeahead-js/-/forks?namespace_key=22',
+ forked_project_path: null,
+ permission: 'Owner',
+ relative_path: '/gitlab-org',
+ markdown_description:
+ '<p data-sourcepos="1:1-1:31" dir="auto">Ad et ipsam earum id aut nobis.</p>',
+ can_create_project: true,
+ marked_for_deletion: false,
+ };
+
+ const DUMMY_PATH = '/dummy/path';
+
+ const createWrapper = propsData => {
+ wrapper = shallowMount(ForkGroupsListItem, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...propsData,
+ },
+ });
+ };
+
+ it('renders pending removal badge if applicable', () => {
+ createWrapper({ group: { ...DEFAULT_GROUP_DATA, marked_for_deletion: true } });
+
+ expect(wrapper.find(GlBadge).text()).toBe('pending removal');
+ });
+
+ it('renders go to fork button if has forked project', () => {
+ createWrapper({ group: { ...DEFAULT_GROUP_DATA, forked_project_path: DUMMY_PATH } });
+
+ expect(wrapper.find(GlButton).text()).toBe('Go to fork');
+ expect(wrapper.find(GlButton).attributes().href).toBe(DUMMY_PATH);
+ });
+
+ it('renders select button if has no forked project', () => {
+ createWrapper({
+ group: { ...DEFAULT_GROUP_DATA, forked_project_path: null, fork_path: DUMMY_PATH },
+ });
+
+ expect(wrapper.find(GlButton).text()).toBe('Select');
+ expect(wrapper.find('form').attributes().action).toBe(DUMMY_PATH);
+ });
+
+ it('renders link to current group', () => {
+ const DUMMY_FULL_NAME = 'dummy';
+ createWrapper({
+ group: { ...DEFAULT_GROUP_DATA, relative_path: DUMMY_PATH, full_name: DUMMY_FULL_NAME },
+ });
+
+ expect(
+ wrapper
+ .findAll(GlLink)
+ .filter(w => w.text() === DUMMY_FULL_NAME)
+ .at(0)
+ .attributes().href,
+ ).toBe(DUMMY_PATH);
+ });
+});
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js
new file mode 100644
index 00000000000..979dff78eba
--- /dev/null
+++ b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js
@@ -0,0 +1,133 @@
+import AxiosMockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import createFlash from '~/flash';
+import ForkGroupsList from '~/pages/projects/forks/new/components/fork_groups_list.vue';
+import ForkGroupsListItem from '~/pages/projects/forks/new/components/fork_groups_list_item.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+jest.mock('~/flash', () => jest.fn());
+
+describe('Fork groups list component', () => {
+ let wrapper;
+ let axiosMock;
+
+ const DEFAULT_PROPS = {
+ endpoint: '/dummy',
+ hasReachedProjectLimit: false,
+ };
+
+ const replyWith = (...args) => axiosMock.onGet(DEFAULT_PROPS.endpoint).reply(...args);
+
+ const createWrapper = propsData => {
+ wrapper = shallowMount(ForkGroupsList, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...propsData,
+ },
+ stubs: {
+ GlTabs: {
+ template: '<div><slot></slot><slot name="tabs-end"></slot></div>',
+ },
+ },
+ });
+ };
+
+ beforeEach(() => {
+ axiosMock = new AxiosMockAdapter(axios);
+ });
+
+ afterEach(() => {
+ axiosMock.reset();
+
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ it('fires load groups request on mount', async () => {
+ replyWith(200, { namespaces: [] });
+ createWrapper();
+
+ await waitForPromises();
+
+ expect(axiosMock.history.get[0].url).toBe(DEFAULT_PROPS.endpoint);
+ });
+
+ it('displays flash if loading groups fails', async () => {
+ replyWith(500);
+ createWrapper();
+
+ await waitForPromises();
+
+ expect(createFlash).toHaveBeenCalled();
+ });
+
+ it('displays loading indicator while loading groups', () => {
+ replyWith(() => new Promise(() => {}));
+ createWrapper();
+
+ expect(wrapper.contains(GlLoadingIcon)).toBe(true);
+ });
+
+ it('displays empty text if no groups are available', async () => {
+ const EMPTY_TEXT = 'No available groups to fork the project.';
+ replyWith(200, { namespaces: [] });
+ createWrapper();
+
+ await waitForPromises();
+
+ expect(wrapper.text()).toContain(EMPTY_TEXT);
+ });
+
+ it('displays filter field when groups are available', async () => {
+ replyWith(200, { namespaces: [{ name: 'dummy1' }, { name: 'dummy2' }] });
+ createWrapper();
+
+ await waitForPromises();
+
+ expect(wrapper.contains(GlSearchBoxByType)).toBe(true);
+ });
+
+ it('renders list items for each available group', async () => {
+ const namespaces = [{ name: 'dummy1' }, { name: 'dummy2' }, { name: 'otherdummy' }];
+ const hasReachedProjectLimit = true;
+
+ replyWith(200, { namespaces });
+ createWrapper({ hasReachedProjectLimit });
+
+ await waitForPromises();
+
+ expect(wrapper.findAll(ForkGroupsListItem)).toHaveLength(namespaces.length);
+
+ namespaces.forEach((namespace, idx) => {
+ expect(
+ wrapper
+ .findAll(ForkGroupsListItem)
+ .at(idx)
+ .props(),
+ ).toStrictEqual({ group: namespace, hasReachedProjectLimit });
+ });
+ });
+
+ it('filters repositories on the fly', async () => {
+ replyWith(200, {
+ namespaces: [{ name: 'dummy1' }, { name: 'dummy2' }, { name: 'otherdummy' }],
+ });
+ createWrapper();
+ await waitForPromises();
+ wrapper.find(GlSearchBoxByType).vm.$emit('input', 'other');
+ await nextTick();
+
+ expect(wrapper.findAll(ForkGroupsListItem)).toHaveLength(1);
+ expect(
+ wrapper
+ .findAll(ForkGroupsListItem)
+ .at(0)
+ .props().group.name,
+ ).toBe('otherdummy');
+ });
+});
diff --git a/spec/frontend/pages/projects/graphs/code_coverage_spec.js b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
index 4990985b076..30c7ff78c6e 100644
--- a/spec/frontend/pages/projects/graphs/code_coverage_spec.js
+++ b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
@@ -5,7 +5,7 @@ import { GlAreaChart } from '@gitlab/ui/dist/charts';
import axios from '~/lib/utils/axios_utils';
import CodeCoverage from '~/pages/projects/graphs/components/code_coverage.vue';
-import codeCoverageMockData from './mock_data';
+import { codeCoverageMockData, sortedDataByDates } from './mock_data';
import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status';
@@ -52,6 +52,10 @@ describe('Code Coverage', () => {
expect(findAreaChart().exists()).toBe(true);
});
+ it('sorts the dates in ascending order', () => {
+ expect(wrapper.vm.sortedData).toEqual(sortedDataByDates);
+ });
+
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
diff --git a/spec/frontend/pages/projects/graphs/mock_data.js b/spec/frontend/pages/projects/graphs/mock_data.js
index a15f861ee7a..28d97b9d3f0 100644
--- a/spec/frontend/pages/projects/graphs/mock_data.js
+++ b/spec/frontend/pages/projects/graphs/mock_data.js
@@ -1,60 +1,69 @@
-export default [
+export const codeCoverageMockData = [
{
group_name: 'rspec',
data: [
- { date: '2020-04-30', coverage: 40.0 },
- { date: '2020-05-01', coverage: 80.0 },
- { date: '2020-05-02', coverage: 99.0 },
- { date: '2020-05-10', coverage: 80.0 },
- { date: '2020-05-15', coverage: 70.0 },
{ date: '2020-05-20', coverage: 69.0 },
+ { date: '2020-05-15', coverage: 70.0 },
+ { date: '2020-05-10', coverage: 80.0 },
+ { date: '2020-05-02', coverage: 99.0 },
+ { date: '2020-05-01', coverage: 80.0 },
+ { date: '2020-04-30', coverage: 40.0 },
],
},
{
group_name: 'cypress',
data: [
- { date: '2022-07-30', coverage: 1.0 },
- { date: '2022-08-01', coverage: 2.4 },
- { date: '2022-08-02', coverage: 5.0 },
- { date: '2022-08-10', coverage: 15.0 },
- { date: '2022-08-15', coverage: 30.0 },
{ date: '2022-08-20', coverage: 40.0 },
+ { date: '2022-08-15', coverage: 30.0 },
+ { date: '2022-08-10', coverage: 15.0 },
+ { date: '2022-08-02', coverage: 5.0 },
+ { date: '2022-08-01', coverage: 2.4 },
+ { date: '2022-07-30', coverage: 1.0 },
],
},
{
group_name: 'karma',
data: [
- { date: '2020-05-01', coverage: 94.0 },
- { date: '2020-05-02', coverage: 94.0 },
- { date: '2020-05-03', coverage: 94.0 },
- { date: '2020-05-04', coverage: 94.0 },
- { date: '2020-05-05', coverage: 92.0 },
- { date: '2020-05-06', coverage: 91.0 },
- { date: '2020-05-07', coverage: 78.0 },
- { date: '2020-05-08', coverage: 94.0 },
- { date: '2020-05-09', coverage: 94.0 },
- { date: '2020-05-10', coverage: 94.0 },
- { date: '2020-05-11', coverage: 94.0 },
- { date: '2020-05-12', coverage: 94.0 },
- { date: '2020-05-13', coverage: 92.0 },
- { date: '2020-05-14', coverage: 91.0 },
- { date: '2020-05-15', coverage: 78.0 },
- { date: '2020-05-16', coverage: 94.0 },
- { date: '2020-05-17', coverage: 94.0 },
- { date: '2020-05-18', coverage: 93.0 },
- { date: '2020-05-19', coverage: 92.0 },
- { date: '2020-05-20', coverage: 91.0 },
- { date: '2020-05-21', coverage: 90.0 },
- { date: '2020-05-22', coverage: 91.0 },
- { date: '2020-05-23', coverage: 92.0 },
- { date: '2020-05-24', coverage: 75.0 },
- { date: '2020-05-25', coverage: 74.0 },
- { date: '2020-05-26', coverage: 74.0 },
- { date: '2020-05-27', coverage: 74.0 },
- { date: '2020-05-28', coverage: 80.0 },
- { date: '2020-05-29', coverage: 85.0 },
- { date: '2020-05-30', coverage: 92.0 },
{ date: '2020-05-31', coverage: 91.0 },
+ { date: '2020-05-30', coverage: 94.0 },
+ { date: '2020-05-29', coverage: 94.0 },
+ { date: '2020-05-28', coverage: 92.0 },
+ { date: '2020-05-27', coverage: 91.0 },
+ { date: '2020-05-26', coverage: 78.0 },
+ { date: '2020-05-25', coverage: 94.0 },
+ { date: '2020-05-24', coverage: 94.0 },
+ { date: '2020-05-23', coverage: 94.0 },
+ { date: '2020-05-22', coverage: 94.0 },
+ { date: '2020-05-21', coverage: 94.0 },
+ { date: '2020-05-20', coverage: 92.0 },
+ { date: '2020-05-19', coverage: 91.0 },
+ { date: '2020-05-18', coverage: 78.0 },
+ { date: '2020-05-17', coverage: 94.0 },
+ { date: '2020-05-16', coverage: 94.0 },
+ { date: '2020-05-15', coverage: 93.0 },
+ { date: '2020-05-14', coverage: 92.0 },
+ { date: '2020-05-13', coverage: 91.0 },
+ { date: '2020-05-12', coverage: 90.0 },
+ { date: '2020-05-11', coverage: 91.0 },
+ { date: '2020-05-10', coverage: 92.0 },
+ { date: '2020-05-09', coverage: 75.0 },
+ { date: '2020-05-08', coverage: 74.0 },
+ { date: '2020-05-07', coverage: 74.0 },
+ { date: '2020-05-06', coverage: 74.0 },
+ { date: '2020-05-05', coverage: 80.0 },
+ { date: '2020-05-04', coverage: 85.0 },
+ { date: '2020-05-03', coverage: 92.0 },
+ { date: '2020-05-02', coverage: 94.0 },
+ { date: '2020-05-01', coverage: 94.0 },
],
},
];
+
+export const sortedDataByDates = [
+ { date: '2020-04-30', coverage: 40.0 },
+ { date: '2020-05-01', coverage: 80.0 },
+ { date: '2020-05-02', coverage: 99.0 },
+ { date: '2020-05-10', coverage: 80.0 },
+ { date: '2020-05-15', coverage: 70.0 },
+ { date: '2020-05-20', coverage: 69.0 },
+];
diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
index 9cc1d6eeb5a..9a119377542 100644
--- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
+++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
@@ -1,4 +1,5 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
+import { trimText } from 'helpers/text_helper';
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
describe('Interval Pattern Input Component', () => {
@@ -14,15 +15,22 @@ describe('Interval Pattern Input Component', () => {
everyWeek: `0 ${mockHour} * * ${mockWeekDayIndex}`,
everyMonth: `0 ${mockHour} ${mockDay} * *`,
};
-
- const findEveryDayRadio = () => wrapper.find('#every-day');
- const findEveryWeekRadio = () => wrapper.find('#every-week');
- const findEveryMonthRadio = () => wrapper.find('#every-month');
- const findCustomRadio = () => wrapper.find('#custom');
+ const customKey = 'custom';
+ const everyDayKey = 'everyDay';
+ const cronIntervalNotInPreset = `0 12 * * *`;
+
+ const findEveryDayRadio = () => wrapper.find(`[data-testid=${everyDayKey}]`);
+ const findEveryWeekRadio = () => wrapper.find('[data-testid="everyWeek"]');
+ const findEveryMonthRadio = () => wrapper.find('[data-testid="everyMonth"]');
+ const findCustomRadio = () => wrapper.find(`[data-testid="${customKey}"]`);
const findCustomInput = () => wrapper.find('#schedule_cron');
- const selectEveryDayRadio = () => findEveryDayRadio().setChecked();
- const selectEveryWeekRadio = () => findEveryWeekRadio().setChecked();
- const selectEveryMonthRadio = () => findEveryMonthRadio().setChecked();
+ const findAllLabels = () => wrapper.findAll('label');
+ const findSelectedRadio = () =>
+ wrapper.findAll('input[type="radio"]').wrappers.find(x => x.element.checked);
+ const findSelectedRadioKey = () => findSelectedRadio()?.attributes('data-testid');
+ const selectEveryDayRadio = () => findEveryDayRadio().trigger('click');
+ const selectEveryWeekRadio = () => findEveryWeekRadio().trigger('click');
+ const selectEveryMonthRadio = () => findEveryMonthRadio().trigger('click');
const selectCustomRadio = () => findCustomRadio().trigger('click');
const createWrapper = (props = {}, data = {}) => {
@@ -30,7 +38,7 @@ describe('Interval Pattern Input Component', () => {
throw new Error('A wrapper already exists');
}
- wrapper = shallowMount(IntervalPatternInput, {
+ wrapper = mount(IntervalPatternInput, {
propsData: { ...props },
data() {
return {
@@ -63,8 +71,8 @@ describe('Interval Pattern Input Component', () => {
createWrapper();
});
- it('to a non empty string when no initial value is not passed', () => {
- expect(findCustomInput()).not.toBe('');
+ it('defaults to every day value when no `initialCronInterval` is passed', () => {
+ expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyDay);
});
});
@@ -85,20 +93,20 @@ describe('Interval Pattern Input Component', () => {
createWrapper();
});
- it('when a default option is selected', () => {
+ it('when a default option is selected', async () => {
selectEveryDayRadio();
- return wrapper.vm.$nextTick().then(() => {
- expect(findCustomInput().attributes('disabled')).toBeUndefined();
- });
+ await wrapper.vm.$nextTick();
+
+ expect(findCustomInput().attributes('disabled')).toBeUndefined();
});
- it('when the custom option is selected', () => {
+ it('when the custom option is selected', async () => {
selectCustomRadio();
- return wrapper.vm.$nextTick().then(() => {
- expect(findCustomInput().attributes('disabled')).toBeUndefined();
- });
+ await wrapper.vm.$nextTick();
+
+ expect(findCustomInput().attributes('disabled')).toBeUndefined();
});
});
@@ -115,40 +123,83 @@ describe('Interval Pattern Input Component', () => {
});
});
+ describe('Time strings', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('renders each label for radio options properly', () => {
+ const labels = findAllLabels().wrappers.map(el => trimText(el.text()));
+
+ expect(labels).toEqual([
+ 'Every day (at 4:00am)',
+ 'Every week (Monday at 4:00am)',
+ 'Every month (Day 1 at 4:00am)',
+ 'Custom ( Cron syntax )',
+ ]);
+ });
+ });
+
describe('User Actions with radio buttons', () => {
- it.each`
- desc | initialCronInterval | act | expectedValue
- ${'when everyday is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryDayRadio} | ${cronIntervalPresets.everyDay}
- ${'when everyweek is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryWeekRadio} | ${cronIntervalPresets.everyWeek}
- ${'when everymonth is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryMonthRadio} | ${cronIntervalPresets.everyMonth}
- ${'when custom is selected, add space to value'} | ${cronIntervalPresets.everyMonth} | ${selectCustomRadio} | ${`${cronIntervalPresets.everyMonth} `}
- `('$desc', ({ initialCronInterval, act, expectedValue }) => {
- createWrapper({ initialCronInterval });
+ describe('Default option', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('when everyday is selected, update value', async () => {
+ selectEveryWeekRadio();
+ await wrapper.vm.$nextTick();
+ expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyWeek);
+
+ selectEveryDayRadio();
+ await wrapper.vm.$nextTick();
+ expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyDay);
+ });
+ });
+
+ describe('Other options', () => {
+ it.each`
+ desc | initialCronInterval | act | expectedValue
+ ${'when everyweek is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryWeekRadio} | ${cronIntervalPresets.everyWeek}
+ ${'when everymonth is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryMonthRadio} | ${cronIntervalPresets.everyMonth}
+ ${'when custom is selected, value remains the same'} | ${cronIntervalPresets.everyMonth} | ${selectCustomRadio} | ${cronIntervalPresets.everyMonth}
+ `('$desc', async ({ initialCronInterval, act, expectedValue }) => {
+ createWrapper({ initialCronInterval });
+
+ act();
- act();
+ await wrapper.vm.$nextTick();
- return wrapper.vm.$nextTick().then(() => {
expect(findCustomInput().element.value).toBe(expectedValue);
});
});
});
+
describe('User actions with input field for Cron syntax', () => {
beforeEach(() => {
createWrapper();
});
- it('when editing the cron input it selects the custom radio button', () => {
+ it('when editing the cron input it selects the custom radio button', async () => {
const newValue = '0 * * * *';
+ expect(findSelectedRadioKey()).toBe(everyDayKey);
+
findCustomInput().setValue(newValue);
- expect(wrapper.vm.cronInterval).toBe(newValue);
+ await wrapper.vm.$nextTick;
+
+ expect(findSelectedRadioKey()).toBe(customKey);
});
+ });
- it('when value of input is one of the defaults, it selects the corresponding radio button', () => {
- findCustomInput().setValue(cronIntervalPresets.everyWeek);
+ describe('Edit form field', () => {
+ beforeEach(() => {
+ createWrapper({ initialCronInterval: cronIntervalNotInPreset });
+ });
- expect(wrapper.vm.cronInterval).toBe(cronIntervalPresets.everyWeek);
+ it('loads with the custom option being selected', () => {
+ expect(findSelectedRadioKey()).toBe(customKey);
});
});
});