diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 7e7f62e41dd6887016cffaeb518dd76640329d2e..ca224a68f792c46e71c9a7800af7631bbadb69bf 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -651,15 +651,41 @@ Non-determinism is the breeding ground for flaky and brittle specs. Such specs e ### Faking `Date` for determinism -Consider using `useFakeDate` to ensure a consistent value is returned with every `new Date()` or `Date.now()`. +`Date` is faked by default in our Jest environment. This means every call to `Date()` or `Date.now()` returns a fixed deterministic value. + +If you really need to change the default fake date, you can call `useFakeDate` within any `describe` block, and +the date will be replaced for that specs within that `describe` context only: ```javascript import { useFakeDate } from 'helpers/fake_date'; describe('cool/component', () => { - useFakeDate(); + // Default fake `Date` + const TODAY = new Date(); - // ... + // NOTE: `useFakeDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.). + describe("on Ada Lovelace's Birthday", () => { + useFakeDate(1815, 11, 10) + + it('Date is no longer default', () => { + expect(new Date()).not.toEqual(TODAY); + }); + }); + + it('Date is still default in this scope', () => { + expect(new Date()).toEqual(TODAY) + }); +}) +``` + +Similarly, if you really need to use the real `Date` class, then you can import and call `useRealDate` within any `describe` block: + +```javascript +import { useRealDate } from 'helpers/fake_date'; + +// NOTE: `useRealDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.). +describe('with real date', () => { + useRealDate(); }); ``` diff --git a/ee/spec/frontend/analytics/code_review_analytics/components/__snapshots__/merge_request_table_spec.js.snap b/ee/spec/frontend/analytics/code_review_analytics/components/__snapshots__/merge_request_table_spec.js.snap index e966d3c3dfe917cc2a074e4fdbc0580bdc9416b0..89d524f5490d049a05a0b9ba294e6c62178fd945 100644 --- a/ee/spec/frontend/analytics/code_review_analytics/components/__snapshots__/merge_request_table_spec.js.snap +++ b/ee/spec/frontend/analytics/code_review_analytics/components/__snapshots__/merge_request_table_spec.js.snap @@ -117,7 +117,7 @@ exports[`MergeRequestTable component template matches the snapshot 1`] = ` </li> <li> - opened just now + opened 5 months ago </li> <li> diff --git a/ee/spec/frontend/analytics/code_review_analytics/components/merge_request_table_spec.js b/ee/spec/frontend/analytics/code_review_analytics/components/merge_request_table_spec.js index afb5cfff3db639e399450e4e8d51423f6eb5821b..2dd86e334f78960d27a793105b6bdfe35cc04369 100644 --- a/ee/spec/frontend/analytics/code_review_analytics/components/merge_request_table_spec.js +++ b/ee/spec/frontend/analytics/code_review_analytics/components/merge_request_table_spec.js @@ -58,8 +58,6 @@ describe('MergeRequestTable component', () => { describe('template', () => { beforeEach(() => { - jest.spyOn(global, 'Date').mockImplementationOnce(() => new Date('2020-03-09T11:01:58.135Z')); - bootstrap({ mergeRequests: mockMergeRequests }); }); diff --git a/ee/spec/frontend/analytics/repository_analytics/components/download_test_coverage_spec.js b/ee/spec/frontend/analytics/repository_analytics/components/download_test_coverage_spec.js index 1f5a24b585e95475bac7ce4b69ba3a15ad2963a1..466cddcf0321469da656330904dd8d5a087f97d0 100644 --- a/ee/spec/frontend/analytics/repository_analytics/components/download_test_coverage_spec.js +++ b/ee/spec/frontend/analytics/repository_analytics/components/download_test_coverage_spec.js @@ -2,12 +2,10 @@ import { GlAlert, GlDropdown, GlDropdownItem, GlModal } from '@gitlab/ui'; import { shallowMount, createLocalVue } from '@vue/test-utils'; import DownloadTestCoverage from 'ee/analytics/repository_analytics/components/download_test_coverage.vue'; import SelectProjectsDropdown from 'ee/analytics/repository_analytics/components/select_projects_dropdown.vue'; -import { useFakeDate } from 'helpers/fake_date'; const localVue = createLocalVue(); describe('Download test coverage component', () => { - useFakeDate(); let wrapper; const findCodeCoverageModalButton = () => @@ -73,8 +71,6 @@ describe('Download test coverage component', () => { }); describe('when selecting a project', () => { - // Due to the fake_date helper, we can always expect today's date to be 2020-07-06 - // and the default date 30 days ago to be 2020-06-06 const groupAnalyticsCoverageReportsPathWithDates = `${injectedProperties.groupAnalyticsCoverageReportsPath}?start_date=2020-06-06&end_date=2020-07-06`; describe('with all projects selected', () => { diff --git a/ee/spec/frontend/analytics/repository_analytics/components/test_coverage_summary_spec.js b/ee/spec/frontend/analytics/repository_analytics/components/test_coverage_summary_spec.js index b8b1ec77a64a937a633374b33a1a079db0976dca..6afa5b35d2d95ad516f6d10150055df74f0fa5fb 100644 --- a/ee/spec/frontend/analytics/repository_analytics/components/test_coverage_summary_spec.js +++ b/ee/spec/frontend/analytics/repository_analytics/components/test_coverage_summary_spec.js @@ -3,14 +3,12 @@ import { mount, createLocalVue } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import TestCoverageSummary from 'ee/analytics/repository_analytics/components/test_coverage_summary.vue'; import getGroupTestCoverage from 'ee/analytics/repository_analytics/graphql/queries/get_group_test_coverage.query.graphql'; -import { useFakeDate } from 'helpers/fake_date'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; const localVue = createLocalVue(); describe('Test coverage table component', () => { - useFakeDate(); let wrapper; let fakeApollo; diff --git a/ee/spec/frontend/analytics/shared/components/daterange_spec.js b/ee/spec/frontend/analytics/shared/components/daterange_spec.js index b17ee4c04f7d6ecc76b124229ed4147dce09527a..04eb2807c99c76d91c5c39e98d8f4e53aa1eb7b9 100644 --- a/ee/spec/frontend/analytics/shared/components/daterange_spec.js +++ b/ee/spec/frontend/analytics/shared/components/daterange_spec.js @@ -1,5 +1,6 @@ import { GlDaterangePicker } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; +import { useFakeDate } from 'helpers/fake_date'; import Daterange from 'ee/analytics/shared/components/daterange.vue'; const defaultProps = { @@ -8,6 +9,8 @@ const defaultProps = { }; describe('Daterange component', () => { + useFakeDate(2019, 8, 25); + let wrapper; const factory = (props = defaultProps) => { @@ -19,10 +22,6 @@ describe('Daterange component', () => { }); }; - beforeEach(() => { - jest.spyOn(global.Date, 'now').mockImplementation(() => new Date('2019-09-25T00:00:00Z')); - }); - afterEach(() => { wrapper.destroy(); }); diff --git a/ee/spec/frontend/burndown_chart/burn_chart_data_spec.js b/ee/spec/frontend/burndown_chart/burn_chart_data_spec.js index fb432ede616f40caa13d69c4c1515f336fc4e1dc..0efd65991f32060451d3c88712520a0d6cdce79e 100644 --- a/ee/spec/frontend/burndown_chart/burn_chart_data_spec.js +++ b/ee/spec/frontend/burndown_chart/burn_chart_data_spec.js @@ -1,6 +1,7 @@ import dateFormat from 'dateformat'; import timezoneMock from 'timezone-mock'; import BurndownChartData from 'ee/burndown_chart/burn_chart_data'; +import { useFakeDate } from 'helpers/fake_date'; describe('BurndownChartData', () => { const startDate = '2017-03-01'; @@ -68,16 +69,7 @@ describe('BurndownChartData', () => { }); describe('when viewing before due date', () => { - const realDateNow = Date.now; - - beforeAll(() => { - const today = jest.fn(() => new Date(2017, 2, 2)); - global.Date.now = today; - }); - - afterAll(() => { - global.Date.now = realDateNow; - }); + useFakeDate(2017, 2, 2); it('counts until today if milestone due date > date today', () => { const chartData = burndownChartData.generateBurndownTimeseries(); diff --git a/ee/spec/frontend/burndown_chart/components/burn_charts_spec.js b/ee/spec/frontend/burndown_chart/components/burn_charts_spec.js index 5d9671b9fc3ad84fc821a259eabd1e07f75fa27c..9e7be97927b636666fe759af05a21793f0d71d08 100644 --- a/ee/spec/frontend/burndown_chart/components/burn_charts_spec.js +++ b/ee/spec/frontend/burndown_chart/components/burn_charts_spec.js @@ -10,7 +10,7 @@ import TimeboxSummaryCards from 'ee/burndown_chart/components/timebox_summary_ca import { useFakeDate } from 'helpers/fake_date'; import { day1, day2, day3, day4 } from '../mock_data'; -function fakeDate({ date }) { +function useFakeDateFromDay({ date }) { const [year, month, day] = date.split('-'); useFakeDate(year, month - 1, day); @@ -201,12 +201,12 @@ describe('burndown_chart', () => { // some separate tests for the update function since it has a bunch of logic describe('padSparseBurnupData function', () => { + useFakeDateFromDay(day4); + beforeEach(() => { createComponent({ props: { startDate: day1.date, dueDate: day4.date }, }); - - fakeDate(day4); }); it('pads data from startDate if no startDate values', () => { @@ -236,15 +236,18 @@ describe('burndown_chart', () => { }); }); - it('if dueDate is in the future, pad data up to current date using last existing value', () => { - fakeDate(day3); + describe('when dueDate is in the future', () => { + // day3 is before the day4 we set to dueDate in the beforeEach + useFakeDateFromDay(day3); - const result = wrapper.vm.padSparseBurnupData([day1, day2]); + it('pad data up to current date using last existing value', () => { + const result = wrapper.vm.padSparseBurnupData([day1, day2]); - expect(result.length).toBe(3); - expect(result[2]).toEqual({ - ...day2, - date: day3.date, + expect(result.length).toBe(3); + expect(result[2]).toEqual({ + ...day2, + date: day3.date, + }); }); }); diff --git a/ee/spec/frontend/environments/environment_alert_spec.js b/ee/spec/frontend/environments/environment_alert_spec.js index 66067857df30d00328946314c6e4c39f78fe77c0..690626695bcfc2df0376209719ccef6adfa1200e 100644 --- a/ee/spec/frontend/environments/environment_alert_spec.js +++ b/ee/spec/frontend/environments/environment_alert_spec.js @@ -1,15 +1,12 @@ import { mount } from '@vue/test-utils'; import EnvironmentAlert from 'ee/environments/components/environment_alert.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; -import { useFakeDate } from 'helpers/fake_date'; describe('Environment Alert', () => { let wrapper; const DEFAULT_PROVIDE = { projectPath: 'test-org/test' }; const DEFAULT_PROPS = { environment: { name: 'staging' } }; - useFakeDate(); - const factory = (props = {}, provide = {}) => { wrapper = mount(EnvironmentAlert, { propsData: { diff --git a/ee/spec/frontend/geo_nodes/components/geo_node_last_updated_spec.js b/ee/spec/frontend/geo_nodes/components/geo_node_last_updated_spec.js index acceded8c512fc3dfc7b152137d447d42a88b6d1..76fab7806fcbdf7a3365db276b759259a8ba4f83 100644 --- a/ee/spec/frontend/geo_nodes/components/geo_node_last_updated_spec.js +++ b/ee/spec/frontend/geo_nodes/components/geo_node_last_updated_spec.js @@ -11,7 +11,8 @@ import { differenceInMilliseconds } from '~/lib/utils/datetime_utility'; describe('GeoNodeLastUpdated', () => { let wrapper; - const staleStatusTime = differenceInMilliseconds(STATUS_DELAY_THRESHOLD_MS); + // The threshold is exclusive so -1 + const staleStatusTime = differenceInMilliseconds(STATUS_DELAY_THRESHOLD_MS) - 1; const nonStaleStatusTime = new Date().getTime(); const defaultProps = { diff --git a/ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js b/ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js index ab12e877d986a81da5b1af55f2a07c28b468e0ce..a3f225af5b8cbc140ed77b382ad52ced2990d7b6 100644 --- a/ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js +++ b/ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js @@ -47,8 +47,9 @@ describe('RotationsListSectionComponent', () => { }); describe('when the timeframe includes today', () => { + useFakeDate(2021, 0, 14); + beforeEach(() => { - useFakeDate(2021, 0, 14); createComponent(); }); @@ -77,8 +78,9 @@ describe('RotationsListSectionComponent', () => { }); describe('when the timeframe does not include today', () => { + useFakeDate(2021, 0, 31); + beforeEach(() => { - useFakeDate(2021, 0, 31); createComponent(); }); diff --git a/ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js b/ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js index 5bdcdb2dd2425d6ad7a8bc7c7d4a4c49179067fb..a48a26fd7cecf278c764cfc5813f026db0dfc89f 100644 --- a/ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js +++ b/ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js @@ -7,10 +7,14 @@ import * as dateTimeUtility from '~/lib/utils/datetime_utility'; describe('Schedule Common Mixins', () => { // January 3rd, 2018 useFakeDate(2018, 0, 3); - const today = new Date(); + let today; let wrapper; + beforeEach(() => { + today = new Date(); + }); + const component = { template: `<span></span>`, props: { diff --git a/ee/spec/frontend/security_dashboard/components/csv_export_button_spec.js b/ee/spec/frontend/security_dashboard/components/csv_export_button_spec.js index 296e85dc048112a1395518e5eced3158c978858d..2ea1bfb9efd2e1e9863a9ed1ccbe956ac46edcec 100644 --- a/ee/spec/frontend/security_dashboard/components/csv_export_button_spec.js +++ b/ee/spec/frontend/security_dashboard/components/csv_export_button_spec.js @@ -4,7 +4,6 @@ import MockAdapter from 'axios-mock-adapter'; import CsvExportButton, { STORAGE_KEY, } from 'ee/security_dashboard/components/csv_export_button.vue'; -import { useFakeDate } from 'helpers/fake_date'; import { TEST_HOST } from 'helpers/test_constants'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import axios from '~/lib/utils/axios_utils'; @@ -14,7 +13,6 @@ import statusCodes from '~/lib/utils/http_status'; jest.mock('~/flash'); jest.mock('~/lib/utils/downloader'); -useFakeDate(); const mockReportDate = formatDate(new Date(), 'isoDateTime'); const vulnerabilitiesExportEndpoint = `${TEST_HOST}/vulnerability_findings.csv`; diff --git a/ee/spec/frontend/threat_monitoring/components/network_policy_list_spec.js b/ee/spec/frontend/threat_monitoring/components/network_policy_list_spec.js index c5e59cedd5de4f7a94500ea45de35f26536e6130..3f12b79ba2c60f3c4bcc1b1ccf38c0e1ba92cd56 100644 --- a/ee/spec/frontend/threat_monitoring/components/network_policy_list_spec.js +++ b/ee/spec/frontend/threat_monitoring/components/network_policy_list_spec.js @@ -4,15 +4,12 @@ import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_li import PolicyDrawer from 'ee/threat_monitoring/components/policy_editor/policy_drawer.vue'; import { PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants'; import createStore from 'ee/threat_monitoring/store'; -import { useFakeDate } from 'helpers/fake_date'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { mockPoliciesResponse } from '../mock_data'; const mockData = mockPoliciesResponse.map((policy) => convertObjectPropsToCamelCase(policy)); describe('NetworkPolicyList component', () => { - useFakeDate(); - let store; let wrapper; diff --git a/spec/frontend/__helpers__/fake_date.js b/spec/frontend/__helpers__/fake_date/fake_date.js similarity index 67% rename from spec/frontend/__helpers__/fake_date.js rename to spec/frontend/__helpers__/fake_date/fake_date.js index 5391ae047975f72a768d84d5aef7ba7a1b0cb16a..bc088ad96b64e0952c9a1877fa21d9047d78f739 100644 --- a/spec/frontend/__helpers__/fake_date.js +++ b/spec/frontend/__helpers__/fake_date/fake_date.js @@ -1,11 +1,13 @@ // Frida Kahlo's birthday (6 = July) -export const DEFAULT_ARGS = [2020, 6, 6]; +const DEFAULT_ARGS = [2020, 6, 6]; const RealDate = Date; const isMocked = (val) => Boolean(val.mock); -export const createFakeDateClass = (ctorDefault) => { +const createFakeDateClass = (ctorDefaultParam = []) => { + const ctorDefault = ctorDefaultParam.length ? ctorDefaultParam : DEFAULT_ARGS; + const FakeDate = new Proxy(RealDate, { construct: (target, argArray) => { const ctorArgs = argArray.length ? argArray : ctorDefault; @@ -39,11 +41,20 @@ export const createFakeDateClass = (ctorDefault) => { return FakeDate; }; -export const useFakeDate = (...args) => { - const FakeDate = createFakeDateClass(args.length ? args : DEFAULT_ARGS); +const setGlobalDateToFakeDate = (...args) => { + const FakeDate = createFakeDateClass(args); global.Date = FakeDate; }; -export const useRealDate = () => { +const setGlobalDateToRealDate = () => { global.Date = RealDate; }; + +// We use commonjs so that the test environment module can pick this up +// eslint-disable-next-line import/no-commonjs +module.exports = { + setGlobalDateToFakeDate, + setGlobalDateToRealDate, + createFakeDateClass, + RealDate, +}; diff --git a/spec/frontend/__helpers__/fake_date_spec.js b/spec/frontend/__helpers__/fake_date/fake_date_spec.js similarity index 81% rename from spec/frontend/__helpers__/fake_date_spec.js rename to spec/frontend/__helpers__/fake_date/fake_date_spec.js index b3ed13e238a90ae2c0a8a30d0cf3d1dfd76ab648..730765e52d29406625cde7b90ba670cb2b0d9ed9 100644 --- a/spec/frontend/__helpers__/fake_date_spec.js +++ b/spec/frontend/__helpers__/fake_date/fake_date_spec.js @@ -1,15 +1,11 @@ -import { createFakeDateClass, DEFAULT_ARGS, useRealDate } from './fake_date'; +import { createFakeDateClass } from './fake_date'; describe('spec/helpers/fake_date', () => { describe('createFakeDateClass', () => { let FakeDate; - beforeAll(() => { - useRealDate(); - }); - beforeEach(() => { - FakeDate = createFakeDateClass(DEFAULT_ARGS); + FakeDate = createFakeDateClass(); }); it('should use default args', () => { diff --git a/spec/frontend/__helpers__/fake_date/index.js b/spec/frontend/__helpers__/fake_date/index.js new file mode 100644 index 0000000000000000000000000000000000000000..3d1b124ce7961f438c9a55d04ddcf6ba9249f2c2 --- /dev/null +++ b/spec/frontend/__helpers__/fake_date/index.js @@ -0,0 +1,2 @@ +export * from './fake_date'; +export * from './jest'; diff --git a/spec/frontend/__helpers__/fake_date/jest.js b/spec/frontend/__helpers__/fake_date/jest.js new file mode 100644 index 0000000000000000000000000000000000000000..65e45619049eb096cf9bd93bae47542111e67717 --- /dev/null +++ b/spec/frontend/__helpers__/fake_date/jest.js @@ -0,0 +1,41 @@ +import { createJestExecutionWatcher } from '../jest_execution_watcher'; +import { RealDate, createFakeDateClass } from './fake_date'; + +const throwInsideExecutionError = (fnName) => { + throw new Error(`Cannot call "${fnName}" during test execution (i.e. within "it", "beforeEach", "beforeAll", etc.). + +Instead, please move the call to "${fnName}" inside the "describe" block itself. + + describe('', () => { + + ${fnName}(); + + it('', () => { + - ${fnName}(); + }) + }) +`); +}; + +const isExecutingTest = createJestExecutionWatcher(); + +export const useDateInScope = (fnName, factory) => { + if (isExecutingTest()) { + throwInsideExecutionError(fnName); + } + + let origDate; + + beforeAll(() => { + origDate = global.Date; + global.Date = factory(); + }); + + afterAll(() => { + global.Date = origDate; + }); +}; + +export const useFakeDate = (...args) => + useDateInScope('useFakeDate', () => createFakeDateClass(args)); + +export const useRealDate = () => useDateInScope('useRealDate', () => RealDate); diff --git a/spec/frontend/__helpers__/jest_execution_watcher.js b/spec/frontend/__helpers__/jest_execution_watcher.js new file mode 100644 index 0000000000000000000000000000000000000000..0fc3d330ec31e03ec7d9f855fde2c40c966004e6 --- /dev/null +++ b/spec/frontend/__helpers__/jest_execution_watcher.js @@ -0,0 +1,12 @@ +export const createJestExecutionWatcher = () => { + let isExecuting = false; + + beforeAll(() => { + isExecuting = true; + }); + afterAll(() => { + isExecuting = false; + }); + + return () => isExecuting; +}; diff --git a/spec/frontend/access_tokens/components/expires_at_field_spec.js b/spec/frontend/access_tokens/components/expires_at_field_spec.js index cd235d0afa5bccbc5dd0fa9995417f2837184773..4a2815e6931c56aacebb7104568090de75d62724 100644 --- a/spec/frontend/access_tokens/components/expires_at_field_spec.js +++ b/spec/frontend/access_tokens/components/expires_at_field_spec.js @@ -1,10 +1,7 @@ import { shallowMount } from '@vue/test-utils'; -import { useFakeDate } from 'helpers/fake_date'; import ExpiresAtField from '~/access_tokens/components/expires_at_field.vue'; describe('~/access_tokens/components/expires_at_field', () => { - useFakeDate(); - let wrapper; const createComponent = () => { diff --git a/spec/frontend/analytics/instance_statistics/components/projects_and_groups_chart_spec.js b/spec/frontend/analytics/instance_statistics/components/projects_and_groups_chart_spec.js index bf94e476ea3db175af62c52a1a554202cdabae61..93c1e330fa7b5032799bae940403a430e631ce85 100644 --- a/spec/frontend/analytics/instance_statistics/components/projects_and_groups_chart_spec.js +++ b/spec/frontend/analytics/instance_statistics/components/projects_and_groups_chart_spec.js @@ -3,7 +3,6 @@ import { GlLineChart } from '@gitlab/ui/dist/charts'; import { GlAlert } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { useFakeDate } from 'helpers/fake_date'; import ProjectsAndGroupChart from '~/analytics/instance_statistics/components/projects_and_groups_chart.vue'; import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; import projectsQuery from '~/analytics/instance_statistics/graphql/queries/projects.query.graphql'; @@ -45,8 +44,8 @@ describe('ProjectsAndGroupChart', () => { return shallowMount(ProjectsAndGroupChart, { props: { - startDate: useFakeDate(2020, 9, 26), - endDate: useFakeDate(2020, 10, 1), + startDate: new Date(2020, 9, 26), + endDate: new Date(2020, 10, 1), totalDataPoints: mockCountsData2.length, }, localVue, diff --git a/spec/frontend/analytics/instance_statistics/components/users_chart_spec.js b/spec/frontend/analytics/instance_statistics/components/users_chart_spec.js index b9fa30643df772f3faaf7b684dd0225719d68952..b35ab8ec646e55f328f9b353a2f67d3c6d0294c6 100644 --- a/spec/frontend/analytics/instance_statistics/components/users_chart_spec.js +++ b/spec/frontend/analytics/instance_statistics/components/users_chart_spec.js @@ -3,7 +3,6 @@ import { GlAreaChart } from '@gitlab/ui/dist/charts'; import { GlAlert } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { useFakeDate } from 'helpers/fake_date'; import UsersChart from '~/analytics/instance_statistics/components/users_chart.vue'; import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; import usersQuery from '~/analytics/instance_statistics/graphql/queries/users.query.graphql'; @@ -31,8 +30,8 @@ describe('UsersChart', () => { return shallowMount(UsersChart, { props: { - startDate: useFakeDate(2020, 9, 26), - endDate: useFakeDate(2020, 10, 1), + startDate: new Date(2020, 9, 26), + endDate: new Date(2020, 10, 1), totalDataPoints: mockCountsData2.length, }, localVue, diff --git a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js index d99933a1ee9d4b9c84712d89ca82122775e13d05..f050d924000965243cb6fafdd1fb497173031114 100644 --- a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js +++ b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js @@ -69,7 +69,7 @@ describe('CompareDropdownLayout', () => { expect(findListItemsData()).toEqual([ { href: 'version/1', - text: 'version 1 (base) abcdef1 1 commit 2 years ago', + text: 'version 1 (base) abcdef1 1 commit 1 year ago', createdAt: TEST_CREATED_AT, isActive: true, }, diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js index c055702d832f7c9bc533f3e648d005f9dcb1eb71..83702b22198f4e6b22915e6c53003e251e542aba 100644 --- a/spec/frontend/environment.js +++ b/spec/frontend/environment.js @@ -4,6 +4,7 @@ const path = require('path'); const { ErrorWithStack } = require('jest-util'); const JSDOMEnvironment = require('jest-environment-jsdom'); const { TEST_HOST } = require('./__helpers__/test_constants'); +const { setGlobalDateToFakeDate } = require('./__helpers__/fake_date/fake_date'); const ROOT_PATH = path.resolve(__dirname, '../..'); @@ -12,6 +13,10 @@ class CustomEnvironment extends JSDOMEnvironment { // Setup testURL so that window.location is setup properly super({ ...config, testURL: TEST_HOST }, context); + // Fake the `Date` for `jsdom` which fixes things like document.cookie + // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39496#note_503084332 + setGlobalDateToFakeDate(); + Object.assign(context.console, { error(...args) { throw new ErrorWithStack( diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js index 5b1d397ec0b5c033c3174f88ecf45ef7a739c186..ac642bb19b5b83f303a8f8873a4fdce97975cb13 100644 --- a/spec/frontend/frequent_items/components/app_spec.js +++ b/spec/frontend/frequent_items/components/app_spec.js @@ -3,6 +3,7 @@ import Vue from 'vue'; import { mountComponentWithStore } from 'helpers/vue_mount_component_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import waitForPromises from 'helpers/wait_for_promises'; +import { useRealDate } from 'helpers/fake_date'; import axios from '~/lib/utils/axios_utils'; import appComponent from '~/frequent_items/components/app.vue'; import eventHub from '~/frequent_items/event_hub'; @@ -93,23 +94,27 @@ describe('Frequent Items App Component', () => { expect(projects.length).toBe(1); }); - it('should increase frequency of report if it was logged multiple times over the course of an hour', () => { - let projects; - const newTimestamp = Date.now() + HOUR_IN_MS + 1; + describe('with real date', () => { + useRealDate(); - vm.logItemAccess(session.storageKey, session.project); - projects = JSON.parse(storage[session.storageKey]); + it('should increase frequency of report if it was logged multiple times over the course of an hour', () => { + let projects; + const newTimestamp = Date.now() + HOUR_IN_MS + 1; - expect(projects[0].frequency).toBe(1); + vm.logItemAccess(session.storageKey, session.project); + projects = JSON.parse(storage[session.storageKey]); - vm.logItemAccess(session.storageKey, { - ...session.project, - lastAccessedOn: newTimestamp, - }); - projects = JSON.parse(storage[session.storageKey]); + expect(projects[0].frequency).toBe(1); - expect(projects[0].frequency).toBe(2); - expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn); + vm.logItemAccess(session.storageKey, { + ...session.project, + lastAccessedOn: newTimestamp, + }); + projects = JSON.parse(storage[session.storageKey]); + + expect(projects[0].frequency).toBe(2); + expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn); + }); }); it('should always update project metadata', () => { diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js index 65e52d050841af9b6b1a78daba7567ab7e05a7b8..e84bb5074bc61a647bb0d637f952ee345875ac1a 100644 --- a/spec/frontend/issuable_list/components/issuable_item_spec.js +++ b/spec/frontend/issuable_list/components/issuable_item_spec.js @@ -1,6 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import { GlLink, GlLabel, GlIcon, GlFormCheckbox } from '@gitlab/ui'; - +import { useFakeDate } from 'helpers/fake_date'; import IssuableItem from '~/issuable_list/components/issuable_item.vue'; import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; @@ -19,6 +19,9 @@ const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots }); describe('IssuableItem', () => { + // The mock data is dependent that this is after our default date + useFakeDate(2020, 11, 11); + const mockLabels = mockIssuable.labels.nodes; const mockAuthor = mockIssuable.author; const originalUrl = gon.gitlab_url; diff --git a/spec/frontend/issuable_show/components/issuable_body_spec.js b/spec/frontend/issuable_show/components/issuable_body_spec.js index 4ffbbad4f37b0d90a6d1ad82478949a2b2cd51f7..d7455e2774a79eecf9461804fcb5af4679e9d2e8 100644 --- a/spec/frontend/issuable_show/components/issuable_body_spec.js +++ b/spec/frontend/issuable_show/components/issuable_body_spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; +import { useFakeDate } from 'helpers/fake_date'; import IssuableBody from '~/issuable_show/components/issuable_body.vue'; @@ -35,6 +36,9 @@ const createComponent = (propsData = issuableBodyProps) => }); describe('IssuableBody', () => { + // Some assertions expect a date later than our default + useFakeDate(2020, 11, 11); + let wrapper; beforeEach(() => { @@ -98,11 +102,8 @@ describe('IssuableBody', () => { it('renders issuable edit info', () => { const editedEl = wrapper.find('small'); - const sanitizedText = editedEl.text().replace(/\n/g, ' ').replace(/\s+/g, ' '); - expect(sanitizedText).toContain('Edited'); - expect(sanitizedText).toContain('ago'); - expect(sanitizedText).toContain(`by ${mockIssuable.updatedBy.name}`); + expect(editedEl.text()).toMatchInterpolatedText('Edited 3 months ago by Administrator'); }); it('renders issuable-edit-form when `editFormVisible` prop is true', async () => { diff --git a/spec/frontend/issues_list/components/issuable_spec.js b/spec/frontend/issues_list/components/issuable_spec.js index dcc3542e90532603f8625fecf831e1dfefff0843..776d0f2c2de5fb3125177a6bd475b26b042fdee4 100644 --- a/spec/frontend/issues_list/components/issuable_spec.js +++ b/spec/frontend/issues_list/components/issuable_spec.js @@ -12,9 +12,15 @@ import { simpleIssue, testAssignees, testLabels } from '../issuable_list_test_da jest.mock('~/user_popovers'); -const TEST_NOW = '2019-08-28T20:03:04.713Z'; -const TEST_MONTH_AGO = '2019-07-28'; -const TEST_MONTH_LATER = '2019-09-30'; +const TODAY = new Date(); + +const createTestDateFromDelta = (timeDelta) => + formatDate(new Date(TODAY.getTime() + timeDelta), 'yyyy-mm-dd'); + +// TODO: Encapsulate date helpers https://gitlab.com/gitlab-org/gitlab/-/issues/320883 +const MONTHS_IN_MS = 1000 * 60 * 60 * 24 * 31; +const TEST_MONTH_AGO = createTestDateFromDelta(-MONTHS_IN_MS); +const TEST_MONTH_LATER = createTestDateFromDelta(MONTHS_IN_MS); const DATE_FORMAT = 'mmm d, yyyy'; const TEST_USER_NAME = 'Tyler Durden'; const TEST_BASE_URL = `${TEST_HOST}/issues`; @@ -26,16 +32,8 @@ const TEST_MILESTONE = { const TEXT_CLOSED = 'CLOSED'; const TEST_META_COUNT = 100; -// Use FixedDate so that time sensitive info in snapshots don't fail -class FixedDate extends Date { - constructor(date = TEST_NOW) { - super(date); - } -} - describe('Issuable component', () => { let issuable; - let DateOrig; let wrapper; const factory = (props = {}, scopedLabelsAvailable = false) => { @@ -63,15 +61,6 @@ describe('Issuable component', () => { wrapper = null; }); - beforeAll(() => { - DateOrig = window.Date; - window.Date = FixedDate; - }); - - afterAll(() => { - window.Date = DateOrig; - }); - const checkExists = (findFn) => () => findFn().exists(); const hasIcon = (iconName, iconWrapper = wrapper) => iconWrapper.findAll(GlIcon).wrappers.some((icon) => icon.props('name') === iconName); diff --git a/spec/frontend/releases/components/release_block_footer_spec.js b/spec/frontend/releases/components/release_block_footer_spec.js index b3161f9fc0dc43280d1bf7490b37ee917d93f011..cf2f71a78f90d40d1e1416ed7da609d04d6a17a8 100644 --- a/spec/frontend/releases/components/release_block_footer_spec.js +++ b/spec/frontend/releases/components/release_block_footer_spec.js @@ -8,19 +8,9 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; const originalRelease = getJSONFixture('api/releases/release.json'); -const mockFutureDate = new Date(9999, 0, 0).toISOString(); -let mockIsFutureRelease = false; - -jest.mock('~/vue_shared/mixins/timeago', () => ({ - methods: { - timeFormatted() { - return mockIsFutureRelease ? 'in 1 month' : '7 fortnights ago'; - }, - tooltipTitle() { - return 'February 30, 2401'; - }, - }, -})); +// TODO: Encapsulate date helpers https://gitlab.com/gitlab-org/gitlab/-/issues/320883 +const MONTHS_IN_MS = 1000 * 60 * 60 * 24 * 31; +const mockFutureDate = new Date(new Date().getTime() + MONTHS_IN_MS).toISOString(); describe('Release block footer', () => { let wrapper; @@ -44,7 +34,6 @@ describe('Release block footer', () => { afterEach(() => { wrapper.destroy(); wrapper = null; - mockIsFutureRelease = false; }); const commitInfoSection = () => wrapper.find('.js-commit-info'); @@ -88,7 +77,7 @@ describe('Release block footer', () => { it('renders the author and creation time info', () => { expect(trimText(authorDateInfoSection().text())).toBe( - `Created 7 fortnights ago by ${release.author.username}`, + `Created 1 year ago by ${release.author.username}`, ); }); @@ -100,7 +89,6 @@ describe('Release block footer', () => { describe('renders the author and creation time info with future release date', () => { beforeEach(() => { - mockIsFutureRelease = true; factory({ releasedAt: mockFutureDate }); }); @@ -113,7 +101,6 @@ describe('Release block footer', () => { describe('when the release date is in the future', () => { beforeEach(() => { - mockIsFutureRelease = true; factory({ releasedAt: mockFutureDate }); }); @@ -177,13 +164,12 @@ describe('Release block footer', () => { beforeEach(() => factory({ author: undefined })); it('renders the release date without the author name', () => { - expect(trimText(authorDateInfoSection().text())).toBe(`Created 7 fortnights ago`); + expect(trimText(authorDateInfoSection().text())).toBe(`Created 1 year ago`); }); }); describe('future release without any author info', () => { beforeEach(() => { - mockIsFutureRelease = true; factory({ author: undefined, releasedAt: mockFutureDate }); }); diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index a122b06fddadac340de0598f6308a3056024a47c..61746b3c453f2f2c85d55fa48dce8b343825966c 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -3,6 +3,7 @@ import 'jquery'; import * as jqueryMatchers from 'custom-jquery-matchers'; import { config as testUtilsConfig } from '@vue/test-utils'; +import { setGlobalDateToFakeDate } from 'helpers/fake_date'; import Translate from '~/vue_shared/translate'; import { initializeTestTimeout } from './__helpers__/timeout'; import { getJSONFixture, loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures'; @@ -20,6 +21,10 @@ process.on('unhandledRejection', global.promiseRejectionHandler); setupManualMocks(); +// Fake the `Date` for the rest of the jest spec runtime environment. +// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39496#note_503084332 +setGlobalDateToFakeDate(); + afterEach(() => // give Promises a bit more time so they fail the right test new Promise(setImmediate).then(() => {