Commit 92013227 authored by Sean McGivern's avatar Sean McGivern

Revert "Merge branch 'jswain_whats_new_tabs' into 'master'"

This reverts merge request !47852
parent 81aa1c82
...@@ -2,15 +2,13 @@ ...@@ -2,15 +2,13 @@
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { import {
GlDrawer, GlDrawer,
GlBadge,
GlIcon,
GlLink,
GlInfiniteScroll, GlInfiniteScroll,
GlResizeObserverDirective, GlResizeObserverDirective,
GlTabs,
GlTab,
GlBadge,
GlLoadingIcon,
} from '@gitlab/ui'; } from '@gitlab/ui';
import SkeletonLoader from './skeleton_loader.vue'; import SkeletonLoader from './skeleton_loader.vue';
import Feature from './feature.vue';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { getDrawerBodyHeight } from '../utils/get_drawer_body_height'; import { getDrawerBodyHeight } from '../utils/get_drawer_body_height';
...@@ -19,13 +17,11 @@ const trackingMixin = Tracking.mixin(); ...@@ -19,13 +17,11 @@ const trackingMixin = Tracking.mixin();
export default { export default {
components: { components: {
GlDrawer, GlDrawer,
GlBadge,
GlIcon,
GlLink,
GlInfiniteScroll, GlInfiniteScroll,
GlTabs,
GlTab,
SkeletonLoader, SkeletonLoader,
Feature,
GlBadge,
GlLoadingIcon,
}, },
directives: { directives: {
GlResizeObserver: GlResizeObserverDirective, GlResizeObserver: GlResizeObserverDirective,
...@@ -35,19 +31,11 @@ export default { ...@@ -35,19 +31,11 @@ export default {
storageKey: { storageKey: {
type: String, type: String,
required: true, required: true,
}, default: null,
versions: {
type: Array,
required: true,
},
gitlabDotCom: {
type: Boolean,
required: false,
default: false,
}, },
}, },
computed: { computed: {
...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight', 'fetching']), ...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight']),
}, },
mounted() { mounted() {
this.openDrawer(this.storageKey); this.openDrawer(this.storageKey);
...@@ -61,25 +49,14 @@ export default { ...@@ -61,25 +49,14 @@ export default {
methods: { methods: {
...mapActions(['openDrawer', 'closeDrawer', 'fetchItems', 'setDrawerBodyHeight']), ...mapActions(['openDrawer', 'closeDrawer', 'fetchItems', 'setDrawerBodyHeight']),
bottomReached() { bottomReached() {
const page = this.pageInfo.nextPage; if (this.pageInfo.nextPage) {
if (page) { this.fetchItems(this.pageInfo.nextPage);
this.fetchItems({ page });
} }
}, },
handleResize() { handleResize() {
const height = getDrawerBodyHeight(this.$refs.drawer.$el); const height = getDrawerBodyHeight(this.$refs.drawer.$el);
this.setDrawerBodyHeight(height); this.setDrawerBodyHeight(height);
}, },
featuresForVersion(version) {
return this.features.filter(feature => {
return feature.release === parseFloat(version);
});
},
fetchVersion(version) {
if (this.featuresForVersion(version).length === 0) {
this.fetchItems({ version });
}
},
}, },
}; };
</script> </script>
...@@ -96,39 +73,64 @@ export default { ...@@ -96,39 +73,64 @@ export default {
<template #header> <template #header>
<h4 class="page-title gl-my-2">{{ __("What's new at GitLab") }}</h4> <h4 class="page-title gl-my-2">{{ __("What's new at GitLab") }}</h4>
</template> </template>
<template v-if="features.length"> <gl-infinite-scroll
<gl-infinite-scroll v-if="features.length"
v-if="gitlabDotCom" :fetched-items="features.length"
:fetched-items="features.length" :max-list-height="drawerBodyHeight"
:max-list-height="drawerBodyHeight" class="gl-p-0"
class="gl-p-0" @bottomReached="bottomReached"
@bottomReached="bottomReached" >
> <template #items>
<template #items> <div
<feature v-for="feature in features" :key="feature.title" :feature="feature" /> v-for="feature in features"
</template> :key="feature.title"
</gl-infinite-scroll> class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
<gl-tabs v-else :style="{ height: `${drawerBodyHeight}px` }" class="gl-p-0">
<gl-tab
v-for="(version, index) in versions"
:key="version"
@click="fetchVersion(version)"
> >
<template #title> <gl-link
<span>{{ version }}</span> :href="feature.url"
<gl-badge v-if="index === 0">{{ __('Your Version') }}</gl-badge> target="_blank"
</template> class="whats-new-item-title-link"
<gl-loading-icon v-if="fetching" size="lg" class="text-center" /> data-track-event="click_whats_new_item"
<template v-else> :data-track-label="feature.title"
<feature :data-track-property="feature.url"
v-for="feature in featuresForVersion(version)" >
:key="feature.title" <h5 class="gl-font-lg">{{ feature.title }}</h5>
:feature="feature" </gl-link>
<div v-if="feature.packages" class="gl-mb-3">
<gl-badge
v-for="package_name in feature.packages"
:key="package_name"
size="sm"
class="whats-new-item-badge gl-mr-2"
>
<gl-icon name="license" />{{ package_name }}
</gl-badge>
</div>
<gl-link
:href="feature.url"
target="_blank"
data-track-event="click_whats_new_item"
:data-track-label="feature.title"
:data-track-property="feature.url"
>
<img
:alt="feature.title"
:src="feature.image_url"
class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
/> />
</template> </gl-link>
</gl-tab> <p class="gl-pt-3">{{ feature.body }}</p>
</gl-tabs> <gl-link
</template> :href="feature.url"
target="_blank"
data-track-event="click_whats_new_item"
:data-track-label="feature.title"
:data-track-property="feature.url"
>{{ __('Learn more') }}</gl-link
>
</div>
</template>
</gl-infinite-scroll>
<div v-else class="gl-mt-5"> <div v-else class="gl-mt-5">
<skeleton-loader /> <skeleton-loader />
<skeleton-loader /> <skeleton-loader />
......
<script>
import { GlBadge, GlIcon, GlLink } from '@gitlab/ui';
export default {
components: {
GlBadge,
GlIcon,
GlLink,
},
props: {
feature: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
<gl-link
:href="feature.url"
target="_blank"
class="whats-new-item-title-link"
data-track-event="click_whats_new_item"
:data-track-label="feature.title"
:data-track-property="feature.url"
>
<h5 class="gl-font-lg" data-test-id="feature-title">{{ feature.title }}</h5>
</gl-link>
<div v-if="feature.packages" class="gl-mb-3">
<gl-badge
v-for="packageName in feature.packages"
:key="packageName"
size="sm"
class="whats-new-item-badge gl-mr-2"
>
<gl-icon name="license" />{{ packageName }}
</gl-badge>
</div>
<gl-link
:href="feature.url"
target="_blank"
data-track-event="click_whats_new_item"
:data-track-label="feature.title"
:data-track-property="feature.url"
>
<img
:alt="feature.title"
:src="feature.image_url"
class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
/>
</gl-link>
<p class="gl-pt-3">{{ feature.body }}</p>
<gl-link
:href="feature.url"
target="_blank"
data-track-event="click_whats_new_item"
:data-track-label="feature.title"
:data-track-property="feature.url"
>{{ __('Learn more') }}</gl-link
>
</div>
</template>
...@@ -10,6 +10,8 @@ export default el => { ...@@ -10,6 +10,8 @@ export default el => {
if (whatsNewApp) { if (whatsNewApp) {
store.dispatch('openDrawer'); store.dispatch('openDrawer');
} else { } else {
const storageKey = getStorageKey(el);
whatsNewApp = new Vue({ whatsNewApp = new Vue({
el, el,
store, store,
...@@ -26,11 +28,7 @@ export default el => { ...@@ -26,11 +28,7 @@ export default el => {
}, },
render(createElement) { render(createElement) {
return createElement('app', { return createElement('app', {
props: { props: { storageKey },
storageKey: getStorageKey(el),
versions: JSON.parse(el.getAttribute('data-versions')),
gitlabDotCom: el.getAttribute('data-gitlab-dot-com'),
},
}); });
}, },
}); });
......
...@@ -13,7 +13,7 @@ export default { ...@@ -13,7 +13,7 @@ export default {
localStorage.setItem(storageKey, JSON.stringify(false)); localStorage.setItem(storageKey, JSON.stringify(false));
} }
}, },
fetchItems({ commit, state }, { page, version } = { page: null, version: null }) { fetchItems({ commit, state }, page) {
if (state.fetching) { if (state.fetching) {
return false; return false;
} }
...@@ -24,7 +24,6 @@ export default { ...@@ -24,7 +24,6 @@ export default {
.get('/-/whats_new', { .get('/-/whats_new', {
params: { params: {
page, page,
version,
}, },
}) })
.then(({ data, headers }) => { .then(({ data, headers }) => {
......
...@@ -6,32 +6,6 @@ ...@@ -6,32 +6,6 @@
.gl-infinite-scroll-legend { .gl-infinite-scroll-legend {
@include gl-display-none; @include gl-display-none;
} }
.gl-tabs {
@include gl-overflow-y-auto;
}
.gl-tabs-nav {
flex-wrap: nowrap;
overflow-x: scroll;
align-items: stretch;
.nav-item {
@include gl-flex-shrink-0;
a {
@include gl-h-full;
line-height: 1.5;
}
}
}
.gl-spinner-container {
@include gl-w-full;
@include gl-absolute;
top: 50%;
transform: translateY(-50%);
}
} }
.with-performance-bar .whats-new-drawer { .with-performance-bar .whats-new-drawer {
......
# frozen_string_literal: true # frozen_string_literal: true
class WhatsNewController < ApplicationController class WhatsNewController < ApplicationController
include Gitlab::Utils::StrongMemoize
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :check_feature_flag before_action :check_feature_flag, :check_valid_page_param, :set_pagination_headers
before_action :check_valid_page_param, :set_pagination_headers, unless: -> { has_version_param? }
feature_category :navigation feature_category :navigation
def index def index
respond_to do |format| respond_to do |format|
format.js do format.js do
render json: highlight_items render json: most_recent_items
end end
end end
end end
...@@ -32,25 +29,15 @@ class WhatsNewController < ApplicationController ...@@ -32,25 +29,15 @@ class WhatsNewController < ApplicationController
params[:page]&.to_i || 1 params[:page]&.to_i || 1
end end
def highlights def most_recent
strong_memoize(:highlights) do @most_recent ||= ReleaseHighlight.paginated(page: current_page)
if has_version_param?
ReleaseHighlight.for_version(version: params[:version])
else
ReleaseHighlight.paginated(page: current_page)
end
end
end end
def highlight_items def most_recent_items
highlights.map {|item| Gitlab::WhatsNew::ItemPresenter.present(item) } most_recent[:items].map {|item| Gitlab::WhatsNew::ItemPresenter.present(item) }
end end
def set_pagination_headers def set_pagination_headers
response.set_header('X-Next-Page', highlights.next_page) response.set_header('X-Next-Page', most_recent[:next_page])
end
def has_version_param?
params[:version].present?
end end
end end
...@@ -6,14 +6,10 @@ module WhatsNewHelper ...@@ -6,14 +6,10 @@ module WhatsNewHelper
end end
def whats_new_storage_key def whats_new_storage_key
most_recent_version = ReleaseHighlight.versions&.first most_recent_version = ReleaseHighlight.most_recent_version
return unless most_recent_version return unless most_recent_version
['display-whats-new-notification', most_recent_version].join('-') ['display-whats-new-notification', most_recent_version].join('-')
end end
def whats_new_versions
ReleaseHighlight.versions
end
end end
...@@ -3,17 +3,6 @@ ...@@ -3,17 +3,6 @@
class ReleaseHighlight class ReleaseHighlight
CACHE_DURATION = 1.hour CACHE_DURATION = 1.hour
FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml') FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml')
RELEASE_VERSIONS_IN_A_YEAR = 12
def self.for_version(version:)
index = self.versions.index(version)
return if index.nil?
page = index + 1
self.paginated(page: page)
end
def self.paginated(page: 1) def self.paginated(page: 1)
Rails.cache.fetch(cache_key(page), expires_in: CACHE_DURATION) do Rails.cache.fetch(cache_key(page), expires_in: CACHE_DURATION) do
...@@ -21,7 +10,10 @@ class ReleaseHighlight ...@@ -21,7 +10,10 @@ class ReleaseHighlight
next if items.nil? next if items.nil?
QueryResult.new(items: items, next_page: next_page(current_page: page)) {
items: items,
next_page: next_page(current_page: page)
}
end end
end end
...@@ -61,25 +53,15 @@ class ReleaseHighlight ...@@ -61,25 +53,15 @@ class ReleaseHighlight
next_page if self.file_paths[next_index] next_page if self.file_paths[next_index]
end end
def self.most_recent_item_count def self.most_recent_version
Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:recent_item_count', expires_in: CACHE_DURATION) do Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:release_version', expires_in: CACHE_DURATION) do
self.paginated&.items&.count self.paginated&.[](:items)&.first&.[]('release')
end end
end end
def self.versions def self.most_recent_item_count
Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:versions', expires_in: CACHE_DURATION) do Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:recent_item_count', expires_in: CACHE_DURATION) do
versions = self.file_paths.first(RELEASE_VERSIONS_IN_A_YEAR).map do |path| self.paginated&.[](:items)&.count
/\d*\_(\d*\_\d*)\.yml$/.match(path).captures[0].gsub(/0(?=\d)/, "").tr("_", ".")
end
versions.uniq
end end
end end
QueryResult = Struct.new(:items, :next_page, keyword_init: true) do
include Enumerable
delegate :each, to: :items
end
end end
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left') = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
- if ::Feature.enabled?(:whats_new_drawer, current_user) - if ::Feature.enabled?(:whats_new_drawer, current_user)
#whats-new-app{ data: { storage_key: whats_new_storage_key, versions: whats_new_versions, gitlab_dot_com: Gitlab.dev_env_org_or_com? } } #whats-new-app{ data: { storage_key: whats_new_storage_key } }
- if can?(current_user, :update_user_status, current_user) - if can?(current_user, :update_user_status, current_user)
.js-set-status-modal-wrapper{ data: user_status_data } .js-set-status-modal-wrapper{ data: user_status_data }
...@@ -31747,9 +31747,6 @@ msgstr "" ...@@ -31747,9 +31747,6 @@ msgstr ""
msgid "Your U2F device was registered!" msgid "Your U2F device was registered!"
msgstr "" msgstr ""
msgid "Your Version"
msgstr ""
msgid "Your WebAuthn device did not send a valid JSON response." msgid "Your WebAuthn device did not send a valid JSON response."
msgstr "" msgstr ""
......
import { createLocalVue, mount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { GlDrawer, GlInfiniteScroll, GlTabs } from '@gitlab/ui'; import { GlDrawer, GlInfiniteScroll } from '@gitlab/ui';
import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import App from '~/whats_new/components/app.vue'; import App from '~/whats_new/components/app.vue';
...@@ -16,18 +16,12 @@ const localVue = createLocalVue(); ...@@ -16,18 +16,12 @@ const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
describe('App', () => { describe('App', () => {
const propsData = { storageKey: 'storage-key' };
let wrapper; let wrapper;
let store; let store;
let actions; let actions;
let state; let state;
let trackingSpy; let trackingSpy;
let gitlabDotCom = true;
const buildProps = () => ({
storageKey: 'storage-key',
versions: ['3.11', '3.10'],
gitlabDotCom,
});
const buildWrapper = () => { const buildWrapper = () => {
actions = { actions = {
...@@ -51,7 +45,7 @@ describe('App', () => { ...@@ -51,7 +45,7 @@ describe('App', () => {
wrapper = mount(App, { wrapper = mount(App, {
localVue, localVue,
store, store,
propsData: buildProps(), propsData,
directives: { directives: {
GlResizeObserver: createMockDirective(), GlResizeObserver: createMockDirective(),
}, },
...@@ -59,171 +53,112 @@ describe('App', () => { ...@@ -59,171 +53,112 @@ describe('App', () => {
}; };
const findInfiniteScroll = () => wrapper.find(GlInfiniteScroll); const findInfiniteScroll = () => wrapper.find(GlInfiniteScroll);
const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached');
const setup = async () => { beforeEach(async () => {
document.body.dataset.page = 'test-page'; document.body.dataset.page = 'test-page';
document.body.dataset.namespaceId = 'namespace-840'; document.body.dataset.namespaceId = 'namespace-840';
trackingSpy = mockTracking('_category_', null, jest.spyOn); trackingSpy = mockTracking('_category_', null, jest.spyOn);
buildWrapper(); buildWrapper();
wrapper.vm.$store.state.features = [ wrapper.vm.$store.state.features = [{ title: 'Whats New Drawer', url: 'www.url.com' }];
{ title: 'Whats New Drawer', url: 'www.url.com', release: 3.11 },
];
wrapper.vm.$store.state.drawerBodyHeight = MOCK_DRAWER_BODY_HEIGHT; wrapper.vm.$store.state.drawerBodyHeight = MOCK_DRAWER_BODY_HEIGHT;
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
}; });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
unmockTracking(); unmockTracking();
}); });
describe('gitlab.com', () => { const getDrawer = () => wrapper.find(GlDrawer);
beforeEach(() => {
setup();
});
const getDrawer = () => wrapper.find(GlDrawer);
it('contains a drawer', () => {
expect(getDrawer().exists()).toBe(true);
});
it('dispatches openDrawer and tracking calls when mounted', () => {
expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
label: 'namespace_id',
value: 'namespace-840',
});
});
it('dispatches closeDrawer when clicking close', () => {
getDrawer().vm.$emit('close');
expect(actions.closeDrawer).toHaveBeenCalled();
});
it.each([true, false])('passes open property', async openState => {
wrapper.vm.$store.state.open = openState;
await wrapper.vm.$nextTick();
expect(getDrawer().props('open')).toBe(openState);
});
it('renders features when provided via ajax', () => {
expect(actions.fetchItems).toHaveBeenCalled();
expect(wrapper.find('[data-test-id="feature-title"]').text()).toBe('Whats New Drawer');
});
it('send an event when feature item is clicked', () => {
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
const link = wrapper.find('.whats-new-item-title-link'); it('contains a drawer', () => {
triggerEvent(link.element); expect(getDrawer().exists()).toBe(true);
});
expect(trackingSpy.mock.calls[1]).toMatchObject([ it('dispatches openDrawer and tracking calls when mounted', () => {
'_category_', expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
'click_whats_new_item', expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
{ label: 'namespace_id',
label: 'Whats New Drawer', value: 'namespace-840',
property: 'www.url.com',
},
]);
}); });
});
it('renders infinite scroll', () => { it('dispatches closeDrawer when clicking close', () => {
const scroll = findInfiniteScroll(); getDrawer().vm.$emit('close');
expect(actions.closeDrawer).toHaveBeenCalled();
expect(scroll.props()).toMatchObject({ });
fetchedItems: wrapper.vm.$store.state.features.length,
maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
});
});
describe('bottomReached', () => { it.each([true, false])('passes open property', async openState => {
const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached'); wrapper.vm.$store.state.open = openState;
beforeEach(() => { await wrapper.vm.$nextTick();
actions.fetchItems.mockClear();
});
it('when nextPage exists it calls fetchItems', () => { expect(getDrawer().props('open')).toBe(openState);
wrapper.vm.$store.state.pageInfo = { nextPage: 840 }; });
emitBottomReached();
expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { page: 840 }); it('renders features when provided via ajax', () => {
}); expect(actions.fetchItems).toHaveBeenCalled();
expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
});
it('when nextPage does not exist it does not call fetchItems', () => { it('send an event when feature item is clicked', () => {
wrapper.vm.$store.state.pageInfo = { nextPage: null }; trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
emitBottomReached();
expect(actions.fetchItems).not.toHaveBeenCalled(); const link = wrapper.find('.whats-new-item-title-link');
}); triggerEvent(link.element);
});
it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => { expect(trackingSpy.mock.calls[1]).toMatchObject([
const { value } = getBinding(getDrawer().element, 'gl-resize-observer'); '_category_',
'click_whats_new_item',
value(); {
label: 'Whats New Drawer',
property: 'www.url.com',
},
]);
});
expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element); it('renders infinite scroll', () => {
const scroll = findInfiniteScroll();
expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith( expect(scroll.props()).toMatchObject({
expect.any(Object), fetchedItems: wrapper.vm.$store.state.features.length,
MOCK_DRAWER_BODY_HEIGHT, maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
);
}); });
}); });
describe('self managed', () => { describe('bottomReached', () => {
const findTabs = () => wrapper.find(GlTabs);
const clickSecondTab = async () => {
const secondTab = wrapper.findAll('.nav-link').at(1);
await secondTab.trigger('click');
await new Promise(resolve => requestAnimationFrame(resolve));
};
beforeEach(() => { beforeEach(() => {
gitlabDotCom = false; actions.fetchItems.mockClear();
setup();
}); });
it('renders tabs with drawer body height and content', () => { it('when nextPage exists it calls fetchItems', () => {
const scroll = findInfiniteScroll(); wrapper.vm.$store.state.pageInfo = { nextPage: 840 };
const tabs = findTabs(); emitBottomReached();
expect(scroll.exists()).toBe(false); expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), 840);
expect(tabs.attributes().style).toBe(`height: ${MOCK_DRAWER_BODY_HEIGHT}px;`);
expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
}); });
describe('fetchVersion', () => { it('when nextPage does not exist it does not call fetchItems', () => {
beforeEach(() => { wrapper.vm.$store.state.pageInfo = { nextPage: null };
actions.fetchItems.mockClear(); emitBottomReached();
});
it('when version isnt fetched, clicking a tab calls fetchItems', async () => { expect(actions.fetchItems).not.toHaveBeenCalled();
const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion'); });
await clickSecondTab(); });
expect(fetchVersionSpy).toHaveBeenCalledWith('3.10'); it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => {
expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { version: '3.10' }); const { value } = getBinding(getDrawer().element, 'gl-resize-observer');
});
it('when version has been fetched, clicking a tab calls fetchItems', async () => { value();
wrapper.vm.$store.state.features.push({ title: 'GitLab Stories', release: 3.1 });
await wrapper.vm.$nextTick();
const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion'); expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element);
await clickSecondTab();
expect(fetchVersionSpy).toHaveBeenCalledWith('3.10'); expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith(
expect(actions.fetchItems).not.toHaveBeenCalled(); expect.any(Object),
expect(wrapper.find('.tab-pane.active h5').text()).toBe('GitLab Stories'); MOCK_DRAWER_BODY_HEIGHT,
}); );
});
}); });
}); });
...@@ -41,23 +41,6 @@ describe('whats new actions', () => { ...@@ -41,23 +41,6 @@ describe('whats new actions', () => {
axiosMock.restore(); axiosMock.restore();
}); });
it('passes arguments', () => {
axiosMock.reset();
axiosMock
.onGet('/-/whats_new', { params: { page: 8, version: 40 } })
.replyOnce(200, [{ title: 'GitLab Stories' }]);
testAction(
actions.fetchItems,
{ page: 8, version: 40 },
{},
expect.arrayContaining([
{ type: types.ADD_FEATURES, payload: [{ title: 'GitLab Stories' }] },
]),
);
});
it('if already fetching, does not fetch', () => { it('if already fetching, does not fetch', () => {
testAction(actions.fetchItems, {}, { fetching: true }, []); testAction(actions.fetchItems, {}, { fetching: true }, []);
}); });
......
...@@ -10,7 +10,7 @@ RSpec.describe WhatsNewHelper do ...@@ -10,7 +10,7 @@ RSpec.describe WhatsNewHelper do
let(:release_item) { double(:item) } let(:release_item) { double(:item) }
before do before do
allow(ReleaseHighlight).to receive(:versions).and_return([84.0]) allow(ReleaseHighlight).to receive(:most_recent_version).and_return(84.0)
end end
it { is_expected.to eq('display-whats-new-notification-84.0') } it { is_expected.to eq('display-whats-new-notification-84.0') }
...@@ -18,7 +18,7 @@ RSpec.describe WhatsNewHelper do ...@@ -18,7 +18,7 @@ RSpec.describe WhatsNewHelper do
context 'when most recent release highlights do NOT exist' do context 'when most recent release highlights do NOT exist' do
before do before do
allow(ReleaseHighlight).to receive(:versions).and_return(nil) allow(ReleaseHighlight).to receive(:most_recent_version).and_return(nil)
end end
it { is_expected.to be_nil } it { is_expected.to be_nil }
...@@ -44,14 +44,4 @@ RSpec.describe WhatsNewHelper do ...@@ -44,14 +44,4 @@ RSpec.describe WhatsNewHelper do
end end
end end
end end
describe '#whats_new_versions' do
let(:versions) { [84.0] }
it 'returns ReleaseHighlight.versions' do
expect(ReleaseHighlight).to receive(:versions).and_return(versions)
expect(helper.whats_new_versions).to eq(versions)
end
end
end end
...@@ -3,44 +3,21 @@ ...@@ -3,44 +3,21 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ReleaseHighlight do RSpec.describe ReleaseHighlight do
let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) } describe '#paginated' do
let(:cache_mock) { double(:cache_mock) } let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
let(:cache_mock) { double(:cache_mock) }
before do
allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
allow(cache_mock).to receive(:fetch).with('release_highlight:file_paths', expires_in: 1.hour).and_yield
end
after do
ReleaseHighlight.instance_variable_set(:@file_paths, nil)
end
describe '.for_version' do
subject { ReleaseHighlight.for_version(version: version) }
let(:version) { '1.1' }
context 'with version param that exists' do
it 'returns items from that version' do
expect(subject.items.first['title']).to eq("It's gonna be a bright")
end
end
context 'with version param that does NOT exist' do
let(:version) { '84.0' }
it 'returns nil' do
expect(subject).to be_nil
end
end
end
describe '.paginated' do
let(:dot_com) { false } let(:dot_com) { false }
before do before do
allow(Gitlab).to receive(:com?).and_return(dot_com) allow(Gitlab).to receive(:com?).and_return(dot_com)
allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
expect(Rails).to receive(:cache).twice.and_return(cache_mock) expect(Rails).to receive(:cache).twice.and_return(cache_mock)
expect(cache_mock).to receive(:fetch).with('release_highlight:file_paths', expires_in: 1.hour).and_yield
end
after do
ReleaseHighlight.instance_variable_set(:@file_paths, nil)
end end
context 'with page param' do context 'with page param' do
...@@ -113,51 +90,46 @@ RSpec.describe ReleaseHighlight do ...@@ -113,51 +90,46 @@ RSpec.describe ReleaseHighlight do
end end
end end
describe '.most_recent_item_count' do describe '.most_recent_version' do
subject { ReleaseHighlight.most_recent_item_count } subject { ReleaseHighlight.most_recent_version }
context 'when recent release items exist' do context 'when version exist' do
it 'returns the count from the most recent file' do let(:release_item) { double(:item) }
allow(ReleaseHighlight).to receive(:paginated).and_return(double(:paginated, items: [double(:item)]))
expect(subject).to eq(1) before do
allow(ReleaseHighlight).to receive(:paginated).and_return({ items: [release_item] })
allow(release_item).to receive(:[]).with('release').and_return(84.0)
end end
it { is_expected.to eq(84.0) }
end end
context 'when recent release items do NOT exist' do context 'when most recent release highlights do NOT exist' do
it 'returns nil' do before do
allow(ReleaseHighlight).to receive(:paginated).and_return(nil) allow(ReleaseHighlight).to receive(:paginated).and_return(nil)
expect(subject).to be_nil
end end
it { is_expected.to be_nil }
end end
end end
describe '.versions' do describe '#most_recent_item_count' do
it 'returns versions from the file paths' do subject { ReleaseHighlight.most_recent_item_count }
expect(ReleaseHighlight.versions).to eq(['1.5', '1.2', '1.1'])
end
context 'when there are more than 12 versions' do context 'when recent release items exist' do
let(:file_paths) do it 'returns the count from the most recent file' do
i = 0 allow(ReleaseHighlight).to receive(:paginated).and_return({ items: [double(:item)] })
Array.new(20) { "20201225_01_#{i += 1}.yml" }
end
it 'limits to 12 versions' do expect(subject).to eq(1)
allow(ReleaseHighlight).to receive(:file_paths).and_return(file_paths)
expect(ReleaseHighlight.versions.count).to eq(12)
end end
end end
end
describe 'QueryResult' do
subject { ReleaseHighlight::QueryResult.new(items: items, next_page: 2) }
let(:items) { [:item] } context 'when recent release items do NOT exist' do
it 'returns nil' do
allow(ReleaseHighlight).to receive(:paginated).and_return(nil)
it 'responds to map' do expect(subject).to be_nil
expect(subject.map(&:to_s)).to eq(items.map(&:to_s)) end
end end
end end
end end
...@@ -4,22 +4,22 @@ require 'spec_helper' ...@@ -4,22 +4,22 @@ require 'spec_helper'
RSpec.describe WhatsNewController do RSpec.describe WhatsNewController do
describe 'whats_new_path' do describe 'whats_new_path' do
let(:item) { double(:item) }
let(:highlights) { double(:highlight, items: [item], map: [item].map, next_page: 2) }
context 'with whats_new_drawer feature enabled' do context 'with whats_new_drawer feature enabled' do
before do before do
stub_feature_flags(whats_new_drawer: true) stub_feature_flags(whats_new_drawer: true)
end end
context 'with no page param' do context 'with no page param' do
let(:most_recent) { { items: [item], next_page: 2 } }
let(:item) { double(:item) }
it 'responds with paginated data and headers' do it 'responds with paginated data and headers' do
allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(highlights) allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(most_recent)
allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item) allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
get whats_new_path, xhr: true get whats_new_path, xhr: true
expect(response.body).to eq(highlights.items.to_json) expect(response.body).to eq(most_recent[:items].to_json)
expect(response.headers['X-Next-Page']).to eq(2) expect(response.headers['X-Next-Page']).to eq(2)
end end
end end
...@@ -37,18 +37,6 @@ RSpec.describe WhatsNewController do ...@@ -37,18 +37,6 @@ RSpec.describe WhatsNewController do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
context 'with version param' do
it 'returns items without pagination headers' do
allow(ReleaseHighlight).to receive(:for_version).with(version: '42').and_return(highlights)
allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
get whats_new_path(version: 42), xhr: true
expect(response.body).to eq(highlights.items.to_json)
expect(response.headers['X-Next-Page']).to be_nil
end
end
end end
context 'with whats_new_drawer feature disabled' do context 'with whats_new_drawer feature disabled' do
......
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