Commit fe72dff0 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'ps-fix-frequent-items-last-accessed-on' into 'master'

Fix frequent items timestamps not updated

See merge request gitlab-org/gitlab!64737
parents 57d4114c 2a333556
...@@ -35,13 +35,15 @@ export const getTopFrequentItems = (items) => { ...@@ -35,13 +35,15 @@ export const getTopFrequentItems = (items) => {
}; };
export const updateExistingFrequentItem = (frequentItem, item) => { export const updateExistingFrequentItem = (frequentItem, item) => {
const accessedOverHourAgo = // `frequentItem` comes from localStorage and it's possible it doesn't have a `lastAccessedOn`
Math.abs(item.lastAccessedOn - frequentItem.lastAccessedOn) / HOUR_IN_MS > 1; const neverAccessed = !frequentItem.lastAccessedOn;
const shouldUpdate =
neverAccessed || Math.abs(item.lastAccessedOn - frequentItem.lastAccessedOn) / HOUR_IN_MS > 1;
return { return {
...item, ...item,
frequency: accessedOverHourAgo ? frequentItem.frequency + 1 : frequentItem.frequency, frequency: shouldUpdate ? frequentItem.frequency + 1 : frequentItem.frequency,
lastAccessedOn: accessedOverHourAgo ? Date.now() : frequentItem.lastAccessedOn, lastAccessedOn: shouldUpdate ? Date.now() : frequentItem.lastAccessedOn,
}; };
}; };
......
...@@ -20,6 +20,10 @@ export default { ...@@ -20,6 +20,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
currentItem: {
type: Object,
required: true,
},
containerClass: { containerClass: {
type: String, type: String,
required: false, required: false,
...@@ -43,6 +47,12 @@ export default { ...@@ -43,6 +47,12 @@ export default {
{ id: 'secondary', menuItems: this.linksSecondary }, { id: 'secondary', menuItems: this.linksSecondary },
].filter((x) => x.menuItems?.length); ].filter((x) => x.menuItems?.length);
}, },
currentItemTimestamped() {
return {
...this.currentItem,
lastAccessedOn: Date.now(),
};
},
}, },
mounted() { mounted() {
// For historic reasons, the frequent-items-app component requires this too start up. // For historic reasons, the frequent-items-app component requires this too start up.
...@@ -62,7 +72,7 @@ export default { ...@@ -62,7 +72,7 @@ export default {
> >
<div class="frequent-items-dropdown-content gl-w-full! gl-pt-0!"> <div class="frequent-items-dropdown-content gl-w-full! gl-pt-0!">
<vuex-module-provider :vuex-module="frequentItemsVuexModule"> <vuex-module-provider :vuex-module="frequentItemsVuexModule">
<frequent-items-app v-bind="$attrs" /> <frequent-items-app :current-item="currentItemTimestamped" v-bind="$attrs" />
</vuex-module-provider> </vuex-module-provider>
</div> </div>
</div> </div>
......
...@@ -66,35 +66,36 @@ describe('Frequent Items utils spec', () => { ...@@ -66,35 +66,36 @@ describe('Frequent Items utils spec', () => {
}); });
describe('updateExistingFrequentItem', () => { describe('updateExistingFrequentItem', () => {
let mockedProject; const LAST_ACCESSED = 1497979281815;
const WITHIN_AN_HOUR = LAST_ACCESSED + HOUR_IN_MS;
beforeEach(() => { const OVER_AN_HOUR = WITHIN_AN_HOUR + 1;
mockedProject = { const EXISTING_ITEM = Object.freeze({
...mockProject, ...mockProject,
frequency: 1, frequency: 1,
lastAccessedOn: 1497979281815, lastAccessedOn: 1497979281815,
};
}); });
it('updates item if accessed over an hour ago', () => { it.each`
const newTimestamp = Date.now() + HOUR_IN_MS + 1; desc | existingProps | newProps | expected
${'updates item if accessed over an hour ago'} | ${{}} | ${{ lastAccessedOn: OVER_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }}
${'does not update is accessed with an hour'} | ${{}} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: EXISTING_ITEM.lastAccessedOn, frequency: 1 }}
${'updates if lastAccessedOn not found'} | ${{ lastAccessedOn: undefined }} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }}
`('$desc', ({ existingProps, newProps, expected }) => {
const newItem = { const newItem = {
...mockedProject, ...EXISTING_ITEM,
lastAccessedOn: newTimestamp, ...newProps,
}; };
const result = updateExistingFrequentItem(mockedProject, newItem); const existingItem = {
...EXISTING_ITEM,
expect(result.frequency).toBe(mockedProject.frequency + 1); ...existingProps,
});
it('does not update item if accessed within the hour', () => {
const newItem = {
...mockedProject,
lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS,
}; };
const result = updateExistingFrequentItem(mockedProject, newItem);
expect(result.frequency).toBe(mockedProject.frequency); const result = updateExistingFrequentItem(existingItem, newItem);
expect(result).toEqual({
...newItem,
...expected,
});
}); });
}); });
......
...@@ -111,6 +111,7 @@ describe('~/nav/components/responsive_app.vue', () => { ...@@ -111,6 +111,7 @@ describe('~/nav/components/responsive_app.vue', () => {
containerClass: 'gl-px-3', containerClass: 'gl-px-3',
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace, frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace,
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule, frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule,
currentItem: {},
linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary, linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary,
linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary, linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary,
}; };
...@@ -118,6 +119,7 @@ describe('~/nav/components/responsive_app.vue', () => { ...@@ -118,6 +119,7 @@ describe('~/nav/components/responsive_app.vue', () => {
containerClass: 'gl-px-3', containerClass: 'gl-px-3',
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace, frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace,
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule, frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule,
currentItem: {},
linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary, linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary,
linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary, linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary,
}; };
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import FrequentItemsApp from '~/frequent_items/components/app.vue'; import FrequentItemsApp from '~/frequent_items/components/app.vue';
import { FREQUENT_ITEMS_PROJECTS } from '~/frequent_items/constants'; import { FREQUENT_ITEMS_PROJECTS } from '~/frequent_items/constants';
...@@ -82,7 +83,9 @@ describe('~/nav/components/top_nav_container_view.vue', () => { ...@@ -82,7 +83,9 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
it('renders frequent items app', () => { it('renders frequent items app', () => {
expect(findFrequentItemsApp()).toEqual({ expect(findFrequentItemsApp()).toEqual({
vuexModule: DEFAULT_PROPS.frequentItemsVuexModule, vuexModule: DEFAULT_PROPS.frequentItemsVuexModule,
props: expect.objectContaining(TEST_OTHER_PROPS), props: expect.objectContaining(
merge({ currentItem: { lastAccessedOn: Date.now() } }, TEST_OTHER_PROPS),
),
attributes: expect.objectContaining(EXTRA_ATTRS), attributes: expect.objectContaining(EXTRA_ATTRS),
}); });
}); });
......
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