diff --git a/packages/html-reporter/src/copyToClipboard.css b/packages/html-reporter/src/copyToClipboard.css
new file mode 100644
index 0000000000..5790b626c0
--- /dev/null
+++ b/packages/html-reporter/src/copyToClipboard.css
@@ -0,0 +1,34 @@
+/*
+ Copyright (c) Microsoft Corporation.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+.copy-icon {
+ flex: none;
+ height: 24px;
+ width: 24px;
+ border: none;
+ outline: none;
+ color: var(--color-fg-default);
+ background: transparent;
+ padding: 4px;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ border-radius: 4px;
+}
+
+.copy-icon:not(:disabled):hover {
+ background-color: var(--color-border-default);
+}
diff --git a/packages/html-reporter/src/copyToClipboard.tsx b/packages/html-reporter/src/copyToClipboard.tsx
new file mode 100644
index 0000000000..a24015a671
--- /dev/null
+++ b/packages/html-reporter/src/copyToClipboard.tsx
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as React from 'react';
+import * as icons from './icons';
+import './copyToClipboard.css';
+
+export const CopyToClipboard: React.FunctionComponent<{
+ value: string,
+}> = ({ value }) => {
+ type IconType = 'copy' | 'check' | 'cross';
+ const [icon, setIcon] = React.useState('copy');
+ const handleCopy = React.useCallback(() => {
+ navigator.clipboard.writeText(value).then(() => {
+ setIcon('check');
+ setTimeout(() => {
+ setIcon('copy');
+ }, 3000);
+ }, () => {
+ setIcon('cross');
+ });
+ }, [value]);
+ const iconElement = icon === 'check' ? icons.check() : icon === 'cross' ? icons.cross() : icons.copy();
+ return ;
+};
diff --git a/packages/html-reporter/src/icons.tsx b/packages/html-reporter/src/icons.tsx
index 86376bdab4..c3eb471b4d 100644
--- a/packages/html-reporter/src/icons.tsx
+++ b/packages/html-reporter/src/icons.tsx
@@ -106,3 +106,10 @@ export const trace = () => {
export const empty = () => {
return ;
};
+
+export const copy = () => {
+ return ;
+};
diff --git a/packages/html-reporter/src/links.css b/packages/html-reporter/src/links.css
index 73ae3e64d4..4abe8a6caa 100644
--- a/packages/html-reporter/src/links.css
+++ b/packages/html-reporter/src/links.css
@@ -102,4 +102,11 @@
line-height: normal;
padding: 8px;
font-family: monospace;
+ position: relative;
+}
+
+.attachment-body .copy-icon {
+ position: absolute;
+ right: 5px;
+ top: 5px;
}
diff --git a/packages/html-reporter/src/links.tsx b/packages/html-reporter/src/links.tsx
index 2aa835d7d6..daece0aefe 100644
--- a/packages/html-reporter/src/links.tsx
+++ b/packages/html-reporter/src/links.tsx
@@ -18,6 +18,7 @@ import type { TestAttachment } from './types';
import * as React from 'react';
import * as icons from './icons';
import { TreeItem } from './treeItem';
+import { CopyToClipboard } from './copyToClipboard';
import './links.css';
export function navigate(href: string) {
@@ -71,7 +72,7 @@ export const AttachmentLink: React.FunctionComponent<{
{attachment.path && {linkName || attachment.name}}
{attachment.body && {attachment.name}}
} loadChildren={attachment.body ? () => {
- return [{attachment.body}
];
+ return [{attachment.body}
];
} : undefined} depth={0} style={{ lineHeight: '32px' }}>;
};