diff options
Diffstat (limited to 'spec/frontend/lib/utils')
-rw-r--r-- | spec/frontend/lib/utils/breadcrumbs_spec.js | 22 | ||||
-rw-r--r-- | spec/frontend/lib/utils/common_utils_spec.js | 2 | ||||
-rw-r--r-- | spec/frontend/lib/utils/datetime/date_format_utility_spec.js | 12 | ||||
-rw-r--r-- | spec/frontend/lib/utils/datetime/locale_dateformat_spec.js | 177 | ||||
-rw-r--r-- | spec/frontend/lib/utils/datetime/timeago_utility_spec.js | 6 | ||||
-rw-r--r-- | spec/frontend/lib/utils/datetime_utility_spec.js | 15 | ||||
-rw-r--r-- | spec/frontend/lib/utils/secret_detection_spec.js | 1 | ||||
-rw-r--r-- | spec/frontend/lib/utils/vuex_module_mappers_spec.js | 133 |
8 files changed, 213 insertions, 155 deletions
diff --git a/spec/frontend/lib/utils/breadcrumbs_spec.js b/spec/frontend/lib/utils/breadcrumbs_spec.js index 3c29e3723d3..481e3db521c 100644 --- a/spec/frontend/lib/utils/breadcrumbs_spec.js +++ b/spec/frontend/lib/utils/breadcrumbs_spec.js @@ -26,24 +26,20 @@ describe('Breadcrumbs utils', () => { `; const mockRouter = jest.fn(); - let MockComponent; - let mockApolloProvider; - beforeEach(() => { - MockComponent = Vue.component('MockComponent', { - render: (createElement) => - createElement('span', { - attrs: { - 'data-testid': 'mock-component', - }, - }), - }); - mockApolloProvider = createMockApollo(); + const MockComponent = Vue.component('MockComponent', { + render: (createElement) => + createElement('span', { + attrs: { + 'data-testid': 'mock-component', + }, + }), }); + const mockApolloProvider = createMockApollo(); + afterEach(() => { resetHTMLFixture(); - MockComponent = null; }); describe('injectVueAppBreadcrumbs', () => { diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js index 6295914b127..5c2bcd48f3e 100644 --- a/spec/frontend/lib/utils/common_utils_spec.js +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -151,7 +151,7 @@ describe('common_utils', () => { jest.spyOn(window, 'scrollBy'); document.body.innerHTML += ` <div id="parent"> - <div class="navbar-gitlab" style="position: fixed; top: 0; height: 50px;"></div> + <div class="header-logged-out" style="position: fixed; top: 0; height: 50px;"></div> <div style="height: 2000px; margin-top: 50px;"></div> <div id="user-content-test" style="height: 2000px;"></div> </div> diff --git a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js index 65018fe1625..79b09654f00 100644 --- a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js +++ b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js @@ -122,12 +122,12 @@ describe('date_format_utility.js', () => { describe('formatTimeAsSummary', () => { it.each` unit | value | result - ${'months'} | ${1.5} | ${'1.5M'} - ${'weeks'} | ${1.25} | ${'1.5w'} - ${'days'} | ${2} | ${'2d'} - ${'hours'} | ${10} | ${'10h'} - ${'minutes'} | ${20} | ${'20m'} - ${'seconds'} | ${10} | ${'<1m'} + ${'months'} | ${1.5} | ${'1.5 months'} + ${'weeks'} | ${1.25} | ${'1.5 weeks'} + ${'days'} | ${2} | ${'2 days'} + ${'hours'} | ${10} | ${'10 hours'} + ${'minutes'} | ${20} | ${'20 minutes'} + ${'seconds'} | ${10} | ${'<1 minute'} ${'seconds'} | ${0} | ${'-'} `('will format $value $unit to $result', ({ unit, value, result }) => { expect(utils.formatTimeAsSummary({ [unit]: value })).toBe(result); diff --git a/spec/frontend/lib/utils/datetime/locale_dateformat_spec.js b/spec/frontend/lib/utils/datetime/locale_dateformat_spec.js new file mode 100644 index 00000000000..3200f0cc7d7 --- /dev/null +++ b/spec/frontend/lib/utils/datetime/locale_dateformat_spec.js @@ -0,0 +1,177 @@ +import { DATE_TIME_FORMATS, localeDateFormat } from '~/lib/utils/datetime/locale_dateformat'; +import { setLanguage } from 'jest/__helpers__/locale_helper'; +import * as localeFns from '~/locale'; + +describe('localeDateFormat (en-US)', () => { + const date = new Date('1983-07-09T14:15:23.123Z'); + const sameDay = new Date('1983-07-09T18:27:09.198Z'); + const sameMonth = new Date('1983-07-12T12:36:02.654Z'); + const nextYear = new Date('1984-01-10T07:47:54.947Z'); + + beforeEach(() => { + setLanguage('en-US'); + localeDateFormat.reset(); + }); + + /* + Depending on the ICU/Intl version, formatted strings might contain + characters which aren't a normal space, e.g. U+2009 THIN SPACE in formatRange or + U+202F NARROW NO-BREAK SPACE between time and AM/PM. + + In order for the specs to be more portable and easier to read, as git/gitlab aren't + great at rendering these other spaces, we replace them U+0020 SPACE + */ + function expectDateString(str) { + // eslint-disable-next-line jest/valid-expect + return expect(str.replace(/[\s\u2009]+/g, ' ')); + } + + describe('#asDateTime', () => { + it('exposes a working date formatter', () => { + expectDateString(localeDateFormat.asDateTime.format(date)).toBe('Jul 9, 1983, 2:15 PM'); + expectDateString(localeDateFormat.asDateTime.format(nextYear)).toBe('Jan 10, 1984, 7:47 AM'); + }); + + it('exposes a working date range formatter', () => { + expectDateString(localeDateFormat.asDateTime.formatRange(date, nextYear)).toBe( + 'Jul 9, 1983, 2:15 PM – Jan 10, 1984, 7:47 AM', + ); + expectDateString(localeDateFormat.asDateTime.formatRange(date, sameMonth)).toBe( + 'Jul 9, 1983, 2:15 PM – Jul 12, 1983, 12:36 PM', + ); + expectDateString(localeDateFormat.asDateTime.formatRange(date, sameDay)).toBe( + 'Jul 9, 1983, 2:15 – 6:27 PM', + ); + }); + + it.each([ + ['automatic', 0, '2:15 PM'], + ['h12 preference', 1, '2:15 PM'], + ['h24 preference', 2, '14:15'], + ])("respects user's hourCycle preference: %s", (_, timeDisplayFormat, result) => { + window.gon.time_display_format = timeDisplayFormat; + expectDateString(localeDateFormat.asDateTime.format(date)).toContain(result); + expectDateString(localeDateFormat.asDateTime.formatRange(date, nextYear)).toContain(result); + }); + }); + + describe('#asDateTimeFull', () => { + it('exposes a working date formatter', () => { + expectDateString(localeDateFormat.asDateTimeFull.format(date)).toBe( + 'July 9, 1983 at 2:15:23 PM GMT', + ); + expectDateString(localeDateFormat.asDateTimeFull.format(nextYear)).toBe( + 'January 10, 1984 at 7:47:54 AM GMT', + ); + }); + + it('exposes a working date range formatter', () => { + expectDateString(localeDateFormat.asDateTimeFull.formatRange(date, nextYear)).toBe( + 'July 9, 1983 at 2:15:23 PM GMT – January 10, 1984 at 7:47:54 AM GMT', + ); + expectDateString(localeDateFormat.asDateTimeFull.formatRange(date, sameMonth)).toBe( + 'July 9, 1983 at 2:15:23 PM GMT – July 12, 1983 at 12:36:02 PM GMT', + ); + expectDateString(localeDateFormat.asDateTimeFull.formatRange(date, sameDay)).toBe( + 'July 9, 1983, 2:15:23 PM GMT – 6:27:09 PM GMT', + ); + }); + + it.each([ + ['automatic', 0, '2:15:23 PM'], + ['h12 preference', 1, '2:15:23 PM'], + ['h24 preference', 2, '14:15:23'], + ])("respects user's hourCycle preference: %s", (_, timeDisplayFormat, result) => { + window.gon.time_display_format = timeDisplayFormat; + expectDateString(localeDateFormat.asDateTimeFull.format(date)).toContain(result); + expectDateString(localeDateFormat.asDateTimeFull.formatRange(date, nextYear)).toContain( + result, + ); + }); + }); + + describe('#asDate', () => { + it('exposes a working date formatter', () => { + expectDateString(localeDateFormat.asDate.format(date)).toBe('Jul 9, 1983'); + expectDateString(localeDateFormat.asDate.format(nextYear)).toBe('Jan 10, 1984'); + }); + + it('exposes a working date range formatter', () => { + expectDateString(localeDateFormat.asDate.formatRange(date, nextYear)).toBe( + 'Jul 9, 1983 – Jan 10, 1984', + ); + expectDateString(localeDateFormat.asDate.formatRange(date, sameMonth)).toBe( + 'Jul 9 – 12, 1983', + ); + expectDateString(localeDateFormat.asDate.formatRange(date, sameDay)).toBe('Jul 9, 1983'); + }); + }); + + describe('#asTime', () => { + it('exposes a working date formatter', () => { + expectDateString(localeDateFormat.asTime.format(date)).toBe('2:15 PM'); + expectDateString(localeDateFormat.asTime.format(nextYear)).toBe('7:47 AM'); + }); + + it('exposes a working date range formatter', () => { + expectDateString(localeDateFormat.asTime.formatRange(date, nextYear)).toBe( + '7/9/1983, 2:15 PM – 1/10/1984, 7:47 AM', + ); + expectDateString(localeDateFormat.asTime.formatRange(date, sameMonth)).toBe( + '7/9/1983, 2:15 PM – 7/12/1983, 12:36 PM', + ); + expectDateString(localeDateFormat.asTime.formatRange(date, sameDay)).toBe('2:15 – 6:27 PM'); + }); + + it.each([ + ['automatic', 0, '2:15 PM'], + ['h12 preference', 1, '2:15 PM'], + ['h24 preference', 2, '14:15'], + ])("respects user's hourCycle preference: %s", (_, timeDisplayFormat, result) => { + window.gon.time_display_format = timeDisplayFormat; + expectDateString(localeDateFormat.asTime.format(date)).toContain(result); + expectDateString(localeDateFormat.asTime.formatRange(date, nextYear)).toContain(result); + }); + }); + + describe('#reset', () => { + it('removes the cached formatters', () => { + const spy = jest.spyOn(localeFns, 'createDateTimeFormat'); + + localeDateFormat.asDate.format(date); + localeDateFormat.asDate.format(date); + expect(spy).toHaveBeenCalledTimes(1); + + localeDateFormat.reset(); + + localeDateFormat.asDate.format(date); + localeDateFormat.asDate.format(date); + expect(spy).toHaveBeenCalledTimes(2); + }); + }); + + describe.each(DATE_TIME_FORMATS)('formatter for %p', (format) => { + it('is defined', () => { + expect(localeDateFormat[format]).toBeDefined(); + expect(localeDateFormat[format].format(date)).toBeDefined(); + expect(localeDateFormat[format].formatRange(date, nextYear)).toBeDefined(); + }); + + it('getting the formatter multiple times, just calls the Intl API once', () => { + const spy = jest.spyOn(localeFns, 'createDateTimeFormat'); + + localeDateFormat[format].format(date); + localeDateFormat[format].format(date); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('getting the formatter memoized the correct formatter', () => { + const spy = jest.spyOn(localeFns, 'createDateTimeFormat'); + + expect(localeDateFormat[format].format(date)).toBe(localeDateFormat[format].format(date)); + + expect(spy).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/spec/frontend/lib/utils/datetime/timeago_utility_spec.js b/spec/frontend/lib/utils/datetime/timeago_utility_spec.js index 44db4cf88a2..53ed524116e 100644 --- a/spec/frontend/lib/utils/datetime/timeago_utility_spec.js +++ b/spec/frontend/lib/utils/datetime/timeago_utility_spec.js @@ -1,5 +1,6 @@ -import { DATE_ONLY_FORMAT } from '~/lib/utils/datetime/constants'; import { getTimeago, localTimeAgo, timeFor, duration } from '~/lib/utils/datetime/timeago_utility'; +import { DATE_ONLY_FORMAT, localeDateFormat } from '~/lib/utils/datetime/locale_dateformat'; + import { s__ } from '~/locale'; import '~/commons/bootstrap'; @@ -143,7 +144,7 @@ describe('TimeAgo utils', () => { it.each` updateTooltip | title ${false} | ${'some time'} - ${true} | ${'Feb 18, 2020 10:22pm UTC'} + ${true} | ${'February 18, 2020 at 10:22:32 PM GMT'} `( `has content: '${text}' and tooltip: '$title' with updateTooltip = $updateTooltip`, ({ updateTooltip, title }) => { @@ -168,6 +169,7 @@ describe('TimeAgo utils', () => { ${1} | ${'12-hour'} | ${'Feb 18, 2020, 10:22 PM'} ${2} | ${'24-hour'} | ${'Feb 18, 2020, 22:22'} `(`'$display' renders as '$text'`, ({ timeDisplayFormat, text }) => { + localeDateFormat.reset(); gon.time_display_relative = false; gon.time_display_format = timeDisplayFormat; diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 330bfca7029..73a4af2c85d 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -800,6 +800,21 @@ describe('date addition/subtraction methods', () => { ); }); + describe('nYearsBefore', () => { + it.each` + date | numberOfYears | expected + ${'2020-07-06'} | ${4} | ${'2016-07-06'} + ${'2020-07-06'} | ${1} | ${'2019-07-06'} + `( + 'returns $expected for "$numberOfYears year(s) before $date"', + ({ date, numberOfYears, expected }) => { + expect(datetimeUtility.nYearsBefore(new Date(date), numberOfYears)).toEqual( + new Date(expected), + ); + }, + ); + }); + describe('nMonthsBefore', () => { // The previous month (February) has 28 days const march2019 = '2019-03-15T00:00:00.000Z'; diff --git a/spec/frontend/lib/utils/secret_detection_spec.js b/spec/frontend/lib/utils/secret_detection_spec.js index 761062f0340..a8da6e8969f 100644 --- a/spec/frontend/lib/utils/secret_detection_spec.js +++ b/spec/frontend/lib/utils/secret_detection_spec.js @@ -31,6 +31,7 @@ describe('containsSensitiveToken', () => { 'token: gloas-a8cc74ccb0de004d09a968705ba49099229b288b3de43f26c473a9d8d7fb7693', 'https://example.com/feed?feed_token=123456789_abcdefghij', 'glpat-1234567890 and feed_token=ABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'token: gldt-cgyKc1k_AsnEpmP-5fRL', ]; it.each(sensitiveMessages)('returns true for message: %s', (message) => { diff --git a/spec/frontend/lib/utils/vuex_module_mappers_spec.js b/spec/frontend/lib/utils/vuex_module_mappers_spec.js deleted file mode 100644 index 9070903728b..00000000000 --- a/spec/frontend/lib/utils/vuex_module_mappers_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import { mount } from '@vue/test-utils'; -import Vue from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { - mapVuexModuleActions, - mapVuexModuleGetters, - mapVuexModuleState, - REQUIRE_STRING_ERROR_MESSAGE, -} from '~/lib/utils/vuex_module_mappers'; - -const TEST_MODULE_NAME = 'testModuleName'; - -Vue.use(Vuex); - -// setup test component and store ---------------------------------------------- -// -// These are used to indirectly test `vuex_module_mappers`. -const TestComponent = { - props: { - vuexModule: { - type: String, - required: true, - }, - }, - computed: { - ...mapVuexModuleState((vm) => vm.vuexModule, { name: 'name', value: 'count' }), - ...mapVuexModuleGetters((vm) => vm.vuexModule, ['hasValue', 'hasName']), - stateJson() { - return JSON.stringify({ - name: this.name, - value: this.value, - }); - }, - gettersJson() { - return JSON.stringify({ - hasValue: this.hasValue, - hasName: this.hasName, - }); - }, - }, - methods: { - ...mapVuexModuleActions((vm) => vm.vuexModule, ['increment']), - }, - template: ` -<div> - <pre data-testid="state">{{ stateJson }}</pre> - <pre data-testid="getters">{{ gettersJson }}</pre> -</div>`, -}; - -const createTestStore = () => { - return new Vuex.Store({ - modules: { - [TEST_MODULE_NAME]: { - namespaced: true, - state: { - name: 'Lorem', - count: 0, - }, - mutations: { - INCREMENT: (state, amount) => { - state.count += amount; - }, - }, - actions: { - increment({ commit }, amount) { - commit('INCREMENT', amount); - }, - }, - getters: { - hasValue: (state) => state.count > 0, - hasName: (state) => Boolean(state.name.length), - }, - }, - }, - }); -}; - -describe('~/lib/utils/vuex_module_mappers', () => { - let store; - let wrapper; - - const getJsonInTemplate = (testId) => - JSON.parse(wrapper.find(`[data-testid="${testId}"]`).text()); - const getMappedState = () => getJsonInTemplate('state'); - const getMappedGetters = () => getJsonInTemplate('getters'); - - beforeEach(() => { - store = createTestStore(); - - wrapper = mount(TestComponent, { - propsData: { - vuexModule: TEST_MODULE_NAME, - }, - store, - }); - }); - - describe('from module defined by prop', () => { - it('maps state', () => { - expect(getMappedState()).toEqual({ - name: store.state[TEST_MODULE_NAME].name, - value: store.state[TEST_MODULE_NAME].count, - }); - }); - - it('maps getters', () => { - expect(getMappedGetters()).toEqual({ - hasName: true, - hasValue: false, - }); - }); - - it('maps action', () => { - jest.spyOn(store, 'dispatch'); - - expect(store.dispatch).not.toHaveBeenCalled(); - - wrapper.vm.increment(10); - - expect(store.dispatch).toHaveBeenCalledWith(`${TEST_MODULE_NAME}/increment`, 10); - }); - }); - - describe('with non-string object value', () => { - it('throws helpful error', () => { - expect(() => mapVuexModuleActions((vm) => vm.bogus, { foo: () => {} })).toThrow( - REQUIRE_STRING_ERROR_MESSAGE, - ); - }); - }); -}); |