@@ -4,14 +4,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
44import { CopyType } from '../../../../clipboard/types' ;
55import { Muya } from '../../../../muya' ;
66
7- // Characterization coverage for the code-block copy button wiring
8- // (Code._listen, code.ts:104-134). copyHandler.spec only exercises the
9- // downstream COPY_CODE_CONTENT setData branch — the button-click →
10- // editor.clipboard.copy(CopyType.COPY_CODE_CONTENT, text) hookup is otherwise
11- // untested. The button is the `a.mu-code-copy` first child of the `.mu-code`
12- // node; clicking it copies the raw code text verbatim, and mousedown
13- // preventDefaults so the caret/selection does not move.
14-
157const bootedHosts : HTMLElement [ ] = [ ] ;
168
179beforeEach ( ( ) => {
@@ -24,6 +16,7 @@ afterEach(() => {
2416 host . remove ( ) ;
2517 }
2618 vi . restoreAllMocks ( ) ;
19+ vi . useRealTimers ( ) ;
2720} ) ;
2821
2922function bootMuya ( markdown : string , options : Record < string , unknown > = { } ) : Muya {
@@ -42,7 +35,7 @@ describe('code-block copy button', () => {
4235 const muya = bootMuya ( THREE_LINE_FENCE ) ;
4336 const copySpy = vi . spyOn ( muya . editor . clipboard , 'copy' ) . mockImplementation ( ( ) => { } ) ;
4437
45- const button = muya . domNode . querySelector < HTMLElement > ( 'a .mu-code-copy' ) ;
38+ const button = muya . domNode . querySelector < HTMLElement > ( '.mu-code-copy' ) ;
4639 expect ( button ) . not . toBeNull ( ) ;
4740
4841 button ! . dispatchEvent ( new MouseEvent ( 'click' , { bubbles : true } ) ) ;
@@ -58,17 +51,33 @@ describe('code-block copy button', () => {
5851 const muya = bootMuya ( '```js\nsolo line\n```\n' ) ;
5952 const copySpy = vi . spyOn ( muya . editor . clipboard , 'copy' ) . mockImplementation ( ( ) => { } ) ;
6053
61- const button = muya . domNode . querySelector < HTMLElement > ( 'a .mu-code-copy' ) ! ;
54+ const button = muya . domNode . querySelector < HTMLElement > ( '.mu-code-copy' ) ! ;
6255 button . dispatchEvent ( new MouseEvent ( 'click' , { bubbles : true } ) ) ;
6356
6457 expect ( copySpy ) . toHaveBeenCalledTimes ( 1 ) ;
6558 expect ( copySpy ) . toHaveBeenCalledWith ( CopyType . COPY_CODE_CONTENT , 'solo line' ) ;
6659 } ) ;
6760
61+ it . each ( [ 'false' , 'reject' ] ) ( 'restores the accessible label after a %s clipboard failure' , async ( failure ) => {
62+ vi . useFakeTimers ( ) ;
63+ const writer = failure === 'reject'
64+ ? vi . fn ( ) . mockRejectedValue ( new Error ( 'clipboard unavailable' ) )
65+ : vi . fn ( ) . mockResolvedValue ( false ) ;
66+ const muya = bootMuya ( THREE_LINE_FENCE , { clipboardWriteText : writer } ) ;
67+ const button = muya . domNode . querySelector < HTMLButtonElement > ( '.mu-code-copy' ) ! ;
68+ button . click ( ) ;
69+ await vi . advanceTimersByTimeAsync ( 0 ) ;
70+ expect ( button . getAttribute ( 'aria-label' ) ) . toBe ( muya . i18n . t ( 'Copy failed' ) ) ;
71+ expect ( button . dataset . copyFeedback ) . toBe ( muya . i18n . t ( 'Copy failed' ) ) ;
72+ await vi . advanceTimersByTimeAsync ( 2000 ) ;
73+ expect ( button . getAttribute ( 'aria-label' ) ) . toBe ( muya . i18n . t ( 'Copy content' ) ) ;
74+ expect ( button . dataset . copyFeedback ) . toBeUndefined ( ) ;
75+ } ) ;
76+
6877 it ( 'preventDefaults the mousedown so the caret/selection does not move' , ( ) => {
6978 const muya = bootMuya ( THREE_LINE_FENCE ) ;
7079
71- const button = muya . domNode . querySelector < HTMLElement > ( 'a .mu-code-copy' ) ! ;
80+ const button = muya . domNode . querySelector < HTMLElement > ( '.mu-code-copy' ) ! ;
7281 const mousedown = new MouseEvent ( 'mousedown' , { bubbles : true , cancelable : true } ) ;
7382 button . dispatchEvent ( mousedown ) ;
7483
0 commit comments