"local-rules/use-visibility-enum": "warn",
"local-rules/convert-class-to-function-component": "warn",
"local-rules/no-conditional-rendering-of-deferredspinner": "warn",
- "local-rules/use-jest-mocked": "warn"
+ "local-rules/use-jest-mocked": "warn",
+ "local-rules/use-await-expect-tohaveatooltipwithcontent": "warn"
}
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import '@testing-library/jest-dom';
-import { configure } from '@testing-library/react';
+import { configure, fireEvent, screen } from '@testing-library/react';
configure({
asyncUtilTimeout: 3000,
});
+
+// Don't forget to update src/main/js/types/jest.d.ts when registering custom matchers.
+expect.extend({
+ async toHaveATooltipWithContent(received: any, content: string) {
+ if (!(received instanceof Element)) {
+ return {
+ pass: false,
+ message: () => `Received object is not an HTMLElement, and cannot have a tooltip`,
+ };
+ }
+
+ fireEvent.pointerEnter(received);
+ const tooltip = await screen.findByRole('tooltip');
+
+ const result = tooltip.textContent?.includes(content)
+ ? {
+ pass: true,
+ message: () => `Tooltip content "${tooltip.textContent}" contains expected "${content}"`,
+ }
+ : {
+ pass: false,
+ message: () =>
+ `Tooltip content "${tooltip.textContent}" does not contain expected "${content}"`,
+ };
+
+ fireEvent.pointerLeave(received);
+
+ return result;
+ },
+});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+const { RuleTester } = require('eslint');
+const useJestMocked = require('../use-await-expect-tohaveatooltipwithcontent');
+
+const ruleTester = new RuleTester({
+ parser: require.resolve('@typescript-eslint/parser'),
+});
+
+ruleTester.run('use-await-expect-tohaveatooltipwithcontent', useJestMocked, {
+ valid: [
+ {
+ code: `await expect(node).toHaveATooltipWithContent("Help text");`,
+ },
+ ],
+ invalid: [
+ {
+ code: `expect(node).toHaveATooltipWithContent("Help text");`,
+ errors: [{ messageId: 'useAwaitExpectToHaveATooltipWithContent' }],
+ },
+ ],
+});
'use-componentqualifier-enum': require('./use-componentqualifier-enum'),
'use-metrickey-enum': require('./use-metrickey-enum'),
'use-metrictype-enum': require('./use-metrictype-enum'),
+ 'use-await-expect-tohaveatooltipwithcontent': require('./use-await-expect-tohaveatooltipwithcontent'),
};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+module.exports = {
+ meta: {
+ messages: {
+ useAwaitExpectToHaveATooltipWithContent:
+ 'expect.toHaveATooltipWithContent() is asynchronous; you must prefix expect() with await',
+ },
+ },
+ create(context) {
+ return {
+ Identifier(node) {
+ if (
+ node.name === 'toHaveATooltipWithContent' &&
+ node.parent?.parent?.parent?.type !== 'AwaitExpression'
+ ) {
+ context.report({ node, messageId: 'useAwaitExpectToHaveATooltipWithContent' });
+ }
+ },
+ };
+ },
+};
import { TabKeys } from '../../../components/rules/RuleTabViewer';
import { renderOwaspTop102021Category } from '../../../helpers/security-standard';
import { mockLoggedInUser, mockRawIssue } from '../../../helpers/testMocks';
-import { findTooltipWithContent } from '../../../helpers/testReactTestingUtils';
import { ComponentQualifier } from '../../../types/component';
import { IssueType } from '../../../types/issues';
import {
// Select an issue with an advanced rule
await user.click(await ui.issueItemAction7.find());
- expect(
- findTooltipWithContent('issue.quick_fix_available_with_sonarlint_no_link')
- ).toBeInTheDocument();
+ await expect(
+ screen.getByText('issue.quick_fix_available_with_sonarlint_no_link')
+ ).toHaveATooltipWithContent('issue.quick_fix_available_with_sonarlint');
+
expect(
screen.getByRole('status', {
name: 'issue.resolution.badge.DEPRECATED',
})}
id={this.id}
role="tooltip"
- aria-hidden={!isInteractive || !isVisible}
+ aria-hidden={!isVisible}
>
{isInteractive && (
<span className="a11y-hidden">{translate('tooltip_is_interactive')}</span>
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { findTooltipWithContent, renderComponent } from '../../../helpers/testReactTestingUtils';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
import FacetBox, { FacetBoxProps } from '../FacetBox';
import FacetHeader from '../FacetHeader';
import FacetItem from '../FacetItem';
it('should correctly render a header with helper text', async () => {
renderFacet(undefined, { helper: 'Help text' });
- await userEvent.tab();
- await userEvent.tab();
- expect(findTooltipWithContent('Help text')).toBeInTheDocument();
+ await expect(screen.getByRole('img', { description: 'Help text' })).toHaveATooltipWithContent(
+ 'Help text'
+ );
});
it('should correctly render a header with value data', () => {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { uniqueId } from 'lodash';
import * as React from 'react';
export interface IconProps extends React.AriaAttributes {
'aria-hidden': hidden,
...iconProps
}: Props) {
+ const id = uniqueId('icon');
return (
<svg
className={className}
width={width}
xmlnsXlink="http://www.w3.org/1999/xlink"
xmlSpace="preserve"
+ role="img"
+ aria-describedby={description && !hidden ? id : undefined}
{...iconProps}
>
{label && !hidden && <title>{label}</title>}
- {description && !hidden && <desc>{description}</desc>}
+ {description && !hidden && <desc id={id}>{description}</desc>}
{children}
</svg>
);
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render correctly 1`] = `"<div><svg height="16" style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;" version="1.1" viewBox="0 0 16 16" width="16" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"><path d="test-path"></path></svg></div>"`;
+exports[`should render correctly 1`] = `"<div><svg height="16" style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;" version="1.1" viewBox="0 0 16 16" width="16" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" role="img"><path d="test-path"></path></svg></div>"`;
import IssuesServiceMock from '../../../api/mocks/IssuesServiceMock';
import { KeyboardKeys } from '../../../helpers/keycodes';
import { mockIssue, mockLoggedInUser, mockRawIssue } from '../../../helpers/testMocks';
-import { findTooltipWithContent, renderAppRoutes } from '../../../helpers/testReactTestingUtils';
+import { renderAppRoutes } from '../../../helpers/testReactTestingUtils';
import { byLabelText, byRole, byText } from '../../../helpers/testSelector';
import {
IssueActions,
expect(screen.getByRole('status', { name: 'ESLINT' })).toBeInTheDocument();
});
- it('should render the SonarLint icon correctly', () => {
+ it('should render the SonarLint icon correctly', async () => {
renderIssue({ issue: mockIssue(false, { quickFixAvailable: true }) });
- expect(
- findTooltipWithContent('issue.quick_fix_available_with_sonarlint_no_link')
- ).toBeInTheDocument();
+ await expect(
+ screen.getByText('issue.quick_fix_available_with_sonarlint_no_link')
+ ).toHaveATooltipWithContent('issue.quick_fix_available_with_sonarlint');
});
it('should render correctly with a checkbox', async () => {
expect(onCheck).toHaveBeenCalledWith(issue.key);
});
- it('should correctly render any code variants', () => {
+ it('should correctly render any code variants', async () => {
const { ui } = getPageObject();
renderIssue({ issue: mockIssue(false, { codeVariants: ['variant 1', 'variant 2'] }) });
- expect(ui.variants(2).get()).toBeInTheDocument();
- expect(findTooltipWithContent('variant 1, variant 2', undefined, 'div')).toBeInTheDocument();
+ await expect(ui.variants(2).get()).toHaveATooltipWithContent('variant 1, variant 2');
});
});
>
<svg
height="16"
+ role="img"
space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
version="1.1"
}
/* eslint-enable testing-library/no-node-access */
+/**
+ * @deprecated Use our custom toHaveATooltipWithContent() matcher instead.
+ */
export function findTooltipWithContent(
text: Matcher,
target?: HTMLElement,
selector = 'svg > desc'
) {
+ // eslint-disable-next-line no-console
+ console.warn(`The usage of findTooltipWithContent() is deprecated; use expect.toHaveATooltipWithContent() instead.
+Example:
+ await expect(node).toHaveATooltipWithContent('foo.bar');`);
return target
? within(target).getByText(text, { selector })
: screen.getByText(text, { selector });
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+
+declare namespace jest {
+ interface Matchers<R> {
+ toHaveATooltipWithContent(content: string): Promise<CustomMatcherResult>;
+ }
+}