Skip to content
Snippets Groups Projects
Commit 18fab372 authored by Paul Slaughter's avatar Paul Slaughter :two:
Browse files

Merge branch '236004-use-fake-date-everywhere' into 'master'

Step 2 - Setup `useFakeDate` by default in Jest

See merge request !39496
parents e0727b2a 8c01e68f
No related branches found
No related tags found
No related merge requests found
Pipeline #254208399 passed
Showing
with 132 additions and 62 deletions
......@@ -655,15 +655,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();
});
```
......
......@@ -117,7 +117,7 @@ exports[`MergeRequestTable component template matches the snapshot 1`] = `
</li>
<li>
opened just now
opened 5 months ago
</li>
<li>
......
......@@ -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 });
});
......
......@@ -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', () => {
......
......@@ -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;
......
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();
});
......
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();
......
......@@ -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,
});
});
});
......
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: {
......
......@@ -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 = {
......
......@@ -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();
});
......
......@@ -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: {
......
......@@ -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`;
......
......@@ -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;
......
// 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,
};
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', () => {
......
export * from './fake_date';
export * from './jest';
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);
export const createJestExecutionWatcher = () => {
let isExecuting = false;
beforeAll(() => {
isExecuting = true;
});
afterAll(() => {
isExecuting = false;
});
return () => isExecuting;
};
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 = () => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment