Skip to content

Commit

Permalink
PMM-13386 Link to update page from update panel (#775)
Browse files Browse the repository at this point in the history
* PMM-13386 Link to update page from update panel

* PMM-13386 Fix Available update & run unit tests for panels
  • Loading branch information
matejkubinec authored Oct 7, 2024
1 parent 7acb1ea commit 1c16128
Show file tree
Hide file tree
Showing 51 changed files with 239 additions and 1,531 deletions.
12 changes: 12 additions & 0 deletions jest.percona.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const grafanaConfig = require('./jest.config');

module.exports = {
...grafanaConfig,
roots: [
'<rootDir>/public/app/percona',
'<rootDir>/public/app/plugins/datasource/pmm-pt-summary-datasource',
'<rootDir>/public/app/plugins/panel/pmm-check',
'<rootDir>/public/app/plugins/panel/pmm-pt-summary-panel',
'<rootDir>/public/app/plugins/panel/pmm-update',
],
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"lint:fix": "yarn lint:ts --fix",
"jest": "jest --notify --watch",
"jest-ci": "mkdir -p reports/junit && export JEST_JUNIT_OUTPUT_DIR=reports/junit && jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%}",
"jest-percona-ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%} --roots public/app/percona",
"jest-percona-ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%} --config jest.percona.config.js",
"packages:build": "nx run-many -t build --projects='@grafana/*'",
"packages:clean": "rimraf ./npm-artifacts && lerna run clean --parallel",
"packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';

import { config } from '@grafana/runtime';
import { CheckService } from 'app/percona/check/Check.service';
import { configureStore } from 'app/store/configureStore';
import { StoreState } from 'app/types';
import { OrgRole, StoreState } from 'app/types';

import { Failed } from './Failed';

jest.mock('app/percona/check/Check.service');

describe('Failed::', () => {
beforeEach(() => {
config.bootData.user.isGrafanaAdmin = true;
config.bootData.user.orgRole = OrgRole.Admin;
});

it('should render a sum of total failed checks with severity details', async () => {
jest.spyOn(CheckService, 'getAllFailedChecks').mockImplementationOnce(async () => [
{
Expand Down Expand Up @@ -56,6 +62,8 @@ describe('Failed::', () => {
</Provider>
);

await waitFor(() => expect(CheckService.getAllFailedChecks).toHaveBeenCalled());

await waitFor(() => expect(screen.getByTestId('db-check-panel-critical').textContent).toEqual('3'));
await waitFor(() => expect(screen.getByTestId('db-check-panel-error').textContent).toEqual('0'));
await waitFor(() => expect(screen.getByTestId('db-check-panel-warning').textContent).toEqual('2'));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const Messages = {
upgrade: 'Upgrade',
upgradeTo: (version: string) => `Upgrade to ${version}`,
};
17 changes: 0 additions & 17 deletions public/app/plugins/panel/pmm-update/UpdatePanel.service.ts

This file was deleted.

43 changes: 22 additions & 21 deletions public/app/plugins/panel/pmm-update/UpdatePanel.styles.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { css } from '@emotion/css';

export const panel = css`
display: flex;
flex-direction: column;
position: relative;
height: 100%;
export const styles = {
panel: css`
display: flex;
flex-direction: column;
position: relative;
height: 100%;
p {
margin-bottom: 0;
}
@media (max-width: 1281px) {
#pmm-update-widget h2 {
font-size: 1.55rem;
margin-bottom: 0.1rem;
p {
margin-bottom: 0;
}
}
`;
export const middleSectionWrapper = css`
align-items: center;
display: flex;
flex: 1;
justify-content: center;
`;
@media (max-width: 1281px) {
#pmm-update-widget h2 {
font-size: 1.55rem;
margin-bottom: 0.1rem;
}
}
`,
middleSectionWrapper: css`
align-items: center;
display: flex;
flex: 1;
justify-content: center;
`,
};
81 changes: 31 additions & 50 deletions public/app/plugins/panel/pmm-update/UpdatePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,63 @@
import React, { FC, MouseEvent, useEffect, useState } from 'react';
import React, { FC, MouseEvent, useState } from 'react';

import { Button, IconName, Spinner } from '@grafana/ui';
import { getPerconaUser, getPerconaSettings } from 'app/percona/shared/core/selectors';
import { Button, Spinner } from '@grafana/ui';
import { PMM_UPDATES_LINK } from 'app/percona/shared/components/PerconaBootstrapper/PerconaNavigation';
import { checkUpdatesAction } from 'app/percona/shared/core/reducers/updates';
import { getPerconaUser, getPerconaSettings, getUpdatesInfo } from 'app/percona/shared/core/selectors';
import { useAppDispatch } from 'app/store/store';
import { useSelector } from 'app/types';

import { Messages } from './UpdatePanel.messages';
import * as styles from './UpdatePanel.styles';
import { AvailableUpdate, CurrentVersion, InfoBox, LastCheck, ProgressModal } from './components';
import { usePerformUpdate, useVersionDetails } from './hooks';
import { styles } from './UpdatePanel.styles';
import { formatDateWithTime } from './UpdatePanel.utils';
import { AvailableUpdate, CurrentVersion, InfoBox, LastCheck } from './components';

export const UpdatePanel: FC<{}> = () => {
export const UpdatePanel: FC = () => {
const isOnline = navigator.onLine;
const {
isLoading: isLoadingVersionDetails,
installed,
latest,
latestNewsUrl,
updateAvailable,
lastChecked,
} = useSelector(getUpdatesInfo);
const { result: settings, loading: isLoadingSettings } = useSelector(getPerconaSettings);
const dispatch = useAppDispatch();
const [forceUpdate, setForceUpdate] = useState(false);
const [showModal, setShowModal] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const { isAuthorized } = useSelector(getPerconaUser);
const { result: settings, loading: isLoadingSettings } = useSelector(getPerconaSettings);
const [
{ installedVersionDetails, lastCheckDate, nextVersionDetails, isUpdateAvailable },
fetchVersionErrorMessage,
isLoadingVersionDetails,
isDefaultView,
getCurrentVersionDetails,
] = useVersionDetails();
const [output, updateErrorMessage, isUpdated, updateFailed, launchUpdate] = usePerformUpdate();
const isDefaultView = !latest;
const isLoading = isLoadingVersionDetails || isLoadingSettings;

const handleCheckForUpdates = (e: MouseEvent) => {
if (e.altKey) {
setForceUpdate(true);
}

getCurrentVersionDetails({ force: true });
dispatch(checkUpdatesAction());
};

useEffect(() => {
setErrorMessage(fetchVersionErrorMessage || updateErrorMessage);

const timeout = setTimeout(() => {
setErrorMessage('');
}, 5000);

return () => {
clearTimeout(timeout);
};
}, [fetchVersionErrorMessage, updateErrorMessage]);

const handleUpdate = () => {
setShowModal(true);
launchUpdate();
const handleOpenUpdates = () => {
window.location.assign(PMM_UPDATES_LINK.url!);
};

return (
<>
<div className={styles.panel}>
<CurrentVersion installedVersionDetails={installedVersionDetails} />
{isUpdateAvailable && !isDefaultView && settings?.updatesEnabled && isAuthorized && !isLoading && isOnline ? (
<AvailableUpdate nextVersionDetails={nextVersionDetails} />
{!!installed && <CurrentVersion currentVersion={installed} />}
{updateAvailable && !isDefaultView && settings?.updatesEnabled && isAuthorized && !isLoading && isOnline ? (
<AvailableUpdate nextVersion={latest} newsLink={latestNewsUrl} />
) : null}
{isLoading ? (
<div className={styles.middleSectionWrapper}>
<Spinner />
</div>
) : (
<>
{(isUpdateAvailable || forceUpdate) && settings?.updatesEnabled && isAuthorized && isOnline ? (
{(updateAvailable || forceUpdate) && settings?.updatesEnabled && isAuthorized && isOnline ? (
<div className={styles.middleSectionWrapper}>
{/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */}
<Button onClick={handleUpdate} icon={'fa fa-download' as IconName} variant="secondary">
{Messages.upgradeTo(nextVersionDetails?.nextVersion)}
<Button onClick={handleOpenUpdates} icon="download-alt" variant="secondary">
{!!latest?.version ? Messages.upgradeTo(latest.version) : Messages.upgrade}
</Button>
</div>
) : (
Expand All @@ -84,17 +73,9 @@ export const UpdatePanel: FC<{}> = () => {
<LastCheck
disabled={isLoading || !settings?.updatesEnabled || !isOnline}
onCheckForUpdates={handleCheckForUpdates}
lastCheckDate={lastCheckDate}
lastCheckDate={lastChecked ? formatDateWithTime(lastChecked) : ''}
/>
</div>
<ProgressModal
errorMessage={errorMessage}
isOpen={showModal}
isUpdated={isUpdated}
output={output}
updateFailed={updateFailed}
version={nextVersionDetails?.nextVersion}
/>
</>
);
};
17 changes: 8 additions & 9 deletions public/app/plugins/panel/pmm-update/UpdatePanel.utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { formatDateWithYear, formatDateWithTime } from './UpdatePanel.utils';
import { ISOTimestamp } from './types';

describe('UpdatePanel utils', () => {
const timestamp1 = '2020-06-08T19:16:57Z';
Expand All @@ -8,15 +7,15 @@ describe('UpdatePanel utils', () => {
const timestamp4 = '2021-04-06T00:00:00Z';

it('should format an ISO 8601 timestamp correctly as date without time', () => {
expect(formatDateWithYear(timestamp1 as ISOTimestamp)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp2 as ISOTimestamp)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp3 as ISOTimestamp)).toBe('June 07, 2020 UTC');
expect(formatDateWithYear(timestamp4 as ISOTimestamp)).toBe('April 06, 2021 UTC');
expect(formatDateWithYear(timestamp1)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp2)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp3)).toBe('June 07, 2020 UTC');
expect(formatDateWithYear(timestamp4)).toBe('April 06, 2021 UTC');
});
it('should format an ISO 8601 timestamp correctly as date with time', () => {
expect(formatDateWithTime(timestamp1 as ISOTimestamp)).toBe('June 08, 19:16 UTC');
expect(formatDateWithTime(timestamp2 as ISOTimestamp)).toBe('June 08, 0:06 UTC');
expect(formatDateWithTime(timestamp3 as ISOTimestamp)).toBe('June 07, 23:36 UTC');
expect(formatDateWithTime(timestamp4 as ISOTimestamp)).toBe('April 06, 0:00 UTC');
expect(formatDateWithTime(timestamp1)).toBe('June 08, 19:16 UTC');
expect(formatDateWithTime(timestamp2)).toBe('June 08, 0:06 UTC');
expect(formatDateWithTime(timestamp3)).toBe('June 07, 23:36 UTC');
expect(formatDateWithTime(timestamp4)).toBe('April 06, 0:00 UTC');
});
});
6 changes: 2 additions & 4 deletions public/app/plugins/panel/pmm-update/UpdatePanel.utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { format } from 'date-fns';
import { enUS } from 'date-fns/locale';

import { ISOTimestamp } from './types';

export const formatDateWithTime = (timestamp: ISOTimestamp) => {
export const formatDateWithTime = (timestamp: string) => {
const date = new Date(timestamp);
return `${format(date.valueOf() + date.getTimezoneOffset() * 60 * 1000, 'MMMM dd, H:mm', { locale: enUS })} UTC`;
};

export const formatDateWithYear = (timestamp: ISOTimestamp) => {
export const formatDateWithYear = (timestamp: string) => {
const date = new Date(timestamp);
return `${format(date.valueOf() + date.getTimezoneOffset() * 60 * 1000, 'MMMM dd, yyyy', { locale: enUS })} UTC`;
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { css } from '@emotion/css';

import { GrafanaTheme } from '@grafana/data';
import { GrafanaTheme2 } from '@grafana/data';

export const getStyles = ({ spacing, typography }: GrafanaTheme) => ({
export const getStyles = ({ spacing, typography }: GrafanaTheme2) => ({
availableUpdate: css`
align-items: flex-start;
display: flex;
font-weight: ${typography.weight.bold};
font-weight: ${typography.fontWeightBold};
justify-content: flex-start;
line-height: ${typography.lineHeight.sm};
margin-top: ${spacing.xs};
line-height: ${typography.bodySmall.lineHeight};
margin-top: ${spacing(0.5)};
> div {
display: flex;
Expand All @@ -21,18 +21,18 @@ export const getStyles = ({ spacing, typography }: GrafanaTheme) => ({
`,
whatsNewLink: css`
height: 1em;
margin-top: ${spacing.xs};
margin-top: ${spacing(0.5)};
padding: 0;
`,
releaseDate: css`
font-size: ${typography.size.sm};
font-weight: ${typography.weight.regular};
font-weight: ${typography.fontWeightRegular};
`,
latestVersion: css`
margin-right: ${spacing.xs};
margin-right: ${spacing(0.5)};
`,
infoIcon: css`
margin-left: ${spacing.xs};
margin-right: ${spacing.sm};
margin-left: ${spacing(0.5)};
margin-right: ${spacing(1)};
`,
});
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';

import { LatestInformation } from 'app/percona/shared/core/reducers/updates';

import { AvailableUpdate } from './AvailableUpdate';

const nextFullVersion = 'x.y.z-rc.j+1234567890';
const nextVersion = 'x.y.z';
const version = 'x.y.z';
const tag = 'percona/pmm-server:x.y.z-rc.j+1234567890';
const newsLink = 'https://percona.com';
const nextVersionDate = '23 Jun';
const timestamp = '2024-06-23T00:00:00.000Z';

const nextVersionDetails = {
nextVersionDate,
nextVersion,
nextFullVersion,
newsLink,
const nextVersion: LatestInformation = {
version,
tag,
timestamp,
};

describe('AvailableUpdate::', () => {
it('should show only the short version by default', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

expect(screen.getByTestId('update-latest-version').textContent).toEqual(nextVersion);
expect(screen.getByTestId('update-latest-version')).toHaveTextContent(version);
});

it('should show the news link if present', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

expect(screen.getByTestId('update-news-link')).toBeTruthy();
});

it('should show the full version on alt-click', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

fireEvent.click(screen.getByTestId('update-latest-section'), { altKey: true });

expect(screen.getByTestId('update-latest-version').textContent).toEqual(nextFullVersion);
expect(screen.getByTestId('update-latest-version')).toHaveTextContent(tag);
});
});
Loading

0 comments on commit 1c16128

Please sign in to comment.