diff --git a/packages/html-reporter/src/copyToClipboard.css b/packages/html-reporter/src/copyToClipboard.css index 5790b626c0..818100fff7 100644 --- a/packages/html-reporter/src/copyToClipboard.css +++ b/packages/html-reporter/src/copyToClipboard.css @@ -20,15 +20,31 @@ width: 24px; border: none; outline: none; - color: var(--color-fg-default); + color: var(--color-fg-muted); background: transparent; padding: 4px; cursor: pointer; display: inline-flex; align-items: center; + justify-content: center; border-radius: 4px; } +.copy-icon svg { + margin: 0; +} + .copy-icon:not(:disabled):hover { background-color: var(--color-border-default); } + +.copy-button-container { + visibility: hidden; + display: inline-flex; + margin-left: 8px; + vertical-align: bottom; +} + +.copy-value-container:hover .copy-button-container { + visibility: visible; +} diff --git a/packages/html-reporter/src/copyToClipboard.tsx b/packages/html-reporter/src/copyToClipboard.tsx index a24015a671..17b1dfbf95 100644 --- a/packages/html-reporter/src/copyToClipboard.tsx +++ b/packages/html-reporter/src/copyToClipboard.tsx @@ -18,9 +18,14 @@ import * as React from 'react'; import * as icons from './icons'; import './copyToClipboard.css'; -export const CopyToClipboard: React.FunctionComponent<{ - value: string, -}> = ({ value }) => { +type CopyToClipboardProps = { + value: string; +}; + +/** + * A copy to clipboard button. + */ +export const CopyToClipboard: React.FunctionComponent = ({ value }) => { type IconType = 'copy' | 'check' | 'cross'; const [icon, setIcon] = React.useState('copy'); const handleCopy = React.useCallback(() => { @@ -34,5 +39,21 @@ export const CopyToClipboard: React.FunctionComponent<{ }); }, [value]); const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy(); - return ; + return ; +}; + +type CopyToClipboardContainerProps = CopyToClipboardProps & { + children: React.ReactNode +}; + +/** + * Container for displaying a copy to clipboard button alongside children. + */ +export const CopyToClipboardContainer: React.FunctionComponent = ({ children, value }) => { + return + {children} + + + + ; }; diff --git a/packages/html-reporter/src/testCaseView.spec.tsx b/packages/html-reporter/src/testCaseView.spec.tsx index afe06cebcb..72552f5184 100644 --- a/packages/html-reporter/src/testCaseView.spec.tsx +++ b/packages/html-reporter/src/testCaseView.spec.tsx @@ -76,6 +76,19 @@ test('should render test case', async ({ mount }) => { await expect(component.getByText('My test')).toBeVisible(); }); +test('should render copy buttons for annotations', async ({ mount, page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); + + const component = await mount(); + await expect(component.getByText('Annotation text', { exact: false }).first()).toBeVisible(); + component.getByText('Annotation text', { exact: false }).first().hover(); + await expect(component.getByLabel('Copy to clipboard').first()).toBeVisible(); + await component.getByLabel('Copy to clipboard').first().click(); + const handle = await page.evaluateHandle(() => navigator.clipboard.readText()); + const clipboardContent = await handle.jsonValue(); + expect(clipboardContent).toBe('Annotation text'); +}); + const annotationLinkRenderingTestCase: TestCase = { testId: 'testid', title: 'My test', diff --git a/packages/html-reporter/src/testCaseView.tsx b/packages/html-reporter/src/testCaseView.tsx index 5bed3c8309..1fe4f42ed3 100644 --- a/packages/html-reporter/src/testCaseView.tsx +++ b/packages/html-reporter/src/testCaseView.tsx @@ -26,6 +26,7 @@ import { TestResultView } from './testResultView'; import { linkifyText } from '@web/renderUtils'; import { hashStringToInt, msToString } from './utils'; import { clsx } from '@web/uiUtils'; +import { CopyToClipboardContainer } from './copyToClipboard'; export const TestCaseView: React.FC<{ projectNames: string[], @@ -73,7 +74,7 @@ function TestCaseAnnotationView({ annotation: { type, description } }: { annotat return (
{type} - {description && : {linkifyText(description)}} + {description && : {linkifyText(description)}}
); }