Commit d3711900 authored by Mark Florian's avatar Mark Florian

Merge branch '207912-implementing-filtered-search-advanced-filters' into 'master'

Log explorer: split filters into separate simple and advanced components

Closes #207912

See merge request gitlab-org/gitlab!27484
parents 95c0aa51 54a0fa36
...@@ -7,17 +7,15 @@ import { ...@@ -7,17 +7,15 @@ import {
GlAlert, GlAlert,
GlDropdown, GlDropdown,
GlDropdownHeader, GlDropdownHeader,
GlDropdownDivider,
GlDropdownItem, GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
GlInfiniteScroll, GlInfiniteScroll,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__ } from '~/locale';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue'; import LogSimpleFilters from './log_simple_filters.vue';
import LogAdvancedFilters from './log_advanced_filters.vue';
import LogControlButtons from './log_control_buttons.vue'; import LogControlButtons from './log_control_buttons.vue';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants'; import { defaultTimeRange } from '~/vue_shared/constants';
import { timeRangeFromUrl } from '~/monitoring/utils'; import { timeRangeFromUrl } from '~/monitoring/utils';
import { formatDate } from '../utils'; import { formatDate } from '../utils';
...@@ -28,12 +26,10 @@ export default { ...@@ -28,12 +26,10 @@ export default {
GlAlert, GlAlert,
GlDropdown, GlDropdown,
GlDropdownHeader, GlDropdownHeader,
GlDropdownDivider,
GlDropdownItem, GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
GlInfiniteScroll, GlInfiniteScroll,
DateTimePicker, LogSimpleFilters,
LogAdvancedFilters,
LogControlButtons, LogControlButtons,
}, },
filters: { filters: {
...@@ -63,49 +59,22 @@ export default { ...@@ -63,49 +59,22 @@ export default {
traceHeight: 600, traceHeight: 600,
data() { data() {
return { return {
searchQuery: '',
timeRanges,
isElasticStackCalloutDismissed: false, isElasticStackCalloutDismissed: false,
scrollDownButtonDisabled: true, scrollDownButtonDisabled: true,
}; };
}, },
computed: { computed: {
...mapState('environmentLogs', ['environments', 'timeRange', 'logs', 'pods']), ...mapState('environmentLogs', ['environments', 'timeRange', 'logs', 'pods']),
...mapGetters('environmentLogs', ['trace']), ...mapGetters('environmentLogs', ['trace', 'showAdvancedFilters']),
timeRangeModel: {
get() {
return this.timeRange.selected;
},
set(val) {
this.setTimeRange(val);
},
},
showLoader() { showLoader() {
return this.logs.isLoading; return this.logs.isLoading;
}, },
advancedFeaturesEnabled() {
const environment = this.environments.options.find(
({ name }) => name === this.environments.current,
);
return environment && environment.enable_advanced_logs_querying;
},
disableAdvancedControls() {
return this.environments.isLoading || !this.advancedFeaturesEnabled;
},
shouldShowElasticStackCallout() { shouldShowElasticStackCallout() {
return !this.isElasticStackCalloutDismissed && this.disableAdvancedControls; return (
}, !this.isElasticStackCalloutDismissed &&
(this.environments.isLoading || !this.showAdvancedFilters)
podDropdownText() { );
if (this.pods.current) {
return this.pods.current;
} else if (this.advancedFeaturesEnabled) {
// "All pods" is a valid option when advanced querying is available
return s__('Environments|All pods');
}
return s__('Environments|No pod selected');
}, },
}, },
mounted() { mounted() {
...@@ -121,7 +90,6 @@ export default { ...@@ -121,7 +90,6 @@ export default {
...mapActions('environmentLogs', [ ...mapActions('environmentLogs', [
'setInitData', 'setInitData',
'setSearch', 'setSearch',
'setTimeRange',
'showPodLogs', 'showPodLogs',
'showEnvironment', 'showEnvironment',
'fetchEnvironments', 'fetchEnvironments',
...@@ -131,9 +99,6 @@ export default { ...@@ -131,9 +99,6 @@ export default {
isCurrentEnvironment(envName) { isCurrentEnvironment(envName) {
return envName === this.environments.current; return envName === this.environments.current;
}, },
isCurrentPod(podName) {
return podName === this.pods.current;
},
topReached() { topReached() {
if (!this.logs.isLoading) { if (!this.logs.isLoading) {
this.fetchMoreLogsPrepend(); this.fetchMoreLogsPrepend();
...@@ -167,123 +132,49 @@ export default { ...@@ -167,123 +132,49 @@ export default {
</strong> </strong>
</a> </a>
</gl-alert> </gl-alert>
<div class="top-bar js-top-bar d-flex"> <div class="top-bar d-md-flex border bg-secondary-50 pt-2 pr-1 pb-0 pl-2">
<div class="row mx-n1"> <div class="flex-grow-0">
<gl-form-group <gl-dropdown
id="environments-dropdown-fg" id="environments-dropdown"
label-size="sm" :text="environments.current"
label-for="environments-dropdown" :disabled="environments.isLoading"
class="col-3 px-1" class="mb-2 gl-h-32 pr-2 d-flex d-md-block js-environments-dropdown"
> >
<gl-dropdown <gl-dropdown-header class="text-center">
id="environments-dropdown" {{ s__('Environments|Select environment') }}
:text="environments.current" </gl-dropdown-header>
:disabled="environments.isLoading" <gl-dropdown-item
class="d-flex gl-h-32 js-environments-dropdown" v-for="env in environments.options"
toggle-class="dropdown-menu-toggle" :key="env.id"
@click="showEnvironment(env.name)"
> >
<gl-dropdown-header class="text-center"> <div class="d-flex">
{{ s__('Environments|Select environment') }} <gl-icon
</gl-dropdown-header> :class="{ invisible: !isCurrentEnvironment(env.name) }"
<gl-dropdown-item name="status_success_borderless"
v-for="env in environments.options" />
:key="env.id" <div class="flex-grow-1">{{ env.name }}</div>
@click="showEnvironment(env.name)" </div>
> </gl-dropdown-item>
<div class="d-flex"> </gl-dropdown>
<gl-icon
:class="{ invisible: !isCurrentEnvironment(env.name) }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ env.name }}</div>
</div>
</gl-dropdown-item>
</gl-dropdown>
</gl-form-group>
<gl-form-group
id="pods-dropdown-fg"
label-size="sm"
label-for="pods-dropdown"
class="col-3 px-1"
>
<gl-dropdown
id="pods-dropdown"
:text="podDropdownText"
:disabled="environments.isLoading"
class="d-flex gl-h-32 js-pods-dropdown"
toggle-class="dropdown-menu-toggle"
>
<gl-dropdown-header class="text-center">
{{ s__('Environments|Filter by pod') }}
</gl-dropdown-header>
<template v-if="advancedFeaturesEnabled">
<gl-dropdown-item key="all-pods" @click="showPodLogs(null)">
<div class="d-flex">
<gl-icon
:class="{ invisible: !isCurrentPod(null) }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ s__('Environments|All pods') }}</div>
</div>
</gl-dropdown-item>
<gl-dropdown-divider />
</template>
<gl-dropdown-item v-if="!pods.options.length" :disabled="true">
<span class="text-muted">
{{ s__('Environments|No pods to display') }}
</span>
</gl-dropdown-item>
<gl-dropdown-item
v-for="podName in pods.options"
:key="podName"
class="text-nowrap"
@click="showPodLogs(podName)"
>
<div class="d-flex">
<gl-icon
:class="{ invisible: !isCurrentPod(podName) }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ podName }}</div>
</div>
</gl-dropdown-item>
</gl-dropdown>
</gl-form-group>
<gl-form-group id="search-fg" label-size="sm" label-for="search" class="col-3 px-1">
<gl-search-box-by-click
v-model.trim="searchQuery"
:disabled="disableAdvancedControls"
:placeholder="s__('Environments|Search')"
class="js-logs-search"
type="search"
autofocus
@submit="setSearch(searchQuery)"
/>
</gl-form-group>
<gl-form-group
id="dates-fg"
label-size="sm"
label-for="time-window-dropdown"
class="col-3 px-1"
>
<date-time-picker
ref="dateTimePicker"
v-model="timeRangeModel"
class="w-100 gl-h-32"
right
:disabled="disableAdvancedControls"
:options="timeRanges"
/>
</gl-form-group>
</div> </div>
<log-advanced-filters
v-if="showAdvancedFilters"
ref="log-advanced-filters"
class="d-md-flex flex-grow-1"
:disabled="environments.isLoading"
/>
<log-simple-filters
v-else
ref="log-simple-filters"
class="d-md-flex flex-grow-1"
:disabled="environments.isLoading"
/>
<log-control-buttons <log-control-buttons
ref="scrollButtons" ref="scrollButtons"
class="controllers" class="flex-grow-0 pr-2 mb-2 controllers"
:scroll-down-button-disabled="scrollDownButtonDisabled" :scroll-down-button-disabled="scrollDownButtonDisabled"
@refresh="showPodLogs(pods.current)" @refresh="showPodLogs(pods.current)"
@scrollDown="scrollDown" @scrollDown="scrollDown"
......
<script>
import { s__ } from '~/locale';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import { mapActions, mapState } from 'vuex';
import {
GlIcon,
GlDropdown,
GlDropdownHeader,
GlDropdownDivider,
GlDropdownItem,
GlSearchBoxByClick,
} from '@gitlab/ui';
import { timeRanges } from '~/vue_shared/constants';
export default {
components: {
GlIcon,
GlDropdown,
GlDropdownHeader,
GlDropdownDivider,
GlDropdownItem,
GlSearchBoxByClick,
DateTimePicker,
},
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
timeRanges,
searchQuery: '',
};
},
computed: {
...mapState('environmentLogs', ['timeRange', 'pods']),
timeRangeModel: {
get() {
return this.timeRange.selected;
},
set(val) {
this.setTimeRange(val);
},
},
podDropdownText() {
return this.pods.current || s__('Environments|All pods');
},
},
methods: {
...mapActions('environmentLogs', ['setSearch', 'showPodLogs', 'setTimeRange']),
isCurrentPod(podName) {
return podName === this.pods.current;
},
},
};
</script>
<template>
<div>
<gl-dropdown
ref="podsDropdown"
:text="podDropdownText"
:disabled="disabled"
class="mb-2 gl-h-32 pr-2 d-flex d-md-block flex-grow-0 qa-pods-dropdown"
>
<gl-dropdown-header class="text-center">
{{ s__('Environments|Filter by pod') }}
</gl-dropdown-header>
<gl-dropdown-item v-if="!pods.options.length" disabled>
<span ref="noPodsMsg" class="text-muted">
{{ s__('Environments|No pods to display') }}
</span>
</gl-dropdown-item>
<template v-else>
<gl-dropdown-item ref="allPodsOption" key="all-pods" @click="showPodLogs(null)">
<div class="d-flex">
<gl-icon
:class="{ invisible: pods.current !== null }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ s__('Environments|All pods') }}</div>
</div>
</gl-dropdown-item>
<gl-dropdown-divider />
<gl-dropdown-item
v-for="podName in pods.options"
:key="podName"
class="text-nowrap"
@click="showPodLogs(podName)"
>
<div class="d-flex">
<gl-icon
:class="{ invisible: !isCurrentPod(podName) }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ podName }}</div>
</div>
</gl-dropdown-item>
</template>
</gl-dropdown>
<gl-search-box-by-click
ref="searchBox"
v-model.trim="searchQuery"
:disabled="disabled"
:placeholder="s__('Environments|Search')"
class="mb-2 pr-2 flex-grow-1"
type="search"
autofocus
@submit="setSearch(searchQuery)"
/>
<date-time-picker
ref="dateTimePicker"
v-model="timeRangeModel"
:disabled="disabled"
:options="timeRanges"
class="mb-2 gl-h-32 pr-2 d-block date-time-picker-wrapper"
right
/>
</div>
</template>
<script>
import { s__ } from '~/locale';
import { mapActions, mapState } from 'vuex';
import { GlIcon, GlDropdown, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
export default {
components: {
GlIcon,
GlDropdown,
GlDropdownHeader,
GlDropdownItem,
},
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
searchQuery: '',
};
},
computed: {
...mapState('environmentLogs', ['pods']),
podDropdownText() {
return this.pods.current || s__('Environments|No pod selected');
},
},
methods: {
...mapActions('environmentLogs', ['showPodLogs']),
isCurrentPod(podName) {
return podName === this.pods.current;
},
},
};
</script>
<template>
<div>
<gl-dropdown
ref="podsDropdown"
:text="podDropdownText"
:disabled="disabled"
class="mb-2 gl-h-32 pr-2 d-flex d-md-block flex-grow-0 qa-pods-dropdown"
>
<gl-dropdown-header class="text-center">
{{ s__('Environments|Select pod') }}
</gl-dropdown-header>
<gl-dropdown-item v-if="!pods.options.length" disabled>
<span ref="noPodsMsg" class="text-muted">
{{ s__('Environments|No pods to display') }}
</span>
</gl-dropdown-item>
<gl-dropdown-item
v-for="podName in pods.options"
:key="podName"
class="text-nowrap"
@click="showPodLogs(podName)"
>
<div class="d-flex">
<gl-icon
:class="{ invisible: !isCurrentPod(podName) }"
name="status_success_borderless"
/>
<div class="flex-grow-1">{{ podName }}</div>
</div>
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>
...@@ -5,5 +5,9 @@ const mapTrace = ({ timestamp = null, pod = '', message = '' }) => ...@@ -5,5 +5,9 @@ const mapTrace = ({ timestamp = null, pod = '', message = '' }) =>
export const trace = state => state.logs.lines.map(mapTrace).join('\n'); export const trace = state => state.logs.lines.map(mapTrace).join('\n');
// prevent babel-plugin-rewire from generating an invalid default during karma tests export const showAdvancedFilters = state => {
export default () => {}; const environment = state.environments.options.find(
({ name }) => name === state.environments.current,
);
return Boolean(environment?.enable_advanced_logs_querying);
};
...@@ -379,25 +379,19 @@ ...@@ -379,25 +379,19 @@
} }
.top-bar { .top-bar {
@include build-trace-top-bar($gl-line-height * 3); .date-time-picker-wrapper,
position: relative; .dropdown-toggle {
top: 0; @include media-breakpoint-up(md) {
width: 140px;
.dropdown-menu-toggle { }
width: 200px;
@include media-breakpoint-up(sm) { @include media-breakpoint-up(lg) {
width: 300px; width: 160px;
} }
} }
.controllers { .controllers {
@include build-controllers(16px, flex-end, true, 2); @include build-controllers(16px, flex-end, false, 2);
}
.refresh-control {
@include build-controllers(16px, flex-end, true, 0);
margin-left: 2px;
} }
} }
......
---
title: Improve logs filters on mobile, simplify kubernetes API logs filters
merge_request: 27484
author:
type: added
...@@ -7825,6 +7825,9 @@ msgstr "" ...@@ -7825,6 +7825,9 @@ msgstr ""
msgid "Environments|Select environment" msgid "Environments|Select environment"
msgstr "" msgstr ""
msgid "Environments|Select pod"
msgstr ""
msgid "Environments|Show all" msgid "Environments|Show all"
msgstr "" msgstr ""
......
...@@ -29,7 +29,7 @@ describe 'Environment > Pod Logs', :js do ...@@ -29,7 +29,7 @@ describe 'Environment > Pod Logs', :js do
wait_for_requests wait_for_requests
page.within('.js-environments-dropdown') do page.within('.js-environments-dropdown') do
toggle = find(".dropdown-menu-toggle:not([disabled])") toggle = find(".dropdown-toggle:not([disabled])")
expect(toggle).to have_content(environment.name) expect(toggle).to have_content(environment.name)
...@@ -47,8 +47,8 @@ describe 'Environment > Pod Logs', :js do ...@@ -47,8 +47,8 @@ describe 'Environment > Pod Logs', :js do
wait_for_requests wait_for_requests
page.within('.js-pods-dropdown') do page.within('.qa-pods-dropdown') do
find(".dropdown-menu-toggle:not([disabled])").click find(".dropdown-toggle:not([disabled])").click
dropdown_items = find(".dropdown-menu").all(".dropdown-item:not([disabled])") dropdown_items = find(".dropdown-menu").all(".dropdown-item:not([disabled])")
expect(dropdown_items.size).to eq(1) expect(dropdown_items.size).to eq(1)
......
import Vue from 'vue'; import { GlSprintf, GlIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlSprintf, GlIcon, GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; 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'; import EnvironmentLogs from '~/logs/components/environment_logs.vue';
import { createStore } from '~/logs/stores'; import { createStore } from '~/logs/stores';
...@@ -13,7 +11,6 @@ import { ...@@ -13,7 +11,6 @@ import {
mockLogsResult, mockLogsResult,
mockTrace, mockTrace,
mockPodName, mockPodName,
mockSearch,
mockEnvironmentsEndpoint, mockEnvironmentsEndpoint,
mockDocumentationPath, mockDocumentationPath,
} from '../mock_data'; } from '../mock_data';
...@@ -29,7 +26,6 @@ jest.mock('lodash/throttle', () => ...@@ -29,7 +26,6 @@ jest.mock('lodash/throttle', () =>
); );
describe('EnvironmentLogs', () => { describe('EnvironmentLogs', () => {
let EnvironmentLogsComponent;
let store; let store;
let dispatch; let dispatch;
let wrapper; let wrapper;
...@@ -44,13 +40,9 @@ describe('EnvironmentLogs', () => { ...@@ -44,13 +40,9 @@ describe('EnvironmentLogs', () => {
const updateControlBtnsMock = jest.fn(); const updateControlBtnsMock = jest.fn();
const findEnvironmentsDropdown = () => wrapper.find('.js-environments-dropdown'); const findEnvironmentsDropdown = () => wrapper.find('.js-environments-dropdown');
const findPodsDropdown = () => wrapper.find('.js-pods-dropdown');
const findPodsDropdownItems = () => const findSimpleFilters = () => wrapper.find({ ref: 'log-simple-filters' });
findPodsDropdown() const findAdvancedFilters = () => wrapper.find({ ref: 'log-advanced-filters' });
.findAll(GlDropdownItem)
.filter(itm => !itm.attributes('disabled'));
const findSearchBar = () => wrapper.find('.js-logs-search');
const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
const findInfoAlert = () => wrapper.find('.js-elasticsearch-alert'); const findInfoAlert = () => wrapper.find('.js-elasticsearch-alert');
const findLogControlButtons = () => wrapper.find({ name: 'log-control-buttons-stub' }); const findLogControlButtons = () => wrapper.find({ name: 'log-control-buttons-stub' });
...@@ -79,7 +71,7 @@ describe('EnvironmentLogs', () => { ...@@ -79,7 +71,7 @@ describe('EnvironmentLogs', () => {
}; };
const initWrapper = () => { const initWrapper = () => {
wrapper = shallowMount(EnvironmentLogsComponent, { wrapper = shallowMount(EnvironmentLogs, {
propsData, propsData,
store, store,
stubs: { stubs: {
...@@ -111,7 +103,6 @@ describe('EnvironmentLogs', () => { ...@@ -111,7 +103,6 @@ describe('EnvironmentLogs', () => {
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
state = store.state.environmentLogs; state = store.state.environmentLogs;
EnvironmentLogsComponent = Vue.extend(EnvironmentLogs);
jest.spyOn(store, 'dispatch').mockResolvedValue(); jest.spyOn(store, 'dispatch').mockResolvedValue();
...@@ -132,17 +123,10 @@ describe('EnvironmentLogs', () => { ...@@ -132,17 +123,10 @@ describe('EnvironmentLogs', () => {
expect(wrapper.isVueInstance()).toBe(true); expect(wrapper.isVueInstance()).toBe(true);
expect(wrapper.isEmpty()).toBe(false); expect(wrapper.isEmpty()).toBe(false);
// top bar
expect(findEnvironmentsDropdown().is(GlDropdown)).toBe(true); expect(findEnvironmentsDropdown().is(GlDropdown)).toBe(true);
expect(findPodsDropdown().is(GlDropdown)).toBe(true); expect(findSimpleFilters().exists()).toBe(true);
expect(findLogControlButtons().exists()).toBe(true); expect(findLogControlButtons().exists()).toBe(true);
expect(findSearchBar().exists()).toBe(true);
expect(findSearchBar().is(GlSearchBoxByClick)).toBe(true);
expect(findTimeRangePicker().exists()).toBe(true);
expect(findTimeRangePicker().is(DateTimePicker)).toBe(true);
// log trace
expect(findInfiniteScroll().exists()).toBe(true); expect(findInfiniteScroll().exists()).toBe(true);
expect(findLogTrace().exists()).toBe(true); expect(findLogTrace().exists()).toBe(true);
}); });
...@@ -181,20 +165,6 @@ describe('EnvironmentLogs', () => { ...@@ -181,20 +165,6 @@ describe('EnvironmentLogs', () => {
expect(findEnvironmentsDropdown().findAll(GlDropdownItem).length).toBe(0); expect(findEnvironmentsDropdown().findAll(GlDropdownItem).length).toBe(0);
}); });
it('displays a disabled pods dropdown', () => {
expect(findPodsDropdown().attributes('disabled')).toBe('true');
expect(findPodsDropdownItems()).toHaveLength(0);
});
it('displays a disabled search bar', () => {
expect(findSearchBar().exists()).toBe(true);
expect(findSearchBar().attributes('disabled')).toBe('true');
});
it('displays a disabled time window dropdown', () => {
expect(findTimeRangePicker().attributes('disabled')).toBe('true');
});
it('does not update buttons state', () => { it('does not update buttons state', () => {
expect(updateControlBtnsMock).not.toHaveBeenCalled(); expect(updateControlBtnsMock).not.toHaveBeenCalled();
}); });
...@@ -237,17 +207,14 @@ describe('EnvironmentLogs', () => { ...@@ -237,17 +207,14 @@ describe('EnvironmentLogs', () => {
initWrapper(); initWrapper();
}); });
it('displays a disabled time window dropdown', () => {
expect(findTimeRangePicker().attributes('disabled')).toBe('true');
});
it('displays a disabled search bar', () => {
expect(findSearchBar().attributes('disabled')).toBe('true');
});
it('displays an alert to upgrade to ES', () => { it('displays an alert to upgrade to ES', () => {
expect(findInfoAlert().exists()).toBe(true); expect(findInfoAlert().exists()).toBe(true);
}); });
it('displays simple filters for kubernetes logs API', () => {
expect(findSimpleFilters().exists()).toBe(true);
expect(findAdvancedFilters().exists()).toBe(false);
});
}); });
describe('state with data', () => { describe('state with data', () => {
...@@ -271,21 +238,6 @@ describe('EnvironmentLogs', () => { ...@@ -271,21 +238,6 @@ describe('EnvironmentLogs', () => {
updateControlBtnsMock.mockReset(); updateControlBtnsMock.mockReset();
}); });
it('displays an enabled search bar', () => {
expect(findSearchBar().attributes('disabled')).toBeFalsy();
// input a query and click `search`
findSearchBar().vm.$emit('input', mockSearch);
findSearchBar().vm.$emit('submit');
expect(dispatch).toHaveBeenCalledWith(`${module}/setInitData`, expect.any(Object));
expect(dispatch).toHaveBeenCalledWith(`${module}/setSearch`, mockSearch);
});
it('displays an enabled time window dropdown', () => {
expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
});
it('does not display an alert to upgrade to ES', () => { it('does not display an alert to upgrade to ES', () => {
expect(findInfoAlert().exists()).toBe(false); expect(findInfoAlert().exists()).toBe(false);
}); });
...@@ -306,24 +258,16 @@ describe('EnvironmentLogs', () => { ...@@ -306,24 +258,16 @@ describe('EnvironmentLogs', () => {
const item = items.at(i); const item = items.at(i);
if (item.text() !== mockEnvName) { if (item.text() !== mockEnvName) {
expect(item.find(GlIcon).classes()).toContain('invisible'); expect(item.find(GlIcon).classes('invisible')).toBe(true);
} else { } else {
// selected expect(item.find(GlIcon).classes('invisible')).toBe(false);
expect(item.find(GlIcon).classes()).not.toContain('invisible');
} }
}); });
}); });
it('populates pods dropdown', () => { it('displays advanced filters for elasticsearch logs API', () => {
const items = findPodsDropdownItems(); expect(findSimpleFilters().exists()).toBe(false);
expect(findAdvancedFilters().exists()).toBe(true);
expect(findPodsDropdown().props('text')).toBe(mockPodName);
expect(items.length).toBe(mockPods.length + 1);
expect(items.at(0).text()).toBe('All pods');
mockPods.forEach((pod, i) => {
const item = items.at(i + 1);
expect(item.text()).toBe(pod);
});
}); });
it('shows infinite scroll with height and no content', () => { it('shows infinite scroll with height and no content', () => {
...@@ -331,19 +275,6 @@ describe('EnvironmentLogs', () => { ...@@ -331,19 +275,6 @@ describe('EnvironmentLogs', () => {
expect(getInfiniteScrollAttr('fetched-items')).toBe(mockTrace.length); expect(getInfiniteScrollAttr('fetched-items')).toBe(mockTrace.length);
}); });
it('dropdown has one pod selected', () => {
const items = findPodsDropdownItems();
mockPods.forEach((pod, i) => {
const item = items.at(i);
if (item.text() !== mockPodName) {
expect(item.find(GlIcon).classes()).toContain('invisible');
} else {
// selected
expect(item.find(GlIcon).classes()).not.toContain('invisible');
}
});
});
it('populates logs trace', () => { it('populates logs trace', () => {
const trace = findLogTrace(); const trace = findLogTrace();
expect(trace.text().split('\n').length).toBe(mockTrace.length); expect(trace.text().split('\n').length).toBe(mockTrace.length);
...@@ -371,17 +302,6 @@ describe('EnvironmentLogs', () => { ...@@ -371,17 +302,6 @@ describe('EnvironmentLogs', () => {
); );
}); });
it('pod name, trace is refreshed', () => {
const items = findPodsDropdownItems();
const index = 2; // any pod
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
items.at(index + 1).vm.$emit('click');
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
});
it('refresh button, trace is refreshed', () => { it('refresh button, trace is refreshed', () => {
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything()); expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
......
import { GlIcon, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { defaultTimeRange } from '~/vue_shared/constants';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { createStore } from '~/logs/stores';
import { mockPods, mockSearch } from '../mock_data';
import LogAdvancedFilters from '~/logs/components/log_advanced_filters.vue';
const module = 'environmentLogs';
describe('LogAdvancedFilters', () => {
let store;
let dispatch;
let wrapper;
let state;
const findPodsDropdown = () => wrapper.find({ ref: 'podsDropdown' });
const findPodsNoPodsText = () => wrapper.find({ ref: 'noPodsMsg' });
const findPodsDropdownItems = () =>
findPodsDropdown()
.findAll(GlDropdownItem)
.filter(item => !item.is('[disabled]'));
const findPodsDropdownItemsSelected = () =>
findPodsDropdownItems()
.filter(item => {
return !item.find(GlIcon).classes('invisible');
})
.at(0);
const findSearchBox = () => wrapper.find({ ref: 'searchBox' });
const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
const mockStateLoading = () => {
state.timeRange.selected = defaultTimeRange;
state.timeRange.current = convertToFixedRange(defaultTimeRange);
state.pods.options = [];
state.pods.current = null;
};
const mockStateWithData = () => {
state.timeRange.selected = defaultTimeRange;
state.timeRange.current = convertToFixedRange(defaultTimeRange);
state.pods.options = mockPods;
state.pods.current = null;
};
const initWrapper = (propsData = {}) => {
wrapper = shallowMount(LogAdvancedFilters, {
propsData: {
...propsData,
},
store,
});
};
beforeEach(() => {
store = createStore();
state = store.state.environmentLogs;
jest.spyOn(store, 'dispatch').mockResolvedValue();
dispatch = store.dispatch;
});
afterEach(() => {
store.dispatch.mockReset();
if (wrapper) {
wrapper.destroy();
}
});
it('displays UI elements', () => {
initWrapper();
expect(wrapper.isVueInstance()).toBe(true);
expect(wrapper.isEmpty()).toBe(false);
expect(findPodsDropdown().exists()).toBe(true);
expect(findSearchBox().exists()).toBe(true);
expect(findTimeRangePicker().exists()).toBe(true);
});
describe('disabled state', () => {
beforeEach(() => {
mockStateLoading();
initWrapper({
disabled: true,
});
});
it('displays disabled filters', () => {
expect(findPodsDropdown().props('text')).toBe('All pods');
expect(findPodsDropdown().attributes('disabled')).toBeTruthy();
expect(findSearchBox().attributes('disabled')).toBeTruthy();
expect(findTimeRangePicker().attributes('disabled')).toBeTruthy();
});
});
describe('when the state is loading', () => {
beforeEach(() => {
mockStateLoading();
initWrapper();
});
it('displays a enabled filters', () => {
expect(findPodsDropdown().props('text')).toBe('All pods');
expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
expect(findSearchBox().attributes('disabled')).toBeFalsy();
expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
});
it('displays an empty pods dropdown', () => {
expect(findPodsNoPodsText().exists()).toBe(true);
expect(findPodsDropdownItems()).toHaveLength(0);
});
});
describe('when the state has data', () => {
beforeEach(() => {
mockStateWithData();
initWrapper();
});
it('displays an enabled pods dropdown', () => {
expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
expect(findPodsDropdown().props('text')).toBe('All pods');
});
it('displays options in a pods dropdown', () => {
const items = findPodsDropdownItems();
expect(items).toHaveLength(mockPods.length + 1);
});
it('displays "all pods" selected in a pods dropdown', () => {
const selected = findPodsDropdownItemsSelected();
expect(selected.text()).toBe('All pods');
});
it('displays options in date time picker', () => {
const options = findTimeRangePicker().props('options');
expect(options).toEqual(expect.any(Array));
expect(options.length).toBeGreaterThan(0);
});
describe('when the user interacts', () => {
it('clicks on a all options, showPodLogs is dispatched with null', () => {
const items = findPodsDropdownItems();
items.at(0).vm.$emit('click');
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, null);
});
it('clicks on a pod name, showPodLogs is dispatched with pod name', () => {
const items = findPodsDropdownItems();
const index = 2; // any pod
items.at(index + 1).vm.$emit('click'); // skip "All pods" option
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
});
it('clicks on search, a serches is done', () => {
expect(findSearchBox().attributes('disabled')).toBeFalsy();
// input a query and click `search`
findSearchBox().vm.$emit('input', mockSearch);
findSearchBox().vm.$emit('submit');
expect(dispatch).toHaveBeenCalledWith(`${module}/setSearch`, mockSearch);
});
it('selects a new time range', () => {
expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
const mockRange = { start: 'START_DATE', end: 'END_DATE' };
findTimeRangePicker().vm.$emit('input', mockRange);
expect(dispatch).toHaveBeenCalledWith(`${module}/setTimeRange`, mockRange);
});
});
});
});
import { GlIcon, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { createStore } from '~/logs/stores';
import { mockPods, mockPodName } from '../mock_data';
import LogSimpleFilters from '~/logs/components/log_simple_filters.vue';
const module = 'environmentLogs';
describe('LogSimpleFilters', () => {
let store;
let dispatch;
let wrapper;
let state;
const findPodsDropdown = () => wrapper.find({ ref: 'podsDropdown' });
const findPodsNoPodsText = () => wrapper.find({ ref: 'noPodsMsg' });
const findPodsDropdownItems = () =>
findPodsDropdown()
.findAll(GlDropdownItem)
.filter(item => !item.is('[disabled]'));
const mockPodsLoading = () => {
state.pods.options = [];
state.pods.current = null;
};
const mockPodsLoaded = () => {
state.pods.options = mockPods;
state.pods.current = mockPodName;
};
const initWrapper = (propsData = {}) => {
wrapper = shallowMount(LogSimpleFilters, {
propsData: {
...propsData,
},
store,
});
};
beforeEach(() => {
store = createStore();
state = store.state.environmentLogs;
jest.spyOn(store, 'dispatch').mockResolvedValue();
dispatch = store.dispatch;
});
afterEach(() => {
store.dispatch.mockReset();
if (wrapper) {
wrapper.destroy();
}
});
it('displays UI elements', () => {
initWrapper();
expect(wrapper.isVueInstance()).toBe(true);
expect(wrapper.isEmpty()).toBe(false);
expect(findPodsDropdown().exists()).toBe(true);
});
describe('disabled state', () => {
beforeEach(() => {
mockPodsLoading();
initWrapper({
disabled: true,
});
});
it('displays a disabled pods dropdown', () => {
expect(findPodsDropdown().props('text')).toBe('No pod selected');
expect(findPodsDropdown().attributes('disabled')).toBeTruthy();
});
});
describe('loading state', () => {
beforeEach(() => {
mockPodsLoading();
initWrapper();
});
it('displays an enabled pods dropdown', () => {
expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
expect(findPodsDropdown().props('text')).toBe('No pod selected');
});
it('displays an empty pods dropdown', () => {
expect(findPodsNoPodsText().exists()).toBe(true);
expect(findPodsDropdownItems()).toHaveLength(0);
});
});
describe('pods available state', () => {
beforeEach(() => {
mockPodsLoaded();
initWrapper();
});
it('displays an enabled pods dropdown', () => {
expect(findPodsDropdown().attributes('disabled')).toBeFalsy();
expect(findPodsDropdown().props('text')).toBe(mockPods[0]);
});
it('displays a pods dropdown with items', () => {
expect(findPodsNoPodsText().exists()).toBe(false);
expect(findPodsDropdownItems()).toHaveLength(mockPods.length);
});
it('dropdown has one pod selected', () => {
const items = findPodsDropdownItems();
mockPods.forEach((pod, i) => {
const item = items.at(i);
if (item.text() !== mockPodName) {
expect(item.find(GlIcon).classes('invisible')).toBe(true);
} else {
expect(item.find(GlIcon).classes('invisible')).toBe(false);
}
});
});
it('when the user clicks on a pod, showPodLogs is dispatched', () => {
const items = findPodsDropdownItems();
const index = 2; // any pod
expect(dispatch).not.toHaveBeenCalledWith(`${module}/showPodLogs`, expect.anything());
items.at(index).vm.$emit('click');
expect(dispatch).toHaveBeenCalledWith(`${module}/showPodLogs`, mockPods[index]);
});
});
});
import * as getters from '~/logs/stores/getters'; import { trace, showAdvancedFilters } from '~/logs/stores/getters';
import logsPageState from '~/logs/stores/state'; import logsPageState from '~/logs/stores/state';
import { mockLogsResult, mockTrace } from '../mock_data'; import { mockLogsResult, mockTrace, mockEnvName, mockEnvironments } from '../mock_data';
describe('Logs Store getters', () => { describe('Logs Store getters', () => {
let state; let state;
...@@ -13,7 +13,7 @@ describe('Logs Store getters', () => { ...@@ -13,7 +13,7 @@ describe('Logs Store getters', () => {
describe('trace', () => { describe('trace', () => {
describe('when state is initialized', () => { describe('when state is initialized', () => {
it('returns an empty string', () => { it('returns an empty string', () => {
expect(getters.trace(state)).toEqual(''); expect(trace(state)).toEqual('');
}); });
}); });
...@@ -23,7 +23,7 @@ describe('Logs Store getters', () => { ...@@ -23,7 +23,7 @@ describe('Logs Store getters', () => {
}); });
it('returns an empty string', () => { it('returns an empty string', () => {
expect(getters.trace(state)).toEqual(''); expect(trace(state)).toEqual('');
}); });
}); });
...@@ -33,7 +33,42 @@ describe('Logs Store getters', () => { ...@@ -33,7 +33,42 @@ describe('Logs Store getters', () => {
}); });
it('returns an empty string', () => { it('returns an empty string', () => {
expect(getters.trace(state)).toEqual(mockTrace.join('\n')); expect(trace(state)).toEqual(mockTrace.join('\n'));
});
});
});
describe('showAdvancedFilters', () => {
describe('when no environments are set', () => {
beforeEach(() => {
state.environments.current = mockEnvName;
state.environments.options = [];
});
it('returns false', () => {
expect(showAdvancedFilters(state)).toBe(false);
});
});
describe('when the environment supports filters', () => {
beforeEach(() => {
state.environments.current = mockEnvName;
state.environments.options = mockEnvironments;
});
it('returns true', () => {
expect(showAdvancedFilters(state)).toBe(true);
});
});
describe('when the environment does not support filters', () => {
beforeEach(() => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvironments[1].name;
});
it('returns true', () => {
expect(showAdvancedFilters(state)).toBe(false);
}); });
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment