The "NotSoISO" part of the old names was incorrect; the return value is definitely ISO 8601.tags/10.1.0.73491
@@ -22,6 +22,7 @@ import { cloneDeep, groupBy, sortBy } from 'lodash'; | |||
import { PAGE_SIZE } from '../../apps/background-tasks/constants'; | |||
import { parseDate } from '../../helpers/dates'; | |||
import { mockTask } from '../../helpers/mocks/tasks'; | |||
import { isDefined } from '../../helpers/types'; | |||
import { ActivityRequestParameters, Task, TaskStatuses, TaskTypes } from '../../types/tasks'; | |||
import { | |||
cancelAllTasks, | |||
@@ -63,7 +64,7 @@ export default class ComputeEngineServiceMock { | |||
constructor() { | |||
(cancelAllTasks as jest.Mock).mockImplementation(this.handleCancelAllTasks); | |||
(cancelTask as jest.Mock).mockImplementation(this.handleCancelTask); | |||
(getActivity as jest.Mock).mockImplementation(this.handleGetActivity); | |||
jest.mocked(getActivity).mockImplementation(this.handleGetActivity); | |||
(getStatus as jest.Mock).mockImplementation(this.handleGetStatus); | |||
(getTypes as jest.Mock).mockImplementation(this.handleGetTypes); | |||
(getTask as jest.Mock).mockImplementation(this.handleGetTask); | |||
@@ -96,7 +97,6 @@ export default class ComputeEngineServiceMock { | |||
handleGetActivity = (data: ActivityRequestParameters) => { | |||
let results = cloneDeep(this.tasks); | |||
results = results.filter((task) => { | |||
return !( | |||
(data.component && task.componentKey !== data.component) || | |||
@@ -115,12 +115,10 @@ export default class ComputeEngineServiceMock { | |||
}); | |||
if (data.onlyCurrents) { | |||
/* | |||
* This is more complex in real life, but it's a good enough approximation to suit tests | |||
*/ | |||
results = Object.values(groupBy(results, (t) => t.componentKey)).map( | |||
(tasks) => sortBy(tasks, (t) => t.executedAt).pop()! | |||
); | |||
// This is more complex in real life, but it's a good enough approximation to suit tests. | |||
results = Object.values(groupBy(results, (t) => t.componentKey)) | |||
.map((tasks) => sortBy(tasks, (t) => t.executedAt).pop()) | |||
.filter(isDefined); | |||
} | |||
const page = data.p ?? 1; |
@@ -22,7 +22,7 @@ import * as React from 'react'; | |||
import { showLicense } from '../../api/editions'; | |||
import LicensePromptModal from '../../apps/marketplace/components/LicensePromptModal'; | |||
import { Location, Router, withRouter } from '../../components/hoc/withRouter'; | |||
import { parseDate, toShortNotSoISOString } from '../../helpers/dates'; | |||
import { parseDate, toShortISO8601String } from '../../helpers/dates'; | |||
import { hasMessage } from '../../helpers/l10n'; | |||
import { get, save } from '../../helpers/storage'; | |||
import { AppState } from '../../types/appstate'; | |||
@@ -71,7 +71,7 @@ export class StartupModal extends React.PureComponent<Props & StateProps, State> | |||
showLicense() | |||
.then((license) => { | |||
if (!license || !license.isValidEdition) { | |||
save(LICENSE_PROMPT, toShortNotSoISOString(new Date()), currentUser.login); | |||
save(LICENSE_PROMPT, toShortISO8601String(new Date()), currentUser.login); | |||
this.setState({ open: true }); | |||
} | |||
}) |
@@ -21,8 +21,9 @@ import { differenceInDays } from 'date-fns'; | |||
import { shallow, ShallowWrapper } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { showLicense } from '../../../api/editions'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { toShortISO8601String } from '../../../helpers/dates'; | |||
import { hasMessage } from '../../../helpers/l10n'; | |||
import { mockLicense } from '../../../helpers/mocks/editions'; | |||
import { get, save } from '../../../helpers/storage'; | |||
import { mockAppState, mockLocation, mockRouter } from '../../../helpers/testMocks'; | |||
import { waitAndUpdate } from '../../../helpers/testUtils'; | |||
@@ -45,7 +46,7 @@ jest.mock('../../../helpers/l10n', () => ({ | |||
jest.mock('../../../helpers/dates', () => ({ | |||
parseDate: jest.fn().mockReturnValue('parsed-date'), | |||
toShortNotSoISOString: jest.fn().mockReturnValue('short-not-iso-date'), | |||
toShortISO8601String: jest.fn().mockReturnValue('short-not-iso-date'), | |||
})); | |||
jest.mock('date-fns', () => ({ differenceInDays: jest.fn().mockReturnValue(1) })); | |||
@@ -60,12 +61,12 @@ const LOGGED_IN_USER: LoggedInUser = { | |||
}; | |||
beforeEach(() => { | |||
(differenceInDays as jest.Mock<any>).mockClear(); | |||
(hasMessage as jest.Mock<any>).mockClear(); | |||
(get as jest.Mock<any>).mockClear(); | |||
(save as jest.Mock<any>).mockClear(); | |||
(showLicense as jest.Mock<any>).mockClear(); | |||
(toShortNotSoISOString as jest.Mock<any>).mockClear(); | |||
jest.mocked(differenceInDays).mockClear(); | |||
jest.mocked(hasMessage).mockClear(); | |||
jest.mocked(get).mockClear(); | |||
jest.mocked(save).mockClear(); | |||
jest.mocked(showLicense).mockClear(); | |||
jest.mocked(toShortISO8601String).mockClear(); | |||
}); | |||
it('should render only the children', async () => { | |||
@@ -76,14 +77,14 @@ it('should render only the children', async () => { | |||
await shouldNotHaveModals(getWrapper({ appState: mockAppState({ canAdmin: false }) })); | |||
(hasMessage as jest.Mock<any>).mockReturnValueOnce(false); | |||
jest.mocked(hasMessage).mockReturnValueOnce(false); | |||
await shouldNotHaveModals(getWrapper()); | |||
(showLicense as jest.Mock<any>).mockResolvedValueOnce({ isValidEdition: true }); | |||
jest.mocked(showLicense).mockResolvedValueOnce(mockLicense({ isValidEdition: true })); | |||
await shouldNotHaveModals(getWrapper()); | |||
(get as jest.Mock<any>).mockReturnValueOnce('date'); | |||
(differenceInDays as jest.Mock<any>).mockReturnValueOnce(0); | |||
jest.mocked(get).mockReturnValueOnce('date'); | |||
jest.mocked(differenceInDays).mockReturnValueOnce(0); | |||
await shouldNotHaveModals(getWrapper()); | |||
await shouldNotHaveModals( | |||
@@ -99,11 +100,11 @@ it('should render license prompt', async () => { | |||
await shouldDisplayLicense(getWrapper()); | |||
expect(save).toHaveBeenCalledWith('sonarqube.license.prompt', 'short-not-iso-date', 'luke'); | |||
(get as jest.Mock<any>).mockReturnValueOnce('date'); | |||
(differenceInDays as jest.Mock<any>).mockReturnValueOnce(1); | |||
jest.mocked(get).mockReturnValueOnce('date'); | |||
jest.mocked(differenceInDays).mockReturnValueOnce(1); | |||
await shouldDisplayLicense(getWrapper()); | |||
(showLicense as jest.Mock<any>).mockResolvedValueOnce({ isValidEdition: false }); | |||
jest.mocked(showLicense).mockResolvedValueOnce(mockLicense({ isValidEdition: false })); | |||
await shouldDisplayLicense(getWrapper()); | |||
}); | |||
@@ -21,8 +21,13 @@ import { screen, waitFor, within } from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { byLabelText, byPlaceholderText, byRole, byText } from 'testing-library-selector'; | |||
import ComputeEngineServiceMock from '../../../api/mocks/ComputeEngineServiceMock'; | |||
import { parseDate } from '../../../helpers/dates'; | |||
import { mockAppState } from '../../../helpers/testMocks'; | |||
import { RenderContext, renderAppWithAdminContext } from '../../../helpers/testReactTestingUtils'; | |||
import { | |||
RenderContext, | |||
dateInputEvent, | |||
renderAppWithAdminContext, | |||
} from '../../../helpers/testReactTestingUtils'; | |||
import { EditionKey } from '../../../types/editions'; | |||
import { TaskStatuses, TaskTypes } from '../../../types/tasks'; | |||
import routes from '../routes'; | |||
@@ -87,6 +92,8 @@ describe('The Global background task page', () => { | |||
}); | |||
computeEngineServiceMock.addTask({ status: TaskStatuses.Pending, type: TaskTypes.IssueSync }); | |||
computeEngineServiceMock.addTask({ | |||
executedAt: '2022-02-04T11:45:36+0200', | |||
submittedAt: '2022-02-04T11:45:35+0200', | |||
componentKey: 'otherComponent', | |||
status: TaskStatuses.Success, | |||
type: TaskTypes.AppRefresh, | |||
@@ -99,25 +106,36 @@ describe('The Global background task page', () => { | |||
expect(ui.getAllRows()).toHaveLength(4); | |||
// Status filter. | |||
await ui.changeTaskFilter('status', 'background_task.status.IN_PROGRESS'); | |||
expect(ui.getAllRows()).toHaveLength(1); | |||
await ui.changeTaskFilter('status', 'background_task.status.ALL'); | |||
expect(ui.getAllRows()).toHaveLength(5); | |||
// Type filter. | |||
await ui.changeTaskFilter('type', `background_task.type.${TaskTypes.AppRefresh}`); | |||
expect(ui.getAllRows()).toHaveLength(3); | |||
// Latest analysis. | |||
await user.click(ui.onlyLatestAnalysis.get()); | |||
expect(ui.getAllRows()).toHaveLength(2); | |||
await user.click(ui.onlyLatestAnalysis.get()); | |||
/* | |||
* Must test date range filters, but it requires refactoring the DateRange component | |||
*/ | |||
// Reset filters. | |||
await user.click(ui.resetFilters.get()); | |||
expect(ui.getAllRows()).toHaveLength(4); | |||
// Date filters. | |||
await ui.setDateRange('2022-02-01', '2022-02-04'); | |||
expect(ui.getAllRows()).toHaveLength(1); | |||
// Reset filters. | |||
await user.click(ui.resetFilters.get()); | |||
expect(ui.getAllRows()).toHaveLength(4); | |||
// Search. | |||
await user.click(ui.search.get()); | |||
await user.keyboard('other'); | |||
expect(ui.getAllRows()).toHaveLength(1); | |||
// Reset filters | |||
@@ -298,6 +316,8 @@ function getPageObject() { | |||
numberOfWorkers: byText('background_tasks.number_of_workers'), | |||
onlyLatestAnalysis: byRole('checkbox', { name: 'yes' }), | |||
search: byPlaceholderText('background_tasks.search_by_task_or_component'), | |||
fromDateInput: byRole('textbox', { name: 'start_date' }), | |||
toDateInput: byRole('textbox', { name: 'end_date' }), | |||
resetFilters: byRole('button', { name: 'reset_verb' }), | |||
showMoreButton: byRole('button', { name: 'show_more' }), | |||
reloadButton: byRole('button', { name: 'reload' }), | |||
@@ -309,20 +329,33 @@ function getPageObject() { | |||
const ui = { | |||
...selectors, | |||
appLoaded: async () => { | |||
async appLoaded() { | |||
await waitFor(() => { | |||
expect(selectors.loading.query()).not.toBeInTheDocument(); | |||
}); | |||
}, | |||
getAllRows: () => screen.getAllByRole('row').slice(1), // Excludes header | |||
getAllRows() { | |||
return screen.getAllByRole('row').slice(1); // Excludes header | |||
}, | |||
changeTaskFilter: async (fieldLabel: string, value: string) => { | |||
async changeTaskFilter(fieldLabel: string, value: string) { | |||
await user.click(screen.getByLabelText(fieldLabel, { selector: 'input' })); | |||
await user.click(screen.getByText(value)); | |||
}, | |||
clickOnTaskAction: async (rowIndex: number, label: string) => { | |||
async setDateRange(from?: string, to?: string) { | |||
const dateInput = dateInputEvent(user); | |||
if (from) { | |||
await dateInput.pickDate(ui.fromDateInput.get(), parseDate(from)); | |||
} | |||
if (to) { | |||
await dateInput.pickDate(ui.toDateInput.get(), parseDate(to)); | |||
} | |||
}, | |||
async clickOnTaskAction(rowIndex: number, label: string) { | |||
const row = ui.getAllRows()[rowIndex]; | |||
expect(row).toBeVisible(); | |||
await user.click(within(row).getByRole('button', { name: 'background_tasks.show_actions' })); |
@@ -32,7 +32,7 @@ import ListFooter from '../../../components/controls/ListFooter'; | |||
import Suggestions from '../../../components/embed-docs-modal/Suggestions'; | |||
import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; | |||
import DeferredSpinner from '../../../components/ui/DeferredSpinner'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { parseAsDate } from '../../../helpers/query'; | |||
import { Task, TaskStatuses } from '../../../types/tasks'; | |||
@@ -162,11 +162,11 @@ export class BackgroundTasksApp extends React.PureComponent<Props, State> { | |||
}); | |||
if (nextQuery.minSubmittedAt) { | |||
nextQuery.minSubmittedAt = toShortNotSoISOString(nextQuery.minSubmittedAt); | |||
nextQuery.minSubmittedAt = toShortISO8601String(nextQuery.minSubmittedAt); | |||
} | |||
if (nextQuery.maxExecutedAt) { | |||
nextQuery.maxExecutedAt = toShortNotSoISOString(nextQuery.maxExecutedAt); | |||
nextQuery.maxExecutedAt = toShortISO8601String(nextQuery.maxExecutedAt); | |||
} | |||
this.props.router.push({ |
@@ -17,7 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { toShortNotSoISOString } from '../../helpers/dates'; | |||
import { toShortISO8601String } from '../../helpers/dates'; | |||
import { ActivityRequestParameters, Task, TaskStatuses } from '../../types/tasks'; | |||
import { ALL_TYPES, CURRENTS, STATUSES } from './constants'; | |||
@@ -65,11 +65,11 @@ export function mapFiltersToParameters(filters: Partial<Query> = {}) { | |||
} | |||
if (filters.minSubmittedAt) { | |||
parameters.minSubmittedAt = toShortNotSoISOString(filters.minSubmittedAt); | |||
parameters.minSubmittedAt = toShortISO8601String(filters.minSubmittedAt); | |||
} | |||
if (filters.maxExecutedAt) { | |||
parameters.maxExecutedAt = toShortNotSoISOString(filters.maxExecutedAt); | |||
parameters.maxExecutedAt = toShortISO8601String(filters.maxExecutedAt); | |||
} | |||
if (filters.query) { |
@@ -34,7 +34,7 @@ import { | |||
getBranchLikeQuery, | |||
isMainBranch, | |||
} from '../../../helpers/branch-like'; | |||
import { parseDate, toNotSoISOString } from '../../../helpers/dates'; | |||
import { parseDate, toISO8601WithOffsetString } from '../../../helpers/dates'; | |||
import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures'; | |||
import { | |||
extractStatusConditionsFromApplicationStatusChildProject, | |||
@@ -78,7 +78,7 @@ export const BRANCH_OVERVIEW_ACTIVITY_GRAPH = 'sonar_branch_overview.graph'; | |||
export const NO_CI_DETECTED = 'undetected'; | |||
// Get all history data over the past year. | |||
const FROM_DATE = toNotSoISOString(new Date().setFullYear(new Date().getFullYear() - 1)); | |||
const FROM_DATE = toISO8601WithOffsetString(new Date().setFullYear(new Date().getFullYear() - 1)); | |||
export default class BranchOverview extends React.PureComponent<Props, State> { | |||
mounted = false; |
@@ -22,7 +22,7 @@ import { isEqual } from 'date-fns'; | |||
import * as React from 'react'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
import DateFormatter from '../../../components/intl/DateFormatter'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { ComponentQualifier } from '../../../types/component'; | |||
import { ParsedAnalysis } from '../../../types/project-activity'; | |||
@@ -143,7 +143,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro | |||
{days.map((day) => ( | |||
<li | |||
className="project-activity-day" | |||
data-day={toShortNotSoISOString(Number(day))} | |||
data-day={toShortISO8601String(Number(day))} | |||
key={day} | |||
> | |||
<h3> |
@@ -21,7 +21,7 @@ import { subDays } from 'date-fns'; | |||
import { throttle } from 'lodash'; | |||
import * as React from 'react'; | |||
import { getProjectActivity } from '../../../api/projectActivity'; | |||
import { parseDate, toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { parseDate, toShortISO8601String } from '../../../helpers/dates'; | |||
import { Analysis, ParsedAnalysis } from '../../../types/project-activity'; | |||
import { Dict } from '../../../types/types'; | |||
import BranchAnalysisListRenderer from './BranchAnalysisListRenderer'; | |||
@@ -81,7 +81,7 @@ export default class BranchAnalysisList extends React.PureComponent<Props, State | |||
return getProjectActivity({ | |||
branch, | |||
project: component, | |||
from: range ? toShortNotSoISOString(subDays(new Date(), range)) : undefined, | |||
from: range ? toShortISO8601String(subDays(new Date(), range)) : undefined, | |||
}).then((result: { analyses: Analysis[] }) => { | |||
// If the selected analysis wasn't found in the default 30 days range, redo the search | |||
if (initial && analysis && !result.analyses.find((a) => a.key === analysis)) { |
@@ -26,7 +26,7 @@ import Tooltip from '../../../components/controls/Tooltip'; | |||
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter'; | |||
import TimeFormatter from '../../../components/intl/TimeFormatter'; | |||
import DeferredSpinner from '../../../components/ui/DeferredSpinner'; | |||
import { parseDate, toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { parseDate, toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { ParsedAnalysis } from '../../../types/project-activity'; | |||
import Events from '../../projectActivity/components/Events'; | |||
@@ -173,7 +173,7 @@ function BranchAnalysisListRenderer( | |||
{days.map((day) => ( | |||
<li | |||
className="branch-analysis-day" | |||
data-day={toShortNotSoISOString(Number(day))} | |||
data-day={toShortISO8601String(Number(day))} | |||
key={day} | |||
> | |||
<div className="branch-analysis-date"> |
@@ -22,7 +22,7 @@ import { setNewCodePeriod } from '../../../api/newCodePeriod'; | |||
import Modal from '../../../components/controls/Modal'; | |||
import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||
import DeferredSpinner from '../../../components/ui/DeferredSpinner'; | |||
import { toNotSoISOString } from '../../../helpers/dates'; | |||
import { toISO8601WithOffsetString } from '../../../helpers/dates'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { Branch, BranchWithNewCodePeriod } from '../../../types/branch-like'; | |||
import { ParsedAnalysis } from '../../../types/project-activity'; | |||
@@ -113,7 +113,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop | |||
this.props.onClose(branch.name, { | |||
type, | |||
value, | |||
effectiveValue: analysisDate && toNotSoISOString(analysisDate), | |||
effectiveValue: analysisDate && toISO8601WithOffsetString(analysisDate), | |||
}); | |||
}, | |||
() => { |
@@ -19,13 +19,13 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import { bulkApplyTemplate, getPermissionTemplates } from '../../api/permissions'; | |||
import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; | |||
import Modal from '../../components/controls/Modal'; | |||
import Select from '../../components/controls/Select'; | |||
import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; | |||
import { Alert } from '../../components/ui/Alert'; | |||
import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker'; | |||
import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; | |||
import { toNotSoISOString } from '../../helpers/dates'; | |||
import { toISO8601WithOffsetString } from '../../helpers/dates'; | |||
import { addGlobalErrorMessageFromAPI } from '../../helpers/globalMessages'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
import { PermissionTemplate } from '../../types/types'; | |||
@@ -94,7 +94,7 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S | |||
templateId: permissionTemplate, | |||
} | |||
: { | |||
analyzedBefore: analyzedBefore && toNotSoISOString(analyzedBefore), | |||
analyzedBefore: analyzedBefore && toISO8601WithOffsetString(analyzedBefore), | |||
onProvisionedOnly: this.props.provisioned || undefined, | |||
qualifiers: this.props.qualifier, | |||
q: this.props.query || undefined, |
@@ -19,10 +19,10 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import { bulkDeleteProjects } from '../../api/components'; | |||
import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; | |||
import Modal from '../../components/controls/Modal'; | |||
import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; | |||
import { Alert } from '../../components/ui/Alert'; | |||
import { toNotSoISOString } from '../../helpers/dates'; | |||
import { toISO8601WithOffsetString } from '../../helpers/dates'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
export interface Props { | |||
@@ -60,7 +60,7 @@ export default class DeleteModal extends React.PureComponent<Props, State> { | |||
projects: this.props.selection.join(), | |||
} | |||
: { | |||
analyzedBefore: analyzedBefore && toNotSoISOString(analyzedBefore), | |||
analyzedBefore: analyzedBefore && toISO8601WithOffsetString(analyzedBefore), | |||
onProvisionedOnly: this.props.provisioned || undefined, | |||
qualifiers: this.props.qualifier, | |||
q: this.props.query || undefined, |
@@ -20,13 +20,13 @@ | |||
import { debounce, uniq, without } from 'lodash'; | |||
import * as React from 'react'; | |||
import { Helmet } from 'react-helmet-async'; | |||
import { getComponents, Project } from '../../api/components'; | |||
import { Project, getComponents } from '../../api/components'; | |||
import { changeProjectDefaultVisibility } from '../../api/permissions'; | |||
import { getValue } from '../../api/settings'; | |||
import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext'; | |||
import ListFooter from '../../components/controls/ListFooter'; | |||
import Suggestions from '../../components/embed-docs-modal/Suggestions'; | |||
import { toShortNotSoISOString } from '../../helpers/dates'; | |||
import { toShortISO8601String } from '../../helpers/dates'; | |||
import { throwGlobalError } from '../../helpers/error'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { hasGlobalPermission } from '../../helpers/users'; | |||
@@ -109,7 +109,7 @@ export class ProjectManagementApp extends React.PureComponent<Props, State> { | |||
requestProjects = () => { | |||
const { analyzedBefore } = this.state; | |||
const parameters = { | |||
analyzedBefore: analyzedBefore && toShortNotSoISOString(analyzedBefore), | |||
analyzedBefore: analyzedBefore && toShortISO8601String(analyzedBefore), | |||
onProvisionedOnly: this.state.provisioned || undefined, | |||
p: this.state.page !== 1 ? this.state.page : undefined, | |||
ps: PAGE_SIZE, |
@@ -21,7 +21,7 @@ import * as React from 'react'; | |||
import { getProfileChangelog } from '../../../api/quality-profiles'; | |||
import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; | |||
import DeferredSpinner from '../../../components/ui/DeferredSpinner'; | |||
import { parseDate, toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { parseDate, toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { withQualityProfilesContext } from '../qualityProfilesContext'; | |||
import { Profile, ProfileChangelogEvent } from '../types'; | |||
@@ -117,8 +117,8 @@ export class ChangelogContainer extends React.PureComponent<Props, State> { | |||
handleDateRangeChange = ({ from, to }: { from?: Date; to?: Date }) => { | |||
const path = getProfileChangelogPath(this.props.profile.name, this.props.profile.language, { | |||
since: from && toShortNotSoISOString(from), | |||
to: to && toShortNotSoISOString(to), | |||
since: from && toShortISO8601String(from), | |||
to: to && toShortISO8601String(to), | |||
}); | |||
this.props.router.push(path); | |||
}; |
@@ -21,7 +21,7 @@ import { sortBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { searchRules } from '../../../api/rules'; | |||
import Link from '../../../components/common/Link'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { formatMeasure } from '../../../helpers/measures'; | |||
import { getRulesUrl } from '../../../helpers/urls'; | |||
@@ -54,7 +54,7 @@ export default class EvolutionRules extends React.PureComponent<{}, State> { | |||
this.state = {}; | |||
const startDate = new Date(); | |||
startDate.setFullYear(startDate.getFullYear() - 1); | |||
this.periodStartDate = toShortNotSoISOString(startDate); | |||
this.periodStartDate = toShortISO8601String(startDate); | |||
} | |||
componentDidMount() { |
@@ -21,7 +21,7 @@ import * as React from 'react'; | |||
import withAppStateContext from '../../../app/components/app-state/withAppStateContext'; | |||
import { ClipboardButton } from '../../../components/controls/clipboard'; | |||
import { Alert } from '../../../components/ui/Alert'; | |||
import { toShortNotSoISOString } from '../../../helpers/dates'; | |||
import { toShortISO8601String } from '../../../helpers/dates'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { AppState } from '../../../types/appstate'; | |||
import PageActions from './PageActions'; | |||
@@ -86,7 +86,7 @@ function PageHeader(props: Props) { | |||
copyValue={`SonarQube ID information | |||
Server ID: ${serverId} | |||
Version: ${version} | |||
Date: ${toShortNotSoISOString(Date.now())} | |||
Date: ${toShortISO8601String(Date.now())} | |||
`} | |||
> | |||
{translate('system.copy_id_info')} |
@@ -29,7 +29,7 @@ import Select, { LabelValueSelectOption } from '../../components/controls/Select | |||
import Suggestions from '../../components/embed-docs-modal/Suggestions'; | |||
import { useManageProvider } from '../../components/hooks/useManageProvider'; | |||
import DeferredSpinner from '../../components/ui/DeferredSpinner'; | |||
import { now, toNotSoISOString } from '../../helpers/dates'; | |||
import { now, toISO8601WithOffsetString } from '../../helpers/dates'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { IdentityProvider, Paging } from '../../types/types'; | |||
import { User } from '../../types/users'; | |||
@@ -59,16 +59,16 @@ export default function UsersApp() { | |||
switch (usersActivity) { | |||
case UserActivity.ActiveSonarLintUser: | |||
return { | |||
slLastConnectedAfter: toNotSoISOString(nowDateMinus30Days), | |||
slLastConnectedAfter: toISO8601WithOffsetString(nowDateMinus30Days), | |||
}; | |||
case UserActivity.ActiveSonarQubeUser: | |||
return { | |||
lastConnectedAfter: toNotSoISOString(nowDateMinus30Days), | |||
slLastConnectedBefore: toNotSoISOString(nowDateMinus30DaysAnd1Second), | |||
lastConnectedAfter: toISO8601WithOffsetString(nowDateMinus30Days), | |||
slLastConnectedBefore: toISO8601WithOffsetString(nowDateMinus30DaysAnd1Second), | |||
}; | |||
case UserActivity.InactiveUser: | |||
return { | |||
lastConnectedBefore: toNotSoISOString(nowDateMinus30DaysAnd1Second), | |||
lastConnectedBefore: toISO8601WithOffsetString(nowDateMinus30DaysAnd1Second), | |||
}; | |||
default: | |||
return {}; |
@@ -22,12 +22,12 @@ import * as dates from '../dates'; | |||
const { parseDate } = dates; | |||
const recentDate = parseDate('2017-08-16T12:00:00.000Z'); | |||
it('toShortNotSoISOString', () => { | |||
expect(dates.toShortNotSoISOString(recentDate)).toBe('2017-08-16'); | |||
it('toShortISO8601String', () => { | |||
expect(dates.toShortISO8601String(recentDate)).toBe('2017-08-16'); | |||
}); | |||
it('toNotSoISOString', () => { | |||
expect(dates.toNotSoISOString(recentDate)).toBe('2017-08-16T12:00:00+0000'); | |||
it('toISO8601WithOffsetString', () => { | |||
expect(dates.toISO8601WithOffsetString(recentDate)).toBe('2017-08-16T12:00:00+0000'); | |||
}); | |||
it('isValidDate', () => { |
@@ -30,15 +30,18 @@ export function parseDate(rawDate: ParsableDate): Date { | |||
return new Date(rawDate); | |||
} | |||
export function toShortNotSoISOString(rawDate: ParsableDate): string { | |||
export function toShortISO8601String(rawDate: ParsableDate): string { | |||
const date = parseDate(rawDate); | |||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; | |||
} | |||
export function toNotSoISOString(rawDate: ParsableDate): string { | |||
export function toISO8601WithOffsetString(rawDate: ParsableDate): string { | |||
const date = parseDate(rawDate); | |||
const dateWithoutZone = date.toISOString().split('.')[0]; | |||
return dateWithoutZone + '+0000'; | |||
// JS ISO Date implementation returns a datetime in UTC time (suffixed by "Z"). But the backend | |||
// expects a datetime with a timeoffset (e.g., +0200). UTC time is actually "+0000", so we convert | |||
// the string to this other format for the backend. The backend also doesn't expect milliseconds, so | |||
// we truncate that part, too. | |||
return date.toISOString().split('.')[0] + '+0000'; | |||
} | |||
export function isValidDate(date: Date): boolean { |
@@ -20,7 +20,7 @@ | |||
import { fetchL10nBundle } from '../api/l10n'; | |||
import { L10nBundle, L10nBundleRequestParams } from '../types/l10nBundle'; | |||
import { Dict } from '../types/types'; | |||
import { toNotSoISOString } from './dates'; | |||
import { toISO8601WithOffsetString } from './dates'; | |||
const DEFAULT_LOCALE = 'en'; | |||
const DEFAULT_MESSAGES: Dict<string> = { | |||
@@ -70,7 +70,7 @@ export async function loadL10nBundle() { | |||
}); | |||
const bundle = { | |||
timestamp: toNotSoISOString(new Date()), | |||
timestamp: toISO8601WithOffsetString(new Date()), | |||
locale: effectiveLocale, | |||
messages, | |||
}; |
@@ -19,7 +19,7 @@ | |||
*/ | |||
import { isEqual, isNil, omitBy } from 'lodash'; | |||
import { RawQuery } from '../types/types'; | |||
import { isValidDate, parseDate, toNotSoISOString, toShortNotSoISOString } from './dates'; | |||
import { isValidDate, parseDate, toISO8601WithOffsetString, toShortISO8601String } from './dates'; | |||
export function queriesEqual(a: RawQuery, b: RawQuery): boolean { | |||
const keysA = Object.keys(a); | |||
@@ -91,7 +91,10 @@ export function parseAsOptionalArray<T>( | |||
return value ? parseAsArray(value, itemParser) : undefined; | |||
} | |||
export function serializeDate(value?: Date, serializer = toNotSoISOString): string | undefined { | |||
export function serializeDate( | |||
value?: Date, | |||
serializer = toISO8601WithOffsetString | |||
): string | undefined { | |||
if (value != null) { | |||
return serializer(value); | |||
} | |||
@@ -99,7 +102,7 @@ export function serializeDate(value?: Date, serializer = toNotSoISOString): stri | |||
} | |||
export function serializeDateShort(value: Date | undefined): string | undefined { | |||
return serializeDate(value, toShortNotSoISOString); | |||
return serializeDate(value, toShortISO8601String); | |||
} | |||
export function serializeString(value: string | undefined): string | undefined { |
@@ -20,7 +20,7 @@ | |||
import { getAllValues } from '../api/settings'; | |||
import { SettingsKey } from '../types/settings'; | |||
import { TokenExpiration, UserToken } from '../types/token'; | |||
import { now, toShortNotSoISOString } from './dates'; | |||
import { now, toShortISO8601String } from './dates'; | |||
import { translate } from './l10n'; | |||
export const EXPIRATION_OPTIONS = [ | |||
@@ -67,7 +67,7 @@ export async function getAvailableExpirationOptions() { | |||
export function computeTokenExpirationDate(days: number) { | |||
const expirationDate = now(); | |||
expirationDate.setDate(expirationDate.getDate() + days); | |||
return toShortNotSoISOString(expirationDate); | |||
return toShortISO8601String(expirationDate); | |||
} | |||
export function getNextTokenName(tokenNameBase: string, tokens: UserToken[]) { |