]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22298 Fix clipboard a11y
authorJeremy Davis <jeremy.davis@sonarsource.com>
Thu, 10 Oct 2024 15:14:10 +0000 (17:14 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 11 Oct 2024 20:02:44 +0000 (20:02 +0000)
20 files changed:
server/sonar-web/design-system/src/components/DropdownMenu.tsx
server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
server/sonar-web/design-system/src/components/__tests__/clipboard-test.tsx
server/sonar-web/design-system/src/components/clipboard.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsHeader.tsx
server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx
server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx
server/sonar-web/src/main/js/apps/settings/encryption/__tests__/EncryptionApp-it.tsx
server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/__snapshots__/BitbucketPipelinesTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GithubActionTutorial-it.tsx.snap
server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/GitLabCITutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/other/__tests__/OtherTutorial-it.tsx
server/sonar-web/src/main/js/components/tutorials/test-utils.ts

index e3be15c7a1ab3300c9eb91b0080db693b40f497a..1251298bfcb5625e6aed65cc27166677446bbef9 100644 (file)
@@ -29,7 +29,7 @@ import { InputSizeKeys, ThemedProps } from '../types/theme';
 import { BaseLink, LinkProps } from './Link';
 import NavLink from './NavLink';
 import { Tooltip } from './Tooltip';
-import { ClipboardBase } from './clipboard';
+import { useCopyClipboardEffect } from './clipboard';
 import { Checkbox } from './input/Checkbox';
 
 interface Props extends React.HtmlHTMLAttributes<HTMLMenuElement> {
@@ -223,23 +223,17 @@ interface ItemCopyProps {
 
 export function ItemCopy(props: ItemCopyProps) {
   const { children, className, copyValue, tooltipOverlay } = props;
+
+  const [copySuccess, handleCopy] = useCopyClipboardEffect(copyValue);
+
   return (
-    <ClipboardBase>
-      {({ setCopyButton, copySuccess }) => (
-        <Tooltip content={tooltipOverlay} visible={copySuccess}>
-          <li role="none">
-            <ItemButtonStyled
-              className={className}
-              data-clipboard-text={copyValue}
-              ref={setCopyButton}
-              role="menuitem"
-            >
-              {children}
-            </ItemButtonStyled>
-          </li>
-        </Tooltip>
-      )}
-    </ClipboardBase>
+    <Tooltip content={tooltipOverlay} visible={copySuccess}>
+      <li role="none">
+        <ItemButtonStyled className={className} onClick={handleCopy} role="menuitem">
+          {children}
+        </ItemButtonStyled>
+      </li>
+    </Tooltip>
   );
 }
 
index 1d7d505e179cf56c393cca6febc4406d26fb24c3..ac2ea24e1994b2445f65074fc479bb2ba96b33d6 100644 (file)
@@ -16,16 +16,8 @@ exports[`should highlight code content correctly 1`] = `
   border-radius: 0.25rem;
 }
 
-.emotion-4 {
+.emotion-3 {
   box-sizing: border-box;
-  -webkit-text-decoration: none;
-  text-decoration: none;
-  outline: none;
-  border: var(--border);
-  color: var(--color);
-  background-color: var(--background);
-  -webkit-transition: background-color 0.2s ease;
-  transition: background-color 0.2s ease;
   display: -webkit-inline-box;
   display: -webkit-inline-flex;
   display: -ms-inline-flexbox;
@@ -34,19 +26,28 @@ exports[`should highlight code content correctly 1`] = `
   -webkit-box-align: center;
   -ms-flex-align: center;
   align-items: center;
-  height: 2.25rem;
-  font: var(--echoes-typography-text-default-semi-bold);
-  padding-left: 1rem;
-  padding-right: 1rem;
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-  border-radius: 0.5rem;
+  padding: var(--echoes-dimension-space-0) var(--button-padding);
+  height: var(--button-height);
+  min-height: var(--button-height);
+  overflow: hidden;
+  font: var(--echoes-typography-others-label);
+  color: var(--button-color);
+  -webkit-text-decoration: none;
+  text-decoration: none;
+  background-color: var(--button-background);
+  border: var(--button-border);
+  border-radius: var(--echoes-border-radius-400);
+  outline: none;
   cursor: pointer;
-  --background: rgb(255,255,255);
-  --backgroundHover: rgb(239,242,249);
-  --color: rgb(62,67,87);
-  --focus: rgba(197,205,223,0.2);
-  --border: 1px solid rgb(197,205,223);
+  --button-color: var(--echoes-color-text-default);
+  --button-border: var(--echoes-color-border-bold) solid var(--echoes-border-width-default);
+  --button-background: var(--echoes-color-background-default);
+  --button-background-hover: var(--echoes-color-background-default-hover);
+  --button-background-active: var(--echoes-color-background-default-active);
+  --button-background-focus: var(--echoes-color-background-default);
+  --button-background-disabled: var(--echoes-color-background-disabled);
+  --button-padding: var(--echoes-dimension-space-150);
+  --button-height: var(--echoes-sizes-buttons-large);
   -webkit-user-select: none;
   -moz-user-select: none;
   -ms-user-select: none;
@@ -57,90 +58,115 @@ exports[`should highlight code content correctly 1`] = `
   position: absolute;
 }
 
-.emotion-4:hover,
-.emotion-4:active {
-  color: var(--color);
-  background-color: var(--backgroundHover);
+.emotion-3:focus,
+.emotion-3:focus-visible {
+  background-color: var(--button-background-focus);
+  outline: var(--echoes-color-focus-default) solid var(--echoes-focus-border-width-default);
+  outline-offset: var(--echoes-focus-border-offset-default);
 }
 
-.emotion-4:focus,
-.emotion-4:active,
-.emotion-4:focus-visible {
-  color: var(--color);
+.emotion-3:hover {
+  background-color: var(--button-background-hover);
 }
 
-.emotion-4:focus-visible {
-  outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default);
-  outline-offset: var(--echoes-focus-border-offset-default);
+.emotion-3:active {
+  background-color: var(--button-background-active);
 }
 
-.emotion-4:disabled,
-.emotion-4:disabled:hover {
+.emotion-3:disabled,
+.emotion-3:disabled:has(:hover, :active, :focus, :focus-visible) {
   color: var(--echoes-color-text-disabled);
-  background-color: rgb(239,242,249);
-  border: 1px solid rgb(197,205,223);
+  background-color: var(--button-background-disabled);
+  border: none;
   cursor: not-allowed;
+  pointer-events: none;
 }
 
-.emotion-4>svg {
-  margin-right: 0.25rem;
+.code-snippet-highlighted-oneline .emotion-3 {
+  bottom: 0.5rem;
 }
 
-.emotion-4 [disabled] {
-  pointer-events: none;
+.emotion-5 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  gap: var(--echoes-dimension-space-75);
+  overflow: hidden;
 }
 
-.code-snippet-highlighted-oneline .emotion-4 {
-  bottom: 0.5rem;
+.emotion-7 {
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  display: inline-block;
+  font-size: calc(1em + 4px);
+  font-style: normal;
+  font-weight: normal;
+  height: calc(2em - 16px);
+  line-height: calc(2em - 16px);
+  text-align: center;
+  vertical-align: bottom;
+  width: calc(2em - 16px);
+  font-family: 'Material Symbols Rounded';
+}
+
+.emotion-9 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
-.emotion-6 code {
+.emotion-11 code {
   font: var(--echoes-typography-code-default);
   background: rgb(252,252,253);
   color: rgb(51,53,60);
 }
 
-.emotion-6 code.hljs {
+.emotion-11 code.hljs {
   padding: unset;
 }
 
-.emotion-6 .hljs-meta,
-.emotion-6 .hljs-variable {
+.emotion-11 .hljs-meta,
+.emotion-11 .hljs-variable {
   color: rgb(51,53,60);
 }
 
-.emotion-6 .hljs-doctag,
-.emotion-6 .hljs-title,
-.emotion-6 .hljs-title.class_,
-.emotion-6 .hljs-title.function_ {
+.emotion-11 .hljs-doctag,
+.emotion-11 .hljs-title,
+.emotion-11 .hljs-title.class_,
+.emotion-11 .hljs-title.function_ {
   color: rgb(34,84,192);
 }
 
-.emotion-6 .hljs-comment {
+.emotion-11 .hljs-comment {
   font: var(--echoes-typography-code-comment);
   color: rgb(109,111,119);
 }
 
-.emotion-6 .hljs-keyword,
-.emotion-6 .hljs-tag,
-.emotion-6 .hljs-type {
+.emotion-11 .hljs-keyword,
+.emotion-11 .hljs-tag,
+.emotion-11 .hljs-type {
   color: rgb(152,29,150);
 }
 
-.emotion-6 .hljs-literal,
-.emotion-6 .hljs-number {
+.emotion-11 .hljs-literal,
+.emotion-11 .hljs-number {
   color: rgb(126,83,5);
 }
 
-.emotion-6 .hljs-string {
+.emotion-11 .hljs-string {
   color: rgb(32,105,31);
 }
 
-.emotion-6 .hljs-meta .hljs-keyword {
+.emotion-11 .hljs-meta .hljs-keyword {
   color: rgb(47,103,48);
 }
 
-.emotion-6 .sonar-underline {
+.emotion-11 .sonar-underline {
   -webkit-text-decoration: underline rgb(253,162,155);
   text-decoration: underline rgb(253,162,155);
   -webkit-text-decoration: underline rgb(253,162,155) wavy;
@@ -149,17 +175,17 @@ exports[`should highlight code content correctly 1`] = `
   text-decoration-skip-ink: none;
 }
 
-.emotion-6.code-wrap {
+.emotion-11.code-wrap {
   white-space: pre-wrap;
   word-break: break-all;
 }
 
-.emotion-6.code-wrap.wrap-words {
+.emotion-11.code-wrap.wrap-words {
   word-break: normal;
   overflow-wrap: break-word;
 }
 
-.emotion-6 mark {
+.emotion-11 mark {
   font-weight: 400;
   padding: 0.25rem;
   border-radius: 0.25rem;
@@ -172,32 +198,28 @@ exports[`should highlight code content correctly 1`] = `
     class="sw-code fs-mask emotion-0 emotion-1"
   >
     <button
-      aria-describedby="tooltip-3"
-      class="sw-select-none emotion-2 emotion-3 emotion-4 emotion-5"
-      data-clipboard-text="<prop>foobar<prop>"
+      class="sw-select-none emotion-2 emotion-3 emotion-4"
+      data-state="closed"
       type="button"
     >
-      <svg
-        aria-hidden="true"
-        class="octicon octicon-copy"
-        fill="currentColor"
-        focusable="false"
-        height="16"
-        style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
-        viewBox="0 0 16 16"
-        width="16"
+      <span
+        class="emotion-5 emotion-6"
       >
-        <path
-          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
-        />
-        <path
-          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
-        />
-      </svg>
-      Copy
+        <span
+          aria-hidden="true"
+          class="emotion-7 emotion-8"
+        >
+          
+        </span>
+        <span
+          class="emotion-9 emotion-10"
+        >
+          Copy
+        </span>
+      </span>
     </button>
     <span
-      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-6 emotion-7"
+      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-11 emotion-12"
     >
       <pre>
         &lt;prop&gt;foobar&lt;prop&gt;
@@ -223,16 +245,8 @@ exports[`should show full size when multiline with no editing 1`] = `
   border-radius: 0.25rem;
 }
 
-.emotion-4 {
+.emotion-3 {
   box-sizing: border-box;
-  -webkit-text-decoration: none;
-  text-decoration: none;
-  outline: none;
-  border: var(--border);
-  color: var(--color);
-  background-color: var(--background);
-  -webkit-transition: background-color 0.2s ease;
-  transition: background-color 0.2s ease;
   display: -webkit-inline-box;
   display: -webkit-inline-flex;
   display: -ms-inline-flexbox;
@@ -241,19 +255,28 @@ exports[`should show full size when multiline with no editing 1`] = `
   -webkit-box-align: center;
   -ms-flex-align: center;
   align-items: center;
-  height: 2.25rem;
-  font: var(--echoes-typography-text-default-semi-bold);
-  padding-left: 1rem;
-  padding-right: 1rem;
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-  border-radius: 0.5rem;
+  padding: var(--echoes-dimension-space-0) var(--button-padding);
+  height: var(--button-height);
+  min-height: var(--button-height);
+  overflow: hidden;
+  font: var(--echoes-typography-others-label);
+  color: var(--button-color);
+  -webkit-text-decoration: none;
+  text-decoration: none;
+  background-color: var(--button-background);
+  border: var(--button-border);
+  border-radius: var(--echoes-border-radius-400);
+  outline: none;
   cursor: pointer;
-  --background: rgb(255,255,255);
-  --backgroundHover: rgb(239,242,249);
-  --color: rgb(62,67,87);
-  --focus: rgba(197,205,223,0.2);
-  --border: 1px solid rgb(197,205,223);
+  --button-color: var(--echoes-color-text-default);
+  --button-border: var(--echoes-color-border-bold) solid var(--echoes-border-width-default);
+  --button-background: var(--echoes-color-background-default);
+  --button-background-hover: var(--echoes-color-background-default-hover);
+  --button-background-active: var(--echoes-color-background-default-active);
+  --button-background-focus: var(--echoes-color-background-default);
+  --button-background-disabled: var(--echoes-color-background-disabled);
+  --button-padding: var(--echoes-dimension-space-150);
+  --button-height: var(--echoes-sizes-buttons-large);
   -webkit-user-select: none;
   -moz-user-select: none;
   -ms-user-select: none;
@@ -264,90 +287,115 @@ exports[`should show full size when multiline with no editing 1`] = `
   position: absolute;
 }
 
-.emotion-4:hover,
-.emotion-4:active {
-  color: var(--color);
-  background-color: var(--backgroundHover);
+.emotion-3:focus,
+.emotion-3:focus-visible {
+  background-color: var(--button-background-focus);
+  outline: var(--echoes-color-focus-default) solid var(--echoes-focus-border-width-default);
+  outline-offset: var(--echoes-focus-border-offset-default);
 }
 
-.emotion-4:focus,
-.emotion-4:active,
-.emotion-4:focus-visible {
-  color: var(--color);
+.emotion-3:hover {
+  background-color: var(--button-background-hover);
 }
 
-.emotion-4:focus-visible {
-  outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default);
-  outline-offset: var(--echoes-focus-border-offset-default);
+.emotion-3:active {
+  background-color: var(--button-background-active);
 }
 
-.emotion-4:disabled,
-.emotion-4:disabled:hover {
+.emotion-3:disabled,
+.emotion-3:disabled:has(:hover, :active, :focus, :focus-visible) {
   color: var(--echoes-color-text-disabled);
-  background-color: rgb(239,242,249);
-  border: 1px solid rgb(197,205,223);
+  background-color: var(--button-background-disabled);
+  border: none;
   cursor: not-allowed;
+  pointer-events: none;
 }
 
-.emotion-4>svg {
-  margin-right: 0.25rem;
+.code-snippet-highlighted-oneline .emotion-3 {
+  bottom: 0.5rem;
 }
 
-.emotion-4 [disabled] {
-  pointer-events: none;
+.emotion-5 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  gap: var(--echoes-dimension-space-75);
+  overflow: hidden;
 }
 
-.code-snippet-highlighted-oneline .emotion-4 {
-  bottom: 0.5rem;
+.emotion-7 {
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  display: inline-block;
+  font-size: calc(1em + 4px);
+  font-style: normal;
+  font-weight: normal;
+  height: calc(2em - 16px);
+  line-height: calc(2em - 16px);
+  text-align: center;
+  vertical-align: bottom;
+  width: calc(2em - 16px);
+  font-family: 'Material Symbols Rounded';
 }
 
-.emotion-6 code {
+.emotion-9 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.emotion-11 code {
   font: var(--echoes-typography-code-default);
   background: rgb(252,252,253);
   color: rgb(51,53,60);
 }
 
-.emotion-6 code.hljs {
+.emotion-11 code.hljs {
   padding: unset;
 }
 
-.emotion-6 .hljs-meta,
-.emotion-6 .hljs-variable {
+.emotion-11 .hljs-meta,
+.emotion-11 .hljs-variable {
   color: rgb(51,53,60);
 }
 
-.emotion-6 .hljs-doctag,
-.emotion-6 .hljs-title,
-.emotion-6 .hljs-title.class_,
-.emotion-6 .hljs-title.function_ {
+.emotion-11 .hljs-doctag,
+.emotion-11 .hljs-title,
+.emotion-11 .hljs-title.class_,
+.emotion-11 .hljs-title.function_ {
   color: rgb(34,84,192);
 }
 
-.emotion-6 .hljs-comment {
+.emotion-11 .hljs-comment {
   font: var(--echoes-typography-code-comment);
   color: rgb(109,111,119);
 }
 
-.emotion-6 .hljs-keyword,
-.emotion-6 .hljs-tag,
-.emotion-6 .hljs-type {
+.emotion-11 .hljs-keyword,
+.emotion-11 .hljs-tag,
+.emotion-11 .hljs-type {
   color: rgb(152,29,150);
 }
 
-.emotion-6 .hljs-literal,
-.emotion-6 .hljs-number {
+.emotion-11 .hljs-literal,
+.emotion-11 .hljs-number {
   color: rgb(126,83,5);
 }
 
-.emotion-6 .hljs-string {
+.emotion-11 .hljs-string {
   color: rgb(32,105,31);
 }
 
-.emotion-6 .hljs-meta .hljs-keyword {
+.emotion-11 .hljs-meta .hljs-keyword {
   color: rgb(47,103,48);
 }
 
-.emotion-6 .sonar-underline {
+.emotion-11 .sonar-underline {
   -webkit-text-decoration: underline rgb(253,162,155);
   text-decoration: underline rgb(253,162,155);
   -webkit-text-decoration: underline rgb(253,162,155) wavy;
@@ -356,17 +404,17 @@ exports[`should show full size when multiline with no editing 1`] = `
   text-decoration-skip-ink: none;
 }
 
-.emotion-6.code-wrap {
+.emotion-11.code-wrap {
   white-space: pre-wrap;
   word-break: break-all;
 }
 
-.emotion-6.code-wrap.wrap-words {
+.emotion-11.code-wrap.wrap-words {
   word-break: normal;
   overflow-wrap: break-word;
 }
 
-.emotion-6 mark {
+.emotion-11 mark {
   font-weight: 400;
   padding: 0.25rem;
   border-radius: 0.25rem;
@@ -379,33 +427,28 @@ exports[`should show full size when multiline with no editing 1`] = `
     class="sw-code fs-mask emotion-0 emotion-1"
   >
     <button
-      aria-describedby="tooltip-1"
-      class="sw-select-none emotion-2 emotion-3 emotion-4 emotion-5"
-      data-clipboard-text="foo
-bar"
+      class="sw-select-none emotion-2 emotion-3 emotion-4"
+      data-state="closed"
       type="button"
     >
-      <svg
-        aria-hidden="true"
-        class="octicon octicon-copy"
-        fill="currentColor"
-        focusable="false"
-        height="16"
-        style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
-        viewBox="0 0 16 16"
-        width="16"
+      <span
+        class="emotion-5 emotion-6"
       >
-        <path
-          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
-        />
-        <path
-          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
-        />
-      </svg>
-      Copy
+        <span
+          aria-hidden="true"
+          class="emotion-7 emotion-8"
+        >
+          
+        </span>
+        <span
+          class="emotion-9 emotion-10"
+        >
+          Copy
+        </span>
+      </span>
     </button>
     <span
-      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-6 emotion-7"
+      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-11 emotion-12"
     >
       <pre>
         foo
@@ -432,16 +475,8 @@ exports[`should show reduced size when single line with no editing 1`] = `
   border-radius: 0.25rem;
 }
 
-.emotion-4 {
+.emotion-3 {
   box-sizing: border-box;
-  -webkit-text-decoration: none;
-  text-decoration: none;
-  outline: none;
-  border: var(--border);
-  color: var(--color);
-  background-color: var(--background);
-  -webkit-transition: background-color 0.2s ease;
-  transition: background-color 0.2s ease;
   display: -webkit-inline-box;
   display: -webkit-inline-flex;
   display: -ms-inline-flexbox;
@@ -450,19 +485,28 @@ exports[`should show reduced size when single line with no editing 1`] = `
   -webkit-box-align: center;
   -ms-flex-align: center;
   align-items: center;
-  height: 2.25rem;
-  font: var(--echoes-typography-text-default-semi-bold);
-  padding-left: 1rem;
-  padding-right: 1rem;
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-  border-radius: 0.5rem;
+  padding: var(--echoes-dimension-space-0) var(--button-padding);
+  height: var(--button-height);
+  min-height: var(--button-height);
+  overflow: hidden;
+  font: var(--echoes-typography-others-label);
+  color: var(--button-color);
+  -webkit-text-decoration: none;
+  text-decoration: none;
+  background-color: var(--button-background);
+  border: var(--button-border);
+  border-radius: var(--echoes-border-radius-400);
+  outline: none;
   cursor: pointer;
-  --background: rgb(255,255,255);
-  --backgroundHover: rgb(239,242,249);
-  --color: rgb(62,67,87);
-  --focus: rgba(197,205,223,0.2);
-  --border: 1px solid rgb(197,205,223);
+  --button-color: var(--echoes-color-text-default);
+  --button-border: var(--echoes-color-border-bold) solid var(--echoes-border-width-default);
+  --button-background: var(--echoes-color-background-default);
+  --button-background-hover: var(--echoes-color-background-default-hover);
+  --button-background-active: var(--echoes-color-background-default-active);
+  --button-background-focus: var(--echoes-color-background-default);
+  --button-background-disabled: var(--echoes-color-background-disabled);
+  --button-padding: var(--echoes-dimension-space-150);
+  --button-height: var(--echoes-sizes-buttons-large);
   -webkit-user-select: none;
   -moz-user-select: none;
   -ms-user-select: none;
@@ -475,90 +519,115 @@ exports[`should show reduced size when single line with no editing 1`] = `
   top: 1rem;
 }
 
-.emotion-4:hover,
-.emotion-4:active {
-  color: var(--color);
-  background-color: var(--backgroundHover);
+.emotion-3:focus,
+.emotion-3:focus-visible {
+  background-color: var(--button-background-focus);
+  outline: var(--echoes-color-focus-default) solid var(--echoes-focus-border-width-default);
+  outline-offset: var(--echoes-focus-border-offset-default);
 }
 
-.emotion-4:focus,
-.emotion-4:active,
-.emotion-4:focus-visible {
-  color: var(--color);
+.emotion-3:hover {
+  background-color: var(--button-background-hover);
 }
 
-.emotion-4:focus-visible {
-  outline: var(--echoes-focus-border-width-default) solid var(--echoes-color-focus-default);
-  outline-offset: var(--echoes-focus-border-offset-default);
+.emotion-3:active {
+  background-color: var(--button-background-active);
 }
 
-.emotion-4:disabled,
-.emotion-4:disabled:hover {
+.emotion-3:disabled,
+.emotion-3:disabled:has(:hover, :active, :focus, :focus-visible) {
   color: var(--echoes-color-text-disabled);
-  background-color: rgb(239,242,249);
-  border: 1px solid rgb(197,205,223);
+  background-color: var(--button-background-disabled);
+  border: none;
   cursor: not-allowed;
+  pointer-events: none;
 }
 
-.emotion-4>svg {
-  margin-right: 0.25rem;
+.code-snippet-highlighted-oneline .emotion-3 {
+  bottom: 0.5rem;
 }
 
-.emotion-4 [disabled] {
-  pointer-events: none;
+.emotion-5 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  gap: var(--echoes-dimension-space-75);
+  overflow: hidden;
 }
 
-.code-snippet-highlighted-oneline .emotion-4 {
-  bottom: 0.5rem;
+.emotion-7 {
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  display: inline-block;
+  font-size: calc(1em + 4px);
+  font-style: normal;
+  font-weight: normal;
+  height: calc(2em - 16px);
+  line-height: calc(2em - 16px);
+  text-align: center;
+  vertical-align: bottom;
+  width: calc(2em - 16px);
+  font-family: 'Material Symbols Rounded';
+}
+
+.emotion-9 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
-.emotion-6 code {
+.emotion-11 code {
   font: var(--echoes-typography-code-default);
   background: rgb(252,252,253);
   color: rgb(51,53,60);
 }
 
-.emotion-6 code.hljs {
+.emotion-11 code.hljs {
   padding: unset;
 }
 
-.emotion-6 .hljs-meta,
-.emotion-6 .hljs-variable {
+.emotion-11 .hljs-meta,
+.emotion-11 .hljs-variable {
   color: rgb(51,53,60);
 }
 
-.emotion-6 .hljs-doctag,
-.emotion-6 .hljs-title,
-.emotion-6 .hljs-title.class_,
-.emotion-6 .hljs-title.function_ {
+.emotion-11 .hljs-doctag,
+.emotion-11 .hljs-title,
+.emotion-11 .hljs-title.class_,
+.emotion-11 .hljs-title.function_ {
   color: rgb(34,84,192);
 }
 
-.emotion-6 .hljs-comment {
+.emotion-11 .hljs-comment {
   font: var(--echoes-typography-code-comment);
   color: rgb(109,111,119);
 }
 
-.emotion-6 .hljs-keyword,
-.emotion-6 .hljs-tag,
-.emotion-6 .hljs-type {
+.emotion-11 .hljs-keyword,
+.emotion-11 .hljs-tag,
+.emotion-11 .hljs-type {
   color: rgb(152,29,150);
 }
 
-.emotion-6 .hljs-literal,
-.emotion-6 .hljs-number {
+.emotion-11 .hljs-literal,
+.emotion-11 .hljs-number {
   color: rgb(126,83,5);
 }
 
-.emotion-6 .hljs-string {
+.emotion-11 .hljs-string {
   color: rgb(32,105,31);
 }
 
-.emotion-6 .hljs-meta .hljs-keyword {
+.emotion-11 .hljs-meta .hljs-keyword {
   color: rgb(47,103,48);
 }
 
-.emotion-6 .sonar-underline {
+.emotion-11 .sonar-underline {
   -webkit-text-decoration: underline rgb(253,162,155);
   text-decoration: underline rgb(253,162,155);
   -webkit-text-decoration: underline rgb(253,162,155) wavy;
@@ -567,17 +636,17 @@ exports[`should show reduced size when single line with no editing 1`] = `
   text-decoration-skip-ink: none;
 }
 
-.emotion-6.code-wrap {
+.emotion-11.code-wrap {
   white-space: pre-wrap;
   word-break: break-all;
 }
 
-.emotion-6.code-wrap.wrap-words {
+.emotion-11.code-wrap.wrap-words {
   word-break: normal;
   overflow-wrap: break-word;
 }
 
-.emotion-6 mark {
+.emotion-11 mark {
   font-weight: 400;
   padding: 0.25rem;
   border-radius: 0.25rem;
@@ -590,32 +659,28 @@ exports[`should show reduced size when single line with no editing 1`] = `
     class="sw-code sw-py-6 code-snippet-highlighted-oneline fs-mask emotion-0 emotion-1"
   >
     <button
-      aria-describedby="tooltip-2"
-      class="sw-select-none emotion-2 emotion-3 emotion-4 emotion-5"
-      data-clipboard-text="foobar"
+      class="sw-select-none emotion-2 emotion-3 emotion-4"
+      data-state="closed"
       type="button"
     >
-      <svg
-        aria-hidden="true"
-        class="octicon octicon-copy"
-        fill="currentColor"
-        focusable="false"
-        height="16"
-        style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
-        viewBox="0 0 16 16"
-        width="16"
+      <span
+        class="emotion-5 emotion-6"
       >
-        <path
-          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
-        />
-        <path
-          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
-        />
-      </svg>
-      Copy
+        <span
+          aria-hidden="true"
+          class="emotion-7 emotion-8"
+        >
+          
+        </span>
+        <span
+          class="emotion-9 emotion-10"
+        >
+          Copy
+        </span>
+      </span>
     </button>
     <span
-      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-6 emotion-7"
+      class="hljs sw-overflow-auto sw-pr-24 sw-flex emotion-11 emotion-12"
     >
       <code>
         foobar
index 027b990fff6cce3f63dc5c7812f57f51bd93c5aa..e073810552ed01914808167611046d74ee0dfd31 100644 (file)
@@ -43,9 +43,9 @@ describe('ClipboardButton', () => {
 
     await user.click(screen.getByRole('button', { name: 'Copy' }));
 
-    expect(await screen.findByText('Copied')).toBeVisible();
+    expect(await screen.findByRole('tooltip', { name: 'Copied' })).toBeInTheDocument();
 
-    await waitForElementToBeRemoved(() => screen.queryByText('Copied'));
+    await waitForElementToBeRemoved(() => screen.queryByRole('tooltip', { name: 'Copied' }));
     jest.runAllTimers();
   });
 
@@ -74,9 +74,9 @@ describe('ClipboardIconButton', () => {
 
     await user.click(screen.getByRole('button', { name: 'Copy to clipboard' }));
 
-    expect(await screen.findByText('Copied')).toBeVisible();
+    expect(await screen.findByRole('tooltip', { name: 'Copied' })).toBeInTheDocument();
 
-    await waitForElementToBeRemoved(() => screen.queryByText('Copied'));
+    await waitForElementToBeRemoved(() => screen.queryByRole('tooltip', { name: 'Copied' }));
     jest.runAllTimers();
   });
 });
index 1745e74f2fe32dfdeb7b48a232699705ff5a5363..530880b86c6718ea1085da845c5381511fe4c5d3 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  Button,
+  ButtonIcon,
+  ButtonSize,
+  ButtonVariety,
+  IconCopy,
+  Tooltip,
+  TooltipProvider,
+} from '@sonarsource/echoes-react';
 import classNames from 'classnames';
-import Clipboard from 'clipboard';
-import React from 'react';
-import { INTERACTIVE_TOOLTIP_DELAY } from '../helpers/constants';
-import { ButtonSecondary } from '../sonar-aligned/components/buttons';
-import { DiscreetInteractiveIcon, InteractiveIcon, InteractiveIconSize } from './InteractiveIcon';
-import { Tooltip } from './Tooltip';
-import { CopyIcon } from './icons/CopyIcon';
-import { IconProps } from './icons/Icon';
+import { copy } from 'clipboard';
+import React, { ComponentProps, useCallback, useState } from 'react';
 
 const COPY_SUCCESS_NOTIFICATION_LIFESPAN = 1000;
 
-export interface State {
-  copySuccess: boolean;
-}
-
-interface RenderProps {
-  copySuccess: boolean;
-  setCopyButton: (node: HTMLElement | null) => void;
-}
-
-interface BaseProps {
-  children: (props: RenderProps) => React.ReactNode;
-}
-
-export class ClipboardBase extends React.PureComponent<BaseProps, State> {
-  private clipboard?: Clipboard;
-  private copyButton?: HTMLElement | null;
-  mounted = false;
-  state: State = { copySuccess: false };
-
-  componentDidMount() {
-    this.mounted = true;
-    if (this.copyButton) {
-      this.clipboard = new Clipboard(this.copyButton, {
-        container: this.copyButton.parentElement ?? undefined,
-      });
-      this.clipboard.on('success', this.handleSuccessCopy);
-    }
-  }
-
-  componentDidUpdate(props: BaseProps) {
-    if (this.props.children !== props.children) {
-      if (this.clipboard) {
-        this.clipboard.destroy();
-      }
-      if (this.copyButton) {
-        this.clipboard = new Clipboard(this.copyButton, {
-          container: this.copyButton.parentElement ?? undefined,
-        });
-        this.clipboard.on('success', this.handleSuccessCopy);
-      }
-    }
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-    if (this.clipboard) {
-      this.clipboard.destroy();
-    }
-  }
-
-  setCopyButton = (node: HTMLElement | null) => {
-    this.copyButton = node;
-  };
-
-  handleSuccessCopy = () => {
-    if (this.mounted) {
-      this.setState({ copySuccess: true });
-      setTimeout(() => {
-        if (this.mounted) {
-          this.setState({ copySuccess: false });
-        }
-      }, COPY_SUCCESS_NOTIFICATION_LIFESPAN);
-    }
-  };
-
-  render() {
-    return this.props.children({
-      setCopyButton: this.setCopyButton,
-      copySuccess: this.state.copySuccess,
-    });
-  }
-}
-
 interface ButtonProps {
   children?: React.ReactNode;
   className?: string;
@@ -111,41 +41,42 @@ interface ButtonProps {
   icon?: React.ReactNode;
 }
 
-export function ClipboardButton({
-  icon = <CopyIcon />,
-  className,
-  children,
-  copyValue,
-  copiedLabel = 'Copied',
-  copyLabel = 'Copy',
-}: ButtonProps) {
+export function ClipboardButton(props: ButtonProps) {
+  const {
+    icon = <IconCopy />,
+    className,
+    children,
+    copyValue,
+    copiedLabel = 'Copied',
+    copyLabel = 'Copy',
+  } = props;
+  const [copySuccess, handleCopy] = useCopyClipboardEffect(copyValue);
+
   return (
-    <ClipboardBase>
-      {({ setCopyButton, copySuccess }) => (
-        <Tooltip content={copiedLabel} visible={copySuccess}>
-          <ButtonSecondary
-            className={classNames('sw-select-none', className)}
-            data-clipboard-text={copyValue}
-            icon={icon}
-            ref={setCopyButton}
-          >
-            {children ?? copyLabel}
-          </ButtonSecondary>
-        </Tooltip>
-      )}
-    </ClipboardBase>
+    <TooltipProvider>
+      {/* TODO ^ Remove TooltipProvider after design-system is reintegrated into sonar-web */}
+      <Tooltip content={copiedLabel} isOpen={copySuccess}>
+        <Button
+          className={classNames('sw-select-none', className)}
+          onClick={handleCopy}
+          prefix={icon}
+        >
+          {children ?? copyLabel}
+        </Button>
+      </Tooltip>
+    </TooltipProvider>
   );
 }
 
 interface IconButtonProps {
-  Icon?: React.ComponentType<React.PropsWithChildren<IconProps>>;
+  Icon?: ComponentProps<typeof ButtonIcon>['Icon'];
   'aria-label'?: string;
   className?: string;
   copiedLabel?: string;
   copyLabel?: string;
   copyValue: string;
   discreet?: boolean;
-  size?: InteractiveIconSize;
+  size?: ButtonSize;
 }
 
 export function ClipboardIconButton(props: IconButtonProps) {
@@ -153,37 +84,49 @@ export function ClipboardIconButton(props: IconButtonProps) {
     className,
     copyValue,
     discreet,
-    size = 'small',
-    Icon = CopyIcon,
+    size = ButtonSize.Medium,
+    Icon = IconCopy,
     copiedLabel = 'Copied',
     copyLabel = 'Copy to clipboard',
   } = props;
-  const InteractiveIconComponent = discreet ? DiscreetInteractiveIcon : InteractiveIcon;
+
+  const [copySuccess, handleCopy] = useCopyClipboardEffect(copyValue);
 
   return (
-    <ClipboardBase>
-      {({ setCopyButton, copySuccess }) => {
-        return (
-          <Tooltip
-            content={
-              <div className="sw-w-abs-150 sw-text-center">
-                {copySuccess ? copiedLabel : copyLabel}
-              </div>
-            }
-            mouseEnterDelay={INTERACTIVE_TOOLTIP_DELAY}
-            {...(copySuccess ? { visible: copySuccess } : undefined)}
-          >
-            <InteractiveIconComponent
-              Icon={Icon}
-              aria-label={props['aria-label'] ?? copyLabel}
-              className={className}
-              data-clipboard-text={copyValue}
-              ref={setCopyButton}
-              size={size}
-            />
-          </Tooltip>
-        );
-      }}
-    </ClipboardBase>
+    <TooltipProvider>
+      {/* TODO ^ Remove TooltipProvider after design-system is reintegrated into sonar-web */}
+      <ButtonIcon
+        Icon={Icon}
+        ariaLabel={props['aria-label'] ?? copyLabel}
+        className={className}
+        onClick={handleCopy}
+        size={size}
+        tooltipContent={copySuccess ? copiedLabel : copyLabel}
+        tooltipOptions={copySuccess ? { isOpen: copySuccess } : undefined}
+        variety={discreet ? ButtonVariety.DefaultGhost : ButtonVariety.Default}
+      />
+    </TooltipProvider>
   );
 }
+
+export function useCopyClipboardEffect(copyValue: string) {
+  const [copySuccess, setCopySuccess] = useState(false);
+
+  const handleCopy = useCallback(
+    ({ currentTarget }: React.MouseEvent<HTMLButtonElement>) => {
+      const isSuccess = copy(copyValue) === copyValue;
+      setCopySuccess(isSuccess);
+
+      if (isSuccess) {
+        setTimeout(() => {
+          setCopySuccess(false);
+        }, COPY_SUCCESS_NOTIFICATION_LIFESPAN);
+      }
+
+      currentTarget.focus();
+    },
+    [copyValue],
+  );
+
+  return [copySuccess, handleCopy] as const;
+}
index 5172425030dd76cb2848bdf3649e7bf5b051cb5e..abfad7d141493b60785d4f14e03437629d795fff 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ClipboardIconButton, IssueMessageHighlighting, LinkIcon, Title } from 'design-system';
+import { IconLink } from '@sonarsource/echoes-react';
+import { ClipboardIconButton, IssueMessageHighlighting, Title } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
 import { getPathUrlAsString, getRuleUrl } from '../../../helpers/urls';
@@ -44,7 +45,7 @@ export default function RuleDetailsMeta(props: Readonly<Props>) {
         <Title className="sw-mb-0">
           <IssueMessageHighlighting message={ruleDetails.name} />
           <ClipboardIconButton
-            Icon={LinkIcon}
+            Icon={IconLink}
             aria-label={translate('permalink')}
             className="sw-ml-1 sw-align-bottom"
             copyValue={getPathUrlAsString(ruleUrl, ruleDetails.isExternal)}
index f249c8f48e47f6915bb2c429a3906cff6a600f40..e16b6bb0182821e11298f83b9a305d88da49be87 100644 (file)
@@ -42,10 +42,6 @@ it('renders correctly', async () => {
 
   // Title
   expect(byRole('heading', { name: issue.message }).get()).toBeInTheDocument();
-  expect(byRole('button', { name: 'permalink' }).get()).toHaveAttribute(
-    'data-clipboard-text',
-    'http://localhost/project/issues?issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject',
-  );
 
   // CCT attribute
   const cctBadge = byText(
index c9d1aa14aa0a168521cd01d6cbc6b544d75d1547..452e18f2faa2131f9b57d83bfd31597414299f10 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { IconLink } from '@sonarsource/echoes-react';
 import {
   Badge,
   BasicSeparator,
   ClipboardIconButton,
   IssueMessageHighlighting,
   Link,
-  LinkIcon,
   Note,
   PageContentFontWrapper,
 } from 'design-system';
@@ -172,7 +172,7 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
                   messageFormattings={issue.messageFormattings}
                 />
                 <ClipboardIconButton
-                  Icon={LinkIcon}
+                  Icon={IconLink}
                   aria-label={translate('permalink')}
                   className="sw-ml-1 sw-align-bottom"
                   copyValue={getPathUrlAsString(issueUrl, false)}
index 34fd6605f0f78c6180c0840bca99e1325f6542c5..efa7c66293e65dd0c5afe7b2528e94dc83efeb06 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { IconLink } from '@sonarsource/echoes-react';
 import {
   ClipboardIconButton,
   IssueMessageHighlighting,
   LightLabel,
   LightPrimary,
   Link,
-  LinkIcon,
   StyledPageTitle,
 } from 'design-system';
 import React from 'react';
@@ -73,7 +73,7 @@ export function HotspotHeader(props: HotspotHeaderProps) {
               <IssueMessageHighlighting message={message} messageFormattings={messageFormattings} />
             </LightPrimary>
             <ClipboardIconButton
-              Icon={LinkIcon}
+              Icon={IconLink}
               copiedLabel={translate('copied_action')}
               copyLabel={translate('copy_to_clipboard')}
               className="sw-ml-2"
index fca8c5f30a17ac71f4697864e39ed0607173e58f..cc652ed632366c0ba49bdd1ca31a58cae839cc68 100644 (file)
@@ -19,7 +19,7 @@
  */
 import userEvent from '@testing-library/user-event';
 import React from 'react';
-import { byRole } from '~sonar-aligned/helpers/testSelector';
+import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
 import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock';
 import { renderComponent } from '../../../../helpers/testReactTestingUtils';
 import EncryptionApp from '../EncryptionApp';
@@ -50,7 +50,7 @@ it('should be able to generate new key', async () => {
 
   expect(await ui.appHeading.find()).toBeInTheDocument();
   await user.click(ui.generateSecretButton.get());
-  expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'secretKey');
+  expect(byText('secretKey').get()).toBeInTheDocument();
 });
 
 it('should be able to encrypt property value when secret is registered', async () => {
@@ -61,11 +61,11 @@ it('should be able to encrypt property value when secret is registered', async (
   expect(await ui.appHeading.find()).toBeInTheDocument();
   await user.type(ui.encryptTextarea.get(), 'sonar.announcement.message');
   await user.click(ui.encryptButton.get());
-  expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'encryptedValue');
+  expect(byText('encryptedValue').get()).toBeInTheDocument();
 
   // can generate new secret in view
   await user.click(ui.generateNewSecretButton.get());
-  expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'secretKey');
+  expect(byText('secretKey').get()).toBeInTheDocument();
 });
 
 function renderEncryptionApp() {
index e0e59af446ce4a2b416b65cc2c8a027cda6ee446..756edf56855dab80a4730df14211a4351db53e58 100644 (file)
@@ -39,10 +39,7 @@ describe('System Info Standalone', () => {
     renderSystemApp();
     await ui.appIsLoaded();
 
-    expect(ui.copyIdInformation.get()).toHaveAttribute(
-      'data-clipboard-text',
-      expect.stringContaining(`Server ID: asd564-asd54a-5dsfg45`),
-    );
+    expect(byText('asd564-asd54a-5dsfg45').get()).toBeInTheDocument();
 
     expect(ui.sectionButton('System').get()).toBeInTheDocument();
     expect(screen.queryByRole('cell', { name: 'High Availability' })).not.toBeInTheDocument();
@@ -103,10 +100,7 @@ describe('System Info Cluster', () => {
     expect(ui.downloadLogsButton.query()).not.toBeInTheDocument();
     expect(ui.downloadSystemInfoButton.get()).toBeInTheDocument();
 
-    expect(ui.copyIdInformation.get()).toHaveAttribute(
-      'data-clipboard-text',
-      expect.stringContaining(`Server ID: asd564-asd54a-5dsfg45`),
-    );
+    expect(byText('asd564-asd54a-5dsfg45').get()).toBeInTheDocument();
 
     // Renders health checks
     expect(ui.healthCauseWarning.get()).toBeInTheDocument();
index c649604f45bf55d259092025384b1ea7b3780e17..e5ea1b7114af6e4c72acea5e075de71fb4aa9373 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { queryHelpers, screen, within } from '@testing-library/react';
+import { screen, within } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import * as React from 'react';
 import { byLabelText } from '~sonar-aligned/helpers/testSelector';
@@ -79,23 +79,13 @@ it('should show a permalink on line number', async () => {
   );
 
   expect(
-    /* eslint-disable-next-line testing-library/prefer-presence-queries */
-    queryHelpers.queryByAttribute(
-      'data-clipboard-text',
-      row,
-      'http://localhost/code?id=foo&selected=foo%3Atest1.js&line=1',
-    ),
+    rowScreen.getByRole('menuitem', { name: 'source_viewer.copy_permalink' }),
   ).toBeInTheDocument();
 
   await user.keyboard('[Escape]');
 
   expect(
-    /* eslint-disable-next-line testing-library/prefer-presence-queries */
-    queryHelpers.queryByAttribute(
-      'data-clipboard-text',
-      row,
-      'http://localhost/code?id=foo&selected=foo%3Atest1.js&line=1',
-    ),
+    rowScreen.queryByRole('menuitem', { name: 'source_viewer.copy_permalink' }),
   ).not.toBeInTheDocument();
 
   row = await screen.findByRole('row', { name: / \* 6$/ });
index 13b851236a720acdaea17d64cc98f9b303287ae8..7837ca6c18fd5e6db6ddb3353429760e3e1a92f4 100644 (file)
@@ -150,7 +150,9 @@ function assertServiceEndpointStepIsCorrectlyRendered() {
       name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
     }),
   ).toBeInTheDocument();
-  expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toBe('https://sonarqube.example.com/');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy to clipboard', inlineSnippet: true })).toBe(
+    'https://sonarqube.example.com/',
+  );
   expect(
     screen.getByRole('button', { name: 'onboarding.token.generate.long' }),
   ).toBeInTheDocument();
@@ -163,26 +165,34 @@ function assertDotNetStepIsCorrectlyRendered() {
     }),
   ).toBeInTheDocument();
 
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo');
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true })).toBe(
+    'foo',
+  );
 }
 
 function assertMavenStepIsCorrectlyRendered() {
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('maven, copy additional properties');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'maven, copy additional properties',
+  );
 }
 
 function assertGradleStepIsCorrectlyRendered() {
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('gradle, copy additional properties');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'gradle, copy additional properties',
+  );
 }
 
 function assertObjCStepIsCorrectlyRendered(os: string, arch: string = 'x86_64') {
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     `objectivec ${os} ${arch}, copy shell script`,
   );
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo');
-  expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
-    `objectivec ${os} ${arch}, copy additional properties`,
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true })).toBe(
+    'foo',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(
+    getCopyToClipboardValue({ i: 2, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot(`objectivec ${os} ${arch}, copy additional properties`);
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     `objectivec ${os} ${arch}, copy build-wrapper command`,
   );
 }
@@ -192,20 +202,24 @@ function assertAutomaticCppStepIsCorrectlyRendered() {
 }
 
 function assertManualCppStepIsCorrectlyRendered(os: string, arch: string = 'x86_64') {
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     `manual-cpp ${os} ${arch}, copy shell script`,
   );
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo');
-  expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
-    `manual-cpp ${os} ${arch}, copy additional properties`,
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true })).toBe(
+    'foo',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(
+    getCopyToClipboardValue({ i: 2, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot(`manual-cpp ${os} ${arch}, copy additional properties`);
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     `manual-cpp ${os} ${arch}, copy build-wrapper command`,
   );
 }
 
 function assertOtherStepIsCorrectlyRendered() {
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toBe('foo');
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true })).toBe(
+    'foo',
+  );
 }
 
 function assertFinishStepIsCorrectlyRendered() {
index 54ecf78154a08c89a15b650fd3ec32b3309d5317..e4a6c586fdee6e536237618e359d72eaf9e32d4e 100644 (file)
@@ -30,6 +30,7 @@ import { AlmKeys } from '../../../../types/alm-settings';
 import { Feature } from '../../../../types/features';
 import {
   getCommonNodes,
+  getCopyToClipboardHostURLValue,
   getCopyToClipboardValue,
   getTutorialActionButtons,
   getTutorialBuildButtons,
@@ -64,81 +65,97 @@ it('should follow and complete all steps', async () => {
   expect(await ui.secretsStepTitle.find()).toBeInTheDocument();
 
   // Env variables step
-  expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key');
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key');
-  expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
+  expect(
+    getCopyToClipboardValue({ i: 0, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot('sonar token key');
+  expect(
+    getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot('sonarqube host url key');
+  expect(getCopyToClipboardHostURLValue({ i: 2, name: 'Copy to clipboard' })).toMatchSnapshot(
     'sonarqube host url value',
   );
 
   // Create/update configuration file step
   // Maven
   await user.click(ui.mavenBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: bitbucket-pipelines.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Maven: bitbucket-pipelines.yml',
+  );
 
   // Gradle
   await user.click(ui.gradleBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('Groovy: build.gradle');
   await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: bitbucket-pipelines.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Kotlin: build.gradle.kts',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Gradle: bitbucket-pipelines.yml',
+  );
 
   // .NET
   await user.click(ui.dotnetBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: bitbucket-pipelines.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    '.NET: bitbucket-pipelines.yml',
+  );
 
   // Cpp
   await user.click(ui.cppBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: bitbucket-pipelines.yml',
   );
 
   // Cpp (manual)
   await user.click(ui.autoConfigManual.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual) and Objective-C: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual) and Objective-C: bitbucket-pipelines.yml',
   );
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual arm64) and Objective-C: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual arm64) and Objective-C: bitbucket-pipelines.yml',
   );
 
   // Objective-C
   await user.click(ui.objCBuildButton.get());
   await user.click(ui.x86_64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual) and Objective-C: bitbucket-pipelines.yml',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual) and Objective-C: sonar-project.properties',
   );
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual arm64) and Objective-C: bitbucket-pipelines.yml',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (manual arm64) and Objective-C: sonar-project.properties',
   );
 
   // Dart
   await user.click(ui.dartBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart: sonar-project.properties');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Dart: bitbucket-pipelines.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart: sonar-project.properties',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Dart: bitbucket-pipelines.yml',
+  );
 
   // Other
   await user.click(ui.otherBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: .github/workflows/build.yml',
   );
 
@@ -154,7 +171,7 @@ it('should generate/delete a new token or use existing one', async () => {
   // Generate token
   await user.click(ui.genTokenDialogButton.get());
   await user.click(ui.generateTokenButton.get());
-  expect(getCopyToClipboardValue()).toEqual('generatedtoken2');
+  expect(getCopyToClipboardValue({ inlineSnippet: true })).toEqual('generatedtoken2');
 
   // Revoke current token and create new one
   await user.click(ui.deleteTokenButton.get());
index f4549f870878b222ceb9b4a6d1322caf8fdd87d4..797e6fd2b08f93b42d211d94859a882602efa16f 100644 (file)
@@ -30,8 +30,7 @@ pipelines:
 `;
 
 exports[`should follow and complete all steps: C++ (automatic) and other: .github/workflows/build.yml 1`] = `
-"
-definitions:
+"definitions:
   steps:
     - step: &build-step
         name: SonarQube analysis
@@ -58,8 +57,7 @@ pipelines:
 `;
 
 exports[`should follow and complete all steps: C++ (automatic) and other: bitbucket-pipelines.yml 1`] = `
-"
-definitions:
+"definitions:
   steps:
     - step: &build-step
         name: SonarQube analysis
index 562ef915d5ebee7e15127705fbf9acef7185ba8d..12c20815c999f4dd517e1d9f3e328e7e89890e6a 100644 (file)
@@ -30,6 +30,7 @@ import { AlmKeys } from '../../../../types/alm-settings';
 import { Feature } from '../../../../types/features';
 import {
   getCommonNodes,
+  getCopyToClipboardHostURLValue,
   getCopyToClipboardValue,
   getTutorialActionButtons,
   getTutorialBuildButtons,
@@ -62,90 +63,106 @@ it('should follow and complete all steps', async () => {
   expect(await ui.secretsStepTitle.find()).toBeInTheDocument();
 
   // Env variables step
-  expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key');
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key');
-  expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
+  expect(
+    getCopyToClipboardValue({ i: 0, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot('sonar token key');
+  expect(
+    getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard', inlineSnippet: true }),
+  ).toMatchSnapshot('sonarqube host url key');
+  expect(getCopyToClipboardHostURLValue({ i: 2, name: 'Copy to clipboard' })).toMatchSnapshot(
     'sonarqube host url value',
   );
 
   // Create/update configuration file step
   // Maven
   await user.click(ui.mavenBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: .github/workflows/build.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Maven: .github/workflows/build.yml',
+  );
 
   // Gradle
   await user.click(ui.gradleBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('Groovy: build.gradle');
   await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: .github/workflows/build.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Kotlin: build.gradle.kts',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Gradle: .github/workflows/build.yml',
+  );
 
   // .NET
   await user.click(ui.dotnetBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: .github/workflows/build.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    '.NET: .github/workflows/build.yml',
+  );
 
   // Cpp
   await user.click(ui.cppBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: .github/workflows/build.yml',
   );
 
   await user.click(ui.autoConfigManual.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('C++: sonar-project.properties');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'C++: sonar-project.properties',
+  );
   await user.click(ui.linuxButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ Linux: .github/workflows/build.yml',
   );
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ Linux arm64: .github/workflows/build.yml',
   );
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ Windows: .github/workflows/build.yml',
   );
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ MacOS: .github/workflows/build.yml',
   );
 
   // Objective-C
   await user.click(ui.objCBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'Objective-C: sonar-project.properties',
   );
 
   await user.click(ui.linuxButton.get());
   await user.click(ui.x86_64Button.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'Objective-C Linux: .github/workflows/build.yml',
   );
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'Objective-C Linux arm64: .github/workflows/build.yml',
   );
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'Objective-C Windows: .github/workflows/build.yml',
   );
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'Objective-C MacOS: .github/workflows/build.yml',
   );
 
   // Dart
   await user.click(ui.dartBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart: .github/workflows/build.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart: .github/workflows/build.yml',
+  );
 
   // Other
   await user.click(ui.otherBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: sonar-project.properties',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'C++ (automatic) and other: .github/workflows/build.yml',
   );
 
@@ -161,7 +178,7 @@ it('should generate/delete a new token or use existing one', async () => {
   // Generate token
   await user.click(ui.genTokenDialogButton.get());
   await user.click(ui.generateTokenButton.get());
-  expect(getCopyToClipboardValue()).toEqual('generatedtoken2');
+  expect(getCopyToClipboardValue({ inlineSnippet: true })).toEqual('generatedtoken2');
 
   // Revoke current token and create new one
   await user.click(ui.deleteTokenButton.get());
index bc5a71eb7fcd640ed1b4374eacafe80bd05a8696..e3cd4f02c539c5f9e6224143d3f4c45a0f6ef2ac 100644 (file)
@@ -1,35 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should follow and complete all steps: Dart: .github/workflows/build.yml 1`] = `
-"
-name: Build
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    types: [opened, synchronize, reopened]
-
-jobs:
-  build:
-    name: Build and analyze
-    runs-on: ubuntu-latest
-    steps:
-      - <commands to build your project>
-      - name: Download sonar-scanner
-        run: |
-          curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.1.0.4477-linux-x64.zip
-          unzip $HOME/.sonar/sonar-scanner.zip -o -d $HOME/.sonar/
-      - name: Run sonar-scanner
-        env:
-          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
-          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
-        run: |
-          sonar-scanner-6.1.0.4477-linux-x64/bin/sonar-scanner \\
-            -Dsonar.projectKey=my-project"
-`;
-
 exports[`should follow and complete all steps: .NET: .github/workflows/build.yml 1`] = `
 "name: Build
 
@@ -297,6 +267,35 @@ jobs:
 
 exports[`should follow and complete all steps: C++: sonar-project.properties 1`] = `"sonar.projectKey=my-project"`;
 
+exports[`should follow and complete all steps: Dart: .github/workflows/build.yml 1`] = `
+"name: Build
+
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  build:
+    name: Build and analyze
+    runs-on: ubuntu-latest
+    steps:
+      - <commands to build your project>
+      - name: Download sonar-scanner
+        run: |
+          curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.1.0.4477-linux-x64.zip
+          unzip $HOME/.sonar/sonar-scanner.zip -o -d $HOME/.sonar/
+      - name: Run sonar-scanner
+        env:
+          SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }}
+          SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}
+        run: |
+          sonar-scanner-6.1.0.4477-linux-x64/bin/sonar-scanner \\
+            -Dsonar.projectKey=my-project"
+`;
+
 exports[`should follow and complete all steps: Gradle: .github/workflows/build.yml 1`] = `
 "name: Build
 
index 26fec42cb080a8d3ac90ff44a9a46ece9c52d8ee..93c062e5fa6e8836d90b369d20836678b2b33eff 100644 (file)
@@ -26,6 +26,7 @@ import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUt
 import { byRole } from '../../../../sonar-aligned/helpers/testSelector';
 import {
   getCommonNodes,
+  getCopyToClipboardHostURLValue,
   getCopyToClipboardValue,
   getTutorialActionButtons,
   getTutorialBuildButtons,
@@ -56,47 +57,61 @@ it('should follow and complete all steps', async () => {
   expect(await ui.secretsStepTitle.find()).toBeInTheDocument();
 
   // Env variables step
-  expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key');
-  expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key');
-  expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy to clipboard' })).toMatchSnapshot(
+    'sonar token key',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy to clipboard' })).toMatchSnapshot(
+    'sonarqube host url key',
+  );
+  expect(getCopyToClipboardHostURLValue({ i: 2, name: 'Copy to clipboard' })).toMatchSnapshot(
     'sonarqube host url value',
   );
 
   // Create/update configuration file step
   // Maven
   await user.click(ui.mavenBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: pom.xml');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Maven: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('Maven: pom.xml');
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot('Maven: gitlab-ci.yml');
 
   // Gradle
   await user.click(ui.gradleBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('Groovy: build.gradle');
   await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Kotlin: build.gradle.kts',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot('Gradle: gitlab-ci.yml');
 
   // .NET
   await user.click(ui.dotnetBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('.NET: gitlab-ci.yml');
 
   // C++/Objective-C
   await user.click(ui.cppBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CPP: sonar-project.properties');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CPP: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'CPP: sonar-project.properties',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot('CPP: gitlab-ci.yml');
 
   // c++ manual config
   await user.click(ui.autoConfigManual.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CPP - manual: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'CPP - manual: gitlab-ci.yml',
+  );
 
   // Dart
   await user.click(ui.dartBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart: sonar-project.properties');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Dart: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart: sonar-project.properties',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot('Dart: gitlab-ci.yml');
 
   // Other
   await user.click(ui.otherBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: gitlab-ci.yml');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Other: sonar-project.properties',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot('Other: gitlab-ci.yml');
 
   expect(ui.allSetSentence.get()).toBeInTheDocument();
 });
@@ -110,7 +125,7 @@ it('should generate/delete a new token or use existing one', async () => {
   // Generate token
   await user.click(ui.genTokenDialogButton.get());
   await user.click(ui.generateTokenButton.get());
-  expect(getCopyToClipboardValue()).toEqual('generatedtoken2');
+  expect(getCopyToClipboardValue({ inlineSnippet: true })).toEqual('generatedtoken2');
 
   // Revoke current token and create new one
   await user.click(ui.deleteTokenButton.get());
index 61ccedae4bd34965014a9312f3b886d2e4913f1d..00da86bece43d60f98cd58a9ba308a5ae266cfaa 100644 (file)
@@ -99,101 +99,121 @@ it.each([AlmKeys.BitbucketCloud, AlmKeys.BitbucketServer, AlmKeys.GitHub, AlmKey
 
     // 3. Multibranch Pipeline Job
     expect(ui.multiBranchPipelineSecondListItem(alm).get()).toBeInTheDocument();
-    expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`ref spec`);
+    expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(`ref spec`);
 
     // 4. Create DevOps platform webhook
     expect(ui.webhookStepTitle(alm).get()).toBeInTheDocument();
-    expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(`jenkins url`);
+    expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(`jenkins url`);
 
     // 5. Create jenkinsfile
     // Maven
     await user.click(ui.mavenBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`maven jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(`maven jenkinsfile`);
 
     // Gradle (Groovy)
     await user.click(ui.gradleBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`Groovy: build.gradle file`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `Groovy: build.gradle file`,
+    );
     // Gradle(Kotlin)
     await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`Kotlin: build.gradle.kts file`);
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`gradle jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `Kotlin: build.gradle.kts file`,
+    );
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(`gradle jenkinsfile`);
 
     // .NET
     await user.click(ui.dotnetBuildButton.get());
     await user.click(ui.windowsDotnetCoreButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`windows dotnet core jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `windows dotnet core jenkinsfile`,
+    );
 
     await user.click(ui.windowsDotnetFrameworkButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
       `windows dotnet framework jenkinsfile`,
     );
 
     await user.click(ui.linuxDotnetCoreButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`linux dotnet core jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `linux dotnet core jenkinsfile`,
+    );
 
     // C++ (automatic)
     await user.click(ui.cppBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
       `c++ (automatic and other): build tools sonar-project.properties code`,
     );
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (automatic and other): build tools jenkinsfile`,
     );
 
     // C++ (manual)
     await user.click(ui.autoConfigManual.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`sonar-project.properties code`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `sonar-project.properties code`,
+    );
 
     await user.click(ui.linuxButton.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (manual) and objectivec: linux jenkinsfile`,
     );
 
     await user.click(ui.arm64Button.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (manual) and objectivec: linux arm64 jenkinsfile`,
     );
 
     await user.click(ui.windowsButton.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (manual) and objectivec: windows jenkinsfile`,
     );
 
     await user.click(ui.macosButton.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (manual) and objectivec: macos jenkinsfile`,
     );
 
     // Objective-C
     await user.click(ui.objCBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`sonar-project.properties code`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `sonar-project.properties code`,
+    );
 
     await user.click(ui.linuxButton.get());
     await user.click(ui.x86_64Button.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`objectivec: linux jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+      `objectivec: linux jenkinsfile`,
+    );
 
     await user.click(ui.arm64Button.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `objectivec: linux arm64 jenkinsfile`,
     );
 
     await user.click(ui.windowsButton.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`objectivec: windows jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+      `objectivec: windows jenkinsfile`,
+    );
 
     await user.click(ui.macosButton.get());
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`objectivec: macos jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+      `objectivec: macos jenkinsfile`,
+    );
 
     // Dart
     await user.click(ui.dartBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(`Dart: sonar-project.properties`);
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(`Dart: jenkinsfile`);
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+      `Dart: sonar-project.properties`,
+    );
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(`Dart: jenkinsfile`);
 
     // Other
     await user.click(ui.otherBuildButton.get());
-    expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
       `c++ (automatic and other): build tools sonar-project.properties code`,
     );
-    expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+    expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
       `c++ (automatic and other): build tools jenkinsfile`,
     );
 
index 905eb73b915add26a5322e77a93d10c5ff64c304..b6f15c19ae8d6481f5ac610f96d39da183e63031 100644 (file)
@@ -35,6 +35,10 @@ jest.mock('../../../../api/settings', () => ({
   getAllValues: jest.fn().mockResolvedValue([]),
 }));
 
+jest.mock('clipboard', () => ({
+  copy: jest.fn(),
+}));
+
 const tokenMock = new UserTokensMock();
 
 afterEach(() => {
@@ -100,50 +104,66 @@ it('can choose build tools and copy provided settings', async () => {
 
   // Maven
   await user.click(ui.mavenBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('maven: execute scanner');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot('maven: execute scanner');
 
   // Gradle
   await user.click(ui.gradleBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('gradle: sonarqube plugin');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('gradle: execute scanner');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'gradle: sonarqube plugin',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'gradle: execute scanner',
+  );
 
   // Dotnet - Core
   await user.click(ui.dotnetBuildButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'dotnet core: install scanner globally',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('dotnet core: execute command 1');
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot('dotnet core: execute command 2');
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot('dotnet core: execute command 3');
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'dotnet core: execute command 1',
+  );
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+    'dotnet core: execute command 2',
+  );
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+    'dotnet core: execute command 3',
+  );
 
   // Dotnet - Framework
   await user.click(ui.dotnetFrameworkButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('dotnet framework: execute command 1');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('dotnet framework: execute command 2');
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot('dotnet framework: execute command 3');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'dotnet framework: execute command 1',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'dotnet framework: execute command 2',
+  );
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
+    'dotnet framework: execute command 3',
+  );
 
   // C++ - Automatic
   await user.click(ui.cppBuildButton.get());
   await user.click(ui.linuxButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other linux: download scanner',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other linux: execute scanner',
   );
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other linux arm64: download scanner',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other linux arm64: execute scanner',
   );
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other windows: execute scanner',
   );
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other macos: execute scanner',
   );
 
@@ -151,146 +171,170 @@ it('can choose build tools and copy provided settings', async () => {
   await user.click(ui.autoConfigManual.get());
   await user.click(ui.linuxButton.get());
   await user.click(ui.x86_64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot('c++ (manual) linux: execute scanner');
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+    'c++ (manual) linux: execute scanner',
+  );
 
   // C++ - Linux (ARM64)
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux arm64: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux arm64: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux arm64: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) linux arm64: execute scanner',
   );
 
   // C++ - Windows
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) windows: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) windows: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) windows: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) windows: execute scanner',
   );
 
   // C++ - MacOS
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) macos: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) macos: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'c++ (manual) macos: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot('c++ (manual) macos: execute scanner');
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+    'c++ (manual) macos: execute scanner',
+  );
 
   // Objective-C - Linux (x86_64)
   await user.click(ui.objCBuildButton.get());
   await user.click(ui.linuxButton.get());
   await user.click(ui.x86_64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('objective-c linux: download scanner');
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'objective-c linux: download scanner',
+  );
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot('objective-c linux: execute scanner');
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+    'objective-c linux: execute scanner',
+  );
 
   // Objective-C - Linux (ARM64)
   await user.click(ui.arm64Button.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux arm64: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux arm64: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux arm64: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
     'objective-c linux arm64: execute scanner',
   );
 
   // Objective-C - Windows
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'objective-c windows: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
     'objective-c windows: download scanner',
   );
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'objective-c windows: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
     'objective-c windows: execute scanner',
   );
 
   // Objective-C - MacOS
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'objective-c macos: download build wrapper',
   );
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('objective-c macos: download scanner');
-  expect(getCopyToClipboardValue(2, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'objective-c macos: download scanner',
+  );
+  expect(getCopyToClipboardValue({ i: 2, name: 'Copy' })).toMatchSnapshot(
     'objective-c macos: execute build wrapper',
   );
-  expect(getCopyToClipboardValue(3, 'Copy')).toMatchSnapshot('objective-c macos: execute scanner');
+  expect(getCopyToClipboardValue({ i: 3, name: 'Copy' })).toMatchSnapshot(
+    'objective-c macos: execute scanner',
+  );
 
   // Dart - Linux
   await user.click(ui.dartBuildButton.get());
   await user.click(ui.linuxButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart linux: download scanner');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Dart linux: execute scanner');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart linux: download scanner',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Dart linux: execute scanner',
+  );
 
   // Dart - Windows
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart windows: download scanner');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Dart windows: execute scanner');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart windows: download scanner',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Dart windows: execute scanner',
+  );
 
   // Dart - MacOS
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Dart macos: download scanner');
-  expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Dart macos: execute scanner');
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
+    'Dart macos: download scanner',
+  );
+  expect(getCopyToClipboardValue({ i: 1, name: 'Copy' })).toMatchSnapshot(
+    'Dart macos: execute scanner',
+  );
 
   // Other - Linux
   await user.click(ui.otherBuildButton.get());
   await user.click(ui.linuxButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other linux: execute scanner',
   );
 
   // Other - Windows
   await user.click(ui.windowsButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other windows: execute scanner',
   );
 
   // Other - MacOS
   await user.click(ui.macosButton.get());
-  expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(
+  expect(getCopyToClipboardValue({ i: 0, name: 'Copy' })).toMatchSnapshot(
     'c++ (automatic) and other macos: execute scanner',
   );
 });
index c2f727cd252fc00e1d8f25dd3f3e511203beb898..2aff448489b0f6a234d7453421f95a79642c08e2 100644 (file)
@@ -27,8 +27,30 @@ const CI_TRANSLATE_MAP: Partial<Record<TutorialModes, string>> = {
   [TutorialModes.GitLabCI]: 'gitlab_ci',
 };
 
-export function getCopyToClipboardValue(i = 0, name = 'copy_to_clipboard') {
-  return screen.getAllByRole('button', { name })[i].getAttribute('data-clipboard-text');
+interface GetCopyToClipboardValueArgs {
+  i?: number;
+  inlineSnippet?: boolean;
+  name?: string;
+}
+
+export function getCopyToClipboardValue({
+  i = 0,
+  inlineSnippet = false,
+  name = 'copy_to_clipboard',
+}: GetCopyToClipboardValueArgs = {}) {
+  const button = screen.getAllByRole('button', { name })[i];
+
+  return inlineSnippet
+    ? button.previousSibling?.firstChild?.textContent
+    : button.nextSibling?.firstChild?.textContent;
+}
+
+export function getCopyToClipboardHostURLValue({
+  i = 0,
+  name = 'copy_to_clipboard',
+}: Omit<GetCopyToClipboardValueArgs, 'inlineSnippet'> = {}) {
+  return screen.getAllByRole('button', { name })[i].nextSibling?.nextSibling?.firstChild
+    ?.textContent;
 }
 
 export function getCommonNodes(ci: TutorialModes) {