Skip to content
Snippets Groups Projects
Commit 286fe610 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 4cb5e501
No related branches found
No related tags found
No related merge requests found
Showing
with 529 additions and 340 deletions
Loading
Loading
@@ -45,7 +45,7 @@ describe 'Admin mode', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode
it 'can enter admin mode' do
visit new_admin_session_path
 
fill_in 'password', with: admin.password
fill_in 'user_password', with: admin.password
 
click_button 'Enter Admin Mode'
 
Loading
Loading
@@ -60,7 +60,7 @@ describe 'Admin mode', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode
it 'can enter admin mode' do
visit new_admin_session_path
 
fill_in 'password', with: admin.password
fill_in 'user_password', with: admin.password
 
click_button 'Enter Admin Mode'
 
Loading
Loading
Loading
Loading
@@ -14,7 +14,6 @@ describe('IDE commit editor header', () => {
 
const findDiscardModal = () => wrapper.find({ ref: 'discardModal' });
const findDiscardButton = () => wrapper.find({ ref: 'discardButton' });
const findActionButton = () => wrapper.find({ ref: 'actionButton' });
 
beforeEach(() => {
f = file('file');
Loading
Loading
@@ -28,9 +27,7 @@ describe('IDE commit editor header', () => {
},
});
 
jest.spyOn(wrapper.vm, 'stageChange').mockImplementation();
jest.spyOn(wrapper.vm, 'unstageChange').mockImplementation();
jest.spyOn(wrapper.vm, 'discardFileChanges').mockImplementation();
jest.spyOn(wrapper.vm, 'discardChanges').mockImplementation();
});
 
afterEach(() => {
Loading
Loading
@@ -38,8 +35,8 @@ describe('IDE commit editor header', () => {
wrapper = null;
});
 
it('renders button to discard & stage', () => {
expect(wrapper.vm.$el.querySelectorAll('.btn').length).toBe(2);
it('renders button to discard', () => {
expect(wrapper.vm.$el.querySelectorAll('.btn')).toHaveLength(1);
});
 
describe('discard button', () => {
Loading
Loading
@@ -60,23 +57,7 @@ describe('IDE commit editor header', () => {
it('calls discardFileChanges if dialog result is confirmed', () => {
modal.vm.$emit('ok');
 
expect(wrapper.vm.discardFileChanges).toHaveBeenCalledWith(f.path);
});
});
describe('stage/unstage button', () => {
it('unstages the file if it was already staged', () => {
f.staged = true;
findActionButton().trigger('click');
expect(wrapper.vm.unstageChange).toHaveBeenCalledWith(f.path);
});
it('stages the file if it was not staged', () => {
findActionButton().trigger('click');
expect(wrapper.vm.stageChange).toHaveBeenCalledWith(f.path);
expect(wrapper.vm.discardChanges).toHaveBeenCalledWith(f.path);
});
});
});
import Vue from 'vue';
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { GlSprintf, GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import EnvironmentLogs from '~/logs/components/environment_logs.vue';
Loading
Loading
@@ -20,9 +20,18 @@ import {
 
jest.mock('~/lib/utils/scroll_utils');
 
const module = 'environmentLogs';
jest.mock('lodash/throttle', () =>
jest.fn(func => {
return func;
}),
);
describe('EnvironmentLogs', () => {
let EnvironmentLogsComponent;
let store;
let dispatch;
let wrapper;
let state;
 
Loading
Loading
@@ -32,14 +41,6 @@ describe('EnvironmentLogs', () => {
clusterApplicationsDocumentationPath: mockDocumentationPath,
};
 
const actionMocks = {
setInitData: jest.fn(),
setSearch: jest.fn(),
showPodLogs: jest.fn(),
showEnvironment: jest.fn(),
fetchEnvironments: jest.fn(),
};
const updateControlBtnsMock = jest.fn();
 
const findEnvironmentsDropdown = () => wrapper.find('.js-environments-dropdown');
Loading
Loading
@@ -47,24 +48,25 @@ describe('EnvironmentLogs', () => {
const findSearchBar = () => wrapper.find('.js-logs-search');
const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
const findInfoAlert = () => wrapper.find('.js-elasticsearch-alert');
const findLogControlButtons = () => wrapper.find({ name: 'log-control-buttons-stub' });
const findInfiniteScroll = () => wrapper.find({ ref: 'infiniteScroll' });
const findLogTrace = () => wrapper.find('.js-log-trace');
const findLogFooter = () => wrapper.find({ ref: 'logFooter' });
const getInfiniteScrollAttr = attr => parseInt(findInfiniteScroll().attributes(attr), 10);
 
const mockSetInitData = () => {
state.pods.options = mockPods;
state.environments.current = mockEnvName;
[state.pods.current] = state.pods.options;
 
state.logs.isComplete = false;
state.logs.lines = mockLogsResult;
state.logs.lines = [];
};
 
const mockShowPodLogs = podName => {
const mockShowPodLogs = () => {
state.pods.options = mockPods;
[state.pods.current] = podName;
[state.pods.current] = mockPods;
 
state.logs.isComplete = false;
state.logs.lines = mockLogsResult;
};
 
Loading
Loading
@@ -83,10 +85,21 @@ describe('EnvironmentLogs', () => {
methods: {
update: updateControlBtnsMock,
},
props: {
scrollDownButtonDisabled: false,
},
},
},
methods: {
...actionMocks,
GlInfiniteScroll: {
name: 'gl-infinite-scroll',
template: `
<div>
<slot name="header"></slot>
<slot name="items"></slot>
<slot></slot>
</div>
`,
},
GlSprintf,
},
});
};
Loading
Loading
@@ -95,12 +108,14 @@ describe('EnvironmentLogs', () => {
store = createStore();
state = store.state.environmentLogs;
EnvironmentLogsComponent = Vue.extend(EnvironmentLogs);
jest.spyOn(store, 'dispatch').mockResolvedValue();
dispatch = store.dispatch;
});
 
afterEach(() => {
actionMocks.setInitData.mockReset();
actionMocks.showPodLogs.mockReset();
actionMocks.fetchEnvironments.mockReset();
store.dispatch.mockReset();
 
if (wrapper) {
wrapper.destroy();
Loading
Loading
@@ -124,14 +139,14 @@ describe('EnvironmentLogs', () => {
expect(findTimeRangePicker().is(DateTimePicker)).toBe(true);
 
// log trace
expect(findLogTrace().isEmpty()).toBe(false);
expect(findInfiniteScroll().exists()).toBe(true);
expect(findLogTrace().exists()).toBe(true);
});
 
it('mounted inits data', () => {
initWrapper();
 
expect(actionMocks.setInitData).toHaveBeenCalledTimes(1);
expect(actionMocks.setInitData).toHaveBeenLastCalledWith({
expect(dispatch).toHaveBeenCalledWith(`${module}/setInitData`, {
timeRange: expect.objectContaining({
default: true,
}),
Loading
Loading
@@ -139,18 +154,15 @@ describe('EnvironmentLogs', () => {
podName: null,
});
 
expect(actionMocks.fetchEnvironments).toHaveBeenCalledTimes(1);
expect(actionMocks.fetchEnvironments).toHaveBeenLastCalledWith(mockEnvironmentsEndpoint);
expect(dispatch).toHaveBeenCalledWith(`${module}/fetchEnvironments`, mockEnvironmentsEndpoint);
});
 
describe('loading state', () => {
beforeEach(() => {
state.pods.options = [];
 
state.logs = {
lines: [],
isLoading: true,
};
state.logs.lines = [];
state.logs.isLoading = true;
 
state.environments = {
options: [],
Loading
Loading
@@ -183,6 +195,18 @@ describe('EnvironmentLogs', () => {
expect(updateControlBtnsMock).not.toHaveBeenCalled();
});
 
it('shows an infinite scroll with height and no content', () => {
expect(getInfiniteScrollAttr('max-list-height')).toBeGreaterThan(0);
expect(getInfiniteScrollAttr('fetched-items')).toBe(0);
});
it('shows an infinite scroll container with equal height and max-height ', () => {
const height = getInfiniteScrollAttr('max-list-height');
expect(height).toEqual(expect.any(Number));
expect(findInfiniteScroll().attributes('style')).toMatch(`height: ${height}px;`);
});
it('shows a logs trace', () => {
expect(findLogTrace().text()).toBe('');
expect(
Loading
Loading
@@ -193,14 +217,12 @@ describe('EnvironmentLogs', () => {
});
});
 
describe('legacy environment', () => {
describe('k8s environment', () => {
beforeEach(() => {
state.pods.options = [];
 
state.logs = {
lines: [],
isLoading: false,
};
state.logs.lines = [];
state.logs.isLoading = false;
 
state.environments = {
options: mockEnvironments,
Loading
Loading
@@ -226,9 +248,16 @@ describe('EnvironmentLogs', () => {
 
describe('state with data', () => {
beforeEach(() => {
actionMocks.setInitData.mockImplementation(mockSetInitData);
actionMocks.showPodLogs.mockImplementation(mockShowPodLogs);
actionMocks.fetchEnvironments.mockImplementation(mockFetchEnvs);
dispatch.mockImplementation(actionName => {
if (actionName === `${module}/setInitData`) {
mockSetInitData();
} else if (actionName === `${module}/showPodLogs`) {
mockShowPodLogs();
} else if (actionName === `${module}/fetchEnvironments`) {
mockFetchEnvs();
mockShowPodLogs();
}
});
 
initWrapper();
});
Loading
Loading
@@ -236,10 +265,6 @@ describe('EnvironmentLogs', () => {
afterEach(() => {
scrollDown.mockReset();
updateControlBtnsMock.mockReset();
actionMocks.setInitData.mockReset();
actionMocks.showPodLogs.mockReset();
actionMocks.fetchEnvironments.mockReset();
});
 
it('displays an enabled search bar', () => {
Loading
Loading
@@ -249,8 +274,8 @@ describe('EnvironmentLogs', () => {
findSearchBar().vm.$emit('input', mockSearch);
findSearchBar().vm.$emit('submit');
 
expect(actionMocks.setSearch).toHaveBeenCalledTimes(1);
expect(actionMocks.setSearch).toHaveBeenCalledWith(mockSearch);
expect(dispatch).toHaveBeenCalledWith(`${module}/setInitData`, expect.any(Object));
expect(dispatch).toHaveBeenCalledWith(`${module}/setSearch`, mockSearch);
});
 
it('displays an enabled time window dropdown', () => {
Loading
Loading
@@ -282,18 +307,21 @@ describe('EnvironmentLogs', () => {
});
});
 
it('shows infinite scroll with height and no content', () => {
expect(getInfiniteScrollAttr('max-list-height')).toBeGreaterThan(0);
expect(getInfiniteScrollAttr('fetched-items')).toBe(mockTrace.length);
});
it('populates logs trace', () => {
const trace = findLogTrace();
expect(trace.text().split('\n').length).toBe(mockTrace.length);
expect(trace.text().split('\n')).toEqual(mockTrace);
});
 
it('update control buttons state', () => {
expect(updateControlBtnsMock).toHaveBeenCalledTimes(1);
});
it('populates footer', () => {
const footer = findLogFooter().text();
 
it('scrolls to bottom when loaded', () => {
expect(scrollDown).toHaveBeenCalledTimes(1);
expect(footer).toContain(`${mockLogsResult.length} results`);
});
 
describe('when user clicks', () => {
Loading
Loading
@@ -301,33 +329,99 @@ describe('EnvironmentLogs', () => {
const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
const index = 1; // any env
 
expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(0);
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showEnvironment`, expect.anything());
 
items.at(index).vm.$emit('click');
 
expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(1);
expect(actionMocks.showEnvironment).toHaveBeenLastCalledWith(mockEnvironments[index].name);
expect(dispatch).toHaveBeenCalledWith(
`${module}/showEnvironment`,
mockEnvironments[index].name,
);
});
 
it('pod name, trace is refreshed', () => {
const items = findPodsDropdown().findAll(GlDropdownItem);
const index = 2; // any pod
 
expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(0);
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
 
items.at(index).vm.$emit('click');
 
expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(1);
expect(actionMocks.showPodLogs).toHaveBeenLastCalledWith(mockPods[index]);
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
});
 
it('refresh button, trace is refreshed', () => {
expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(0);
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
 
findLogControlButtons().vm.$emit('refresh');
 
expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(1);
expect(actionMocks.showPodLogs).toHaveBeenLastCalledWith(mockPodName);
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPodName);
});
});
});
describe('listeners', () => {
beforeEach(() => {
initWrapper();
});
it('attaches listeners in components', () => {
expect(findInfiniteScroll().vm.$listeners).toEqual({
topReached: expect.any(Function),
scroll: expect.any(Function),
});
});
it('`topReached` when not loading', () => {
expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
findInfiniteScroll().vm.$emit('topReached');
expect(store.dispatch).toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
});
it('`topReached` does not fetches more logs when already loading', () => {
state.logs.isLoading = true;
findInfiniteScroll().vm.$emit('topReached');
expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
});
it('`topReached` fetches more logs', () => {
state.logs.isLoading = true;
findInfiniteScroll().vm.$emit('topReached');
expect(store.dispatch).not.toHaveBeenCalledWith(`${module}/fetchMoreLogsPrepend`, undefined);
});
it('`scroll` on a scrollable target results in enabled scroll buttons', () => {
const target = { scrollTop: 10, clientHeight: 10, scrollHeight: 21 };
state.logs.isLoading = true;
findInfiniteScroll().vm.$emit('scroll', { target });
return wrapper.vm.$nextTick(() => {
expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(false);
});
});
it('`scroll` on a non-scrollable target in disabled scroll buttons', () => {
const target = { scrollTop: 10, clientHeight: 10, scrollHeight: 20 };
state.logs.isLoading = true;
findInfiniteScroll().vm.$emit('scroll', { target });
return wrapper.vm.$nextTick(() => {
expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(true);
});
});
it('`scroll` on no target results in disabled scroll buttons', () => {
state.logs.isLoading = true;
findInfiniteScroll().vm.$emit('scroll', { target: undefined });
return wrapper.vm.$nextTick(() => {
expect(findLogControlButtons().props('scrollDownButtonDisabled')).toEqual(true);
});
});
});
Loading
Loading
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import LogControlButtons from '~/logs/components/log_control_buttons.vue';
import {
canScroll,
isScrolledToTop,
isScrolledToBottom,
scrollDown,
scrollUp,
} from '~/lib/utils/scroll_utils';
jest.mock('~/lib/utils/scroll_utils');
 
describe('LogControlButtons', () => {
let wrapper;
Loading
Loading
@@ -18,8 +9,14 @@ describe('LogControlButtons', () => {
const findScrollToBottom = () => wrapper.find('.js-scroll-to-bottom');
const findRefreshBtn = () => wrapper.find('.js-refresh-log');
 
const initWrapper = () => {
wrapper = shallowMount(LogControlButtons);
const initWrapper = opts => {
wrapper = shallowMount(LogControlButtons, {
listeners: {
scrollUp: () => {},
scrollDown: () => {},
},
...opts,
});
};
 
afterEach(() => {
Loading
Loading
@@ -55,27 +52,16 @@ describe('LogControlButtons', () => {
describe('when scrolling actions are enabled', () => {
beforeEach(() => {
// mock scrolled to the middle of a long page
canScroll.mockReturnValue(true);
isScrolledToBottom.mockReturnValue(false);
isScrolledToTop.mockReturnValue(false);
initWrapper();
wrapper.vm.update();
return wrapper.vm.$nextTick();
});
 
afterEach(() => {
canScroll.mockReset();
isScrolledToTop.mockReset();
isScrolledToBottom.mockReset();
});
it('click on "scroll to top" scrolls up', () => {
expect(findScrollToTop().is('[disabled]')).toBe(false);
 
findScrollToTop().vm.$emit('click');
 
expect(scrollUp).toHaveBeenCalledTimes(1);
expect(wrapper.emitted('scrollUp')).toHaveLength(1);
});
 
it('click on "scroll to bottom" scrolls down', () => {
Loading
Loading
@@ -83,25 +69,23 @@ describe('LogControlButtons', () => {
 
findScrollToBottom().vm.$emit('click');
 
expect(scrollDown).toHaveBeenCalledTimes(1); // plus one time when trace was loaded
expect(wrapper.emitted('scrollDown')).toHaveLength(1);
});
});
 
describe('when scrolling actions are disabled', () => {
beforeEach(() => {
// mock a short page without a scrollbar
canScroll.mockReturnValue(false);
isScrolledToBottom.mockReturnValue(true);
isScrolledToTop.mockReturnValue(true);
initWrapper();
initWrapper({ listeners: {} });
return wrapper.vm.$nextTick();
});
 
it('buttons are disabled', () => {
wrapper.vm.update();
return wrapper.vm.$nextTick(() => {
expect(findScrollToTop().is('[disabled]')).toBe(true);
expect(findScrollToBottom().is('[disabled]')).toBe(true);
expect(findScrollToTop().exists()).toBe(false);
expect(findScrollToBottom().exists()).toBe(false);
// This should be enabled when gitlab-ui contains:
// https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1149
// expect(findScrollToBottom().is('[disabled]')).toBe(true);
});
});
});
Loading
Loading
export const mockProjectPath = 'root/autodevops-deploy';
const mockProjectPath = 'root/autodevops-deploy';
export const mockEnvName = 'production';
export const mockEnvironmentsEndpoint = `${mockProjectPath}/environments.json`;
export const mockEnvId = '99';
export const mockDocumentationPath = '/documentation.md';
export const mockLogsEndpoint = '/dummy_logs_path.json';
export const mockCursor = 'MOCK_CURSOR';
export const mockNextCursor = 'MOCK_NEXT_CURSOR';
 
const makeMockEnvironment = (id, name, advancedQuerying) => ({
id,
project_path: mockProjectPath,
name,
logs_api_path: '/dummy_logs_path.json',
logs_api_path: mockLogsEndpoint,
enable_advanced_logs_querying: advancedQuerying,
});
 
Loading
Loading
@@ -28,58 +32,22 @@ export const mockPods = [
];
 
export const mockLogsResult = [
{
timestamp: '2019-12-13T13:43:18.2760123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:29:48 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:18.2760123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:26.8420123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:29:57 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:26.8420123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:28.3710123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:29:58 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:28.3710123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:36.8860123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:07 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:36.8860123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:38.4000123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:08 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:38.4000123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:46.8420123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:17 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:46.8430123Z', message: '- -> /' },
{
timestamp: '2019-12-13T13:43:48.3240123Z',
message: '10.36.0.1 - - [16/Oct/2019:06:30:18 UTC] "GET / HTTP/1.1" 200 13',
},
{ timestamp: '2019-12-13T13:43:48.3250123Z', message: '- -> /' },
{ timestamp: '2019-12-13T13:43:18.2760123Z', message: 'Log 1' },
{ timestamp: '2019-12-13T13:43:18.2760123Z', message: 'Log 2' },
{ timestamp: '2019-12-13T13:43:26.8420123Z', message: 'Log 3' },
];
 
export const mockTrace = [
'Dec 13 13:43:18.276Z | 10.36.0.1 - - [16/Oct/2019:06:29:48 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:18.276Z | - -> /',
'Dec 13 13:43:26.842Z | 10.36.0.1 - - [16/Oct/2019:06:29:57 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:26.842Z | - -> /',
'Dec 13 13:43:28.371Z | 10.36.0.1 - - [16/Oct/2019:06:29:58 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:28.371Z | - -> /',
'Dec 13 13:43:36.886Z | 10.36.0.1 - - [16/Oct/2019:06:30:07 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:36.886Z | - -> /',
'Dec 13 13:43:38.400Z | 10.36.0.1 - - [16/Oct/2019:06:30:08 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:38.400Z | - -> /',
'Dec 13 13:43:46.842Z | 10.36.0.1 - - [16/Oct/2019:06:30:17 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:46.843Z | - -> /',
'Dec 13 13:43:48.324Z | 10.36.0.1 - - [16/Oct/2019:06:30:18 UTC] "GET / HTTP/1.1" 200 13',
'Dec 13 13:43:48.325Z | - -> /',
'Dec 13 13:43:18.276Z | Log 1',
'Dec 13 13:43:18.276Z | Log 2',
'Dec 13 13:43:26.842Z | Log 3',
];
 
export const mockResponse = {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
cursor: mockNextCursor,
};
export const mockSearch = 'foo +bar';
Loading
Loading
@@ -10,6 +10,7 @@ import {
showPodLogs,
fetchEnvironments,
fetchLogs,
fetchMoreLogsPrepend,
} from '~/logs/stores/actions';
 
import { defaultTimeRange } from '~/monitoring/constants';
Loading
Loading
@@ -18,7 +19,6 @@ import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
 
import {
mockProjectPath,
mockPodName,
mockEnvironmentsEndpoint,
mockEnvironments,
Loading
Loading
@@ -26,6 +26,10 @@ import {
mockLogsResult,
mockEnvName,
mockSearch,
mockLogsEndpoint,
mockResponse,
mockCursor,
mockNextCursor,
} from '../mock_data';
 
jest.mock('~/flash');
Loading
Loading
@@ -52,6 +56,8 @@ describe('Logs Store actions', () => {
let state;
let mock;
 
const latestGetParams = () => mock.history.get[mock.history.get.length - 1].params;
convertToFixedRange.mockImplementation(range => {
if (range === defaultTimeRange) {
return { ...mockDefaultRange };
Loading
Loading
@@ -75,10 +81,16 @@ describe('Logs Store actions', () => {
 
describe('setInitData', () => {
it('should commit environment and pod name mutation', () =>
testAction(setInitData, { environmentName: mockEnvName, podName: mockPodName }, state, [
{ type: types.SET_PROJECT_ENVIRONMENT, payload: mockEnvName },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
]));
testAction(
setInitData,
{ timeRange: mockFixedRange, environmentName: mockEnvName, podName: mockPodName },
state,
[
{ type: types.SET_TIME_RANGE, payload: mockFixedRange },
{ type: types.SET_PROJECT_ENVIRONMENT, payload: mockEnvName },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
],
));
});
 
describe('setSearch', () => {
Loading
Loading
@@ -140,183 +152,245 @@ describe('Logs Store actions', () => {
});
});
 
describe('fetchLogs', () => {
describe('when the backend responds succesfully', () => {
let expectedMutations;
let expectedActions;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockLogsEndpoint).reply(200, mockResponse);
mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
});
 
afterEach(() => {
mock.reset();
});
 
it('should commit logs and pod data when there is pod name defined', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.pods.current = mockPodName;
const endpoint = '/dummy_logs_path.json';
mock
.onGet(endpoint, {
params: {
pod_name: mockPodName,
...mockDefaultRange,
},
})
.reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
return testAction(
fetchLogs,
null,
state,
[
describe('fetchLogs', () => {
beforeEach(() => {
expectedMutations = [
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
],
[],
);
});
{
type: types.RECEIVE_LOGS_DATA_SUCCESS,
payload: { logs: mockLogsResult, cursor: mockNextCursor },
},
];
 
it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
state.projectPath = mockProjectPath;
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
expectedActions = [];
});
 
const endpoint = '/dummy_logs_path.json';
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
 
mock
.onGet(endpoint, {
params: {
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toMatchObject({
pod_name: mockPodName,
start: mockFixedRange.start,
end: mockFixedRange.end,
},
})
.reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
});
});
});
 
return testAction(
fetchLogs,
null,
state,
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
],
[],
);
});
it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.pods.current = mockPodName;
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
const endpoint = '/dummy_logs_path.json';
it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
state.logs.cursor = mockCursor;
 
mock
.onGet(endpoint, {
params: {
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
},
})
.reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
start: mockFixedRange.start,
end: mockFixedRange.end,
cursor: mockCursor,
});
});
});
 
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
state.pods.current = mockPodName;
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
 
return testAction(
fetchLogs,
null,
state,
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
],
[],
() => {
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
});
// Warning about time ranges was issued
expect(flash).toHaveBeenCalledTimes(1);
expect(flash).toHaveBeenCalledWith(expect.any(String), 'warning');
},
);
});
});
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = mockDefaultRange;
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({});
});
});
});
 
it('should commit logs and pod data when no pod name defined', done => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
describe('fetchMoreLogsPrepend', () => {
beforeEach(() => {
expectedMutations = [
{ type: types.REQUEST_LOGS_DATA_PREPEND },
{
type: types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS,
payload: { logs: mockLogsResult, cursor: mockNextCursor },
},
];
 
const endpoint = '/dummy_logs_path.json';
expectedActions = [];
});
 
mock.onGet(endpoint, { params: { ...mockDefaultRange } }).reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
expectedActions = [];
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toMatchObject({
pod_name: mockPodName,
});
},
);
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
 
testAction(
it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
state.logs.cursor = mockCursor;
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
start: mockFixedRange.start,
end: mockFixedRange.end,
cursor: mockCursor,
});
},
);
});
it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
state.pods.current = mockPodName;
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
});
// Warning about time ranges was issued
expect(flash).toHaveBeenCalledTimes(1);
expect(flash).toHaveBeenCalledWith(expect.any(String), 'warning');
},
);
});
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = mockDefaultRange;
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({});
},
);
});
it('should not commit logs or pod data when it has reached the end', () => {
state.logs.isComplete = true;
state.logs.cursor = null;
return testAction(
fetchMoreLogsPrepend,
null,
state,
[], // no mutations done
[], // no actions dispatched
() => {
expect(mock.history.get).toHaveLength(0);
},
);
});
});
});
describe('when the backend responds with an error', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockLogsEndpoint).reply(500);
});
afterEach(() => {
mock.reset();
});
it('fetchLogs should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
return testAction(
fetchLogs,
null,
state,
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
{ type: types.RECEIVE_PODS_DATA_ERROR },
{ type: types.RECEIVE_LOGS_DATA_ERROR },
],
[],
done,
() => {
expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
},
);
});
 
it('should commit logs and pod errors when backend fails', () => {
it('fetchMoreLogsPrepend should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
 
const endpoint = `/${mockProjectPath}/-/logs/elasticsearch.json?environment_name=${mockEnvName}`;
mock.onGet(endpoint).replyOnce(500);
return testAction(
fetchLogs,
fetchMoreLogsPrepend,
null,
state,
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.RECEIVE_PODS_DATA_ERROR },
{ type: types.RECEIVE_LOGS_DATA_ERROR },
{ type: types.REQUEST_LOGS_DATA_PREPEND },
{ type: types.RECEIVE_LOGS_DATA_PREPEND_ERROR },
],
[],
() => {
expect(flash).toHaveBeenCalledTimes(1);
expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
},
);
});
Loading
Loading
Loading
Loading
@@ -9,6 +9,8 @@ import {
mockPodName,
mockLogsResult,
mockSearch,
mockCursor,
mockNextCursor,
} from '../mock_data';
 
describe('Logs Store Mutations', () => {
Loading
Loading
@@ -73,27 +75,47 @@ describe('Logs Store Mutations', () => {
it('starts loading for logs', () => {
mutations[types.REQUEST_LOGS_DATA](state);
 
expect(state.logs).toEqual(
expect.objectContaining({
lines: [],
isLoading: true,
isComplete: false,
}),
);
expect(state.timeRange.current).toEqual({
start: expect.any(String),
end: expect.any(String),
});
expect(state.logs).toEqual({
lines: [],
cursor: null,
isLoading: true,
isComplete: false,
});
});
});
 
describe('RECEIVE_LOGS_DATA_SUCCESS', () => {
it('receives logs lines', () => {
mutations[types.RECEIVE_LOGS_DATA_SUCCESS](state, mockLogsResult);
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
 
expect(state.logs).toEqual(
expect.objectContaining({
lines: mockLogsResult,
isLoading: false,
isComplete: true,
}),
);
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: mockCursor,
isComplete: false,
});
});
it('receives logs lines and a null cursor to indicate the end', () => {
mutations[types.RECEIVE_LOGS_DATA_SUCCESS](state, {
logs: mockLogsResult,
cursor: null,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: null,
isComplete: true,
});
});
});
 
Loading
Loading
@@ -101,13 +123,77 @@ describe('Logs Store Mutations', () => {
it('receives log data error and stops loading', () => {
mutations[types.RECEIVE_LOGS_DATA_ERROR](state);
 
expect(state.logs).toEqual(
expect.objectContaining({
lines: [],
isLoading: false,
isComplete: true,
}),
);
expect(state.logs).toEqual({
lines: [],
isLoading: false,
cursor: null,
isComplete: false,
});
});
});
describe('REQUEST_LOGS_DATA_PREPEND', () => {
it('receives logs lines and cursor', () => {
mutations[types.REQUEST_LOGS_DATA_PREPEND](state);
expect(state.logs.isLoading).toBe(true);
});
});
describe('RECEIVE_LOGS_DATA_PREPEND_SUCCESS', () => {
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: mockCursor,
isComplete: false,
});
});
it('receives additional logs lines and a new cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockCursor,
});
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: mockNextCursor,
});
expect(state.logs).toEqual({
lines: [...mockLogsResult, ...mockLogsResult],
isLoading: false,
cursor: mockNextCursor,
isComplete: false,
});
});
it('receives logs lines and a null cursor to indicate is complete', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, {
logs: mockLogsResult,
cursor: null,
});
expect(state.logs).toEqual({
lines: mockLogsResult,
isLoading: false,
cursor: null,
isComplete: true,
});
});
});
describe('RECEIVE_LOGS_DATA_PREPEND_ERROR', () => {
it('receives logs lines and cursor', () => {
mutations[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state);
expect(state.logs.isLoading).toBe(false);
});
});
 
Loading
Loading
@@ -121,6 +207,7 @@ describe('Logs Store Mutations', () => {
 
describe('SET_TIME_RANGE', () => {
it('sets a default range', () => {
expect(state.timeRange.selected).toEqual(expect.any(Object));
expect(state.timeRange.current).toEqual(expect.any(Object));
});
 
Loading
Loading
@@ -131,12 +218,13 @@ describe('Logs Store Mutations', () => {
};
mutations[types.SET_TIME_RANGE](state, mockRange);
 
expect(state.timeRange.selected).toEqual(mockRange);
expect(state.timeRange.current).toEqual(mockRange);
});
});
 
describe('REQUEST_PODS_DATA', () => {
it('receives log data error and stops loading', () => {
it('receives pods data', () => {
mutations[types.REQUEST_PODS_DATA](state);
 
expect(state.pods).toEqual(
Loading
Loading
Loading
Loading
@@ -54,10 +54,10 @@ describe('Changed file icon', () => {
});
 
describe.each`
file | iconName | tooltipText | desc
${changedFile()} | ${'file-modified'} | ${'Unstaged modification'} | ${'with file changed'}
${stagedFile()} | ${'file-modified-solid'} | ${'Staged modification'} | ${'with file staged'}
${newFile()} | ${'file-addition'} | ${'Unstaged addition'} | ${'with file new'}
file | iconName | tooltipText | desc
${changedFile()} | ${'file-modified'} | ${'Modified'} | ${'with file changed'}
${stagedFile()} | ${'file-modified-solid'} | ${'Modified'} | ${'with file staged'}
${newFile()} | ${'file-addition'} | ${'Added'} | ${'with file new'}
`('$desc', ({ file, iconName, tooltipText }) => {
beforeEach(() => {
factory({ file });
Loading
Loading
Loading
Loading
@@ -11,12 +11,12 @@ describe Mutations::ResolvesGroup do
 
let(:context) { double }
 
subject(:mutation) { mutation_class.new(object: nil, context: context) }
subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
 
it 'uses the GroupsResolver to resolve groups by path' do
group = create(:group)
 
expect(Resolvers::GroupResolver).to receive(:new).with(object: nil, context: context).and_call_original
expect(Resolvers::GroupResolver).to receive(:new).with(object: nil, context: context, field: nil).and_call_original
expect(mutation.resolve_group(full_path: group.full_path).sync).to eq(group)
end
end
Loading
Loading
@@ -12,7 +12,7 @@ describe Mutations::ResolvesIssuable do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:context) { { current_user: user } }
let(:mutation) { mutation_class.new(object: nil, context: context) }
let(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
 
shared_examples 'resolving an issuable' do |type|
context 'when user has access' do
Loading
Loading
@@ -39,7 +39,7 @@ describe Mutations::ResolvesIssuable do
.and_return(resolved_project)
 
expect(resolver_class).to receive(:new)
.with(object: resolved_project, context: context)
.with(object: resolved_project, context: context, field: nil)
.and_call_original
 
subject
Loading
Loading
@@ -47,7 +47,7 @@ describe Mutations::ResolvesIssuable do
 
it 'uses the ResolvesProject to resolve project' do
expect(Resolvers::ProjectResolver).to receive(:new)
.with(object: nil, context: context)
.with(object: nil, context: context, field: nil)
.and_call_original
 
subject
Loading
Loading
Loading
Loading
@@ -11,12 +11,12 @@ describe Mutations::ResolvesProject do
 
let(:context) { double }
 
subject(:mutation) { mutation_class.new(object: nil, context: context) }
subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
 
it 'uses the ProjectsResolver to resolve projects by path' do
project = create(:project)
 
expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original
expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context, field: nil).and_call_original
expect(mutation.resolve_project(full_path: project.full_path).sync).to eq(project)
end
end
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::Issues::SetConfidential do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:confidential) { true }
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::Issues::SetDueDate do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:due_date) { 2.days.since }
Loading
Loading
Loading
Loading
@@ -13,7 +13,7 @@ describe Mutations::Issues::Update do
due_date: Date.tomorrow
}
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutated_issue) { subject[:issue] }
 
describe '#resolve' do
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::MergeRequests::SetAssignees do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:assignee) { create(:user) }
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::MergeRequests::SetLabels do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:label) { create(:label, project: merge_request.project) }
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::MergeRequests::SetLocked do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:locked) { true }
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::MergeRequests::SetMilestone do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:milestone) { create(:milestone, project: merge_request.project) }
Loading
Loading
Loading
Loading
@@ -7,7 +7,7 @@ describe Mutations::MergeRequests::SetSubscription do
let(:project) { merge_request.project }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:subscribe) { true }
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ describe Mutations::MergeRequests::SetWip do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
 
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
 
describe '#resolve' do
let(:wip) { true }
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment