* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
import * as React from 'react';
import { addFavorite, removeFavorite } from '../../api/favorites';
-import FavoriteButton from '../../components/controls/FavoriteButton';
+import { translate } from '../../helpers/l10n';
+import FavoriteIcon from '../icons/FavoriteIcon';
+import { ButtonLink } from './buttons';
+import Tooltip from './Tooltip';
interface Props {
className?: string;
export default class Favorite extends React.PureComponent<Props, State> {
mounted = false;
+ buttonNode?: HTMLElement | null;
constructor(props: Props) {
super(props);
if (this.props.handleFavorite) {
this.props.handleFavorite(this.props.component, newFavorite);
}
+ if (this.buttonNode) {
+ this.buttonNode.focus();
+ }
});
}
});
const { className, qualifier } = this.props;
const { favorite } = this.state;
+ const tooltip = favorite
+ ? translate('favorite.current', qualifier)
+ : translate('favorite.check', qualifier);
+ const ariaLabel = translate('favorite.action', favorite ? 'remove' : 'add');
+
return (
- <FavoriteButton
- className={className}
- favorite={favorite}
- qualifier={qualifier}
- toggleFavorite={this.toggleFavorite}
- />
+ <Tooltip overlay={tooltip}>
+ <ButtonLink
+ aria-label={ariaLabel}
+ innerRef={node => (this.buttonNode = node)}
+ className={classNames('favorite-link', 'link-no-underline', className)}
+ onClick={this.toggleFavorite}>
+ <FavoriteIcon favorite={favorite} />
+ </ButtonLink>
+ </Tooltip>
);
}
}
-/* */
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import FavoriteIcon from '../icons/FavoriteIcon';
-import { ButtonLink } from './buttons';
-import Tooltip from './Tooltip';
-
-export interface Props {
- className?: string;
- favorite: boolean;
- qualifier: string;
- toggleFavorite: () => void;
-}
-
-export default class FavoriteButton extends React.PureComponent<Props> {
- render() {
- const { className, favorite, qualifier, toggleFavorite } = this.props;
- const tooltip = favorite
- ? translate('favorite.current', qualifier)
- : translate('favorite.check', qualifier);
- const ariaLabel = translate('favorite.action', favorite ? 'remove' : 'add');
-
- return (
- <Tooltip overlay={tooltip}>
- <ButtonLink
- aria-label={ariaLabel}
- className={classNames('favorite-link', 'link-no-underline', className)}
- onClick={toggleFavorite}>
- <FavoriteIcon favorite={favorite} />
- </ButtonLink>
- </Tooltip>
- );
- }
-}
export const DEFAULT_HOMEPAGE: HomePage = { type: 'PROJECTS' };
export class HomePageSelect extends React.PureComponent<Props> {
+ buttonNode?: HTMLElement | null;
+
async setCurrentUserHomepage(homepage: HomePage) {
const { currentUser } = this.props;
await setHomePage(homepage);
this.props.updateCurrentUserHomepage(homepage);
+
+ if (this.buttonNode) {
+ this.buttonNode.focus();
+ }
}
}
<ButtonLink
aria-label={tooltip}
className={classNames('link-no-underline', 'set-homepage-link', this.props.className)}
- onClick={isChecked ? this.handleReset : this.handleClick}>
+ onClick={isChecked ? this.handleReset : this.handleClick}
+ innerRef={node => (this.buttonNode = node)}>
<HomeIcon filled={isChecked} />
</ButtonLink>
)}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { shallow } from 'enzyme';
+import { screen } from '@testing-library/react';
import * as React from 'react';
-import FavoriteButton from '../../../components/controls/FavoriteButton';
+import { addFavorite, removeFavorite } from '../../../api/favorites';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import { ComponentQualifier } from '../../../types/component';
import Favorite from '../Favorite';
jest.mock('../../../api/favorites', () => ({
- addFavorite: jest.fn(() => Promise.resolve()),
- removeFavorite: jest.fn(() => Promise.resolve())
+ addFavorite: jest.fn().mockResolvedValue(null),
+ removeFavorite: jest.fn().mockResolvedValue(null)
}));
-it('renders', () => {
- expect(shallowRender()).toMatchSnapshot();
+it('renders and behaves correctly', async () => {
+ renderFavorite({ favorite: false });
+ let button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
+
+ button.click();
+ await new Promise(setImmediate);
+ expect(addFavorite).toHaveBeenCalled();
+
+ button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
+ expect(button).toHaveFocus();
+
+ button.click();
+ await new Promise(setImmediate);
+ expect(removeFavorite).toHaveBeenCalled();
+
+ button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
+ expect(button).toHaveFocus();
});
-it('calls handleFavorite when given', async () => {
+it('correctly calls handleFavorite if passed', async () => {
const handleFavorite = jest.fn();
- const wrapper = shallowRender(handleFavorite);
- const favoriteBase = wrapper.find(FavoriteButton);
- const toggleFavorite = favoriteBase.prop<Function>('toggleFavorite');
+ renderFavorite({ handleFavorite });
- toggleFavorite();
+ screen.getByRole('button').click();
await new Promise(setImmediate);
expect(handleFavorite).toHaveBeenCalledWith('foo', false);
- toggleFavorite();
+ screen.getByRole('button').click();
await new Promise(setImmediate);
expect(handleFavorite).toHaveBeenCalledWith('foo', true);
});
-function shallowRender(handleFavorite?: () => void) {
- return shallow(
- <Favorite component="foo" favorite={true} handleFavorite={handleFavorite} qualifier="TRK" />
+function renderFavorite(props: Partial<Favorite['props']> = {}) {
+ return renderComponent(
+ <Favorite component="foo" favorite={true} qualifier={ComponentQualifier.Project} {...props} />
);
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { click } from '../../../helpers/testUtils';
-import FavoriteButton, { Props } from '../FavoriteButton';
-
-it('should render favorite', () => {
- const favorite = renderFavoriteBase({ favorite: true });
- expect(favorite).toMatchSnapshot();
-});
-
-it('should render not favorite', () => {
- const favorite = renderFavoriteBase({ favorite: false });
- expect(favorite).toMatchSnapshot();
-});
-
-it('should update properly', () => {
- const favorite = renderFavoriteBase({ favorite: false });
- expect(favorite).toMatchSnapshot();
-
- favorite.setProps({ favorite: true });
- expect(favorite).toMatchSnapshot();
-});
-
-it('should toggle favorite', () => {
- const toggleFavorite = jest.fn();
- const favorite = renderFavoriteBase({ toggleFavorite });
- click(favorite.find('ButtonLink'));
- expect(toggleFavorite).toBeCalled();
-});
-
-function renderFavoriteBase(props: Partial<Props> = {}) {
- return shallow(
- <FavoriteButton favorite={true} qualifier="TRK" toggleFavorite={jest.fn()} {...props} />
- );
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { shallow } from 'enzyme';
+import { screen } from '@testing-library/react';
import * as React from 'react';
-import { ButtonLink } from '../../../components/controls/buttons';
-import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks';
-import { click, waitAndUpdate } from '../../../helpers/testUtils';
-import { HomePage } from '../../../types/users';
+import { setHomePage } from '../../../api/users';
+import { mockLoggedInUser } from '../../../helpers/testMocks';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
import { DEFAULT_HOMEPAGE, HomePageSelect } from '../HomePageSelect';
jest.mock('../../../api/users', () => ({
setHomePage: jest.fn().mockResolvedValue(null)
}));
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('unchecked');
- expect(
- shallowRender({ currentUser: mockLoggedInUser({ homepage: { type: 'MY_PROJECTS' } }) })
- ).toMatchSnapshot('checked');
- expect(
- shallowRender({
- currentUser: mockLoggedInUser({ homepage: DEFAULT_HOMEPAGE }),
- currentPage: DEFAULT_HOMEPAGE
- })
- ).toMatchSnapshot('checked, and on default');
- expect(shallowRender({ currentUser: mockCurrentUser() }).type()).toBeNull();
-});
-
-it('should correctly call webservices', async () => {
+it('renders and behaves correctly', async () => {
const updateCurrentUserHomepage = jest.fn();
- const currentPage: HomePage = { type: 'MY_ISSUES' };
- const wrapper = shallowRender({ updateCurrentUserHomepage, currentPage });
+ renderHomePageSelect({ updateCurrentUserHomepage });
+ const button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
+
+ button.click();
+ await new Promise(setImmediate);
+ expect(setHomePage).toHaveBeenCalledWith({ type: 'MY_PROJECTS' });
+ expect(updateCurrentUserHomepage).toHaveBeenCalled();
+ expect(button).toHaveFocus();
+});
- // Set homepage.
- click(wrapper.find(ButtonLink));
- await waitAndUpdate(wrapper);
- expect(updateCurrentUserHomepage).toHaveBeenLastCalledWith(currentPage);
+it('renders correctly if user is on the homepage', async () => {
+ renderHomePageSelect({ currentUser: mockLoggedInUser({ homepage: { type: 'MY_PROJECTS' } }) });
+ const button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
- // Reset.
- wrapper.setProps({ currentUser: mockLoggedInUser({ homepage: currentPage }) });
- click(wrapper.find(ButtonLink));
- await waitAndUpdate(wrapper);
- expect(updateCurrentUserHomepage).toHaveBeenLastCalledWith(DEFAULT_HOMEPAGE);
+ button.click();
+ await new Promise(setImmediate);
+ expect(setHomePage).toHaveBeenCalledWith(DEFAULT_HOMEPAGE);
+ expect(button).toHaveFocus();
});
-function shallowRender(props: Partial<HomePageSelect['props']> = {}) {
- return shallow<HomePageSelect>(
+function renderHomePageSelect(props: Partial<HomePageSelect['props']> = {}) {
+ return renderComponent(
<HomePageSelect
currentPage={{ type: 'MY_PROJECTS' }}
currentUser={mockLoggedInUser()}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<FavoriteButton
- favorite={true}
- qualifier="TRK"
- toggleFavorite={[Function]}
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render favorite 1`] = `
-<Tooltip
- overlay="favorite.current.TRK"
->
- <ButtonLink
- aria-label="favorite.action.remove"
- className="favorite-link link-no-underline"
- onClick={[MockFunction]}
- >
- <FavoriteIcon
- favorite={true}
- />
- </ButtonLink>
-</Tooltip>
-`;
-
-exports[`should render not favorite 1`] = `
-<Tooltip
- overlay="favorite.check.TRK"
->
- <ButtonLink
- aria-label="favorite.action.add"
- className="favorite-link link-no-underline"
- onClick={[MockFunction]}
- >
- <FavoriteIcon
- favorite={false}
- />
- </ButtonLink>
-</Tooltip>
-`;
-
-exports[`should update properly 1`] = `
-<Tooltip
- overlay="favorite.check.TRK"
->
- <ButtonLink
- aria-label="favorite.action.add"
- className="favorite-link link-no-underline"
- onClick={[MockFunction]}
- >
- <FavoriteIcon
- favorite={false}
- />
- </ButtonLink>
-</Tooltip>
-`;
-
-exports[`should update properly 2`] = `
-<Tooltip
- overlay="favorite.current.TRK"
->
- <ButtonLink
- aria-label="favorite.action.remove"
- className="favorite-link link-no-underline"
- onClick={[MockFunction]}
- >
- <FavoriteIcon
- favorite={true}
- />
- </ButtonLink>
-</Tooltip>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: checked 1`] = `
-<Tooltip
- overlay="homepage.current"
->
- <ButtonLink
- aria-label="homepage.current"
- className="link-no-underline set-homepage-link"
- onClick={[Function]}
- >
- <HomeIcon
- filled={true}
- />
- </ButtonLink>
-</Tooltip>
-`;
-
-exports[`should render correctly: checked, and on default 1`] = `
-<Tooltip
- overlay="homepage.current.is_default"
->
- <span
- aria-label="homepage.current.is_default"
- className="display-inline-block"
- >
- <HomeIcon
- filled={true}
- />
- </span>
-</Tooltip>
-`;
-
-exports[`should render correctly: unchecked 1`] = `
-<Tooltip
- overlay="homepage.check"
->
- <ButtonLink
- aria-label="homepage.check"
- className="link-no-underline set-homepage-link"
- onClick={[Function]}
- >
- <HomeIcon
- filled={false}
- />
- </ButtonLink>
-</Tooltip>
-`;